mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-12-29 18:50:29 -07:00
offset_of! was stabilized in Rust 1.77.0. Use an alternative implemenation that was found on the Rust forums, and whose author agreed to license as MIT for use in QEMU. The alternative allows only one level of field access, but apart from this can be used just by replacing core::mem::offset_of! with qemu_api::offset_of!. The actual implementation of offset_of! is done in a declarative macro, but for simplicity and to avoid introducing an extra level of indentation, the trigger is a procedural macro #[derive(offsets)]. The procedural macro is perhaps a bit overengineered, but it helps introducing some idioms that will be useful in the future as well. Signed-off-by: Junjie Mao <junjie.mao@hotmail.com> Co-developed-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
175 lines
5.5 KiB
Rust
175 lines
5.5 KiB
Rust
// Copyright 2024, Linaro Limited
|
|
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#![cfg_attr(not(MESON), doc = include_str!("../README.md"))]
|
|
|
|
#[allow(
|
|
dead_code,
|
|
improper_ctypes_definitions,
|
|
improper_ctypes,
|
|
non_camel_case_types,
|
|
non_snake_case,
|
|
non_upper_case_globals,
|
|
unsafe_op_in_unsafe_fn,
|
|
clippy::missing_const_for_fn,
|
|
clippy::too_many_arguments,
|
|
clippy::approx_constant,
|
|
clippy::use_self,
|
|
clippy::useless_transmute,
|
|
clippy::missing_safety_doc,
|
|
)]
|
|
#[rustfmt::skip]
|
|
pub mod bindings;
|
|
|
|
unsafe impl Send for bindings::Property {}
|
|
unsafe impl Sync for bindings::Property {}
|
|
unsafe impl Sync for bindings::TypeInfo {}
|
|
unsafe impl Sync for bindings::VMStateDescription {}
|
|
unsafe impl Sync for bindings::VMStateField {}
|
|
unsafe impl Sync for bindings::VMStateInfo {}
|
|
|
|
pub mod c_str;
|
|
pub mod definitions;
|
|
pub mod device_class;
|
|
pub mod offset_of;
|
|
pub mod vmstate;
|
|
pub mod zeroable;
|
|
|
|
use std::{
|
|
alloc::{GlobalAlloc, Layout},
|
|
os::raw::c_void,
|
|
};
|
|
|
|
#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
|
|
extern "C" {
|
|
fn g_aligned_alloc0(
|
|
n_blocks: bindings::gsize,
|
|
n_block_bytes: bindings::gsize,
|
|
alignment: bindings::gsize,
|
|
) -> bindings::gpointer;
|
|
fn g_aligned_free(mem: bindings::gpointer);
|
|
}
|
|
|
|
#[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
|
|
extern "C" {
|
|
fn qemu_memalign(alignment: usize, size: usize) -> *mut c_void;
|
|
fn qemu_vfree(ptr: *mut c_void);
|
|
}
|
|
|
|
extern "C" {
|
|
fn g_malloc0(n_bytes: bindings::gsize) -> bindings::gpointer;
|
|
fn g_free(mem: bindings::gpointer);
|
|
}
|
|
|
|
/// An allocator that uses the same allocator as QEMU in C.
|
|
///
|
|
/// It is enabled by default with the `allocator` feature.
|
|
///
|
|
/// To set it up manually as a global allocator in your crate:
|
|
///
|
|
/// ```ignore
|
|
/// use qemu_api::QemuAllocator;
|
|
///
|
|
/// #[global_allocator]
|
|
/// static GLOBAL: QemuAllocator = QemuAllocator::new();
|
|
/// ```
|
|
#[derive(Clone, Copy, Debug)]
|
|
#[repr(C)]
|
|
pub struct QemuAllocator {
|
|
_unused: [u8; 0],
|
|
}
|
|
|
|
#[cfg_attr(all(feature = "allocator", not(test)), global_allocator)]
|
|
pub static GLOBAL: QemuAllocator = QemuAllocator::new();
|
|
|
|
impl QemuAllocator {
|
|
// From the glibc documentation, on GNU systems, malloc guarantees 16-byte
|
|
// alignment on 64-bit systems and 8-byte alignment on 32-bit systems. See
|
|
// https://www.gnu.org/software/libc/manual/html_node/Malloc-Examples.html.
|
|
// This alignment guarantee also applies to Windows and Android. On Darwin
|
|
// and OpenBSD, the alignment is 16 bytes on both 64-bit and 32-bit systems.
|
|
#[cfg(all(
|
|
target_pointer_width = "32",
|
|
not(any(target_os = "macos", target_os = "openbsd"))
|
|
))]
|
|
pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(8);
|
|
#[cfg(all(
|
|
target_pointer_width = "64",
|
|
not(any(target_os = "macos", target_os = "openbsd"))
|
|
))]
|
|
pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(16);
|
|
#[cfg(all(
|
|
any(target_pointer_width = "32", target_pointer_width = "64"),
|
|
any(target_os = "macos", target_os = "openbsd")
|
|
))]
|
|
pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(16);
|
|
#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
|
|
pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = None;
|
|
|
|
pub const fn new() -> Self {
|
|
Self { _unused: [] }
|
|
}
|
|
}
|
|
|
|
impl Default for QemuAllocator {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
// Sanity check.
|
|
const _: [(); 8] = [(); ::core::mem::size_of::<*mut c_void>()];
|
|
|
|
unsafe impl GlobalAlloc for QemuAllocator {
|
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
|
if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0))
|
|
{
|
|
// SAFETY: g_malloc0() is safe to call.
|
|
unsafe { g_malloc0(layout.size().try_into().unwrap()).cast::<u8>() }
|
|
} else {
|
|
#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
|
|
{
|
|
// SAFETY: g_aligned_alloc0() is safe to call.
|
|
unsafe {
|
|
g_aligned_alloc0(
|
|
layout.size().try_into().unwrap(),
|
|
1,
|
|
layout.align().try_into().unwrap(),
|
|
)
|
|
.cast::<u8>()
|
|
}
|
|
}
|
|
#[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
|
|
{
|
|
// SAFETY: qemu_memalign() is safe to call.
|
|
unsafe { qemu_memalign(layout.align(), layout.size()).cast::<u8>() }
|
|
}
|
|
}
|
|
}
|
|
|
|
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
|
if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0))
|
|
{
|
|
// SAFETY: `ptr` must have been allocated by Self::alloc thus a valid
|
|
// glib-allocated pointer, so `g_free`ing is safe.
|
|
unsafe { g_free(ptr.cast::<_>()) }
|
|
} else {
|
|
#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
|
|
{
|
|
// SAFETY: `ptr` must have been allocated by Self::alloc thus a valid aligned
|
|
// glib-allocated pointer, so `g_aligned_free`ing is safe.
|
|
unsafe { g_aligned_free(ptr.cast::<_>()) }
|
|
}
|
|
#[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
|
|
{
|
|
// SAFETY: `ptr` must have been allocated by Self::alloc thus a valid aligned
|
|
// glib-allocated pointer, so `qemu_vfree`ing is safe.
|
|
unsafe { qemu_vfree(ptr.cast::<_>()) }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(has_offset_of)]
|
|
pub use core::mem::offset_of;
|