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