aboutsummaryrefslogtreecommitdiff
path: root/rust/hw/timer
diff options
context:
space:
mode:
authorZhao Liu <zhao1.liu@intel.com>2025-09-08 12:49:39 +0200
committerPaolo Bonzini <pbonzini@redhat.com>2025-09-17 19:00:56 +0200
commit7da9ee9207c55a783567eb46c71fa89cb5b43461 (patch)
treec8c731f109dc2c0a82b4dbd2435bff9ec047ac2e /rust/hw/timer
parenta71df7e143b57427c1f8a917654e7b0ed1ceb919 (diff)
downloadqemu-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.rs166
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);