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}