diff options
author | Stefan Hajnoczi <stefanha@redhat.com> | 2025-01-29 09:51:03 -0500 |
---|---|---|
committer | Stefan Hajnoczi <stefanha@redhat.com> | 2025-01-29 09:51:03 -0500 |
commit | 871af84dd599fab68c8ed414d9ecbdb2bcfc5801 (patch) | |
tree | 508d69f0e934ceda69c18525c8871797036f2a05 /rust | |
parent | fb49b69bf9fd584546c7d946eaeec90941941d25 (diff) | |
parent | 3b36ee720288ba17962a17b305243ea34100e1f3 (diff) | |
download | qemu-871af84dd599fab68c8ed414d9ecbdb2bcfc5801.zip qemu-871af84dd599fab68c8ed414d9ecbdb2bcfc5801.tar.gz qemu-871af84dd599fab68c8ed414d9ecbdb2bcfc5801.tar.bz2 |
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* target/i386: optimize string instructions
* target/i386: new Sierra Forest and Clearwater Forest models
* rust: type-safe vmstate implementation
* rust: use interior mutability for PL011
* rust: clean ups
* memtxattrs: remove usage of bitfields from MEMTXATTRS_UNSPECIFIED
* gitlab-ci: enable Rust backtraces
# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmeZ6VYUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroMjbQgApuooMOp0z/8Ky4/ux8M8/vrlcNCH
# V1Pm6WzrjEzd9TIMLGr6npOyLOkWI31Aa4o/TuW09SeKE3dpCf/7LYA5VDEtkH79
# F57MgnSj56sMNgu+QZ/SiGvkKJXl+3091jIianrrI0dtX8hPonm6bt55woDvQt3z
# p94+4zzv5G0nc+ncITCDho8sn5itdZWVOjf9n6VCOumMjF4nRSoMkJKYIvjNht6n
# GtjMhYA70tzjkIi4bPyYkhFpMNlAqEDIp2TvPzp6klG5QoUErHIzdzoRTAtE4Dpb
# 7240r6jarQX41TBXGOFq0NrxES1cm5zO/6159D24qZGHGm2hG4nDx+t2jw==
# =ZKFy
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 29 Jan 2025 03:39:50 EST
# gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg: issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full]
# gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full]
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1
# Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83
* tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (49 commits)
gitlab-ci: include full Rust backtraces in test runs
rust: qemu-api: add sub-subclass to the integration tests
rust/zeroable: Implement Zeroable with const_zero macro
rust: qdev: make reset take a shared reference
rust: pl011: drop use of ControlFlow
rust: pl011: pull device-specific code out of MemoryRegionOps callbacks
rust: pl011: remove duplicate definitions
rust: pl011: wrap registers with BqlRefCell
rust: pl011: extract PL011Registers
rust: pl011: pull interrupt updates out of read/write ops
rust: pl011: extract CharBackend receive logic into a separate function
rust: pl011: extract conversion to RegisterOffset
rust: pl011: hide unnecessarily "pub" items from outside pl011::device
rust: pl011: remove unnecessary "extern crate"
rust: prefer NonNull::new to assertions
rust: vmstate: make order of parameters consistent in vmstate_clock
rust: vmstate: remove translation of C vmstate macros
rust: pl011: switch vmstate to new-style macros
rust: qemu_api: add vmstate_struct
rust: vmstate: add public utility macros to implement VMState
...
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'rust')
-rw-r--r-- | rust/hw/char/pl011/src/device.rs | 515 | ||||
-rw-r--r-- | rust/hw/char/pl011/src/device_class.rs | 73 | ||||
-rw-r--r-- | rust/hw/char/pl011/src/lib.rs | 69 | ||||
-rw-r--r-- | rust/hw/char/pl011/src/memory_ops.rs | 25 | ||||
-rw-r--r-- | rust/qemu-api/src/prelude.rs | 2 | ||||
-rw-r--r-- | rust/qemu-api/src/qdev.rs | 16 | ||||
-rw-r--r-- | rust/qemu-api/src/qom.rs | 21 | ||||
-rw-r--r-- | rust/qemu-api/src/vmstate.rs | 652 | ||||
-rw-r--r-- | rust/qemu-api/src/zeroable.rs | 118 | ||||
-rw-r--r-- | rust/qemu-api/tests/tests.rs | 56 |
10 files changed, 927 insertions, 620 deletions
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 994c2fc..8050ede 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,19 +2,27 @@ // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> // SPDX-License-Identifier: GPL-2.0-or-later -use core::ptr::{addr_of_mut, NonNull}; +use core::ptr::{addr_of, addr_of_mut, NonNull}; use std::{ ffi::CStr, - os::raw::{c_int, c_uint, c_void}, + os::raw::{c_int, c_void}, }; use qemu_api::{ - bindings::{self, *}, - c_str, + bindings::{ + error_fatal, hwaddr, memory_region_init_io, qdev_init_clock_in, qdev_new, + qdev_prop_set_chr, qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, + qemu_chr_fe_write_all, qemu_irq, sysbus_connect_irq, sysbus_mmio_map, + sysbus_realize_and_unref, CharBackend, Chardev, Clock, ClockEvent, MemoryRegion, + QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK, + }, + c_str, impl_vmstate_forward, irq::InterruptSource, prelude::*, - qdev::DeviceImpl, + qdev::{DeviceImpl, DeviceState, Property}, qom::{ClassInitImpl, ObjectImpl, ParentField}, + sysbus::{SysBusDevice, SysBusDeviceClass}, + vmstate::VMStateDescription, }; use crate::{ @@ -54,6 +62,7 @@ impl DeviceId { #[repr(transparent)] #[derive(Debug, Default)] pub struct Fifo([registers::Data; PL011_FIFO_DEPTH as usize]); +impl_vmstate_forward!(Fifo); impl Fifo { const fn len(&self) -> u32 { @@ -76,11 +85,8 @@ impl std::ops::Index<u32> for Fifo { } #[repr(C)] -#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)] -/// PL011 Device Model in QEMU -pub struct PL011State { - pub parent_obj: ParentField<SysBusDevice>, - pub iomem: MemoryRegion, +#[derive(Debug, Default, qemu_api_macros::offsets)] +pub struct PL011Registers { #[doc(alias = "fr")] pub flags: registers::Flags, #[doc(alias = "lcr")] @@ -100,8 +106,17 @@ pub struct PL011State { pub read_pos: u32, pub read_count: u32, pub read_trigger: u32, +} + +#[repr(C)] +#[derive(qemu_api_macros::Object, qemu_api_macros::offsets)] +/// PL011 Device Model in QEMU +pub struct PL011State { + pub parent_obj: ParentField<SysBusDevice>, + pub iomem: MemoryRegion, #[doc(alias = "chr")] pub char_backend: CharBackend, + pub regs: BqlRefCell<PL011Registers>, /// QEMU interrupts /// /// ```text @@ -123,6 +138,7 @@ pub struct PL011State { qom_isa!(PL011State : SysBusDevice, DeviceState, Object); +#[repr(C)] pub struct PL011Class { parent_class: <SysBusDevice as ObjectType>::Class, /// The byte string that identifies the device. @@ -155,77 +171,17 @@ impl DeviceImpl for PL011State { fn vmsd() -> Option<&'static VMStateDescription> { Some(&device_class::VMSTATE_PL011) } - const REALIZE: Option<fn(&mut Self)> = Some(Self::realize); - const RESET: Option<fn(&mut Self)> = Some(Self::reset); + const REALIZE: Option<fn(&Self)> = Some(Self::realize); + const RESET: Option<fn(&Self)> = Some(Self::reset); } -impl PL011State { - /// Initializes a pre-allocated, unitialized instance of `PL011State`. - /// - /// # Safety - /// - /// `self` must point to a correctly sized and aligned location for the - /// `PL011State` type. It must not be called more than once on the same - /// location/instance. All its fields are expected to hold unitialized - /// values with the sole exception of `parent_obj`. - unsafe fn init(&mut self) { - const CLK_NAME: &CStr = c_str!("clk"); - - // SAFETY: - // - // self and self.iomem are guaranteed to be valid at this point since callers - // must make sure the `self` reference is valid. - unsafe { - memory_region_init_io( - addr_of_mut!(self.iomem), - addr_of_mut!(*self).cast::<Object>(), - &PL011_OPS, - addr_of_mut!(*self).cast::<c_void>(), - Self::TYPE_NAME.as_ptr(), - 0x1000, - ); - } - - // SAFETY: - // - // self.clock is not initialized at this point; but since `NonNull<_>` is Copy, - // we can overwrite the undefined value without side effects. This is - // safe since all PL011State instances are created by QOM code which - // calls this function to initialize the fields; therefore no code is - // able to access an invalid self.clock value. - unsafe { - let dev: &mut DeviceState = self.upcast_mut(); - self.clock = NonNull::new(qdev_init_clock_in( - dev, - CLK_NAME.as_ptr(), - None, /* pl011_clock_update */ - addr_of_mut!(*self).cast::<c_void>(), - ClockEvent::ClockUpdate.0, - )) - .unwrap(); - } - } - - fn post_init(&self) { - self.init_mmio(&self.iomem); - for irq in self.interrupts.iter() { - self.init_irq(irq); - } - } - - pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow<u64, u64> { +impl PL011Registers { + pub(self) fn read(&mut self, offset: RegisterOffset) -> (bool, u32) { use RegisterOffset::*; - let value = match RegisterOffset::try_from(offset) { - Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { - let device_id = self.get_class().device_id; - u32::from(device_id[(offset - 0xfe0) >> 2]) - } - Err(_) => { - // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); - 0 - } - Ok(DR) => { + let mut update = false; + let result = match offset { + DR => { self.flags.set_receive_fifo_full(false); let c = self.read_fifo[self.read_pos]; if self.read_count > 0 { @@ -236,117 +192,109 @@ impl PL011State { self.flags.set_receive_fifo_empty(true); } if self.read_count + 1 == self.read_trigger { - self.int_level &= !registers::INT_RX; + self.int_level &= !Interrupt::RX.0; } // Update error bits. self.receive_status_error_clear.set_from_data(c); - self.update(); - // Must call qemu_chr_fe_accept_input, so return Continue: - let c = u32::from(c); - return std::ops::ControlFlow::Continue(u64::from(c)); + // Must call qemu_chr_fe_accept_input + update = true; + u32::from(c) } - Ok(RSR) => u32::from(self.receive_status_error_clear), - Ok(FR) => u32::from(self.flags), - Ok(FBRD) => self.fbrd, - Ok(ILPR) => self.ilpr, - Ok(IBRD) => self.ibrd, - Ok(LCR_H) => u32::from(self.line_control), - Ok(CR) => u32::from(self.control), - Ok(FLS) => self.ifl, - Ok(IMSC) => self.int_enabled, - Ok(RIS) => self.int_level, - Ok(MIS) => self.int_level & self.int_enabled, - Ok(ICR) => { + RSR => u32::from(self.receive_status_error_clear), + FR => u32::from(self.flags), + FBRD => self.fbrd, + ILPR => self.ilpr, + IBRD => self.ibrd, + LCR_H => u32::from(self.line_control), + CR => u32::from(self.control), + FLS => self.ifl, + IMSC => self.int_enabled, + RIS => self.int_level, + MIS => self.int_level & self.int_enabled, + ICR => { // "The UARTICR Register is the interrupt clear register and is write-only" // Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR 0 } - Ok(DMACR) => self.dmacr, + DMACR => self.dmacr, }; - std::ops::ControlFlow::Break(value.into()) + (update, result) } - pub fn write(&mut self, offset: hwaddr, value: u64) { + pub(self) fn write( + &mut self, + offset: RegisterOffset, + value: u32, + char_backend: *mut CharBackend, + ) -> bool { // eprintln!("write offset {offset} value {value}"); use RegisterOffset::*; - let value: u32 = value as u32; - match RegisterOffset::try_from(offset) { - Err(_bad_offset) => { - eprintln!("write bad offset {offset} value {value}"); + match offset { + DR => { + // interrupts always checked + let _ = self.loopback_tx(value); + self.int_level |= Interrupt::TX.0; + return true; } - Ok(DR) => { - // ??? Check if transmitter is enabled. - let ch: u8 = value as u8; - // XXX this blocks entire thread. Rewrite to use - // qemu_chr_fe_write and background I/O callbacks - - // SAFETY: self.char_backend is a valid CharBackend instance after it's been - // initialized in realize(). - unsafe { - qemu_chr_fe_write_all(addr_of_mut!(self.char_backend), &ch, 1); - } - self.loopback_tx(value); - self.int_level |= registers::INT_TX; - self.update(); - } - Ok(RSR) => { - self.receive_status_error_clear.reset(); + RSR => { + self.receive_status_error_clear = 0.into(); } - Ok(FR) => { + FR => { // flag writes are ignored } - Ok(ILPR) => { + ILPR => { self.ilpr = value; } - Ok(IBRD) => { + IBRD => { self.ibrd = value; } - Ok(FBRD) => { + FBRD => { self.fbrd = value; } - Ok(LCR_H) => { + LCR_H => { let new_val: registers::LineControl = value.into(); // Reset the FIFO state on FIFO enable or disable if self.line_control.fifos_enabled() != new_val.fifos_enabled() { self.reset_rx_fifo(); self.reset_tx_fifo(); } - if self.line_control.send_break() ^ new_val.send_break() { + let update = (self.line_control.send_break() != new_val.send_break()) && { let mut break_enable: c_int = new_val.send_break().into(); // SAFETY: self.char_backend is a valid CharBackend instance after it's been // initialized in realize(). unsafe { qemu_chr_fe_ioctl( - addr_of_mut!(self.char_backend), + char_backend, CHR_IOCTL_SERIAL_SET_BREAK as i32, addr_of_mut!(break_enable).cast::<c_void>(), ); } - self.loopback_break(break_enable > 0); - } + self.loopback_break(break_enable > 0) + }; self.line_control = new_val; self.set_read_trigger(); + return update; } - Ok(CR) => { + CR => { // ??? Need to implement the enable bit. self.control = value.into(); - self.loopback_mdmctrl(); + return self.loopback_mdmctrl(); } - Ok(FLS) => { + FLS => { self.ifl = value; self.set_read_trigger(); } - Ok(IMSC) => { + IMSC => { self.int_enabled = value; - self.update(); + return true; } - Ok(RIS) => {} - Ok(MIS) => {} - Ok(ICR) => { + RIS => {} + MIS => {} + ICR => { self.int_level &= !value; - self.update(); + return true; } - Ok(DMACR) => { + DMACR => { self.dmacr = value; if value & 3 > 0 { // qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n"); @@ -354,14 +302,12 @@ impl PL011State { } } } + false } #[inline] - fn loopback_tx(&mut self, value: u32) { - if !self.loopback_enabled() { - return; - } - + #[must_use] + fn loopback_tx(&mut self, value: u32) -> bool { // Caveat: // // In real hardware, TX loopback happens at the serial-bit level @@ -379,12 +325,13 @@ impl PL011State { // hardware flow-control is enabled. // // For simplicity, the above described is not emulated. - self.put_fifo(value); + self.loopback_enabled() && self.put_fifo(value) } - fn loopback_mdmctrl(&mut self) { + #[must_use] + fn loopback_mdmctrl(&mut self) -> bool { if !self.loopback_enabled() { - return; + return false; } /* @@ -410,51 +357,32 @@ impl PL011State { // Change interrupts based on updated FR let mut il = self.int_level; - il &= !Interrupt::MS; + il &= !Interrupt::MS.0; if self.flags.data_set_ready() { - il |= Interrupt::DSR as u32; + il |= Interrupt::DSR.0; } if self.flags.data_carrier_detect() { - il |= Interrupt::DCD as u32; + il |= Interrupt::DCD.0; } if self.flags.clear_to_send() { - il |= Interrupt::CTS as u32; + il |= Interrupt::CTS.0; } if self.flags.ring_indicator() { - il |= Interrupt::RI as u32; + il |= Interrupt::RI.0; } self.int_level = il; - self.update(); + true } - fn loopback_break(&mut self, enable: bool) { - if enable { - self.loopback_tx(registers::Data::BREAK.into()); - } + fn loopback_break(&mut self, enable: bool) -> bool { + enable && self.loopback_tx(registers::Data::BREAK.into()) } fn set_read_trigger(&mut self) { self.read_trigger = 1; } - pub fn realize(&mut self) { - // SAFETY: self.char_backend has the correct size and alignment for a - // CharBackend object, and its callbacks are of the correct types. - unsafe { - qemu_chr_fe_set_handlers( - addr_of_mut!(self.char_backend), - Some(pl011_can_receive), - Some(pl011_receive), - Some(pl011_event), - None, - addr_of_mut!(*self).cast::<c_void>(), - core::ptr::null_mut(), - true, - ); - } - } - pub fn reset(&mut self) { self.line_control.reset(); self.receive_status_error_clear.reset(); @@ -487,17 +415,6 @@ impl PL011State { self.flags.set_transmit_fifo_empty(true); } - pub fn can_receive(&self) -> bool { - // trace_pl011_can_receive(s->lcr, s->read_count, r); - self.read_count < self.fifo_depth() - } - - pub fn event(&mut self, event: QEMUChrEvent) { - if event == bindings::QEMUChrEvent::CHR_EVENT_BREAK && !self.loopback_enabled() { - self.put_fifo(registers::Data::BREAK.into()); - } - } - #[inline] pub fn fifo_enabled(&self) -> bool { self.line_control.fifos_enabled() == registers::Mode::FIFO @@ -517,7 +434,8 @@ impl PL011State { 1 } - pub fn put_fifo(&mut self, value: c_uint) { + #[must_use] + pub fn put_fifo(&mut self, value: u32) -> bool { let depth = self.fifo_depth(); assert!(depth > 0); let slot = (self.read_pos + self.read_count) & (depth - 1); @@ -529,19 +447,13 @@ impl PL011State { } if self.read_count == self.read_trigger { - self.int_level |= registers::INT_RX; - self.update(); + self.int_level |= Interrupt::RX.0; + return true; } + false } - pub fn update(&self) { - let flags = self.int_level & self.int_enabled; - for (irq, i) in self.interrupts.iter().zip(IRQMASK) { - irq.set(flags & i != 0); - } - } - - pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> { + pub fn post_load(&mut self) -> Result<(), ()> { /* Sanity-check input state */ if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() { return Err(()); @@ -563,19 +475,188 @@ impl PL011State { } } +impl PL011State { + /// Initializes a pre-allocated, unitialized instance of `PL011State`. + /// + /// # Safety + /// + /// `self` must point to a correctly sized and aligned location for the + /// `PL011State` type. It must not be called more than once on the same + /// location/instance. All its fields are expected to hold unitialized + /// values with the sole exception of `parent_obj`. + unsafe fn init(&mut self) { + const CLK_NAME: &CStr = c_str!("clk"); + + // SAFETY: + // + // self and self.iomem are guaranteed to be valid at this point since callers + // must make sure the `self` reference is valid. + unsafe { + memory_region_init_io( + addr_of_mut!(self.iomem), + addr_of_mut!(*self).cast::<Object>(), + &PL011_OPS, + addr_of_mut!(*self).cast::<c_void>(), + Self::TYPE_NAME.as_ptr(), + 0x1000, + ); + } + + self.regs = Default::default(); + + // SAFETY: + // + // self.clock is not initialized at this point; but since `NonNull<_>` is Copy, + // we can overwrite the undefined value without side effects. This is + // safe since all PL011State instances are created by QOM code which + // calls this function to initialize the fields; therefore no code is + // able to access an invalid self.clock value. + unsafe { + let dev: &mut DeviceState = self.upcast_mut(); + self.clock = NonNull::new(qdev_init_clock_in( + dev, + CLK_NAME.as_ptr(), + None, /* pl011_clock_update */ + addr_of_mut!(*self).cast::<c_void>(), + ClockEvent::ClockUpdate.0, + )) + .unwrap(); + } + } + + fn post_init(&self) { + self.init_mmio(&self.iomem); + for irq in self.interrupts.iter() { + self.init_irq(irq); + } + } + + pub fn read(&mut self, offset: hwaddr, _size: u32) -> u64 { + match RegisterOffset::try_from(offset) { + Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { + let device_id = self.get_class().device_id; + u64::from(device_id[(offset - 0xfe0) >> 2]) + } + Err(_) => { + // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); + 0 + } + Ok(field) => { + let (update_irq, result) = self.regs.borrow_mut().read(field); + if update_irq { + self.update(); + unsafe { + qemu_chr_fe_accept_input(&mut self.char_backend); + } + } + result.into() + } + } + } + + pub fn write(&mut self, offset: hwaddr, value: u64) { + let mut update_irq = false; + if let Ok(field) = RegisterOffset::try_from(offset) { + // qemu_chr_fe_write_all() calls into the can_receive + // callback, so handle writes before entering PL011Registers. + if field == RegisterOffset::DR { + // ??? Check if transmitter is enabled. + let ch: u8 = value as u8; + // SAFETY: char_backend is a valid CharBackend instance after it's been + // initialized in realize(). + // XXX this blocks entire thread. Rewrite to use + // qemu_chr_fe_write and background I/O callbacks + unsafe { + qemu_chr_fe_write_all(&mut self.char_backend, &ch, 1); + } + } + + update_irq = self + .regs + .borrow_mut() + .write(field, value as u32, &mut self.char_backend); + } else { + eprintln!("write bad offset {offset} value {value}"); + } + if update_irq { + self.update(); + } + } + + pub fn can_receive(&self) -> bool { + // trace_pl011_can_receive(s->lcr, s->read_count, r); + let regs = self.regs.borrow(); + regs.read_count < regs.fifo_depth() + } + + pub fn receive(&self, ch: u32) { + let mut regs = self.regs.borrow_mut(); + let update_irq = !regs.loopback_enabled() && regs.put_fifo(ch); + // Release the BqlRefCell before calling self.update() + drop(regs); + + if update_irq { + self.update(); + } + } + + pub fn event(&self, event: QEMUChrEvent) { + let mut update_irq = false; + let mut regs = self.regs.borrow_mut(); + if event == QEMUChrEvent::CHR_EVENT_BREAK && !regs.loopback_enabled() { + update_irq = regs.put_fifo(registers::Data::BREAK.into()); + } + // Release the BqlRefCell before calling self.update() + drop(regs); + + if update_irq { + self.update() + } + } + + pub fn realize(&self) { + // SAFETY: self.char_backend has the correct size and alignment for a + // CharBackend object, and its callbacks are of the correct types. + unsafe { + qemu_chr_fe_set_handlers( + addr_of!(self.char_backend) as *mut CharBackend, + Some(pl011_can_receive), + Some(pl011_receive), + Some(pl011_event), + None, + addr_of!(*self).cast::<c_void>() as *mut c_void, + core::ptr::null_mut(), + true, + ); + } + } + + pub fn reset(&self) { + self.regs.borrow_mut().reset(); + } + + pub fn update(&self) { + let regs = self.regs.borrow(); + let flags = regs.int_level & regs.int_enabled; + for (irq, i) in self.interrupts.iter().zip(IRQMASK) { + irq.set(flags & i != 0); + } + } + + pub fn post_load(&self, _version_id: u32) -> Result<(), ()> { + self.regs.borrow_mut().post_load() + } +} + /// Which bits in the interrupt status matter for each outbound IRQ line ? -pub const IRQMASK: [u32; 6] = [ +const IRQMASK: [u32; 6] = [ /* combined IRQ */ - Interrupt::E - | Interrupt::MS - | Interrupt::RT as u32 - | Interrupt::TX as u32 - | Interrupt::RX as u32, - Interrupt::RX as u32, - Interrupt::TX as u32, - Interrupt::RT as u32, - Interrupt::MS, - Interrupt::E, + Interrupt::E.0 | Interrupt::MS.0 | Interrupt::RT.0 | Interrupt::TX.0 | Interrupt::RX.0, + Interrupt::RX.0, + Interrupt::TX.0, + Interrupt::RT.0, + Interrupt::MS.0, + Interrupt::E.0, ]; /// # Safety @@ -584,11 +665,8 @@ pub const IRQMASK: [u32; 6] = [ /// the same size as [`PL011State`]. We also expect the device is /// readable/writeable from one thread at any time. pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int { - unsafe { - debug_assert!(!opaque.is_null()); - let state = NonNull::new_unchecked(opaque.cast::<PL011State>()); - state.as_ref().can_receive().into() - } + let state = NonNull::new(opaque).unwrap().cast::<PL011State>(); + unsafe { state.as_ref().can_receive().into() } } /// # Safety @@ -599,15 +677,11 @@ pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int { /// /// The buffer and size arguments must also be valid. pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size: c_int) { + let state = NonNull::new(opaque).unwrap().cast::<PL011State>(); unsafe { - debug_assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>()); - if state.as_ref().loopback_enabled() { - return; - } if size > 0 { debug_assert!(!buf.is_null()); - state.as_mut().put_fifo(c_uint::from(buf.read_volatile())) + state.as_ref().receive(u32::from(buf.read_volatile())); } } } @@ -618,11 +692,8 @@ pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size /// the same size as [`PL011State`]. We also expect the device is /// readable/writeable from one thread at any time. pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) { - unsafe { - debug_assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>()); - state.as_mut().event(event) - } + let state = NonNull::new(opaque).unwrap().cast::<PL011State>(); + unsafe { state.as_ref().event(event) } } /// # Safety @@ -647,7 +718,7 @@ pub unsafe extern "C" fn pl011_create( } #[repr(C)] -#[derive(Debug, qemu_api_macros::Object)] +#[derive(qemu_api_macros::Object)] /// PL011 Luminary device model. pub struct PL011Luminary { parent_obj: ParentField<PL011State>, diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index 7f3ca89..8a157a6 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -6,45 +6,64 @@ use core::ptr::NonNull; use std::os::raw::{c_int, c_void}; use qemu_api::{ - bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_subsections, vmstate_uint32, - vmstate_uint32_array, vmstate_unused, zeroable::Zeroable, + bindings::*, c_str, prelude::*, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, + vmstate_subsections, vmstate_unused, zeroable::Zeroable, }; -use crate::device::{PL011State, PL011_FIFO_DEPTH}; +use crate::device::{PL011Registers, PL011State}; +#[allow(clippy::missing_const_for_fn)] extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { - unsafe { - debug_assert!(!opaque.is_null()); - let state = NonNull::new_unchecked(opaque.cast::<PL011State>()); - state.as_ref().migrate_clock - } + let state = NonNull::new(opaque).unwrap().cast::<PL011State>(); + unsafe { state.as_ref().migrate_clock } } /// Migration subsection for [`PL011State`] clock. -pub static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { +static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { name: c_str!("pl011/clock").as_ptr(), version_id: 1, minimum_version_id: 1, needed: Some(pl011_clock_needed), fields: vmstate_fields! { - vmstate_clock!(clock, PL011State), + vmstate_clock!(PL011State, clock), }, ..Zeroable::ZERO }; extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { - unsafe { - debug_assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>()); - let result = state.as_mut().post_load(version_id as u32); - if result.is_err() { - -1 - } else { - 0 - } + let state = NonNull::new(opaque).unwrap().cast::<PL011State>(); + let result = unsafe { state.as_ref().post_load(version_id as u32) }; + if result.is_err() { + -1 + } else { + 0 } } +static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription { + name: c_str!("pl011/regs").as_ptr(), + version_id: 2, + minimum_version_id: 2, + fields: vmstate_fields! { + vmstate_of!(PL011Registers, flags), + vmstate_of!(PL011Registers, line_control), + vmstate_of!(PL011Registers, receive_status_error_clear), + vmstate_of!(PL011Registers, control), + vmstate_of!(PL011Registers, dmacr), + vmstate_of!(PL011Registers, int_enabled), + vmstate_of!(PL011Registers, int_level), + vmstate_of!(PL011Registers, read_fifo), + vmstate_of!(PL011Registers, ilpr), + vmstate_of!(PL011Registers, ibrd), + vmstate_of!(PL011Registers, fbrd), + vmstate_of!(PL011Registers, ifl), + vmstate_of!(PL011Registers, read_pos), + vmstate_of!(PL011Registers, read_count), + vmstate_of!(PL011Registers, read_trigger), + }, + ..Zeroable::ZERO +}; + pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { name: c_str!("pl011").as_ptr(), version_id: 2, @@ -52,21 +71,7 @@ pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { post_load: Some(pl011_post_load), fields: vmstate_fields! { vmstate_unused!(core::mem::size_of::<u32>()), - vmstate_uint32!(flags, PL011State), - vmstate_uint32!(line_control, PL011State), - vmstate_uint32!(receive_status_error_clear, PL011State), - vmstate_uint32!(control, PL011State), - vmstate_uint32!(dmacr, PL011State), - vmstate_uint32!(int_enabled, PL011State), - vmstate_uint32!(int_level, PL011State), - vmstate_uint32_array!(read_fifo, PL011State, PL011_FIFO_DEPTH), - vmstate_uint32!(ilpr, PL011State), - vmstate_uint32!(ibrd, PL011State), - vmstate_uint32!(fbrd, PL011State), - vmstate_uint32!(ifl, PL011State), - vmstate_uint32!(read_pos, PL011State), - vmstate_uint32!(read_count, PL011State), - vmstate_uint32!(read_trigger, PL011State), + vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell<PL011Registers>), }, subsections: vmstate_subsections! { VMSTATE_PL011_CLOCK diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 0a89d39..e2df458 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -25,15 +25,13 @@ #![allow(clippy::upper_case_acronyms)] #![allow(clippy::result_unit_err)] -extern crate bilge; -extern crate bilge_impl; -extern crate qemu_api; - use qemu_api::c_str; -pub mod device; -pub mod device_class; -pub mod memory_ops; +mod device; +mod device_class; +mod memory_ops; + +pub use device::pl011_create; pub const TYPE_PL011: &::std::ffi::CStr = c_str!("pl011"); pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary"); @@ -45,8 +43,8 @@ pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary"); #[doc(alias = "offset")] #[allow(non_camel_case_types)] #[repr(u64)] -#[derive(Debug, qemu_api_macros::TryInto)] -pub enum RegisterOffset { +#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)] +enum RegisterOffset { /// Data Register /// /// A write to this register initiates the actual data transmission @@ -102,10 +100,11 @@ pub enum RegisterOffset { //Reserved = 0x04C, } -pub mod registers { +mod registers { //! Device registers exposed as typed structs which are backed by arbitrary //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. use bilge::prelude::*; + use qemu_api::impl_vmstate_bitsized; /// Receive Status Register / Data Register common error bits /// @@ -172,6 +171,7 @@ pub mod registers { pub errors: Errors, _reserved: u16, } + impl_vmstate_bitsized!(Data); impl Data { // bilge is not very const-friendly, unfortunately @@ -208,6 +208,7 @@ pub mod registers { pub errors: Errors, _reserved_unpredictable: u24, } + impl_vmstate_bitsized!(ReceiveStatusErrorClear); impl ReceiveStatusErrorClear { pub fn set_from_data(&mut self, data: Data) { @@ -280,6 +281,7 @@ pub mod registers { pub ring_indicator: bool, _reserved_zero_no_modify: u23, } + impl_vmstate_bitsized!(Flags); impl Flags { pub fn reset(&mut self) { @@ -354,6 +356,7 @@ pub mod registers { /// 31:8 - Reserved, do not modify, read as zero. _reserved_zero_no_modify: u24, } + impl_vmstate_bitsized!(LineControl); impl LineControl { pub fn reset(&mut self) { @@ -498,6 +501,7 @@ pub mod registers { /// 31:16 - Reserved, do not modify, read as zero. _reserved_zero_no_modify2: u16, } + impl_vmstate_bitsized!(Control); impl Control { pub fn reset(&mut self) { @@ -516,38 +520,23 @@ pub mod registers { } /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC - pub const INT_OE: u32 = 1 << 10; - pub const INT_BE: u32 = 1 << 9; - pub const INT_PE: u32 = 1 << 8; - pub const INT_FE: u32 = 1 << 7; - pub const INT_RT: u32 = 1 << 6; - pub const INT_TX: u32 = 1 << 5; - pub const INT_RX: u32 = 1 << 4; - pub const INT_DSR: u32 = 1 << 3; - pub const INT_DCD: u32 = 1 << 2; - pub const INT_CTS: u32 = 1 << 1; - pub const INT_RI: u32 = 1 << 0; - pub const INT_E: u32 = INT_OE | INT_BE | INT_PE | INT_FE; - pub const INT_MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS; - - #[repr(u32)] - pub enum Interrupt { - OE = 1 << 10, - BE = 1 << 9, - PE = 1 << 8, - FE = 1 << 7, - RT = 1 << 6, - TX = 1 << 5, - RX = 1 << 4, - DSR = 1 << 3, - DCD = 1 << 2, - CTS = 1 << 1, - RI = 1 << 0, - } + pub struct Interrupt(pub u32); impl Interrupt { - pub const E: u32 = INT_OE | INT_BE | INT_PE | INT_FE; - pub const MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS; + pub const OE: Self = Self(1 << 10); + pub const BE: Self = Self(1 << 9); + pub const PE: Self = Self(1 << 8); + pub const FE: Self = Self(1 << 7); + pub const RT: Self = Self(1 << 6); + pub const TX: Self = Self(1 << 5); + pub const RX: Self = Self(1 << 4); + pub const DSR: Self = Self(1 << 3); + pub const DCD: Self = Self(1 << 2); + pub const CTS: Self = Self(1 << 1); + pub const RI: Self = Self(1 << 0); + + pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | Self::FE.0); + pub const MS: Self = Self(Self::RI.0 | Self::DSR.0 | Self::DCD.0 | Self::CTS.0); } } diff --git a/rust/hw/char/pl011/src/memory_ops.rs b/rust/hw/char/pl011/src/memory_ops.rs index c4e8599..432d326 100644 --- a/rust/hw/char/pl011/src/memory_ops.rs +++ b/rust/hw/char/pl011/src/memory_ops.rs @@ -24,28 +24,11 @@ pub static PL011_OPS: MemoryRegionOps = MemoryRegionOps { }; unsafe extern "C" fn pl011_read(opaque: *mut c_void, addr: hwaddr, size: c_uint) -> u64 { - assert!(!opaque.is_null()); - let mut state = unsafe { NonNull::new_unchecked(opaque.cast::<PL011State>()) }; - let val = unsafe { state.as_mut().read(addr, size) }; - match val { - std::ops::ControlFlow::Break(val) => val, - std::ops::ControlFlow::Continue(val) => { - // SAFETY: self.char_backend is a valid CharBackend instance after it's been - // initialized in realize(). - let cb_ptr = unsafe { core::ptr::addr_of_mut!(state.as_mut().char_backend) }; - unsafe { - qemu_chr_fe_accept_input(cb_ptr); - } - - val - } - } + let mut state = NonNull::new(opaque).unwrap().cast::<PL011State>(); + unsafe { state.as_mut() }.read(addr, size) } unsafe extern "C" fn pl011_write(opaque: *mut c_void, addr: hwaddr, data: u64, _size: c_uint) { - unsafe { - assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>()); - state.as_mut().write(addr, data) - } + let mut state = NonNull::new(opaque).unwrap().cast::<PL011State>(); + unsafe { state.as_mut() }.write(addr, data); } diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 4ea70b9..2dc86e1 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -18,3 +18,5 @@ pub use crate::qom::ObjectType; pub use crate::qom_isa; pub use crate::sysbus::SysBusDeviceMethods; + +pub use crate::vmstate::VMState; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 686054e..f4c75c7 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -4,7 +4,7 @@ //! Bindings to create devices and access device functionality from Rust. -use std::ffi::CStr; +use std::{ffi::CStr, ptr::NonNull}; pub use bindings::{DeviceClass, DeviceState, Property}; @@ -23,14 +23,14 @@ pub trait DeviceImpl { /// /// If not `None`, the parent class's `realize` method is overridden /// with the function pointed to by `REALIZE`. - const REALIZE: Option<fn(&mut Self)> = None; + const REALIZE: Option<fn(&Self)> = None; /// If not `None`, the parent class's `reset` method is overridden /// with the function pointed to by `RESET`. /// /// Rust does not yet support the three-phase reset protocol; this is /// usually okay for leaf classes. - const RESET: Option<fn(&mut Self)> = None; + const RESET: Option<fn(&Self)> = None; /// An array providing the properties that the user can set on the /// device. Not a `const` because referencing statics in constants @@ -55,9 +55,8 @@ pub trait DeviceImpl { /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(dev: *mut DeviceState, _errp: *mut *mut Error) { - assert!(!dev.is_null()); - let state = dev.cast::<T>(); - T::REALIZE.unwrap()(unsafe { &mut *state }); + let state = NonNull::new(dev).unwrap().cast::<T>(); + T::REALIZE.unwrap()(unsafe { state.as_ref() }); } /// # Safety @@ -66,9 +65,8 @@ unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(dev: *mut DeviceState, _errp /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. unsafe extern "C" fn rust_reset_fn<T: DeviceImpl>(dev: *mut DeviceState) { - assert!(!dev.is_null()); - let state = dev.cast::<T>(); - T::RESET.unwrap()(unsafe { &mut *state }); + let mut state = NonNull::new(dev).unwrap().cast::<T>(); + T::RESET.unwrap()(unsafe { state.as_mut() }); } impl<T> ClassInitImpl<DeviceClass> for T diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 97901fb..f50ee37 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -58,6 +58,7 @@ use std::{ fmt, ops::{Deref, DerefMut}, os::raw::c_void, + ptr::NonNull, }; pub use bindings::{Object, ObjectClass}; @@ -153,27 +154,34 @@ impl<T: fmt::Display + ObjectType> fmt::Display for ParentField<T> { } unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) { + let mut state = NonNull::new(obj).unwrap().cast::<T>(); // SAFETY: obj is an instance of T, since rust_instance_init<T> // is called from QOM core as the instance_init function // for class T - unsafe { T::INSTANCE_INIT.unwrap()(&mut *obj.cast::<T>()) } + unsafe { + T::INSTANCE_INIT.unwrap()(state.as_mut()); + } } unsafe extern "C" fn rust_instance_post_init<T: ObjectImpl>(obj: *mut Object) { + let state = NonNull::new(obj).unwrap().cast::<T>(); // SAFETY: obj is an instance of T, since rust_instance_post_init<T> // is called from QOM core as the instance_post_init function // for class T - T::INSTANCE_POST_INIT.unwrap()(unsafe { &*obj.cast::<T>() }) + T::INSTANCE_POST_INIT.unwrap()(unsafe { state.as_ref() }); } unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>( klass: *mut ObjectClass, _data: *mut c_void, ) { + let mut klass = NonNull::new(klass) + .unwrap() + .cast::<<T as ObjectType>::Class>(); // SAFETY: klass is a T::Class, since rust_class_init<T> // is called from QOM core as the class_init function // for class T - T::class_init(unsafe { &mut *klass.cast::<T::Class>() }) + T::class_init(unsafe { klass.as_mut() }) } unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut Object) { @@ -581,11 +589,8 @@ pub trait ClassInitImpl<T> { /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. unsafe extern "C" fn rust_unparent_fn<T: ObjectImpl>(dev: *mut Object) { - unsafe { - assert!(!dev.is_null()); - let state = core::ptr::NonNull::new_unchecked(dev.cast::<T>()); - T::UNPARENT.unwrap()(state.as_ref()); - } + let state = NonNull::new(dev).unwrap().cast::<T>(); + T::UNPARENT.unwrap()(unsafe { state.as_ref() }); } impl<T> ClassInitImpl<ObjectClass> for T diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 63c897a..6ac432c 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -4,277 +4,477 @@ //! Helper macros to declare migration state for device models. //! -//! Some macros are direct equivalents to the C macros declared in -//! `include/migration/vmstate.h` while -//! [`vmstate_subsections`](crate::vmstate_subsections) and -//! [`vmstate_fields`](crate::vmstate_fields) are meant to be used when -//! declaring a device model state struct. +//! This module includes four families of macros: +//! +//! * [`vmstate_unused!`](crate::vmstate_unused) and +//! [`vmstate_of!`](crate::vmstate_of), which are used to express the +//! migration format for a struct. This is based on the [`VMState`] trait, +//! which is defined by all migrateable types. +//! +//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and +//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), which help with +//! the definition of the [`VMState`] trait (respectively for transparent +//! structs and for `bilge`-defined types) +//! +//! * helper macros to declare a device model state struct, in particular +//! [`vmstate_subsections`](crate::vmstate_subsections) and +//! [`vmstate_fields`](crate::vmstate_fields). +//! +//! * direct equivalents to the C macros declared in +//! `include/migration/vmstate.h`. These are not type-safe and only provide +//! functionality that is missing from `vmstate_of!`. -pub use crate::bindings::VMStateDescription; +use core::{marker::PhantomData, mem, ptr::NonNull}; -#[doc(alias = "VMSTATE_UNUSED_BUFFER")] +pub use crate::bindings::{VMStateDescription, VMStateField}; +use crate::{ + bindings::{self, VMStateFlags}, + zeroable::Zeroable, +}; + +/// This macro is used to call a function with a generic argument bound +/// to the type of a field. The function must take a +/// [`PhantomData`]`<T>` argument; `T` is the type of +/// field `$field` in the `$typ` type. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::call_func_with_field; +/// # use core::marker::PhantomData; +/// const fn size_of_field<T>(_: PhantomData<T>) -> usize { +/// std::mem::size_of::<T>() +/// } +/// +/// struct Foo { +/// x: u16, +/// }; +/// // calls size_of_field::<u16>() +/// assert_eq!(call_func_with_field!(size_of_field, Foo, x), 2); +/// ``` #[macro_export] -macro_rules! vmstate_unused_buffer { - ($field_exists_fn:expr, $version_id:expr, $size:expr) => {{ - $crate::bindings::VMStateField { - name: c_str!("unused").as_ptr(), - err_hint: ::core::ptr::null(), - offset: 0, - size: $size, - start: 0, - num: 0, - num_offset: 0, - size_offset: 0, - info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, - flags: VMStateFlags::VMS_BUFFER, - vmsd: ::core::ptr::null(), - version_id: $version_id, - struct_version_id: 0, - field_exists: $field_exists_fn, - } - }}; +macro_rules! call_func_with_field { + // Based on the answer by user steffahn (Frank Steffahn) at + // https://users.rust-lang.org/t/inferring-type-of-field/122857 + // and used under MIT license + ($func:expr, $typ:ty, $($field:tt).+) => { + $func(loop { + #![allow(unreachable_code)] + const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { ::core::marker::PhantomData } + // Unreachable code is exempt from checks on uninitialized values. + // Use that trick to infer the type of this PhantomData. + break ::core::marker::PhantomData; + break phantom__(&{ let value__: $typ; value__.$($field).+ }); + }) + }; } -#[doc(alias = "VMSTATE_UNUSED_V")] -#[macro_export] -macro_rules! vmstate_unused_v { - ($version_id:expr, $size:expr) => {{ - $crate::vmstate_unused_buffer!(None, $version_id, $size) - }}; +/// Workaround for lack of `const_refs_static`: references to global variables +/// can be included in a `static`, but not in a `const`; unfortunately, this +/// is exactly what would go in the `VMStateField`'s `info` member. +/// +/// This enum contains the contents of the `VMStateField`'s `info` member, +/// but as an `enum` instead of a pointer. +#[allow(non_camel_case_types)] +pub enum VMStateFieldType { + null, + vmstate_info_bool, + vmstate_info_int8, + vmstate_info_int16, + vmstate_info_int32, + vmstate_info_int64, + vmstate_info_uint8, + vmstate_info_uint16, + vmstate_info_uint32, + vmstate_info_uint64, + vmstate_info_timer, } -#[doc(alias = "VMSTATE_UNUSED")] +/// Workaround for lack of `const_refs_static`. Converts a `VMStateFieldType` +/// to a `*const VMStateInfo`, for inclusion in a `VMStateField`. #[macro_export] -macro_rules! vmstate_unused { - ($size:expr) => {{ - $crate::vmstate_unused_v!(0, $size) - }}; +macro_rules! info_enum_to_ref { + ($e:expr) => { + unsafe { + match $e { + $crate::vmstate::VMStateFieldType::null => ::core::ptr::null(), + $crate::vmstate::VMStateFieldType::vmstate_info_bool => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_bool) + } + $crate::vmstate::VMStateFieldType::vmstate_info_int8 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_int8) + } + $crate::vmstate::VMStateFieldType::vmstate_info_int16 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_int16) + } + $crate::vmstate::VMStateFieldType::vmstate_info_int32 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_int32) + } + $crate::vmstate::VMStateFieldType::vmstate_info_int64 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_int64) + } + $crate::vmstate::VMStateFieldType::vmstate_info_uint8 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint8) + } + $crate::vmstate::VMStateFieldType::vmstate_info_uint16 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint16) + } + $crate::vmstate::VMStateFieldType::vmstate_info_uint32 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32) + } + $crate::vmstate::VMStateFieldType::vmstate_info_uint64 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint64) + } + $crate::vmstate::VMStateFieldType::vmstate_info_timer => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_timer) + } + } + } + }; +} + +/// A trait for types that can be included in a device's migration stream. It +/// provides the base contents of a `VMStateField` (minus the name and offset). +/// +/// # Safety +/// +/// The contents of this trait go straight into structs that are parsed by C +/// code and used to introspect into other structs. Generally, you don't need +/// to implement it except via macros that do it for you, such as +/// `impl_vmstate_bitsized!`. +pub unsafe trait VMState { + /// The `info` member of a `VMStateField` is a pointer and as such cannot + /// yet be included in the [`BASE`](VMState::BASE) associated constant; + /// this is only allowed by Rust 1.83.0 and newer. For now, include the + /// member as an enum which is stored in a separate constant. + const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::null; + + /// The base contents of a `VMStateField` (minus the name and offset) for + /// the type that is implementing the trait. + const BASE: VMStateField; + + /// A flag that is added to another field's `VMStateField` to specify the + /// length's type in a variable-sized array. If this is not a supported + /// type for the length (i.e. if it is not `u8`, `u16`, `u32`), using it + /// in a call to [`vmstate_of!`](crate::vmstate_of) will cause a + /// compile-time error. + const VARRAY_FLAG: VMStateFlags = { + panic!("invalid type for variable-sized array"); + }; +} + +/// Internal utility function to retrieve a type's `VMStateFieldType`; +/// used by [`vmstate_of!`](crate::vmstate_of). +pub const fn vmstate_scalar_type<T: VMState>(_: PhantomData<T>) -> VMStateFieldType { + T::SCALAR_TYPE +} + +/// Internal utility function to retrieve a type's `VMStateField`; +/// used by [`vmstate_of!`](crate::vmstate_of). +pub const fn vmstate_base<T: VMState>(_: PhantomData<T>) -> VMStateField { + T::BASE +} + +/// Internal utility function to retrieve a type's `VMStateFlags` when it +/// is used as the element count of a `VMSTATE_VARRAY`; used by +/// [`vmstate_of!`](crate::vmstate_of). +pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags { + T::VARRAY_FLAG } -#[doc(alias = "VMSTATE_SINGLE_TEST")] +/// Return the `VMStateField` for a field of a struct. The field must be +/// visible in the current scope. +/// +/// Only a limited set of types is supported out of the box: +/// * scalar types (integer and `bool`) +/// * the C struct `QEMUTimer` +/// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, +/// [`BqlCell`](crate::cell::BqlCell), [`BqlRefCell`](crate::cell::BqlRefCell) +/// * a raw pointer to any of the above +/// * a `NonNull` pointer or a `Box` for any of the above +/// * an array of any of the above +/// +/// In order to support other types, the trait `VMState` must be implemented +/// for them. The macros +/// [`impl_vmstate_bitsized!`](crate::impl_vmstate_bitsized) +/// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) help with this. #[macro_export] -macro_rules! vmstate_single_test { - ($field_name:ident, $struct_name:ty, $field_exists_fn:expr, $version_id:expr, $info:expr, $size:expr) => {{ +macro_rules! vmstate_of { + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(,)?) => { $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), 0) + name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() .as_ptr() as *const ::std::os::raw::c_char, - err_hint: ::core::ptr::null(), offset: $crate::offset_of!($struct_name, $field_name), - size: $size, - start: 0, - num: 0, - num_offset: 0, - size_offset: 0, - info: unsafe { $info }, - flags: VMStateFlags::VMS_SINGLE, - vmsd: ::core::ptr::null(), - version_id: $version_id, - struct_version_id: 0, - field_exists: $field_exists_fn, + $(.num_offset: $crate::offset_of!($struct_name, $num),)? + // The calls to `call_func_with_field!` are the magic that + // computes most of the VMStateField from the type of the field. + info: $crate::info_enum_to_ref!($crate::call_func_with_field!( + $crate::vmstate::vmstate_scalar_type, + $struct_name, + $field_name + )), + ..$crate::call_func_with_field!( + $crate::vmstate::vmstate_base, + $struct_name, + $field_name + )$(.with_varray_flag($crate::call_func_with_field!( + $crate::vmstate::vmstate_varray_flag, + $struct_name, + $num)) + $(.with_varray_multiply($factor))?)? } - }}; + }; } -#[doc(alias = "VMSTATE_SINGLE")] -#[macro_export] -macro_rules! vmstate_single { - ($field_name:ident, $struct_name:ty, $version_id:expr, $info:expr, $size:expr) => {{ - $crate::vmstate_single_test!($field_name, $struct_name, None, $version_id, $info, $size) - }}; +impl VMStateFlags { + const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags( + VMStateFlags::VMS_VARRAY_INT32.0 + | VMStateFlags::VMS_VARRAY_UINT8.0 + | VMStateFlags::VMS_VARRAY_UINT16.0 + | VMStateFlags::VMS_VARRAY_UINT32.0, + ); } -#[doc(alias = "VMSTATE_UINT32_V")] -#[macro_export] -macro_rules! vmstate_uint32_v { - ($field_name:ident, $struct_name:ty, $version_id:expr) => {{ - $crate::vmstate_single!( - $field_name, - $struct_name, - $version_id, - ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32), - ::core::mem::size_of::<u32>() - ) - }}; +// Add a couple builder-style methods to VMStateField, allowing +// easy derivation of VMStateField constants from other types. +impl VMStateField { + #[must_use] + pub const fn with_version_id(mut self, version_id: i32) -> Self { + assert!(version_id >= 0); + self.version_id = version_id; + self + } + + #[must_use] + pub const fn with_array_flag(mut self, num: usize) -> Self { + assert!(num <= 0x7FFF_FFFFusize); + assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) == 0); + assert!((self.flags.0 & VMStateFlags::VMS_VARRAY_FLAGS.0) == 0); + if (self.flags.0 & VMStateFlags::VMS_POINTER.0) != 0 { + self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_POINTER.0); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0); + } + self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_SINGLE.0); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0); + self.num = num as i32; + self + } + + #[must_use] + pub const fn with_pointer_flag(mut self) -> Self { + assert!((self.flags.0 & VMStateFlags::VMS_POINTER.0) == 0); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_POINTER.0); + self + } + + #[must_use] + pub const fn with_varray_flag<T: VMState>(mut self, flag: VMStateFlags) -> VMStateField { + assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); + self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0); + self.flags = VMStateFlags(self.flags.0 | flag.0); + self + } + + #[must_use] + pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField { + assert!(num <= 0x7FFF_FFFFu32); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0); + self.num = num as i32; + self + } } -#[doc(alias = "VMSTATE_UINT32")] +/// This macro can be used (by just passing it a type) to forward the `VMState` +/// trait to the first field of a tuple. This is a workaround for lack of +/// support of nested [`offset_of`](core::mem::offset_of) until Rust 1.82.0. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::vmstate::impl_vmstate_forward; +/// pub struct Fifo([u8; 16]); +/// impl_vmstate_forward!(Fifo); +/// ``` #[macro_export] -macro_rules! vmstate_uint32 { - ($field_name:ident, $struct_name:ty) => {{ - $crate::vmstate_uint32_v!($field_name, $struct_name, 0) - }}; +macro_rules! impl_vmstate_forward { + // This is similar to impl_vmstate_transparent below, but it + // uses the same trick as vmstate_of! to obtain the type of + // the first field of the tuple + ($tuple:ty) => { + unsafe impl $crate::vmstate::VMState for $tuple { + const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = + $crate::call_func_with_field!($crate::vmstate::vmstate_scalar_type, $tuple, 0); + const BASE: $crate::bindings::VMStateField = + $crate::call_func_with_field!($crate::vmstate::vmstate_base, $tuple, 0); + } + }; } -#[doc(alias = "VMSTATE_ARRAY")] -#[macro_export] -macro_rules! vmstate_array { - ($field_name:ident, $struct_name:ty, $length:expr, $version_id:expr, $info:expr, $size:expr) => {{ - $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), 0) - .as_bytes() - .as_ptr() as *const ::std::os::raw::c_char, - err_hint: ::core::ptr::null(), - offset: $crate::offset_of!($struct_name, $field_name), - size: $size, - start: 0, - num: $length as _, - num_offset: 0, - size_offset: 0, - info: unsafe { $info }, - flags: VMStateFlags::VMS_ARRAY, - vmsd: ::core::ptr::null(), - version_id: $version_id, - struct_version_id: 0, - field_exists: None, +// Transparent wrappers: just use the internal type + +macro_rules! impl_vmstate_transparent { + ($type:ty where $base:tt: VMState $($where:tt)*) => { + unsafe impl<$base> VMState for $type where $base: VMState $($where)* { + const SCALAR_TYPE: VMStateFieldType = <$base as VMState>::SCALAR_TYPE; + const BASE: VMStateField = VMStateField { + size: mem::size_of::<$type>(), + ..<$base as VMState>::BASE + }; + const VARRAY_FLAG: VMStateFlags = <$base as VMState>::VARRAY_FLAG; } - }}; + }; } -#[doc(alias = "VMSTATE_UINT32_ARRAY_V")] +impl_vmstate_transparent!(std::cell::Cell<T> where T: VMState); +impl_vmstate_transparent!(std::cell::UnsafeCell<T> where T: VMState); +impl_vmstate_transparent!(crate::cell::BqlCell<T> where T: VMState); +impl_vmstate_transparent!(crate::cell::BqlRefCell<T> where T: VMState); + #[macro_export] -macro_rules! vmstate_uint32_array_v { - ($field_name:ident, $struct_name:ty, $length:expr, $version_id:expr) => {{ - $crate::vmstate_array!( - $field_name, - $struct_name, - $length, - $version_id, - ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32), - ::core::mem::size_of::<u32>() - ) - }}; +macro_rules! impl_vmstate_bitsized { + ($type:ty) => { + unsafe impl $crate::vmstate::VMState for $type { + const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = + <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt + as ::bilge::prelude::Number>::UnderlyingType + as $crate::vmstate::VMState>::SCALAR_TYPE; + const BASE: $crate::bindings::VMStateField = + <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt + as ::bilge::prelude::Number>::UnderlyingType + as $crate::vmstate::VMState>::BASE; + const VARRAY_FLAG: $crate::bindings::VMStateFlags = + <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt + as ::bilge::prelude::Number>::UnderlyingType + as $crate::vmstate::VMState>::VARRAY_FLAG; + } + }; } -#[doc(alias = "VMSTATE_UINT32_ARRAY")] -#[macro_export] -macro_rules! vmstate_uint32_array { - ($field_name:ident, $struct_name:ty, $length:expr) => {{ - $crate::vmstate_uint32_array_v!($field_name, $struct_name, $length, 0) - }}; +// Scalar types using predefined VMStateInfos + +macro_rules! impl_vmstate_scalar { + ($info:ident, $type:ty$(, $varray_flag:ident)?) => { + unsafe impl VMState for $type { + const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::$info; + const BASE: VMStateField = VMStateField { + size: mem::size_of::<$type>(), + flags: VMStateFlags::VMS_SINGLE, + ..Zeroable::ZERO + }; + $(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)? + } + }; } -#[doc(alias = "VMSTATE_STRUCT_POINTER_V")] -#[macro_export] -macro_rules! vmstate_struct_pointer_v { - ($field_name:ident, $struct_name:ty, $version_id:expr, $vmsd:expr, $type:ty) => {{ - $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), 0) - .as_bytes() - .as_ptr() as *const ::std::os::raw::c_char, - err_hint: ::core::ptr::null(), - offset: $crate::offset_of!($struct_name, $field_name), - size: ::core::mem::size_of::<*const $type>(), - start: 0, - num: 0, - num_offset: 0, - size_offset: 0, - info: ::core::ptr::null(), - flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0), - vmsd: unsafe { $vmsd }, - version_id: $version_id, - struct_version_id: 0, - field_exists: None, +impl_vmstate_scalar!(vmstate_info_bool, bool); +impl_vmstate_scalar!(vmstate_info_int8, i8); +impl_vmstate_scalar!(vmstate_info_int16, i16); +impl_vmstate_scalar!(vmstate_info_int32, i32); +impl_vmstate_scalar!(vmstate_info_int64, i64); +impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY_UINT8); +impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16); +impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32); +impl_vmstate_scalar!(vmstate_info_uint64, u64); +impl_vmstate_scalar!(vmstate_info_timer, bindings::QEMUTimer); + +// Pointer types using the underlying type's VMState plus VMS_POINTER +// Note that references are not supported, though references to cells +// could be allowed. + +macro_rules! impl_vmstate_pointer { + ($type:ty where $base:tt: VMState $($where:tt)*) => { + unsafe impl<$base> VMState for $type where $base: VMState $($where)* { + const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE; + const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag(); } - }}; + }; +} + +impl_vmstate_pointer!(*const T where T: VMState); +impl_vmstate_pointer!(*mut T where T: VMState); +impl_vmstate_pointer!(NonNull<T> where T: VMState); + +// Unlike C pointers, Box is always non-null therefore there is no need +// to specify VMS_ALLOC. +impl_vmstate_pointer!(Box<T> where T: VMState); + +// Arrays using the underlying type's VMState plus +// VMS_ARRAY/VMS_ARRAY_OF_POINTER + +unsafe impl<T: VMState, const N: usize> VMState for [T; N] { + const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE; + const BASE: VMStateField = <T as VMState>::BASE.with_array_flag(N); } -#[doc(alias = "VMSTATE_ARRAY_OF_POINTER")] +#[doc(alias = "VMSTATE_UNUSED")] #[macro_export] -macro_rules! vmstate_array_of_pointer { - ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr, $info:expr, $type:ty) => {{ +macro_rules! vmstate_unused { + ($size:expr) => {{ $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), 0) - .as_bytes() - .as_ptr() as *const ::std::os::raw::c_char, - version_id: $version_id, - num: $num as _, - info: unsafe { $info }, - size: ::core::mem::size_of::<*const $type>(), - flags: VMStateFlags(VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0), - offset: $crate::offset_of!($struct_name, $field_name), - err_hint: ::core::ptr::null(), - start: 0, - num_offset: 0, - size_offset: 0, - vmsd: ::core::ptr::null(), - struct_version_id: 0, - field_exists: None, + name: $crate::c_str!("unused").as_ptr(), + size: $size, + info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, + flags: $crate::bindings::VMStateFlags::VMS_BUFFER, + ..$crate::zeroable::Zeroable::ZERO } }}; } -#[doc(alias = "VMSTATE_ARRAY_OF_POINTER_TO_STRUCT")] +// FIXME: including the `vmsd` field in a `const` is not possible without +// the const_refs_static feature (stabilized in Rust 1.83.0). Without it, +// it is not possible to use VMS_STRUCT in a transparent manner using +// `vmstate_of!`. While VMSTATE_CLOCK can at least try to be type-safe, +// VMSTATE_STRUCT includes $type only for documentation purposes; it +// is checked against $field_name and $struct_name, but not against $vmsd +// which is what really would matter. +#[doc(alias = "VMSTATE_STRUCT")] #[macro_export] -macro_rules! vmstate_array_of_pointer_to_struct { - ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr, $vmsd:expr, $type:ty) => {{ +macro_rules! vmstate_struct { + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(,)?) => { $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), 0) + name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() .as_ptr() as *const ::std::os::raw::c_char, - version_id: $version_id, - num: $num as _, + $(.num_offset: $crate::offset_of!($struct_name, $num),)? + offset: { + $crate::assert_field_type!($struct_name, $field_name, $type); + $crate::offset_of!($struct_name, $field_name) + }, + size: ::core::mem::size_of::<$type>(), + flags: $crate::bindings::VMStateFlags::VMS_STRUCT, vmsd: unsafe { $vmsd }, - size: ::core::mem::size_of::<*const $type>(), - flags: VMStateFlags( - VMStateFlags::VMS_ARRAY.0 - | VMStateFlags::VMS_STRUCT.0 - | VMStateFlags::VMS_ARRAY_OF_POINTER.0, - ), - offset: $crate::offset_of!($struct_name, $field_name), - err_hint: ::core::ptr::null(), - start: 0, - num_offset: 0, - size_offset: 0, - vmsd: ::core::ptr::null(), - struct_version_id: 0, - field_exists: None, + ..$crate::zeroable::Zeroable::ZERO $( + .with_varray_flag($crate::call_func_with_field!( + $crate::vmstate::vmstate_varray_flag, + $struct_name, + $num)) + $(.with_varray_multiply($factor))?)? } - }}; -} - -#[doc(alias = "VMSTATE_CLOCK_V")] -#[macro_export] -macro_rules! vmstate_clock_v { - ($field_name:ident, $struct_name:ty, $version_id:expr) => {{ - $crate::vmstate_struct_pointer_v!( - $field_name, - $struct_name, - $version_id, - ::core::ptr::addr_of!($crate::bindings::vmstate_clock), - $crate::bindings::Clock - ) - }}; + }; } #[doc(alias = "VMSTATE_CLOCK")] #[macro_export] macro_rules! vmstate_clock { - ($field_name:ident, $struct_name:ty) => {{ - $crate::vmstate_clock_v!($field_name, $struct_name, 0) - }}; -} - -#[doc(alias = "VMSTATE_ARRAY_CLOCK_V")] -#[macro_export] -macro_rules! vmstate_array_clock_v { - ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr) => {{ - $crate::vmstate_array_of_pointer_to_struct!( - $field_name, - $struct_name, - $num, - $version_id, - ::core::ptr::addr_of!($crate::bindings::vmstate_clock), - $crate::bindings::Clock - ) - }}; -} - -#[doc(alias = "VMSTATE_ARRAY_CLOCK")] -#[macro_export] -macro_rules! vmstate_array_clock { - ($field_name:ident, $struct_name:ty, $num:expr) => {{ - $crate::vmstate_array_clock_v!($field_name, $struct_name, $name, 0) + ($struct_name:ty, $field_name:ident) => {{ + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), "\0") + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + offset: { + $crate::assert_field_type!( + $struct_name, + $field_name, + core::ptr::NonNull<$crate::bindings::Clock> + ); + $crate::offset_of!($struct_name, $field_name) + }, + size: ::core::mem::size_of::<*const $crate::bindings::Clock>(), + flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0), + vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) }, + ..$crate::zeroable::Zeroable::ZERO + } }}; } @@ -287,20 +487,8 @@ macro_rules! vmstate_fields { static _FIELDS: &[$crate::bindings::VMStateField] = &[ $($field),*, $crate::bindings::VMStateField { - name: ::core::ptr::null(), - err_hint: ::core::ptr::null(), - offset: 0, - size: 0, - start: 0, - num: 0, - num_offset: 0, - size_offset: 0, - info: ::core::ptr::null(), - flags: VMStateFlags::VMS_END, - vmsd: ::core::ptr::null(), - version_id: 0, - struct_version_id: 0, - field_exists: None, + flags: $crate::bindings::VMStateFlags::VMS_END, + ..$crate::zeroable::Zeroable::ZERO } ]; _FIELDS.as_ptr() diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs index 6125aee..7b04947 100644 --- a/rust/qemu-api/src/zeroable.rs +++ b/rust/qemu-api/src/zeroable.rs @@ -1,13 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later -use std::ptr; - /// Encapsulates the requirement that /// `MaybeUninit::<Self>::zeroed().assume_init()` does not cause undefined /// behavior. This trait in principle could be implemented as just: /// /// ``` -/// pub unsafe trait Zeroable: Default { +/// pub unsafe trait Zeroable { /// const ZERO: Self = unsafe { ::core::mem::MaybeUninit::<Self>::zeroed().assume_init() }; /// } /// ``` @@ -29,58 +27,76 @@ pub unsafe trait Zeroable: Default { const ZERO: Self; } -unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 { - const ZERO: Self = Self { i: 0 }; -} - -unsafe impl Zeroable for crate::bindings::Property { - const ZERO: Self = Self { - name: ptr::null(), - info: ptr::null(), - offset: 0, - bitnr: 0, - bitmask: 0, - set_default: false, - defval: Zeroable::ZERO, - arrayoffset: 0, - arrayinfo: ptr::null(), - arrayfieldsize: 0, - link_type: ptr::null(), - }; +/// A macro that acts similarly to [`core::mem::zeroed()`], only is const +/// +/// ## Safety +/// +/// Similar to `core::mem::zeroed()`, except this zeroes padding bits. Zeroed +/// padding usually isn't relevant to safety, but might be if a C union is used. +/// +/// Just like for `core::mem::zeroed()`, an all zero byte pattern might not +/// be a valid value for a type, as is the case for references `&T` and `&mut +/// T`. Reference types trigger a (denied by default) lint and cause immediate +/// undefined behavior if the lint is ignored +/// +/// ```rust compile_fail +/// use const_zero::const_zero; +/// // error: any use of this value will cause an error +/// // note: `#[deny(const_err)]` on by default +/// const STR: &str = unsafe{const_zero!(&'static str)}; +/// ``` +/// +/// `const_zero` does not work on unsized types: +/// +/// ```rust compile_fail +/// use const_zero::const_zero; +/// // error[E0277]: the size for values of type `[u8]` cannot be known at compilation time +/// const BYTES: [u8] = unsafe{const_zero!([u8])}; +/// ``` +/// ## Differences with `core::mem::zeroed` +/// +/// `const_zero` zeroes padding bits, while `core::mem::zeroed` doesn't +macro_rules! const_zero { + // This macro to produce a type-generic zero constant is taken from the + // const_zero crate (v0.1.1): + // + // https://docs.rs/const-zero/latest/src/const_zero/lib.rs.html + // + // and used under MIT license + ($type_:ty) => {{ + const TYPE_SIZE: ::core::primitive::usize = ::core::mem::size_of::<$type_>(); + union TypeAsBytes { + bytes: [::core::primitive::u8; TYPE_SIZE], + inner: ::core::mem::ManuallyDrop<$type_>, + } + const ZERO_BYTES: TypeAsBytes = TypeAsBytes { + bytes: [0; TYPE_SIZE], + }; + ::core::mem::ManuallyDrop::<$type_>::into_inner(ZERO_BYTES.inner) + }}; } -unsafe impl Zeroable for crate::bindings::VMStateDescription { - const ZERO: Self = Self { - name: ptr::null(), - unmigratable: false, - early_setup: false, - version_id: 0, - minimum_version_id: 0, - priority: crate::bindings::MigrationPriority::MIG_PRI_DEFAULT, - pre_load: None, - post_load: None, - pre_save: None, - post_save: None, - needed: None, - dev_unplug_pending: None, - fields: ptr::null(), - subsections: ptr::null(), +/// A wrapper to implement the `Zeroable` trait through the `const_zero` macro. +macro_rules! impl_zeroable { + ($type:ty) => { + unsafe impl Zeroable for $type { + const ZERO: Self = unsafe { const_zero!($type) }; + } }; } -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 { - const ZERO: Self = Self { - min_access_size: 0, - max_access_size: 0, - unaligned: false, - accepts: None, - }; +// bindgen does not derive Default here +#[allow(clippy::derivable_impls)] +impl Default for crate::bindings::VMStateFlags { + fn default() -> Self { + Self(0) + } } -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 { - const ZERO: Self = Self { - min_access_size: 0, - max_access_size: 0, - unaligned: false, - }; -} +impl_zeroable!(crate::bindings::Property__bindgen_ty_1); +impl_zeroable!(crate::bindings::Property); +impl_zeroable!(crate::bindings::VMStateFlags); +impl_zeroable!(crate::bindings::VMStateField); +impl_zeroable!(crate::bindings::VMStateDescription); +impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_1); +impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_2); diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 526c3f4..5c3e75e 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -14,8 +14,8 @@ use qemu_api::{ cell::{self, BqlCell}, declare_properties, define_property, prelude::*, - qdev::{DeviceImpl, DeviceState, Property}, - qom::{ObjectImpl, ParentField}, + qdev::{DeviceClass, DeviceImpl, DeviceState, Property}, + qom::{ClassInitImpl, ObjectImpl, ParentField}, vmstate::VMStateDescription, zeroable::Zeroable, }; @@ -37,6 +37,10 @@ pub struct DummyState { qom_isa!(DummyState: Object, DeviceState); +pub struct DummyClass { + parent_class: <DeviceState as ObjectType>::Class, +} + declare_properties! { DUMMY_PROPERTIES, define_property!( @@ -49,7 +53,7 @@ declare_properties! { } unsafe impl ObjectType for DummyState { - type Class = <DeviceState as ObjectType>::Class; + type Class = DummyClass; const TYPE_NAME: &'static CStr = c_str!("dummy"); } @@ -67,6 +71,51 @@ impl DeviceImpl for DummyState { } } +// `impl<T> ClassInitImpl<DummyClass> for T` doesn't work since it violates +// orphan rule. +impl ClassInitImpl<DummyClass> for DummyState { + fn class_init(klass: &mut DummyClass) { + <Self as ClassInitImpl<DeviceClass>>::class_init(&mut klass.parent_class); + } +} + +#[derive(qemu_api_macros::offsets)] +#[repr(C)] +#[derive(qemu_api_macros::Object)] +pub struct DummyChildState { + parent: ParentField<DummyState>, +} + +qom_isa!(DummyChildState: Object, DeviceState, DummyState); + +pub struct DummyChildClass { + parent_class: <DummyState as ObjectType>::Class, +} + +unsafe impl ObjectType for DummyChildState { + type Class = DummyChildClass; + const TYPE_NAME: &'static CStr = c_str!("dummy_child"); +} + +impl ObjectImpl for DummyChildState { + type ParentType = DummyState; + const ABSTRACT: bool = false; +} + +impl DeviceImpl for DummyChildState {} + +impl ClassInitImpl<DummyClass> for DummyChildState { + fn class_init(klass: &mut DummyClass) { + <Self as ClassInitImpl<DeviceClass>>::class_init(&mut klass.parent_class); + } +} + +impl ClassInitImpl<DummyChildClass> for DummyChildState { + fn class_init(klass: &mut DummyChildClass) { + <Self as ClassInitImpl<DummyClass>>::class_init(&mut klass.parent_class); + } +} + fn init_qom() { static ONCE: BqlCell<bool> = BqlCell::new(false); @@ -85,6 +134,7 @@ fn test_object_new() { init_qom(); unsafe { object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast()); + object_unref(object_new(DummyChildState::TYPE_NAME.as_ptr()).cast()); } } |