abi_definitions/memory/
allocation.rs

1//! # Memory Management Module
2//!
3//! This module provides low-level memory management functionality for the Xila operating system.
4//! It exposes a C-compatible ABI for memory allocation, deallocation, and memory operations
5//! that can be used by applications and other system components.
6//!
7//! ## Features
8//!
9//! - **Memory Allocation**: Allocate memory blocks with specific size, alignment, and capabilities
10//! - **Memory Deallocation**: Free allocated memory blocks
11//! - **Memory Reallocation**: Resize existing memory blocks
12//! - **Cache Management**: Flush data and instruction caches
13//! - **Memory Protection**: Support for read, write, and execute permissions
14//! - **Memory Capabilities**: Support for executable memory and DMA-capable memory
15//!
16//! ## Safety
17//!
18//! This module provides unsafe functions that directly manipulate memory. Callers must ensure:
19//! - Pointers are valid and properly aligned
20//! - Memory is not accessed after deallocation
21//! - Concurrent access is properly synchronized
22//!
23//! ## Example Usage
24//!
25//! ```c
26//! // Allocate 1024 bytes with 8-byte alignment
27//! void* ptr = xila_memory_allocate(NULL, 1024, 8, 0);
28//! if (ptr != NULL) {
29//!     // Use the memory...
30//!     xila_memory_deallocate(ptr);
31//! }
32//! ```
33#![allow(dead_code)]
34
35use core::ptr::null_mut;
36use core::{ffi::c_void, ptr::NonNull};
37use log::warning;
38use memory::CapabilityFlags;
39use synchronization::blocking_mutex::raw::CriticalSectionRawMutex;
40use synchronization::mutex::Mutex;
41use task::block_on;
42
43use crate::Allocated;
44
45#[repr(C)]
46union MaximumAlignment {
47    u: u128,
48    f: f64,
49    p: *const u8,
50}
51
52const MAXIMUM_ALIGNMENT: usize = align_of::<MaximumAlignment>();
53
54/// Memory protection flags that can be combined using bitwise OR.
55/// These flags determine what operations are allowed on memory regions.
56pub type XilaMemoryProtection = u8;
57
58/// Read permission flag - allows reading from memory
59#[unsafe(no_mangle)]
60pub static XILA_MEMORY_PROTECTION_READ: u8 = memory::Protection::READ_BIT;
61
62/// Write permission flag - allows writing to memory
63#[unsafe(no_mangle)]
64pub static XILA_MEMORY_PROTECTION_WRITE: u8 = memory::Protection::WRITE_BIT;
65
66/// Execute permission flag - allows executing code from memory
67#[unsafe(no_mangle)]
68pub static XILA_MEMORY_PROTECTION_EXECUTE: u8 = memory::Protection::EXECUTE_BIT;
69
70/// Memory capability flags that specify special requirements for allocated memory.
71/// These flags can be combined using bitwise OR to request multiple capabilities.
72pub type XilaMemoryCapabilities = u8;
73
74/// Executable capability - memory can be used for code execution
75#[unsafe(no_mangle)]
76pub static XILA_MEMORY_CAPABILITIES_EXECUTE: XilaMemoryCapabilities =
77    memory::CapabilityFlags::Executable.bits();
78
79/// Direct Memory Access (DMA) capability - memory is accessible by DMA controllers
80#[unsafe(no_mangle)]
81pub static XILA_MEMORY_CAPABILITIES_DIRECT_MEMORY_ACCESS: XilaMemoryCapabilities =
82    memory::CapabilityFlags::DirectMemoryAccess.bits();
83
84/// No special capabilities required - standard memory allocation
85#[unsafe(no_mangle)]
86pub static XILA_MEMORY_CAPABILITIES_NONE: XilaMemoryCapabilities = 0;
87
88static ALLOCATION_MUTEX: Mutex<CriticalSectionRawMutex, ()> = Mutex::new(());
89
90/// Converts a function that returns an `Option<NonNull<P>>` into a raw C-compatible pointer.
91///
92/// This utility function is used internally to convert Rust's safe pointer types
93/// into C-compatible raw pointers. Returns `null_mut()` if the function returns `None`.
94///
95/// # Type Parameters
96///
97/// * `F` - A function that returns `Option<NonNull<P>>`
98/// * `P` - The pointer type being converted
99///
100/// # Parameters
101///
102/// * `Function` - The function to execute and convert the result
103///
104/// # Returns
105///
106/// A raw C-compatible pointer, or null if the function returns `None`
107pub fn into_pointer<F, P>(function: F) -> *mut c_void
108where
109    F: FnOnce() -> Option<NonNull<P>>,
110{
111    match function() {
112        Some(pointer) => pointer.as_ptr() as *mut c_void,
113        None => null_mut(),
114    }
115}
116
117/// Deallocates a previously allocated memory block.
118///
119/// This function frees memory that was previously allocated using `xila_memory_allocate`
120/// or `xila_memory_reallocate`. It's safe to call this function with a null pointer.
121///
122/// # Safety
123///
124/// - The pointer must have been returned by a previous call to `xila_memory_allocate`
125///   or `xila_memory_reallocate`
126/// - The pointer must not be used after this function returns
127///
128/// # Parameters
129///
130/// * `Pointer` - Pointer to the memory block to deallocate, or null
131///
132/// # Examples
133///
134/// ```c
135/// void* ptr = xila_memory_allocate(NULL, 1024, 8, 0);
136/// xila_memory_deallocate(ptr); // Free the memory
137/// xila_memory_deallocate(NULL); // Safe - ignored
138/// ```
139#[unsafe(no_mangle)]
140pub unsafe extern "C" fn xila_memory_deallocate(pointer: *mut c_void) {
141    let _lock = block_on(ALLOCATION_MUTEX.lock());
142
143    let allocated = match unsafe { Allocated::from_user_pointer(pointer as *mut u8) } {
144        Some(alloc) => alloc,
145        None => {
146            return;
147        }
148    };
149
150    let layout = allocated.get_layout().unwrap();
151    let base_pointer = allocated.get_base_pointer();
152
153    unsafe {
154        memory::get_instance().deallocate(base_pointer, layout);
155    }
156}
157
158/// Reallocates a memory block to a new size.
159///
160/// This function changes the size of a previously allocated memory block. If the new size
161/// is larger, the additional memory is uninitialized. If the new size is smaller, the
162/// excess memory is freed. The original data is preserved up to the minimum of the old
163/// and new sizes.
164///
165/// # Safety
166///
167/// - If `Pointer` is not null, it must have been returned by a previous call to
168///   `xila_memory_allocate` or `xila_memory_reallocate`
169/// - The pointer must not be used after this function returns (use the returned pointer instead)
170/// - If the function returns null, the original pointer remains valid
171///
172/// # Parameters
173///
174/// * `Pointer` - Pointer to the memory block to reallocate, or null for new allocation
175/// * `Size` - New size in bytes (0 is equivalent to deallocation)
176///
177/// # Returns
178///
179/// - Pointer to the reallocated memory block
180/// - Null if allocation fails or if size is 0
181/// - If `Pointer` is null, behaves like `xila_memory_allocate` with default alignment
182///
183/// # Examples
184///
185/// ```c
186/// // Allocate initial memory
187/// void* ptr = xila_memory_reallocate(NULL, 1024);
188///
189/// // Expand the memory
190/// ptr = xila_memory_reallocate(ptr, 2048);
191///
192/// // Shrink the memory
193/// ptr = xila_memory_reallocate(ptr, 512);
194///
195/// // Free the memory
196/// xila_memory_reallocate(ptr, 0);
197/// ```
198#[unsafe(no_mangle)]
199pub unsafe extern "C" fn xila_memory_reallocate(pointer: *mut c_void, size: usize) -> *mut c_void {
200    let _lock = block_on(ALLOCATION_MUTEX.lock());
201
202    unsafe {
203        let pointer = pointer as *mut u8;
204
205        let allocated = match Allocated::from_user_pointer(pointer) {
206            Some(alloc) => alloc,
207            None => {
208                drop(_lock);
209                //trace!("xila_memory_reallocate called with null pointer, allocating new memory of size: {}", size);
210                return xila_memory_allocate_core(size);
211            }
212        };
213
214        let old_layout = allocated.get_layout().unwrap();
215        let new_layout = Allocated::get_layout_for_allocation(size, old_layout.align());
216
217        let pointer = memory::get_instance().reallocate(
218            allocated.get_base_pointer(),
219            old_layout,
220            new_layout.size(),
221        );
222
223        let allocated = match Allocated::from_layout(pointer, &new_layout) {
224            Some(alloc) => alloc,
225            None => {
226                warning!(
227                    "xila_memory_reallocate failed to create allocation metadata: New Size: {}, Old Size: {}",
228                    size,
229                    old_layout.size()
230                );
231                return null_mut();
232            }
233        };
234
235        allocated.get_user_pointer() as *mut c_void
236    }
237}
238
239/// Allocates a memory block with specified properties.
240///
241/// This function allocates a block of memory with the specified size, alignment,
242/// and capabilities. The memory is uninitialized and must be properly initialized
243/// before use.
244///
245/// # Safety
246///
247/// This function is unsafe because:
248/// - The returned memory is uninitialized
249/// - The caller must ensure proper deallocation
250/// - The hint address, if providalignment enumed, must be a valid memory address
251///
252/// # Parameters
253///
254/// * `Hint_address` - Preferred memory address (hint only, may be ignored), or null
255/// * `Size` - Size of the memory block in bytes
256/// * `Alignment` - Required memory alignment in bytes (must be a power of 2)
257/// * `Capabilities` - Memory capabilities flags (see `xila_memory_capabilities_*` constants)
258///
259/// # Returns
260///
261/// - Pointer to the allocated memory block
262/// - Null if allocation fails
263///
264/// # Errors
265///
266/// Returns null if:
267/// - Insufficient memory available
268/// - Invalid alignment (not a power of 2)
269/// - Requested capabilities not supported
270/// - Size is too large
271///
272/// # Examples
273///
274/// ```c
275/// // Allocate 1024 bytes with 8-byte alignment
276/// void* ptr = xila_memory_allocate(NULL, 1024, 8, xila_memory_capabilities_none);
277///
278/// // Allocate executable memory for code
279/// void* code_ptr = xila_memory_allocate(NULL, 4096, 4096, XILA_MEMORY_CAPABILITIES_EXECUTE);
280///
281/// // Allocate DMA-capable memory
282/// void* dma_ptr = xila_memory_allocate(NULL, 2048, 32, xila_memory_capabilities_direct_memory_access);
283/// ```
284#[unsafe(no_mangle)]
285pub unsafe extern "C" fn xila_memory_allocate(
286    _: *mut c_void,
287    size: usize,
288    alignment: usize,
289    capabilities: XilaMemoryCapabilities,
290) -> *mut c_void {
291    let _lock = block_on(ALLOCATION_MUTEX.lock());
292
293    //trace!(
294    //    "xila_memory_allocate called with Size: {size}, Alignment: {alignment}, Capabilities: {capabilities:?}"
295    //);
296
297    let layout = Allocated::get_layout_for_allocation(size, alignment);
298
299    let capabilities = match CapabilityFlags::from_bits(capabilities) {
300        Some(flags) => flags,
301        None => {
302            warning!(
303                "xila_memory_allocate called with invalid capabilities: {capabilities:?}, ignoring"
304            );
305            CapabilityFlags::None
306        }
307    };
308
309    let pointer = unsafe { memory::get_instance().allocate(capabilities, layout) };
310
311    let allocated = match Allocated::from_layout(pointer, &layout) {
312        Some(alloc) => alloc,
313        None => {
314            warning!(
315                "xila_memory_allocate failed to create allocation metadata: Size: {size}, Alignment: {alignment}, Capabilities: {capabilities:?}"
316            );
317            return null_mut();
318        }
319    };
320
321    allocated.get_user_pointer() as *mut c_void
322}
323
324/// Allocates a memory block with default properties.
325///
326/// # Safety
327/// This function is unsafe because it dereferences raw pointers.
328#[unsafe(no_mangle)]
329pub unsafe extern "C" fn xila_memory_allocate_core(size: usize) -> *mut c_void {
330    //trace!("xila_memory_allocate_core called with Size: {size}");
331    unsafe {
332        xila_memory_allocate(
333            null_mut(),
334            size,
335            align_of::<usize>(), // Use usize alignment for general-purpose allocations
336            XILA_MEMORY_CAPABILITIES_NONE,
337        )
338    }
339}
340
341/// Returns the system's memory page size.
342///
343/// The page size is the smallest unit of memory that can be allocated by the
344/// virtual memory system. This is useful for applications that need to work
345/// with page-aligned memory or perform memory-mapped I/O operations.
346///
347/// # Returns
348///
349/// The page size in bytes (typically 4096 on most systems)
350///
351/// # Examples
352///
353/// ```c
354/// size_t page_size = xila_memory_get_page_size();
355/// printf("System page size: %zu bytes\n", page_size);
356///
357/// // Allocate page-aligned memory
358/// void* ptr = xila_memory_allocate(NULL, page_size * 2, page_size, 0);
359/// ```
360#[unsafe(no_mangle)]
361pub extern "C" fn xila_memory_get_page_size() -> usize {
362    memory::get_instance().get_page_size()
363}
364
365/// Flushes the data cache.
366///
367/// This function ensures that all pending write operations in the data cache
368/// are written to main memory. This is important for cache coherency in
369/// multi-core systems or when interfacing with DMA controllers.
370///
371/// # Safety
372///
373/// This function is safe to call at any time, but may have performance implications
374/// as it forces cache synchronization.
375///
376/// # Examples
377///
378/// ```c
379/// // After writing data that will be accessed by DMA
380/// memcpy(dma_buffer, data, size);
381/// xila_memory_flush_data_cache();
382/// start_dma_transfer(dma_buffer, size);
383/// ```
384#[unsafe(no_mangle)]
385pub extern "C" fn xila_memory_flush_data_cache() {
386    memory::get_instance().flush_data_cache();
387}
388
389/// Flushes the instruction cache for a specific memory region.
390///
391/// This function invalidates the instruction cache for the specified memory region.
392/// This is necessary after modifying executable code to ensure the processor
393/// sees the updated instructions.
394///
395/// # Safety
396///
397/// - The address must point to valid memory
398/// - The memory region must not be accessed for execution during the flush operation
399/// - The size should not exceed the actual allocated memory size
400///
401/// # Parameters
402///
403/// * `Address` - Starting address of the memory region to flush
404/// * `Size` - Size of the memory region in bytes
405///
406/// # Examples
407///
408/// ```c
409/// // After writing machine code to executable memory
410/// void* code_ptr = xila_memory_allocate(NULL, 4096, 4096, XILA_MEMORY_CAPABILITIES_EXECUTE);
411/// memcpy(code_ptr, machine_code, code_size);
412/// xila_memory_flush_instruction_cache(code_ptr, code_size);
413///
414/// // Now safe to execute the code
415/// ((void(*)())code_ptr)();
416/// ```
417#[unsafe(no_mangle)]
418pub extern "C" fn xila_memory_flush_instruction_cache(_address: *mut c_void, _size: usize) {
419    unsafe {
420        memory::get_instance().flush_instruction_cache(_address as *const u8, _size);
421    }
422}
423
424#[cfg(test)]
425mod tests {
426    //! # Memory Management Tests
427    //!
428    //! This module contains comprehensive tests for the memory management functionality.
429    //! The tests cover various scenarios including basic allocation/deallocation,
430    //! edge cases, error conditions, and stress testing.
431    //!
432    //! ## Test Categories
433    //!
434    //! - **Basic Operations**: Standard allocation, deallocation, and reallocation
435    //! - **Edge Cases**: Zero-size allocations, large alignments, null pointers
436    //! - **Capabilities**: Testing executable and DMA-capable memory
437    //! - **Cache Operations**: Data and instruction cache flushing
438    //! - **Stress Testing**: Multiple allocations and allocation tracking
439    //! - **Error Handling**: Invalid inputs and error recovery
440    use alloc::vec::Vec;
441
442    use super::*;
443
444    /// Tests basic memory allocation and deallocation functionality.
445    ///
446    /// This test verifies that:
447    /// - Memory can be successfully allocated with specified size and alignment
448    /// - Allocated memory is readable and writable
449    /// - Memory can be properly deallocated without errors
450    /// - Data written to memory is correctly stored and retrieved
451    #[test]
452    fn test_allocate_deallocate_basic() {
453        unsafe {
454            // Test basic allocation and deallocation
455            let size = 128;
456            let alignment = 8;
457            let capabilities = 0; // Basic capabilities
458            let hint_address = core::ptr::null_mut();
459
460            let pointer = xila_memory_allocate(hint_address, size, alignment, capabilities);
461            assert!(!pointer.is_null(), "Memory allocation should succeed");
462
463            // Write and read to verify the memory is accessible
464            let ptr = pointer as *mut u8;
465            for i in 0..size {
466                *ptr.add(i) = (i % 256) as u8;
467            }
468
469            for i in 0..size {
470                assert_eq!(
471                    *ptr.add(i),
472                    (i % 256) as u8,
473                    "Memory should be readable and writable"
474                );
475            }
476
477            // Deallocate the memory
478            xila_memory_deallocate(pointer);
479        }
480    }
481
482    /// Tests allocation behavior with zero size.
483    ///
484    /// Zero-size allocations are a special case that different allocators
485    /// may handle differently. This test ensures the implementation handles
486    /// them gracefully without crashing.
487    #[test]
488    fn test_allocate_zero_size() {
489        unsafe {
490            // Test allocation with zero size
491            let size = 0;
492            let alignment = 8;
493            let capabilities = 0;
494            let hint_address = core::ptr::null_mut();
495
496            let pointer = xila_memory_allocate(hint_address, size, alignment, capabilities);
497            // Zero-size allocation might return null or a valid pointer, both are acceptable
498            if !pointer.is_null() {
499                xila_memory_deallocate(pointer);
500            }
501        }
502    }
503
504    /// Tests memory allocation with large alignment requirements.
505    ///
506    /// This test verifies that the allocator can handle large alignment
507    /// requirements (64 bytes) and that the returned pointer is properly
508    /// aligned to the requested boundary.
509    #[test]
510    fn test_allocate_large_alignment() {
511        unsafe {
512            // Test allocation with large alignment
513            let size = 256;
514            let alignment = MAXIMUM_ALIGNMENT;
515            let capabilities = 0;
516            let hint_address = core::ptr::null_mut();
517
518            let pointer = xila_memory_allocate(hint_address, size, alignment, capabilities);
519            assert!(
520                !pointer.is_null(),
521                "Large alignment allocation should succeed"
522            );
523
524            // Verify alignment
525            let addr = pointer as usize;
526            assert_eq!(addr % alignment, 0, "Pointer should be properly aligned");
527
528            xila_memory_deallocate(pointer);
529        }
530    }
531
532    /// Tests memory allocation with small alignments (1, 2, 4 bytes).
533    ///
534    /// This test verifies that the allocator can handle small alignment
535    /// requirements now that CompactLayout supports alignments as low as 1 byte.
536    #[test]
537    fn test_allocate_small_alignments() {
538        unsafe {
539            for &alignment in &[1, 2, 4] {
540                let size = 64;
541                let capabilities = 0;
542                let hint_address = core::ptr::null_mut();
543
544                let pointer = xila_memory_allocate(hint_address, size, alignment, capabilities);
545                assert!(
546                    !pointer.is_null(),
547                    "Allocation with alignment {} should succeed",
548                    alignment
549                );
550
551                // Verify alignment
552                let addr = pointer as usize;
553                assert_eq!(
554                    addr % alignment,
555                    0,
556                    "Pointer should be aligned to {} bytes",
557                    alignment
558                );
559
560                // Write and read to verify the memory is accessible
561                let ptr = pointer as *mut u8;
562                for i in 0..size {
563                    *ptr.add(i) = (i % 256) as u8;
564                }
565
566                for i in 0..size {
567                    assert_eq!(
568                        *ptr.add(i),
569                        (i % 256) as u8,
570                        "Memory should be readable and writable"
571                    );
572                }
573
574                xila_memory_deallocate(pointer);
575            }
576        }
577    }
578
579    /// Tests allocation of executable memory.
580    ///
581    /// This test verifies that memory can be allocated with executable
582    /// capabilities, which is necessary for just-in-time compilation
583    /// and dynamic code generation.
584    #[test]
585    fn test_allocate_executable_capability() {
586        unsafe {
587            // Test allocation with executable capability
588            let size = 128;
589            let alignment = 8;
590            let capabilities = XILA_MEMORY_CAPABILITIES_EXECUTE;
591            let hint_address = core::ptr::null_mut();
592
593            let pointer = xila_memory_allocate(hint_address, size, alignment, capabilities);
594            assert!(
595                !pointer.is_null(),
596                "Executable memory allocation should succeed"
597            );
598
599            xila_memory_deallocate(pointer);
600        }
601    }
602
603    /// Tests allocation of DMA-capable memory.
604    ///
605    /// DMA-capable memory must meet specific hardware requirements and
606    /// may need to be allocated from special memory regions. This test
607    /// is ignored by default as it requires specific hardware support.
608    #[test]
609    #[ignore = "Requires specific hardware support for DMA"]
610    fn test_allocate_dma_capability() {
611        unsafe {
612            // Test allocation with DMA capability
613            let size = 128;
614            let alignment = 8;
615            let capabilities = XILA_MEMORY_CAPABILITIES_DIRECT_MEMORY_ACCESS;
616            let hint_address = core::ptr::null_mut();
617
618            let pointer = xila_memory_allocate(hint_address, size, alignment, capabilities);
619            assert!(!pointer.is_null(), "DMA memory allocation should succeed");
620
621            xila_memory_deallocate(pointer);
622        }
623    }
624
625    /// Tests that deallocating a null pointer is safe.
626    ///
627    /// According to standard behavior, deallocating a null pointer
628    /// should be a no-op and not cause any errors or crashes.
629    #[test]
630    fn test_deallocate_null_pointer() {
631        // Test that deallocating a null pointer doesn't crash
632        unsafe {
633            xila_memory_deallocate(core::ptr::null_mut());
634        }
635    }
636
637    /// Tests reallocation from a null pointer (equivalent to allocation).
638    ///
639    /// When realloc is called with a null pointer, it should behave
640    /// identically to malloc, allocating new memory of the requested size.
641    #[test]
642    fn test_reallocate_null_to_new() {
643        unsafe {
644            // Test reallocating from null (equivalent to allocation)
645            let size = 128;
646            let pointer = xila_memory_reallocate(core::ptr::null_mut(), size);
647            assert!(
648                !pointer.is_null(),
649                "Reallocation from null should work like allocation"
650            );
651
652            // Write to the memory to verify it's usable
653            let ptr = pointer as *mut u8;
654            for i in 0..size {
655                *ptr.add(i) = (i % 256) as u8;
656            }
657
658            xila_memory_deallocate(pointer);
659        }
660    }
661
662    /// Tests expanding memory through reallocation.
663    ///
664    /// This test verifies that existing data is preserved when memory
665    /// is expanded through reallocation, and that the new memory region
666    /// is usable.
667    #[test]
668    fn test_reallocate_expand() {
669        unsafe {
670            // Test expanding memory with reallocation
671            let initial_size = 64;
672            let expanded_size = 128;
673
674            // First allocation
675            let pointer = xila_memory_reallocate(core::ptr::null_mut(), initial_size);
676            assert!(!pointer.is_null(), "Initial allocation should succeed");
677
678            // Fill with test data
679            let ptr = pointer as *mut u8;
680            for i in 0..initial_size {
681                *ptr.add(i) = (i % 256) as u8;
682            }
683
684            // Expand the allocation
685            let new_pointer = xila_memory_reallocate(pointer, expanded_size);
686            assert!(
687                !new_pointer.is_null(),
688                "Reallocation expansion should succeed"
689            );
690
691            // Verify original data is preserved
692            let new_ptr = new_pointer as *mut u8;
693            for i in 0..initial_size {
694                assert_eq!(
695                    *new_ptr.add(i),
696                    (i % 256) as u8,
697                    "Original data should be preserved"
698                );
699            }
700
701            xila_memory_deallocate(new_pointer);
702        }
703    }
704
705    /// Tests shrinking memory through reallocation.
706    ///
707    /// This test verifies that data within the new size bounds is preserved
708    /// when memory is shrunk through reallocation.
709    #[test]
710    fn test_reallocate_shrink() {
711        unsafe {
712            // Test shrinking memory with reallocation
713            let initial_size = 128;
714            let shrunk_size = 64;
715
716            // First allocation
717            let pointer = xila_memory_reallocate(core::ptr::null_mut(), initial_size);
718            assert!(!pointer.is_null(), "Initial allocation should succeed");
719
720            // Fill with test data
721            let ptr = pointer as *mut u8;
722            for i in 0..initial_size {
723                *ptr.add(i) = (i % 256) as u8;
724            }
725
726            // Shrink the allocation
727            let new_pointer = xila_memory_reallocate(pointer, shrunk_size);
728            assert!(
729                !new_pointer.is_null(),
730                "Reallocation shrinking should succeed"
731            );
732
733            // Verify data within new size is preserved
734            let new_ptr = new_pointer as *mut u8;
735            for i in 0..shrunk_size {
736                assert_eq!(
737                    *new_ptr.add(i),
738                    (i % 256) as u8,
739                    "Data within new size should be preserved"
740                );
741            }
742
743            xila_memory_deallocate(new_pointer);
744        }
745    }
746
747    /// Tests reallocation to zero size (equivalent to deallocation).
748    ///
749    /// When realloc is called with size 0, it should behave like free(),
750    /// deallocating the memory and potentially returning null.
751    #[test]
752    fn test_reallocate_to_zero() {
753        unsafe {
754            // Test reallocating to zero size (equivalent to deallocation)
755            let initial_size = 64;
756
757            let pointer = xila_memory_reallocate(core::ptr::null_mut(), initial_size);
758            assert!(!pointer.is_null(), "Initial allocation should succeed");
759
760            let new_pointer = xila_memory_reallocate(pointer, 0);
761            // Zero-size reallocation might return null or a valid pointer
762            // If it returns a valid pointer, we should deallocate it
763            if !new_pointer.is_null() {
764                xila_memory_deallocate(new_pointer);
765            }
766        }
767    }
768
769    /// Tests that the system page size is reasonable and valid.
770    ///
771    /// This test verifies that:
772    /// - Page size is greater than 0
773    /// - Page size is at least 4KB (common minimum)
774    /// - Page size is a power of 2 (hardware requirement)
775    #[test]
776    fn test_get_page_size() {
777        let page_size = xila_memory_get_page_size();
778
779        // Page size should be a power of 2 and at least 4KB on most systems
780        assert!(page_size > 0, "Page size should be greater than 0");
781        assert!(page_size >= 4096, "Page size should be at least 4KB");
782        assert!(
783            page_size.is_power_of_two(),
784            "Page size should be a power of 2"
785        );
786    }
787
788    /// Tests that data cache flushing doesn't cause crashes.
789    ///
790    /// This is a basic safety test to ensure the cache flush operation
791    /// completes without errors. The actual cache flush behavior is
792    /// hardware-dependent and difficult to test directly.
793    #[test]
794    fn test_flush_data_cache() {
795        // Test that flushing data cache doesn't crash
796        xila_memory_flush_data_cache();
797    }
798
799    /// Tests instruction cache flushing on executable memory.
800    ///
801    /// This test verifies that instruction cache flushing works correctly
802    /// on executable memory regions, which is essential for dynamic code
803    /// generation and just-in-time compilation.
804    #[test]
805    fn test_flush_instruction_cache() {
806        unsafe {
807            // Test flushing instruction cache with valid memory
808            let size = 128;
809            let alignment = 8;
810            let capabilities = XILA_MEMORY_CAPABILITIES_EXECUTE;
811            let hint_address = core::ptr::null_mut();
812
813            let pointer = xila_memory_allocate(hint_address, size, alignment, capabilities);
814            assert!(
815                !pointer.is_null(),
816                "Executable memory allocation should succeed"
817            );
818
819            // Flush instruction cache for this memory region
820            xila_memory_flush_instruction_cache(pointer, size);
821
822            xila_memory_deallocate(pointer);
823        }
824    }
825
826    #[test]
827    fn test_multiple_allocations() {
828        unsafe {
829            let mut pointers = Vec::new();
830            let allocation_count = 10;
831            let size = 64;
832            let alignment = 8;
833            let capabilities = 0;
834
835            // Allocate multiple memory blocks
836            for _ in 0..allocation_count {
837                let pointer =
838                    xila_memory_allocate(core::ptr::null_mut(), size, alignment, capabilities);
839                assert!(!pointer.is_null(), "Each allocation should succeed");
840                pointers.push(pointer);
841            }
842
843            // Verify each allocation is unique and writable
844            for (i, &pointer) in pointers.iter().enumerate() {
845                let ptr = pointer as *mut u8;
846                let test_value = (i % 256) as u8;
847                *ptr = test_value;
848                assert_eq!(
849                    *ptr, test_value,
850                    "Each allocation should be independently writable"
851                );
852            }
853
854            // Deallocate all memory blocks
855            for &pointer in &pointers {
856                xila_memory_deallocate(pointer);
857            }
858        }
859    }
860
861    #[test]
862    fn test_reallocation_tracking() {
863        unsafe {
864            // Test that reallocations properly update the tracking table
865            let initial_size = 64;
866            let new_size = 128;
867
868            let pointer = xila_memory_reallocate(core::ptr::null_mut(), initial_size);
869            assert!(!pointer.is_null(), "Initial reallocation should succeed");
870
871            let new_pointer = xila_memory_reallocate(pointer, new_size);
872            assert!(!new_pointer.is_null(), "Reallocation should succeed");
873
874            // The new pointer should be properly tracked
875            xila_memory_deallocate(new_pointer);
876        }
877    }
878}