1*5a5110d2SManos Pitsidianakis // Copyright 2024, Linaro Limited 2*5a5110d2SManos Pitsidianakis // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> 3*5a5110d2SManos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later 4*5a5110d2SManos Pitsidianakis 5*5a5110d2SManos Pitsidianakis #![cfg_attr(not(MESON), doc = include_str!("../README.md"))] 6*5a5110d2SManos Pitsidianakis 7*5a5110d2SManos Pitsidianakis #[allow( 8*5a5110d2SManos Pitsidianakis dead_code, 9*5a5110d2SManos Pitsidianakis improper_ctypes_definitions, 10*5a5110d2SManos Pitsidianakis improper_ctypes, 11*5a5110d2SManos Pitsidianakis non_camel_case_types, 12*5a5110d2SManos Pitsidianakis non_snake_case, 13*5a5110d2SManos Pitsidianakis non_upper_case_globals, 14*5a5110d2SManos Pitsidianakis unsafe_op_in_unsafe_fn, 15*5a5110d2SManos Pitsidianakis clippy::missing_const_for_fn, 16*5a5110d2SManos Pitsidianakis clippy::too_many_arguments, 17*5a5110d2SManos Pitsidianakis clippy::approx_constant, 18*5a5110d2SManos Pitsidianakis clippy::use_self, 19*5a5110d2SManos Pitsidianakis clippy::useless_transmute, 20*5a5110d2SManos Pitsidianakis clippy::missing_safety_doc, 21*5a5110d2SManos Pitsidianakis )] 22*5a5110d2SManos Pitsidianakis #[rustfmt::skip] 23*5a5110d2SManos Pitsidianakis pub mod bindings; 24*5a5110d2SManos Pitsidianakis 25*5a5110d2SManos Pitsidianakis unsafe impl Send for bindings::Property {} 26*5a5110d2SManos Pitsidianakis unsafe impl Sync for bindings::Property {} 27*5a5110d2SManos Pitsidianakis unsafe impl Sync for bindings::TypeInfo {} 28*5a5110d2SManos Pitsidianakis unsafe impl Sync for bindings::VMStateDescription {} 29*5a5110d2SManos Pitsidianakis 30*5a5110d2SManos Pitsidianakis pub mod definitions; 31*5a5110d2SManos Pitsidianakis pub mod device_class; 32*5a5110d2SManos Pitsidianakis 33*5a5110d2SManos Pitsidianakis #[cfg(test)] 34*5a5110d2SManos Pitsidianakis mod tests; 35*5a5110d2SManos Pitsidianakis 36*5a5110d2SManos Pitsidianakis use std::alloc::{GlobalAlloc, Layout}; 37*5a5110d2SManos Pitsidianakis 38*5a5110d2SManos Pitsidianakis #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)] 39*5a5110d2SManos Pitsidianakis extern "C" { 40*5a5110d2SManos Pitsidianakis fn g_aligned_alloc0( 41*5a5110d2SManos Pitsidianakis n_blocks: bindings::gsize, 42*5a5110d2SManos Pitsidianakis n_block_bytes: bindings::gsize, 43*5a5110d2SManos Pitsidianakis alignment: bindings::gsize, 44*5a5110d2SManos Pitsidianakis ) -> bindings::gpointer; 45*5a5110d2SManos Pitsidianakis fn g_aligned_free(mem: bindings::gpointer); 46*5a5110d2SManos Pitsidianakis } 47*5a5110d2SManos Pitsidianakis 48*5a5110d2SManos Pitsidianakis #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))] 49*5a5110d2SManos Pitsidianakis extern "C" { 50*5a5110d2SManos Pitsidianakis fn qemu_memalign(alignment: usize, size: usize) -> *mut ::core::ffi::c_void; 51*5a5110d2SManos Pitsidianakis fn qemu_vfree(ptr: *mut ::core::ffi::c_void); 52*5a5110d2SManos Pitsidianakis } 53*5a5110d2SManos Pitsidianakis 54*5a5110d2SManos Pitsidianakis extern "C" { 55*5a5110d2SManos Pitsidianakis fn g_malloc0(n_bytes: bindings::gsize) -> bindings::gpointer; 56*5a5110d2SManos Pitsidianakis fn g_free(mem: bindings::gpointer); 57*5a5110d2SManos Pitsidianakis } 58*5a5110d2SManos Pitsidianakis 59*5a5110d2SManos Pitsidianakis /// An allocator that uses the same allocator as QEMU in C. 60*5a5110d2SManos Pitsidianakis /// 61*5a5110d2SManos Pitsidianakis /// It is enabled by default with the `allocator` feature. 62*5a5110d2SManos Pitsidianakis /// 63*5a5110d2SManos Pitsidianakis /// To set it up manually as a global allocator in your crate: 64*5a5110d2SManos Pitsidianakis /// 65*5a5110d2SManos Pitsidianakis /// ```ignore 66*5a5110d2SManos Pitsidianakis /// use qemu_api::QemuAllocator; 67*5a5110d2SManos Pitsidianakis /// 68*5a5110d2SManos Pitsidianakis /// #[global_allocator] 69*5a5110d2SManos Pitsidianakis /// static GLOBAL: QemuAllocator = QemuAllocator::new(); 70*5a5110d2SManos Pitsidianakis /// ``` 71*5a5110d2SManos Pitsidianakis #[derive(Clone, Copy, Debug)] 72*5a5110d2SManos Pitsidianakis #[repr(C)] 73*5a5110d2SManos Pitsidianakis pub struct QemuAllocator { 74*5a5110d2SManos Pitsidianakis _unused: [u8; 0], 75*5a5110d2SManos Pitsidianakis } 76*5a5110d2SManos Pitsidianakis 77*5a5110d2SManos Pitsidianakis #[cfg_attr(all(feature = "allocator", not(test)), global_allocator)] 78*5a5110d2SManos Pitsidianakis pub static GLOBAL: QemuAllocator = QemuAllocator::new(); 79*5a5110d2SManos Pitsidianakis 80*5a5110d2SManos Pitsidianakis impl QemuAllocator { 81*5a5110d2SManos Pitsidianakis // From the glibc documentation, on GNU systems, malloc guarantees 16-byte 82*5a5110d2SManos Pitsidianakis // alignment on 64-bit systems and 8-byte alignment on 32-bit systems. See 83*5a5110d2SManos Pitsidianakis // https://www.gnu.org/software/libc/manual/html_node/Malloc-Examples.html. 84*5a5110d2SManos Pitsidianakis // This alignment guarantee also applies to Windows and Android. On Darwin 85*5a5110d2SManos Pitsidianakis // and OpenBSD, the alignment is 16 bytes on both 64-bit and 32-bit systems. 86*5a5110d2SManos Pitsidianakis #[cfg(all( 87*5a5110d2SManos Pitsidianakis target_pointer_width = "32", 88*5a5110d2SManos Pitsidianakis not(any(target_os = "macos", target_os = "openbsd")) 89*5a5110d2SManos Pitsidianakis ))] 90*5a5110d2SManos Pitsidianakis pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(8); 91*5a5110d2SManos Pitsidianakis #[cfg(all( 92*5a5110d2SManos Pitsidianakis target_pointer_width = "64", 93*5a5110d2SManos Pitsidianakis not(any(target_os = "macos", target_os = "openbsd")) 94*5a5110d2SManos Pitsidianakis ))] 95*5a5110d2SManos Pitsidianakis pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(16); 96*5a5110d2SManos Pitsidianakis #[cfg(all( 97*5a5110d2SManos Pitsidianakis any(target_pointer_width = "32", target_pointer_width = "64"), 98*5a5110d2SManos Pitsidianakis any(target_os = "macos", target_os = "openbsd") 99*5a5110d2SManos Pitsidianakis ))] 100*5a5110d2SManos Pitsidianakis pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(16); 101*5a5110d2SManos Pitsidianakis #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] 102*5a5110d2SManos Pitsidianakis pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = None; 103*5a5110d2SManos Pitsidianakis 104*5a5110d2SManos Pitsidianakis pub const fn new() -> Self { 105*5a5110d2SManos Pitsidianakis Self { _unused: [] } 106*5a5110d2SManos Pitsidianakis } 107*5a5110d2SManos Pitsidianakis } 108*5a5110d2SManos Pitsidianakis 109*5a5110d2SManos Pitsidianakis impl Default for QemuAllocator { 110*5a5110d2SManos Pitsidianakis fn default() -> Self { 111*5a5110d2SManos Pitsidianakis Self::new() 112*5a5110d2SManos Pitsidianakis } 113*5a5110d2SManos Pitsidianakis } 114*5a5110d2SManos Pitsidianakis 115*5a5110d2SManos Pitsidianakis // Sanity check. 116*5a5110d2SManos Pitsidianakis const _: [(); 8] = [(); ::core::mem::size_of::<*mut ::core::ffi::c_void>()]; 117*5a5110d2SManos Pitsidianakis 118*5a5110d2SManos Pitsidianakis unsafe impl GlobalAlloc for QemuAllocator { 119*5a5110d2SManos Pitsidianakis unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 120*5a5110d2SManos Pitsidianakis if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0)) 121*5a5110d2SManos Pitsidianakis { 122*5a5110d2SManos Pitsidianakis // SAFETY: g_malloc0() is safe to call. 123*5a5110d2SManos Pitsidianakis unsafe { g_malloc0(layout.size().try_into().unwrap()).cast::<u8>() } 124*5a5110d2SManos Pitsidianakis } else { 125*5a5110d2SManos Pitsidianakis #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)] 126*5a5110d2SManos Pitsidianakis { 127*5a5110d2SManos Pitsidianakis // SAFETY: g_aligned_alloc0() is safe to call. 128*5a5110d2SManos Pitsidianakis unsafe { 129*5a5110d2SManos Pitsidianakis g_aligned_alloc0( 130*5a5110d2SManos Pitsidianakis layout.size().try_into().unwrap(), 131*5a5110d2SManos Pitsidianakis 1, 132*5a5110d2SManos Pitsidianakis layout.align().try_into().unwrap(), 133*5a5110d2SManos Pitsidianakis ) 134*5a5110d2SManos Pitsidianakis .cast::<u8>() 135*5a5110d2SManos Pitsidianakis } 136*5a5110d2SManos Pitsidianakis } 137*5a5110d2SManos Pitsidianakis #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))] 138*5a5110d2SManos Pitsidianakis { 139*5a5110d2SManos Pitsidianakis // SAFETY: qemu_memalign() is safe to call. 140*5a5110d2SManos Pitsidianakis unsafe { qemu_memalign(layout.align(), layout.size()).cast::<u8>() } 141*5a5110d2SManos Pitsidianakis } 142*5a5110d2SManos Pitsidianakis } 143*5a5110d2SManos Pitsidianakis } 144*5a5110d2SManos Pitsidianakis 145*5a5110d2SManos Pitsidianakis unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 146*5a5110d2SManos Pitsidianakis if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0)) 147*5a5110d2SManos Pitsidianakis { 148*5a5110d2SManos Pitsidianakis // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid 149*5a5110d2SManos Pitsidianakis // glib-allocated pointer, so `g_free`ing is safe. 150*5a5110d2SManos Pitsidianakis unsafe { g_free(ptr.cast::<_>()) } 151*5a5110d2SManos Pitsidianakis } else { 152*5a5110d2SManos Pitsidianakis #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)] 153*5a5110d2SManos Pitsidianakis { 154*5a5110d2SManos Pitsidianakis // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid aligned 155*5a5110d2SManos Pitsidianakis // glib-allocated pointer, so `g_aligned_free`ing is safe. 156*5a5110d2SManos Pitsidianakis unsafe { g_aligned_free(ptr.cast::<_>()) } 157*5a5110d2SManos Pitsidianakis } 158*5a5110d2SManos Pitsidianakis #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))] 159*5a5110d2SManos Pitsidianakis { 160*5a5110d2SManos Pitsidianakis // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid aligned 161*5a5110d2SManos Pitsidianakis // glib-allocated pointer, so `qemu_vfree`ing is safe. 162*5a5110d2SManos Pitsidianakis unsafe { qemu_vfree(ptr.cast::<_>()) } 163*5a5110d2SManos Pitsidianakis } 164*5a5110d2SManos Pitsidianakis } 165*5a5110d2SManos Pitsidianakis } 166*5a5110d2SManos Pitsidianakis } 167