15a888c28SDanilo Krummrich // SPDX-License-Identifier: GPL-2.0 25a888c28SDanilo Krummrich 3dd09538fSDanilo Krummrich //! So far the kernel's `Box` and `Vec` types can't be used by userspace test cases, since all users 4dd09538fSDanilo Krummrich //! of those types (e.g. `CString`) use kernel allocators for instantiation. 5dd09538fSDanilo Krummrich //! 6dd09538fSDanilo Krummrich //! In order to allow userspace test cases to make use of such types as well, implement the 7dd09538fSDanilo Krummrich //! `Cmalloc` allocator within the allocator_test module and type alias all kernel allocators to 8dd09538fSDanilo Krummrich //! `Cmalloc`. The `Cmalloc` allocator uses libc's `realloc()` function as allocator backend. 9dd09538fSDanilo Krummrich 105a888c28SDanilo Krummrich #![allow(missing_docs)] 115a888c28SDanilo Krummrich 12dd09538fSDanilo Krummrich use super::{flags::*, AllocError, Allocator, Flags}; 135a888c28SDanilo Krummrich use core::alloc::Layout; 14dd09538fSDanilo Krummrich use core::cmp; 15dd09538fSDanilo Krummrich use core::ptr; 165a888c28SDanilo Krummrich use core::ptr::NonNull; 175a888c28SDanilo Krummrich 18dd09538fSDanilo Krummrich /// The userspace allocator based on libc. 19dd09538fSDanilo Krummrich pub struct Cmalloc; 20dd09538fSDanilo Krummrich 21dd09538fSDanilo Krummrich pub type Kmalloc = Cmalloc; 2261c00478SDanilo Krummrich pub type Vmalloc = Kmalloc; 238362c260SDanilo Krummrich pub type KVmalloc = Kmalloc; 245a888c28SDanilo Krummrich 25dd09538fSDanilo Krummrich extern "C" { 26dd09538fSDanilo Krummrich #[link_name = "aligned_alloc"] libc_aligned_alloc(align: usize, size: usize) -> *mut crate::ffi::c_void27d072acdaSGary Guo fn libc_aligned_alloc(align: usize, size: usize) -> *mut crate::ffi::c_void; 28dd09538fSDanilo Krummrich 29dd09538fSDanilo Krummrich #[link_name = "free"] libc_free(ptr: *mut crate::ffi::c_void)30d072acdaSGary Guo fn libc_free(ptr: *mut crate::ffi::c_void); 31dd09538fSDanilo Krummrich } 32dd09538fSDanilo Krummrich 33dd09538fSDanilo Krummrich // SAFETY: 34dd09538fSDanilo Krummrich // - memory remains valid until it is explicitly freed, 35dd09538fSDanilo Krummrich // - passing a pointer to a valid memory allocation created by this `Allocator` is always OK, 36dd09538fSDanilo Krummrich // - `realloc` provides the guarantees as provided in the `# Guarantees` section. 37dd09538fSDanilo Krummrich unsafe impl Allocator for Cmalloc { realloc( ptr: Option<NonNull<u8>>, layout: Layout, old_layout: Layout, flags: Flags, ) -> Result<NonNull<[u8]>, AllocError>385a888c28SDanilo Krummrich unsafe fn realloc( 39dd09538fSDanilo Krummrich ptr: Option<NonNull<u8>>, 40dd09538fSDanilo Krummrich layout: Layout, 41dd09538fSDanilo Krummrich old_layout: Layout, 42dd09538fSDanilo Krummrich flags: Flags, 435a888c28SDanilo Krummrich ) -> Result<NonNull<[u8]>, AllocError> { 44dd09538fSDanilo Krummrich let src = match ptr { 45dd09538fSDanilo Krummrich Some(src) => { 46dd09538fSDanilo Krummrich if old_layout.size() == 0 { 47dd09538fSDanilo Krummrich ptr::null_mut() 48dd09538fSDanilo Krummrich } else { 49dd09538fSDanilo Krummrich src.as_ptr() 50dd09538fSDanilo Krummrich } 51dd09538fSDanilo Krummrich } 52dd09538fSDanilo Krummrich None => ptr::null_mut(), 53dd09538fSDanilo Krummrich }; 54dd09538fSDanilo Krummrich 55dd09538fSDanilo Krummrich if layout.size() == 0 { 56dd09538fSDanilo Krummrich // SAFETY: `src` is either NULL or was previously allocated with this `Allocator` 57dd09538fSDanilo Krummrich unsafe { libc_free(src.cast()) }; 58dd09538fSDanilo Krummrich 59dd09538fSDanilo Krummrich return Ok(NonNull::slice_from_raw_parts( 60dd09538fSDanilo Krummrich crate::alloc::dangling_from_layout(layout), 61dd09538fSDanilo Krummrich 0, 62dd09538fSDanilo Krummrich )); 63dd09538fSDanilo Krummrich } 64dd09538fSDanilo Krummrich 65*ff64846bSTamir Duberstein // ISO C (ISO/IEC 9899:2011) defines `aligned_alloc`: 66*ff64846bSTamir Duberstein // 67*ff64846bSTamir Duberstein // > The value of alignment shall be a valid alignment supported by the implementation 68*ff64846bSTamir Duberstein // [...]. 69*ff64846bSTamir Duberstein // 70*ff64846bSTamir Duberstein // As an example of the "supported by the implementation" requirement, POSIX.1-2001 (IEEE 71*ff64846bSTamir Duberstein // 1003.1-2001) defines `posix_memalign`: 72*ff64846bSTamir Duberstein // 73*ff64846bSTamir Duberstein // > The value of alignment shall be a power of two multiple of sizeof (void *). 74*ff64846bSTamir Duberstein // 75*ff64846bSTamir Duberstein // and POSIX-based implementations of `aligned_alloc` inherit this requirement. At the time 76*ff64846bSTamir Duberstein // of writing, this is known to be the case on macOS (but not in glibc). 77*ff64846bSTamir Duberstein // 78*ff64846bSTamir Duberstein // Satisfy the stricter requirement to avoid spurious test failures on some platforms. 79*ff64846bSTamir Duberstein let min_align = core::mem::size_of::<*const crate::ffi::c_void>(); 80*ff64846bSTamir Duberstein let layout = layout.align_to(min_align).map_err(|_| AllocError)?; 81*ff64846bSTamir Duberstein let layout = layout.pad_to_align(); 82*ff64846bSTamir Duberstein 83dd09538fSDanilo Krummrich // SAFETY: Returns either NULL or a pointer to a memory allocation that satisfies or 84dd09538fSDanilo Krummrich // exceeds the given size and alignment requirements. 85dd09538fSDanilo Krummrich let dst = unsafe { libc_aligned_alloc(layout.align(), layout.size()) } as *mut u8; 86dd09538fSDanilo Krummrich let dst = NonNull::new(dst).ok_or(AllocError)?; 87dd09538fSDanilo Krummrich 88dd09538fSDanilo Krummrich if flags.contains(__GFP_ZERO) { 89dd09538fSDanilo Krummrich // SAFETY: The preceding calls to `libc_aligned_alloc` and `NonNull::new` 90dd09538fSDanilo Krummrich // guarantee that `dst` points to memory of at least `layout.size()` bytes. 91dd09538fSDanilo Krummrich unsafe { dst.as_ptr().write_bytes(0, layout.size()) }; 92dd09538fSDanilo Krummrich } 93dd09538fSDanilo Krummrich 94dd09538fSDanilo Krummrich if !src.is_null() { 95dd09538fSDanilo Krummrich // SAFETY: 96dd09538fSDanilo Krummrich // - `src` has previously been allocated with this `Allocator`; `dst` has just been 97dd09538fSDanilo Krummrich // newly allocated, hence the memory regions do not overlap. 98dd09538fSDanilo Krummrich // - both` src` and `dst` are properly aligned and valid for reads and writes 99dd09538fSDanilo Krummrich unsafe { 100dd09538fSDanilo Krummrich ptr::copy_nonoverlapping( 101dd09538fSDanilo Krummrich src, 102dd09538fSDanilo Krummrich dst.as_ptr(), 103dd09538fSDanilo Krummrich cmp::min(layout.size(), old_layout.size()), 104dd09538fSDanilo Krummrich ) 105dd09538fSDanilo Krummrich }; 106dd09538fSDanilo Krummrich } 107dd09538fSDanilo Krummrich 108dd09538fSDanilo Krummrich // SAFETY: `src` is either NULL or was previously allocated with this `Allocator` 109dd09538fSDanilo Krummrich unsafe { libc_free(src.cast()) }; 110dd09538fSDanilo Krummrich 111dd09538fSDanilo Krummrich Ok(NonNull::slice_from_raw_parts(dst, layout.size())) 1125a888c28SDanilo Krummrich } 1135a888c28SDanilo Krummrich } 114