diff options
author | Zhao Liu <zhao1.liu@intel.com> | 2025-09-08 12:49:39 +0200 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2025-09-17 19:00:56 +0200 |
commit | 7da9ee9207c55a783567eb46c71fa89cb5b43461 (patch) | |
tree | c8c731f109dc2c0a82b4dbd2435bff9ec047ac2e /rust/hw/timer | |
parent | a71df7e143b57427c1f8a917654e7b0ed1ceb919 (diff) | |
download | qemu-7da9ee9207c55a783567eb46c71fa89cb5b43461.zip qemu-7da9ee9207c55a783567eb46c71fa89cb5b43461.tar.gz qemu-7da9ee9207c55a783567eb46c71fa89cb5b43461.tar.bz2 |
rust: vmstate: convert to use builder pattern
Similar to MemoryRegionOps, the builder pattern has two advantages:
1) it makes it possible to build a VMStateDescription that knows which
types it will be invoked on; 2) it provides a way to wrap the callbacks
and let devices avoid "unsafe".
Unfortunately, building a static VMStateDescription requires the
builder methods to be "const", and because the VMStateFields are
*also* static, this requires const_refs_static. So this requires
Rust 1.83.0.
Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
Link: https://lore.kernel.org/r/20250908105005.2119297-8-pbonzini@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'rust/hw/timer')
-rw-r--r-- | rust/hw/timer/hpet/src/device.rs | 166 |
1 files changed, 69 insertions, 97 deletions
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 01d5a0d..955cf86 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later use std::{ - ffi::{c_int, c_void, CStr}, + ffi::CStr, mem::MaybeUninit, pin::Pin, ptr::{addr_of_mut, null_mut, NonNull}, @@ -27,9 +27,8 @@ use qemu_api::{ sysbus::{SysBusDevice, SysBusDeviceImpl}, timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, uninit_field_mut, - vmstate::VMStateDescription, + vmstate::{self, VMStateDescription, VMStateDescriptionBuilder}, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate, - zeroable::Zeroable, }; use crate::fw_cfg::HPETFwConfig; @@ -213,6 +212,10 @@ pub struct HPETTimer { last: u64, } +// SAFETY: Sync is not automatically derived due to the `state` field, +// which is always dereferenced to a shared reference. +unsafe impl Sync for HPETTimer {} + impl HPETTimer { fn new(index: u8, state: *const HPETState) -> HPETTimer { HPETTimer { @@ -841,7 +844,7 @@ impl HPETState { } } - fn pre_save(&self) -> i32 { + fn pre_save(&self) -> Result<(), vmstate::Infallible> { if self.is_hpet_enabled() { self.counter.set(self.get_ticks()); } @@ -852,10 +855,10 @@ impl HPETState { * that was configured. */ self.num_timers_save.set(self.num_timers as u8); - 0 + Ok(()) } - fn post_load(&self, _version_id: u8) -> i32 { + fn post_load(&self, _version_id: u8) -> Result<(), vmstate::Infallible> { for timer in self.timers.iter().take(self.num_timers) { let mut t = timer.borrow_mut(); @@ -869,7 +872,7 @@ impl HPETState { .set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns()); } - 0 + Ok(()) } fn is_rtc_irq_level_needed(&self) -> bool { @@ -939,97 +942,66 @@ qemu_api::declare_properties! { ), } -unsafe extern "C" fn hpet_rtc_irq_level_needed(opaque: *mut c_void) -> bool { - // SAFETY: - // the pointer is convertible to a reference - let state: &HPETState = unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_ref() }; - state.is_rtc_irq_level_needed() -} - -unsafe extern "C" fn hpet_offset_needed(opaque: *mut c_void) -> bool { - // SAFETY: - // the pointer is convertible to a reference - let state: &HPETState = unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_ref() }; - state.is_offset_needed() -} - -unsafe extern "C" fn hpet_pre_save(opaque: *mut c_void) -> c_int { - // SAFETY: - // the pointer is convertible to a reference - let state: &mut HPETState = - unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_mut() }; - state.pre_save() as c_int -} - -unsafe extern "C" fn hpet_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { - // SAFETY: - // the pointer is convertible to a reference - let state: &mut HPETState = - unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_mut() }; - let version: u8 = version_id.try_into().unwrap(); - state.post_load(version) as c_int -} - -static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription = VMStateDescription { - name: c"hpet/rtc_irq_level".as_ptr(), - version_id: 1, - minimum_version_id: 1, - needed: Some(hpet_rtc_irq_level_needed), - fields: vmstate_fields! { - vmstate_of!(HPETState, rtc_irq_level), - }, - ..Zeroable::ZERO -}; - -static VMSTATE_HPET_OFFSET: VMStateDescription = VMStateDescription { - name: c"hpet/offset".as_ptr(), - version_id: 1, - minimum_version_id: 1, - needed: Some(hpet_offset_needed), - fields: vmstate_fields! { - vmstate_of!(HPETState, hpet_offset), - }, - ..Zeroable::ZERO -}; - -static VMSTATE_HPET_TIMER: VMStateDescription = VMStateDescription { - name: c"hpet_timer".as_ptr(), - version_id: 1, - minimum_version_id: 1, - fields: vmstate_fields! { - vmstate_of!(HPETTimer, index), - vmstate_of!(HPETTimer, config), - vmstate_of!(HPETTimer, cmp), - vmstate_of!(HPETTimer, fsb), - vmstate_of!(HPETTimer, period), - vmstate_of!(HPETTimer, wrap_flag), - vmstate_of!(HPETTimer, qemu_timer), - }, - ..Zeroable::ZERO -}; +static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription<HPETState> = + VMStateDescriptionBuilder::<HPETState>::new() + .name(c"hpet/rtc_irq_level") + .version_id(1) + .minimum_version_id(1) + .needed(&HPETState::is_rtc_irq_level_needed) + .fields(vmstate_fields! { + vmstate_of!(HPETState, rtc_irq_level), + }) + .build(); + +static VMSTATE_HPET_OFFSET: VMStateDescription<HPETState> = + VMStateDescriptionBuilder::<HPETState>::new() + .name(c"hpet/offset") + .version_id(1) + .minimum_version_id(1) + .needed(&HPETState::is_offset_needed) + .fields(vmstate_fields! { + vmstate_of!(HPETState, hpet_offset), + }) + .build(); + +static VMSTATE_HPET_TIMER: VMStateDescription<HPETTimer> = + VMStateDescriptionBuilder::<HPETTimer>::new() + .name(c"hpet_timer") + .version_id(1) + .minimum_version_id(1) + .fields(vmstate_fields! { + vmstate_of!(HPETTimer, index), + vmstate_of!(HPETTimer, config), + vmstate_of!(HPETTimer, cmp), + vmstate_of!(HPETTimer, fsb), + vmstate_of!(HPETTimer, period), + vmstate_of!(HPETTimer, wrap_flag), + vmstate_of!(HPETTimer, qemu_timer), + }) + .build(); const VALIDATE_TIMERS_NAME: &CStr = c"num_timers must match"; -static VMSTATE_HPET: VMStateDescription = VMStateDescription { - name: c"hpet".as_ptr(), - version_id: 2, - minimum_version_id: 2, - pre_save: Some(hpet_pre_save), - post_load: Some(hpet_post_load), - fields: vmstate_fields! { - vmstate_of!(HPETState, config), - vmstate_of!(HPETState, int_status), - vmstate_of!(HPETState, counter), - vmstate_of!(HPETState, num_timers_save), - vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers), - vmstate_struct!(HPETState, timers[0 .. num_timers_save], &VMSTATE_HPET_TIMER, BqlRefCell<HPETTimer>, HPETState::validate_num_timers).with_version_id(0), - }, - subsections: vmstate_subsections! { - VMSTATE_HPET_RTC_IRQ_LEVEL, - VMSTATE_HPET_OFFSET, - }, - ..Zeroable::ZERO -}; +const VMSTATE_HPET: VMStateDescription<HPETState> = + VMStateDescriptionBuilder::<HPETState>::new() + .name(c"hpet") + .version_id(2) + .minimum_version_id(2) + .pre_save(&HPETState::pre_save) + .post_load(&HPETState::post_load) + .fields(vmstate_fields! { + vmstate_of!(HPETState, config), + vmstate_of!(HPETState, int_status), + vmstate_of!(HPETState, counter), + vmstate_of!(HPETState, num_timers_save), + vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers), + vmstate_struct!(HPETState, timers[0 .. num_timers_save], &VMSTATE_HPET_TIMER, BqlRefCell<HPETTimer>, HPETState::validate_num_timers).with_version_id(0), + }) + .subsections(vmstate_subsections!( + VMSTATE_HPET_RTC_IRQ_LEVEL, + VMSTATE_HPET_OFFSET, + )) + .build(); // SAFETY: HPET_PROPERTIES is a valid Property array constructed with the // qemu_api::declare_properties macro. @@ -1040,8 +1012,8 @@ unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState { } impl DeviceImpl for HPETState { - fn vmsd() -> Option<&'static VMStateDescription> { - Some(&VMSTATE_HPET) + fn vmsd() -> Option<VMStateDescription<Self>> { + Some(VMSTATE_HPET) } const REALIZE: Option<fn(&Self) -> qemu_api::Result<()>> = Some(Self::realize); |