diff options
Diffstat (limited to 'rust/hw')
-rw-r--r-- | rust/hw/char/pl011/Cargo.toml | 11 | ||||
-rw-r--r-- | rust/hw/char/pl011/src/device.rs | 4 | ||||
-rw-r--r-- | rust/hw/char/pl011/src/device_class.rs | 13 | ||||
-rw-r--r-- | rust/hw/char/pl011/src/lib.rs | 6 | ||||
-rw-r--r-- | rust/hw/timer/hpet/Cargo.toml | 9 | ||||
-rw-r--r-- | rust/hw/timer/hpet/src/fw_cfg.rs | 6 | ||||
-rw-r--r-- | rust/hw/timer/hpet/src/hpet.rs | 162 | ||||
-rw-r--r-- | rust/hw/timer/hpet/src/lib.rs | 4 |
8 files changed, 178 insertions, 37 deletions
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index f2296ca..a1f431a 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -1,15 +1,16 @@ [package] name = "pl011" version = "0.1.0" -edition = "2021" authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"] -license = "GPL-2.0-or-later" description = "pl011 device model for QEMU" resolver = "2" publish = false -keywords = [] -categories = [] -rust-version = "1.63.0" + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true [lib] crate-type = ["staticlib"] diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index bb2a0f2..7c563ad 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -74,7 +74,7 @@ impl std::ops::Index<u32> for Fifo { } #[repr(C)] -#[derive(Debug, Default, qemu_api_macros::offsets)] +#[derive(Debug, Default)] pub struct PL011Registers { #[doc(alias = "fr")] pub flags: registers::Flags, @@ -98,7 +98,7 @@ pub struct PL011Registers { } #[repr(C)] -#[derive(qemu_api_macros::Object, qemu_api_macros::offsets)] +#[derive(qemu_api_macros::Object)] /// PL011 Device Model in QEMU pub struct PL011State { pub parent_obj: ParentField<SysBusDevice>, diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index b4d4a7e..d328d84 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -3,13 +3,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later use std::{ - os::raw::{c_int, c_void}, + ffi::{c_int, c_void}, ptr::NonNull, }; use qemu_api::{ bindings::{qdev_prop_bool, qdev_prop_chr}, - c_str, prelude::*, vmstate::VMStateDescription, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused, @@ -25,7 +24,7 @@ extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { /// Migration subsection for [`PL011State`] clock. static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { - name: c_str!("pl011/clock").as_ptr(), + name: c"pl011/clock".as_ptr(), version_id: 1, minimum_version_id: 1, needed: Some(pl011_clock_needed), @@ -46,7 +45,7 @@ extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { } static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription { - name: c_str!("pl011/regs").as_ptr(), + name: c"pl011/regs".as_ptr(), version_id: 2, minimum_version_id: 2, fields: vmstate_fields! { @@ -70,7 +69,7 @@ static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription { }; pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { - name: c_str!("pl011").as_ptr(), + name: c"pl011".as_ptr(), version_id: 2, minimum_version_id: 2, post_load: Some(pl011_post_load), @@ -87,14 +86,14 @@ pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { qemu_api::declare_properties! { PL011_PROPERTIES, qemu_api::define_property!( - c_str!("chardev"), + c"chardev", PL011State, char_backend, unsafe { &qdev_prop_chr }, CharBackend ), qemu_api::define_property!( - c_str!("migrate-clk"), + c"migrate-clk", PL011State, migrate_clock, unsafe { &qdev_prop_bool }, diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index dbae769..5c4fbc9 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -12,13 +12,11 @@ //! See [`PL011State`](crate::device::PL011State) for the device model type and //! the [`registers`] module for register types. -use qemu_api::c_str; - mod device; mod device_class; mod registers; 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"); +pub const TYPE_PL011: &::std::ffi::CStr = c"pl011"; +pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c"pl011_luminary"; diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 147f216..6f07502 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -1,11 +1,14 @@ [package] name = "hpet" version = "0.1.0" -edition = "2021" authors = ["Zhao Liu <zhao1.liu@intel.com>"] -license = "GPL-2.0-or-later" description = "IA-PC High Precision Event Timer emulation in Rust" -rust-version = "1.63.0" + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true [lib] crate-type = ["staticlib"] diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs index bef0372..aa08d28 100644 --- a/rust/hw/timer/hpet/src/fw_cfg.rs +++ b/rust/hw/timer/hpet/src/fw_cfg.rs @@ -4,7 +4,7 @@ use std::ptr::addr_of_mut; -use qemu_api::{cell::bql_locked, impl_zeroable, zeroable::Zeroable}; +use qemu_api::{cell::bql_locked, zeroable::Zeroable}; /// Each `HPETState` represents a Event Timer Block. The v1 spec supports /// up to 8 blocks. QEMU only uses 1 block (in PC machine). @@ -18,7 +18,7 @@ pub struct HPETFwEntry { pub min_tick: u16, pub page_prot: u8, } -impl_zeroable!(HPETFwEntry); +unsafe impl Zeroable for HPETFwEntry {} #[repr(C, packed)] #[derive(Copy, Clone, Default)] @@ -26,7 +26,7 @@ pub struct HPETFwConfig { pub count: u8, pub hpet: [HPETFwEntry; HPET_MAX_NUM_EVENT_TIMER_BLOCK], } -impl_zeroable!(HPETFwConfig); +unsafe impl Zeroable for HPETFwConfig {} #[allow(non_upper_case_globals)] #[no_mangle] diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index cbd2ed4..779681d 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later use std::{ - ffi::CStr, + ffi::{c_int, c_void, CStr}, pin::Pin, ptr::{addr_of_mut, null_mut, NonNull}, slice::from_ref, @@ -14,7 +14,6 @@ use qemu_api::{ address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_uint8, }, - c_str, cell::{BqlCell, BqlRefCell}, irq::InterruptSource, memory::{ @@ -25,7 +24,10 @@ use qemu_api::{ qom::{ObjectImpl, ObjectType, ParentField}, qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, - timer::{Timer, CLOCK_VIRTUAL}, + timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, + vmstate::VMStateDescription, + vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate, + zeroable::Zeroable, }; use crate::fw_cfg::HPETFwConfig; @@ -180,7 +182,7 @@ fn timer_handler(timer_cell: &BqlRefCell<HPETTimer>) { /// HPET Timer Abstraction #[repr(C)] -#[derive(Debug, qemu_api_macros::offsets)] +#[derive(Debug)] pub struct HPETTimer { /// timer N index within the timer block (`HPETState`) #[doc(alias = "tn")] @@ -216,7 +218,7 @@ impl HPETTimer { // SAFETY: the HPETTimer will only be used after the timer // is initialized below. qemu_timer: unsafe { Timer::new() }, - state: NonNull::new(state as *const _ as *mut _).unwrap(), + state: NonNull::new((state as *const HPETState).cast_mut()).unwrap(), config: 0, cmp: 0, fsb: 0, @@ -520,7 +522,7 @@ impl HPETTimer { /// HPET Event Timer Block Abstraction #[repr(C)] -#[derive(qemu_api_macros::Object, qemu_api_macros::offsets)] +#[derive(qemu_api_macros::Object)] pub struct HPETState { parent_obj: ParentField<SysBusDevice>, iomem: MemoryRegion, @@ -561,6 +563,7 @@ pub struct HPETState { #[doc(alias = "timer")] timers: [BqlRefCell<HPETTimer>; HPET_MAX_TIMERS as usize], num_timers: BqlCell<u8>, + num_timers_save: BqlCell<u8>, /// Instance id (HPET timer block ID). hpet_id: BqlCell<usize>, @@ -839,6 +842,49 @@ impl HPETState { } } } + + fn pre_save(&self) -> i32 { + if self.is_hpet_enabled() { + self.counter.set(self.get_ticks()); + } + + /* + * The number of timers must match on source and destination, but it was + * also added to the migration stream. Check that it matches the value + * that was configured. + */ + self.num_timers_save.set(self.num_timers.get()); + 0 + } + + fn post_load(&self, _version_id: u8) -> i32 { + for timer in self.timers.iter().take(self.get_num_timers()) { + let mut t = timer.borrow_mut(); + + t.cmp64 = t.calculate_cmp64(t.get_state().counter.get(), t.cmp); + t.last = CLOCK_VIRTUAL.get_ns() - NANOSECONDS_PER_SECOND; + } + + // Recalculate the offset between the main counter and guest time + if !self.hpet_offset_saved { + self.hpet_offset + .set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns()); + } + + 0 + } + + fn is_rtc_irq_level_needed(&self) -> bool { + self.rtc_irq_level.get() != 0 + } + + fn is_offset_needed(&self) -> bool { + self.is_hpet_enabled() && self.hpet_offset_saved + } + + fn validate_num_timers(&self, _version_id: u8) -> bool { + self.num_timers.get() == self.num_timers_save.get() + } } qom_isa!(HPETState: SysBusDevice, DeviceState, Object); @@ -861,7 +907,7 @@ impl ObjectImpl for HPETState { qemu_api::declare_properties! { HPET_PROPERTIES, qemu_api::define_property!( - c_str!("timers"), + c"timers", HPETState, num_timers, unsafe { &qdev_prop_uint8 }, @@ -869,7 +915,7 @@ qemu_api::declare_properties! { default = HPET_MIN_TIMERS ), qemu_api::define_property!( - c_str!("msi"), + c"msi", HPETState, flags, unsafe { &qdev_prop_bit }, @@ -878,7 +924,7 @@ qemu_api::declare_properties! { default = false, ), qemu_api::define_property!( - c_str!("hpet-intcap"), + c"hpet-intcap", HPETState, int_route_cap, unsafe { &qdev_prop_uint32 }, @@ -886,7 +932,7 @@ qemu_api::declare_properties! { default = 0 ), qemu_api::define_property!( - c_str!("hpet-offset-saved"), + c"hpet-offset-saved", HPETState, hpet_offset_saved, unsafe { &qdev_prop_bool }, @@ -895,11 +941,107 @@ 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 +}; + +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: 1, + 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).with_version_id(2), + vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers), + vmstate_struct!(HPETState, timers[0 .. num_timers], &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 +}; + impl DeviceImpl for HPETState { fn properties() -> &'static [Property] { &HPET_PROPERTIES } + fn vmsd() -> Option<&'static VMStateDescription> { + Some(&VMSTATE_HPET) + } + const REALIZE: Option<fn(&Self)> = Some(Self::realize); } diff --git a/rust/hw/timer/hpet/src/lib.rs b/rust/hw/timer/hpet/src/lib.rs index 5e7c961..1954584 100644 --- a/rust/hw/timer/hpet/src/lib.rs +++ b/rust/hw/timer/hpet/src/lib.rs @@ -7,9 +7,7 @@ //! This library implements a device model for the IA-PC HPET (High //! Precision Event Timers) device in QEMU. -use qemu_api::c_str; - pub mod fw_cfg; pub mod hpet; -pub const TYPE_HPET: &::std::ffi::CStr = c_str!("hpet"); +pub const TYPE_HPET: &::std::ffi::CStr = c"hpet"; |