blob: ebf715d399563bd4df42dc9123e1a37847f57581 [file] [log] [blame]
// Copyright (C) 2024 Intel Corporation.
// Author(s): Zhao Liu <zhao1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::{
ffi::CStr,
mem::MaybeUninit,
pin::Pin,
ptr::{addr_of_mut, null_mut, NonNull},
slice::from_ref,
};
use bql::prelude::*;
use common::prelude::*;
use hwcore::prelude::*;
use migration::{self, prelude::*, ToMigrationStateShared};
use qom::prelude::*;
use system::{
bindings::{address_space_memory, address_space_stl_le},
prelude::*,
MEMTXATTRS_UNSPECIFIED,
};
use util::prelude::*;
use crate::fw_cfg::HPETFwConfig;
::trace::include_trace!("hw_timer");
/// Register space for each timer block (`HPET_BASE` is defined in hpet.h).
const HPET_REG_SPACE_LEN: u64 = 0x400; // 1024 bytes
/// Minimum recommended hardware implementation.
const HPET_MIN_TIMERS: usize = 3;
/// Maximum timers in each timer block.
const HPET_MAX_TIMERS: usize = 32;
/// Flags that HPETState.flags supports.
const HPET_FLAG_MSI_SUPPORT_SHIFT: usize = 0;
const HPET_NUM_IRQ_ROUTES: usize = 32;
const HPET_LEGACY_PIT_INT: u32 = 0; // HPET_LEGACY_RTC_INT isn't defined here.
const RTC_ISA_IRQ: usize = 8;
const HPET_CLK_PERIOD: u64 = 10; // 10 ns
const FS_PER_NS: u64 = 1000000; // 1000000 femtoseconds == 1 ns
/// Revision ID (bits 0:7). Revision 1 is implemented (refer to v1.0a spec).
const HPET_CAP_REV_ID_VALUE: u64 = 0x1;
const HPET_CAP_REV_ID_SHIFT: usize = 0;
/// Number of Timers (bits 8:12)
const HPET_CAP_NUM_TIM_SHIFT: usize = 8;
/// Counter Size (bit 13)
const HPET_CAP_COUNT_SIZE_CAP_SHIFT: usize = 13;
/// Legacy Replacement Route Capable (bit 15)
const HPET_CAP_LEG_RT_CAP_SHIFT: usize = 15;
/// Vendor ID (bits 16:31)
const HPET_CAP_VENDER_ID_VALUE: u64 = 0x8086;
const HPET_CAP_VENDER_ID_SHIFT: usize = 16;
/// Main Counter Tick Period (bits 32:63)
const HPET_CAP_CNT_CLK_PERIOD_SHIFT: usize = 32;
/// Overall Enable (bit 0)
const HPET_CFG_ENABLE_SHIFT: usize = 0;
/// Legacy Replacement Route (bit 1)
const HPET_CFG_LEG_RT_SHIFT: usize = 1;
/// Other bits are reserved.
const HPET_CFG_WRITE_MASK: u64 = 0x003;
/// bit 0, 7, and bits 16:31 are reserved.
/// bit 4, 5, 15, and bits 32:64 are read-only.
const HPET_TN_CFG_WRITE_MASK: u64 = 0x7f4e;
/// Timer N Interrupt Type (bit 1)
const HPET_TN_CFG_INT_TYPE_SHIFT: usize = 1;
/// Timer N Interrupt Enable (bit 2)
const HPET_TN_CFG_INT_ENABLE_SHIFT: usize = 2;
/// Timer N Type (Periodic enabled or not, bit 3)
const HPET_TN_CFG_PERIODIC_SHIFT: usize = 3;
/// Timer N Periodic Interrupt Capable (support Periodic or not, bit 4)
const HPET_TN_CFG_PERIODIC_CAP_SHIFT: usize = 4;
/// Timer N Size (timer size is 64-bits or 32 bits, bit 5)
const HPET_TN_CFG_SIZE_CAP_SHIFT: usize = 5;
/// Timer N Value Set (bit 6)
const HPET_TN_CFG_SETVAL_SHIFT: usize = 6;
/// Timer N 32-bit Mode (bit 8)
const HPET_TN_CFG_32BIT_SHIFT: usize = 8;
/// Timer N Interrupt Rout (bits 9:13)
const HPET_TN_CFG_INT_ROUTE_MASK: u64 = 0x3e00;
const HPET_TN_CFG_INT_ROUTE_SHIFT: usize = 9;
/// Timer N FSB Interrupt Enable (bit 14)
const HPET_TN_CFG_FSB_ENABLE_SHIFT: usize = 14;
/// Timer N FSB Interrupt Delivery (bit 15)
const HPET_TN_CFG_FSB_CAP_SHIFT: usize = 15;
/// Timer N Interrupt Routing Capability (bits 32:63)
const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32;
#[derive(common::TryInto)]
#[repr(u64)]
#[allow(non_camel_case_types)]
/// Timer register enumerations, masked by 0x18
enum TimerRegister {
/// Timer N Configuration and Capability Register
CFG = 0,
/// Timer N Comparator Value Register
CMP = 8,
/// Timer N FSB Interrupt Route Register
ROUTE = 16,
}
#[derive(common::TryInto)]
#[repr(u64)]
#[allow(non_camel_case_types)]
/// Global register enumerations
enum GlobalRegister {
/// General Capabilities and ID Register
CAP = 0,
/// General Configuration Register
CFG = 0x10,
/// General Interrupt Status Register
INT_STATUS = 0x20,
/// Main Counter Value Register
COUNTER = 0xF0,
}
enum DecodedRegister<'a> {
/// Global register in the range from `0` to `0xff`
Global(GlobalRegister),
/// Register in the timer block `0x100`...`0x3ff`
Timer(&'a HPETTimer, TimerRegister),
/// Invalid address
#[allow(dead_code)]
Unknown(hwaddr),
}
struct HPETAddrDecode<'a> {
shift: u32,
len: u32,
target: DecodedRegister<'a>,
}
const fn hpet_next_wrap(cur_tick: u64) -> u64 {
(cur_tick | 0xffffffff) + 1
}
const fn hpet_time_after(a: u64, b: u64) -> bool {
((b - a) as i64) < 0
}
const fn ticks_to_ns(value: u64) -> u64 {
value * HPET_CLK_PERIOD
}
const fn ns_to_ticks(value: u64) -> u64 {
value / HPET_CLK_PERIOD
}
// Avoid touching the bits that cannot be written.
const fn hpet_fixup_reg(new: u64, old: u64, mask: u64) -> u64 {
(new & mask) | (old & !mask)
}
const fn activating_bit(old: u64, new: u64, shift: usize) -> bool {
let mask: u64 = 1 << shift;
(old & mask == 0) && (new & mask != 0)
}
const fn deactivating_bit(old: u64, new: u64, shift: usize) -> bool {
let mask: u64 = 1 << shift;
(old & mask != 0) && (new & mask == 0)
}
fn timer_handler(t: &HPETTimer) {
// SFAETY: state field is valid after timer initialization.
let hpet_regs = &unsafe { t.state.as_ref() }.regs;
t.callback(&mut hpet_regs.borrow_mut())
}
#[derive(Debug, Default)]
pub struct HPETTimerRegisters {
// Memory-mapped, software visible timer registers
/// Timer N Configuration and Capability Register
config: u64,
/// Timer N Comparator Value Register
cmp: u64,
/// Timer N FSB Interrupt Route Register
fsb: u64,
// Hidden register state
/// comparator (extended to counter width)
cmp64: u64,
/// Last value written to comparator
period: u64,
/// timer pop will indicate wrap for one-shot 32-bit
/// mode. Next pop will be actual timer expiration.
wrap_flag: bool,
/// last value armed, to avoid timer storms
last: u64,
}
impl HPETTimerRegisters {
/// calculate next value of the general counter that matches the
/// target (either entirely, or the low 32-bit only depending on
/// the timer mode).
fn update_cmp64(&mut self, cur_tick: u64) {
self.cmp64 = if self.is_32bit_mod() {
let mut result: u64 = cur_tick.deposit(0, 32, self.cmp);
if result < cur_tick {
result += 0x100000000;
}
result
} else {
self.cmp
}
}
const fn is_fsb_route_enabled(&self) -> bool {
self.config & (1 << HPET_TN_CFG_FSB_ENABLE_SHIFT) != 0
}
const fn is_periodic(&self) -> bool {
self.config & (1 << HPET_TN_CFG_PERIODIC_SHIFT) != 0
}
const fn is_int_enabled(&self) -> bool {
self.config & (1 << HPET_TN_CFG_INT_ENABLE_SHIFT) != 0
}
const fn is_32bit_mod(&self) -> bool {
self.config & (1 << HPET_TN_CFG_32BIT_SHIFT) != 0
}
const fn is_valset_enabled(&self) -> bool {
self.config & (1 << HPET_TN_CFG_SETVAL_SHIFT) != 0
}
/// True if timer interrupt is level triggered; otherwise, edge triggered.
const fn is_int_level_triggered(&self) -> bool {
self.config & (1 << HPET_TN_CFG_INT_TYPE_SHIFT) != 0
}
const fn clear_valset(&mut self) {
self.config &= !(1 << HPET_TN_CFG_SETVAL_SHIFT);
}
const fn get_individual_route(&self) -> usize {
((self.config & HPET_TN_CFG_INT_ROUTE_MASK) >> HPET_TN_CFG_INT_ROUTE_SHIFT) as usize
}
}
/// HPET Timer Abstraction
#[derive(Debug)]
pub struct HPETTimer {
/// timer N index within the timer block (`HPETState`)
#[doc(alias = "tn")]
index: u8,
qemu_timer: Timer,
/// timer block abstraction containing this timer
state: NonNull<HPETState>,
}
// 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 {
index,
// SAFETY: the HPETTimer will only be used after the timer
// is initialized below.
qemu_timer: unsafe { Timer::new() },
state: NonNull::new(state.cast_mut()).unwrap(),
}
}
fn init_timer(timer: Pin<&mut Self>) {
Timer::init_full(
timer,
None,
CLOCK_VIRTUAL,
Timer::NS,
0,
timer_handler,
|t| &mut t.qemu_timer,
);
}
fn get_state(&self) -> &HPETState {
// SAFETY:
// the pointer is convertible to a reference
unsafe { self.state.as_ref() }
}
fn is_int_active(&self, regs: &HPETRegisters) -> bool {
regs.is_timer_int_active(self.index.into())
}
fn get_int_route(&self, regs: &HPETRegisters) -> usize {
if self.index <= 1 && regs.is_legacy_mode() {
// If LegacyReplacement Route bit is set, HPET specification requires
// timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC,
// timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC.
//
// If the LegacyReplacement Route bit is set, the individual routing
// bits for timers 0 and 1 (APIC or FSB) will have no impact.
//
// FIXME: Consider I/O APIC case.
if self.index == 0 {
0
} else {
RTC_ISA_IRQ
}
} else {
// (If the LegacyReplacement Route bit is set) Timer 2-n will be
// routed as per the routing in the timer n config registers.
// ...
// If the LegacyReplacement Route bit is not set, the individual
// routing bits for each of the timers are used.
regs.tn_regs[self.index as usize].get_individual_route()
}
}
fn set_irq(&self, regs: &HPETRegisters, set: bool) {
let tn_regs = &regs.tn_regs[self.index as usize];
let route = self.get_int_route(regs);
if set && tn_regs.is_int_enabled() && regs.is_hpet_enabled() {
if tn_regs.is_fsb_route_enabled() {
// SAFETY:
// the parameters are valid.
unsafe {
address_space_stl_le(
addr_of_mut!(address_space_memory),
tn_regs.fsb >> 32, // Timer N FSB int addr
tn_regs.fsb as u32, // Timer N FSB int value, truncate!
MEMTXATTRS_UNSPECIFIED,
null_mut(),
);
}
} else if tn_regs.is_int_level_triggered() {
self.get_state().irqs[route].raise();
} else {
self.get_state().irqs[route].pulse();
}
} else if !tn_regs.is_fsb_route_enabled() {
self.get_state().irqs[route].lower();
}
}
fn update_irq(&self, regs: &mut HPETRegisters, set: bool) {
// If Timer N Interrupt Enable bit is 0, "the timer will
// still operate and generate appropriate status bits, but
// will not cause an interrupt"
regs.int_status = regs.int_status.deposit(
self.index.into(),
1,
u64::from(set && regs.tn_regs[self.index as usize].is_int_level_triggered()),
);
self.set_irq(regs, set);
}
fn arm_timer(&self, regs: &mut HPETRegisters, tick: u64) {
let mut ns = regs.get_ns(tick);
let tn_regs = &mut regs.tn_regs[self.index as usize];
// Clamp period to reasonable min value (1 us)
if tn_regs.is_periodic() && ns - tn_regs.last < 1000 {
ns = tn_regs.last + 1000;
}
tn_regs.last = ns;
self.qemu_timer.modify(tn_regs.last);
}
fn set_timer(&self, regs: &mut HPETRegisters) {
let cur_tick: u64 = regs.get_ticks();
let tn_regs = &mut regs.tn_regs[self.index as usize];
tn_regs.wrap_flag = false;
tn_regs.update_cmp64(cur_tick);
let mut next_tick: u64 = tn_regs.cmp64;
if tn_regs.is_32bit_mod() {
// HPET spec says in one-shot 32-bit mode, generate an interrupt when
// counter wraps in addition to an interrupt with comparator match.
if !tn_regs.is_periodic() && tn_regs.cmp64 > hpet_next_wrap(cur_tick) {
tn_regs.wrap_flag = true;
next_tick = hpet_next_wrap(cur_tick);
}
}
self.arm_timer(regs, next_tick);
}
fn del_timer(&self, regs: &mut HPETRegisters) {
// Just remove the timer from the timer_list without destroying
// this timer instance.
self.qemu_timer.delete();
if self.is_int_active(regs) {
// For level-triggered interrupt, this leaves interrupt status
// register set but lowers irq.
self.update_irq(regs, true);
}
}
fn prepare_tn_cfg_reg_new(
&self,
regs: &mut HPETRegisters,
shift: u32,
len: u32,
val: u64,
) -> (u64, u64) {
trace::trace_hpet_ram_write_tn_cfg((shift / 8).try_into().unwrap());
let tn_regs = &regs.tn_regs[self.index as usize];
let old_val: u64 = tn_regs.config;
let mut new_val: u64 = old_val.deposit(shift, len, val);
new_val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK);
// Switch level-type interrupt to edge-type.
if deactivating_bit(old_val, new_val, HPET_TN_CFG_INT_TYPE_SHIFT) {
// Do this before changing timer.regs.config; otherwise, if
// HPET_TN_FSB is set, update_irq will not lower the qemu_irq.
self.update_irq(regs, false);
}
(new_val, old_val)
}
/// Configuration and Capability Register
fn set_tn_cfg_reg(&self, regs: &mut HPETRegisters, shift: u32, len: u32, val: u64) {
// Factor out a prepare_tn_cfg_reg_new() to better handle immutable scope.
let (new_val, old_val) = self.prepare_tn_cfg_reg_new(regs, shift, len, val);
regs.tn_regs[self.index as usize].config = new_val;
if activating_bit(old_val, new_val, HPET_TN_CFG_INT_ENABLE_SHIFT)
&& self.is_int_active(regs)
{
self.update_irq(regs, true);
}
let tn_regs = &mut regs.tn_regs[self.index as usize];
if tn_regs.is_32bit_mod() {
tn_regs.cmp = u64::from(tn_regs.cmp as u32); // truncate!
tn_regs.period = u64::from(tn_regs.period as u32); // truncate!
}
if regs.is_hpet_enabled() {
self.set_timer(regs);
}
}
/// Comparator Value Register
fn set_tn_cmp_reg(&self, regs: &mut HPETRegisters, shift: u32, len: u32, val: u64) {
let tn_regs = &mut regs.tn_regs[self.index as usize];
let mut length = len;
let mut value = val;
if tn_regs.is_32bit_mod() {
// High 32-bits are zero, leave them untouched.
if shift != 0 {
trace::trace_hpet_ram_write_invalid_tn_cmp();
return;
}
length = 64;
value = u64::from(value as u32); // truncate!
}
trace::trace_hpet_ram_write_tn_cmp((shift / 8).try_into().unwrap());
if !tn_regs.is_periodic() || tn_regs.is_valset_enabled() {
tn_regs.cmp = tn_regs.cmp.deposit(shift, length, value);
}
if tn_regs.is_periodic() {
tn_regs.period = tn_regs.period.deposit(shift, length, value);
}
tn_regs.clear_valset();
if regs.is_hpet_enabled() {
self.set_timer(regs);
}
}
/// FSB Interrupt Route Register
fn set_tn_fsb_route_reg(&self, regs: &mut HPETRegisters, shift: u32, len: u32, val: u64) {
let tn_regs = &mut regs.tn_regs[self.index as usize];
tn_regs.fsb = tn_regs.fsb.deposit(shift, len, val);
}
fn reset(&self, regs: &mut HPETRegisters) {
self.del_timer(regs);
let tn_regs = &mut regs.tn_regs[self.index as usize];
tn_regs.cmp = u64::MAX; // Comparator Match Registers reset to all 1's.
tn_regs.config = (1 << HPET_TN_CFG_PERIODIC_CAP_SHIFT) | (1 << HPET_TN_CFG_SIZE_CAP_SHIFT);
if self.get_state().has_msi_flag() {
tn_regs.config |= 1 << HPET_TN_CFG_FSB_CAP_SHIFT;
}
// advertise availability of ioapic int
tn_regs.config |=
(u64::from(self.get_state().int_route_cap)) << HPET_TN_CFG_INT_ROUTE_CAP_SHIFT;
tn_regs.period = 0;
tn_regs.wrap_flag = false;
}
/// timer expiration callback
fn callback(&self, regs: &mut HPETRegisters) {
let cur_tick: u64 = regs.get_ticks();
let tn_regs = &mut regs.tn_regs[self.index as usize];
let next_tick = if tn_regs.is_periodic() && tn_regs.period != 0 {
while hpet_time_after(cur_tick, tn_regs.cmp64) {
tn_regs.cmp64 += tn_regs.period;
}
if tn_regs.is_32bit_mod() {
tn_regs.cmp = u64::from(tn_regs.cmp64 as u32); // truncate!
} else {
tn_regs.cmp = tn_regs.cmp64;
}
Some(tn_regs.cmp64)
} else {
tn_regs.wrap_flag.then_some(tn_regs.cmp64)
};
tn_regs.wrap_flag = false;
if let Some(tick) = next_tick {
self.arm_timer(regs, tick);
}
self.update_irq(regs, true);
}
fn read(&self, target: TimerRegister, regs: &HPETRegisters) -> u64 {
let tn_regs = &regs.tn_regs[self.index as usize];
use TimerRegister::*;
match target {
CFG => tn_regs.config, // including interrupt capabilities
CMP => tn_regs.cmp, // comparator register
ROUTE => tn_regs.fsb,
}
}
fn write(
&self,
target: TimerRegister,
regs: &mut HPETRegisters,
value: u64,
shift: u32,
len: u32,
) {
use TimerRegister::*;
trace::trace_hpet_ram_write_timer_id(self.index);
match target {
CFG => self.set_tn_cfg_reg(regs, shift, len, value),
CMP => self.set_tn_cmp_reg(regs, shift, len, value),
ROUTE => self.set_tn_fsb_route_reg(regs, shift, len, value),
}
}
}
#[derive(Default, ToMigrationState)]
pub struct HPETRegisters {
// HPET block Registers: Memory-mapped, software visible registers
/// General Capabilities and ID Register
///
/// Constant and therefore not migrated.
#[migration_state(omit)]
capability: u64,
/// General Configuration Register
config: u64,
/// General Interrupt Status Register
#[doc(alias = "isr")]
int_status: u64,
/// Main Counter Value Register
#[doc(alias = "hpet_counter")]
counter: u64,
/// HPET Timer N Registers
///
/// Migrated as part of `Migratable<HPETTimer>`
#[migration_state(omit)]
tn_regs: [HPETTimerRegisters; HPET_MAX_TIMERS],
/// Offset of main counter relative to qemu clock.
///
/// Migrated as a subsection and therefore snapshotted into [`HPETState`]
#[migration_state(omit)]
pub hpet_offset: u64,
}
impl HPETRegisters {
fn get_ticks(&self) -> u64 {
ns_to_ticks(CLOCK_VIRTUAL.get_ns() + self.hpet_offset)
}
fn get_ns(&self, tick: u64) -> u64 {
ticks_to_ns(tick) - self.hpet_offset
}
fn is_legacy_mode(&self) -> bool {
self.config & (1 << HPET_CFG_LEG_RT_SHIFT) != 0
}
fn is_hpet_enabled(&self) -> bool {
self.config & (1 << HPET_CFG_ENABLE_SHIFT) != 0
}
fn is_timer_int_active(&self, index: usize) -> bool {
self.int_status & (1 << index) != 0
}
}
/// HPET Event Timer Block Abstraction
#[repr(C)]
#[derive(qom::Object, hwcore::Device)]
pub struct HPETState {
parent_obj: ParentField<SysBusDevice>,
iomem: MemoryRegion,
regs: Migratable<BqlRefCell<HPETRegisters>>,
// Internal state
/// Capabilities that QEMU HPET supports.
/// bit 0: MSI (or FSB) support.
#[property(rename = "msi", bit = HPET_FLAG_MSI_SUPPORT_SHIFT, default = false)]
flags: u32,
hpet_offset_migration: BqlCell<u64>,
#[property(rename = "hpet-offset-saved", default = true)]
hpet_offset_saved: bool,
irqs: [InterruptSource; HPET_NUM_IRQ_ROUTES],
rtc_irq_level: BqlCell<u32>,
pit_enabled: InterruptSource,
/// Interrupt Routing Capability.
/// This field indicates to which interrupts in the I/O (x) APIC
/// the timers' interrupt can be routed, and is encoded in the
/// bits 32:64 of timer N's config register:
#[doc(alias = "intcap")]
#[property(rename = "hpet-intcap", default = 0)]
int_route_cap: u32,
/// HPET timer array managed by this timer block.
#[doc(alias = "timer")]
timers: [Migratable<HPETTimer>; HPET_MAX_TIMERS],
#[property(rename = "timers", default = HPET_MIN_TIMERS)]
num_timers: usize,
num_timers_save: BqlCell<u8>,
/// Instance id (HPET timer block ID).
hpet_id: BqlCell<usize>,
}
impl HPETState {
const fn has_msi_flag(&self) -> bool {
self.flags & (1 << HPET_FLAG_MSI_SUPPORT_SHIFT) != 0
}
fn handle_legacy_irq(&self, irq: u32, level: u32) {
let regs = self.regs.borrow();
if irq == HPET_LEGACY_PIT_INT {
if !regs.is_legacy_mode() {
self.irqs[0].set(level != 0);
}
} else {
self.rtc_irq_level.set(level);
if !regs.is_legacy_mode() {
self.irqs[RTC_ISA_IRQ].set(level != 0);
}
}
}
fn init_timers(this: &mut MaybeUninit<Self>) {
let state = this.as_ptr();
for index in 0..HPET_MAX_TIMERS {
let mut timer = uninit_field_mut!(*this, timers[index]);
// Initialize in two steps, to avoid calling Timer::init_full on a
// temporary that can be moved.
let timer = timer.write(Migratable::new(HPETTimer::new(
index.try_into().unwrap(),
state,
)));
// SAFETY: HPETState is pinned
let timer = unsafe { Pin::new_unchecked(&mut **timer) };
HPETTimer::init_timer(timer);
}
}
/// General Configuration Register
fn set_cfg_reg(&self, regs: &mut HPETRegisters, shift: u32, len: u32, val: u64) {
let old_val = regs.config;
let mut new_val = old_val.deposit(shift, len, val);
new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK);
regs.config = new_val;
if activating_bit(old_val, new_val, HPET_CFG_ENABLE_SHIFT) {
// Enable main counter and interrupt generation.
regs.hpet_offset = ticks_to_ns(regs.counter) - CLOCK_VIRTUAL.get_ns();
for t in self.timers.iter().take(self.num_timers) {
let id = t.index as usize;
let tn_regs = &regs.tn_regs[id];
if tn_regs.is_int_enabled() && t.is_int_active(regs) {
t.update_irq(regs, true);
}
t.set_timer(regs);
}
} else if deactivating_bit(old_val, new_val, HPET_CFG_ENABLE_SHIFT) {
// Halt main counter and disable interrupt generation.
regs.counter = regs.get_ticks();
for t in self.timers.iter().take(self.num_timers) {
t.del_timer(regs);
}
}
// i8254 and RTC output pins are disabled when HPET is in legacy mode
if activating_bit(old_val, new_val, HPET_CFG_LEG_RT_SHIFT) {
self.pit_enabled.set(false);
self.irqs[0].lower();
self.irqs[RTC_ISA_IRQ].lower();
} else if deactivating_bit(old_val, new_val, HPET_CFG_LEG_RT_SHIFT) {
self.irqs[0].lower();
self.pit_enabled.set(true);
self.irqs[RTC_ISA_IRQ].set(self.rtc_irq_level.get() != 0);
}
}
/// General Interrupt Status Register: Read/Write Clear
fn set_int_status_reg(&self, regs: &mut HPETRegisters, shift: u32, _len: u32, val: u64) {
let new_val = val << shift;
let cleared = new_val & regs.int_status;
for t in self.timers.iter().take(self.num_timers) {
if cleared & (1 << t.index) != 0 {
t.update_irq(regs, false);
}
}
}
/// Main Counter Value Register
fn set_counter_reg(&self, regs: &mut HPETRegisters, shift: u32, len: u32, val: u64) {
if regs.is_hpet_enabled() {
// HPET spec says that writes to this register should only be
// done while the counter is halted. So this is an undefined
// behavior. There's no need to forbid it, but when HPET is
// enabled, the changed counter value will not affect the
// tick count (i.e., the previously calculated offset will
// not be changed as well).
trace::trace_hpet_ram_write_counter_write_while_enabled();
}
regs.counter = regs.counter.deposit(shift, len, val);
}
unsafe fn init(mut this: ParentInit<Self>) {
static HPET_RAM_OPS: MemoryRegionOps<HPETState> =
MemoryRegionOpsBuilder::<HPETState>::new()
.read(&HPETState::read)
.write(&HPETState::write)
.little_endian()
.valid_sizes(4, 8)
.impl_sizes(4, 8)
.build();
MemoryRegion::init_io(
&mut uninit_field_mut!(*this, iomem),
&HPET_RAM_OPS,
"hpet",
HPET_REG_SPACE_LEN,
);
// Only consider members with more complex structures. C has already
// initialized memory to all zeros - simple types (bool/u32/usize) can
// rely on this without explicit initialization.
uninit_field_mut!(*this, regs).write(Default::default());
uninit_field_mut!(*this, hpet_offset_migration).write(Default::default());
// Set null_mut for now and post_init() will fill it.
uninit_field_mut!(*this, irqs).write(Default::default());
uninit_field_mut!(*this, rtc_irq_level).write(Default::default());
uninit_field_mut!(*this, pit_enabled).write(Default::default());
uninit_field_mut!(*this, num_timers_save).write(Default::default());
uninit_field_mut!(*this, hpet_id).write(Default::default());
Self::init_timers(&mut this);
}
fn post_init(&self) {
self.init_mmio(&self.iomem);
for irq in self.irqs.iter() {
self.init_irq(irq);
}
}
fn realize(&self) -> util::Result<()> {
ensure!(
(HPET_MIN_TIMERS..=HPET_MAX_TIMERS).contains(&self.num_timers),
"hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}"
);
ensure!(
self.int_route_cap != 0,
"hpet.hpet-intcap property not initialized"
);
self.hpet_id.set(HPETFwConfig::assign_hpet_id()?);
// 64-bit General Capabilities and ID Register; LegacyReplacementRoute.
self.regs.borrow_mut().capability = HPET_CAP_REV_ID_VALUE << HPET_CAP_REV_ID_SHIFT |
1 << HPET_CAP_COUNT_SIZE_CAP_SHIFT |
1 << HPET_CAP_LEG_RT_CAP_SHIFT |
HPET_CAP_VENDER_ID_VALUE << HPET_CAP_VENDER_ID_SHIFT |
((self.num_timers - 1) as u64) << HPET_CAP_NUM_TIM_SHIFT | // indicate the last timer
(HPET_CLK_PERIOD * FS_PER_NS) << HPET_CAP_CNT_CLK_PERIOD_SHIFT; // 10 ns
self.init_gpio_in(2, HPETState::handle_legacy_irq);
self.init_gpio_out(from_ref(&self.pit_enabled));
Ok(())
}
fn reset_hold(&self, _type: ResetType) {
let mut regs = self.regs.borrow_mut();
for t in self.timers.iter().take(self.num_timers) {
t.reset(&mut regs);
}
regs.counter = 0;
regs.config = 0;
regs.hpet_offset = 0;
HPETFwConfig::update_hpet_cfg(
self.hpet_id.get(),
regs.capability as u32,
self.mmio_addr(0).unwrap(),
);
// pit_enabled.set(true) will call irq handler and access regs
// again. We cannot borrow BqlRefCell twice at once. Minimize the
// scope of regs to ensure it will be dropped before irq callback.
drop(regs);
self.pit_enabled.set(true);
// to document that the RTC lowers its output on reset as well
self.rtc_irq_level.set(0);
}
fn decode(&self, mut addr: hwaddr, size: u32) -> HPETAddrDecode<'_> {
let shift = ((addr & 4) * 8) as u32;
let len = std::cmp::min(size * 8, 64 - shift);
addr &= !4;
let target = if (0..=0xff).contains(&addr) {
GlobalRegister::try_from(addr).map(DecodedRegister::Global)
} else {
let timer_id: usize = ((addr - 0x100) / 0x20) as usize;
if timer_id < self.num_timers {
TimerRegister::try_from(addr & 0x18)
.map(|target| DecodedRegister::Timer(&self.timers[timer_id], target))
} else {
trace::trace_hpet_timer_id_out_of_range(timer_id.try_into().unwrap());
Err(addr)
}
};
// `target` is now a Result<DecodedRegister, hwaddr>
// convert the Err case into DecodedRegister as well
let target = target.unwrap_or_else(DecodedRegister::Unknown);
HPETAddrDecode { shift, len, target }
}
fn read(&self, addr: hwaddr, size: u32) -> u64 {
trace::trace_hpet_ram_read(addr);
let HPETAddrDecode { shift, target, .. } = self.decode(addr, size);
let regs = &self.regs.borrow();
use DecodedRegister::*;
use GlobalRegister::*;
(match target {
Timer(t, tn_target) => t.read(tn_target, regs),
Global(CAP) => regs.capability, /* including HPET_PERIOD 0x004 */
Global(CFG) => regs.config,
Global(INT_STATUS) => regs.int_status,
Global(COUNTER) => {
let cur_tick = if regs.is_hpet_enabled() {
regs.get_ticks()
} else {
regs.counter
};
trace::trace_hpet_ram_read_reading_counter((addr & 4) as u8, cur_tick);
cur_tick
}
Unknown(_) => {
trace::trace_hpet_ram_read_invalid();
0
}
}) >> shift
}
fn write(&self, addr: hwaddr, value: u64, size: u32) {
let HPETAddrDecode { shift, len, target } = self.decode(addr, size);
let mut regs = self.regs.borrow_mut();
trace::trace_hpet_ram_write(addr, value);
use DecodedRegister::*;
use GlobalRegister::*;
match target {
Timer(t, tn_target) => t.write(tn_target, &mut regs, value, shift, len),
Global(CAP) => {} // General Capabilities and ID Register: Read Only
Global(CFG) => self.set_cfg_reg(&mut regs, shift, len, value),
Global(INT_STATUS) => self.set_int_status_reg(&mut regs, shift, len, value),
Global(COUNTER) => self.set_counter_reg(&mut regs, shift, len, value),
Unknown(_) => trace::trace_hpet_ram_write_invalid(),
}
}
fn pre_save(&self) -> Result<(), migration::Infallible> {
let mut regs = self.regs.borrow_mut();
self.hpet_offset_migration.set(regs.hpet_offset);
if regs.is_hpet_enabled() {
regs.counter = regs.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 as u8);
Ok(())
}
fn post_load(&self, _version_id: u8) -> Result<(), migration::Infallible> {
let mut regs = self.regs.borrow_mut();
let cnt = regs.counter;
for index in 0..self.num_timers {
let tn_regs = &mut regs.tn_regs[index];
tn_regs.update_cmp64(cnt);
tn_regs.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_migration
.set(ticks_to_ns(regs.counter) - CLOCK_VIRTUAL.get_ns());
}
regs.hpet_offset = self.hpet_offset_migration.get();
Ok(())
}
fn is_rtc_irq_level_needed(&self) -> bool {
self.rtc_irq_level.get() != 0
}
fn is_offset_needed(&self) -> bool {
self.regs.borrow().is_hpet_enabled() && self.hpet_offset_saved
}
fn validate_num_timers(&self, _version_id: u8) -> bool {
self.num_timers == self.num_timers_save.get().into()
}
}
qom_isa!(HPETState: SysBusDevice, DeviceState, Object);
unsafe impl ObjectType for HPETState {
// No need for HPETClass. Just like OBJECT_DECLARE_SIMPLE_TYPE in C.
type Class = <SysBusDevice as ObjectType>::Class;
const TYPE_NAME: &'static CStr = crate::TYPE_HPET;
}
impl ObjectImpl for HPETState {
type ParentType = SysBusDevice;
const INSTANCE_INIT: Option<unsafe fn(ParentInit<Self>)> = Some(Self::init);
const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init);
const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>;
}
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_migration),
})
.build();
#[derive(Default)]
pub struct HPETTimerMigration {
index: u8,
config: u64,
cmp: u64,
fsb: u64,
period: u64,
wrap_flag: u8,
qemu_timer: i64,
}
impl ToMigrationState for HPETTimer {
type Migrated = HPETTimerMigration;
fn snapshot_migration_state(
&self,
target: &mut Self::Migrated,
) -> Result<(), migration::InvalidError> {
let state = self.get_state();
let regs = state.regs.borrow_mut();
let tn_regs = &regs.tn_regs[self.index as usize];
target.index = self.index;
target.config = tn_regs.config;
target.cmp = tn_regs.cmp;
target.fsb = tn_regs.fsb;
target.period = tn_regs.period;
target.wrap_flag = u8::from(tn_regs.wrap_flag);
self.qemu_timer
.snapshot_migration_state(&mut target.qemu_timer)?;
Ok(())
}
fn restore_migrated_state_mut(
&mut self,
source: Self::Migrated,
version_id: u8,
) -> Result<(), migration::InvalidError> {
self.restore_migrated_state(source, version_id)
}
}
impl ToMigrationStateShared for HPETTimer {
fn restore_migrated_state(
&self,
source: Self::Migrated,
version_id: u8,
) -> Result<(), migration::InvalidError> {
let state = self.get_state();
let mut regs = state.regs.borrow_mut();
let tn_regs = &mut regs.tn_regs[self.index as usize];
tn_regs.config = source.config;
tn_regs.cmp = source.cmp;
tn_regs.fsb = source.fsb;
tn_regs.period = source.period;
tn_regs.wrap_flag = source.wrap_flag != 0;
self.qemu_timer
.restore_migrated_state(source.qemu_timer, version_id)?;
Ok(())
}
}
const VMSTATE_HPET_TIMER: VMStateDescription<HPETTimerMigration> =
VMStateDescriptionBuilder::<HPETTimerMigration>::new()
.name(c"hpet_timer")
.version_id(1)
.minimum_version_id(1)
.fields(vmstate_fields! {
vmstate_of!(HPETTimerMigration, index),
vmstate_of!(HPETTimerMigration, config),
vmstate_of!(HPETTimerMigration, cmp),
vmstate_of!(HPETTimerMigration, fsb),
vmstate_of!(HPETTimerMigration, period),
vmstate_of!(HPETTimerMigration, wrap_flag),
vmstate_of!(HPETTimerMigration, qemu_timer),
})
.build();
impl_vmstate_struct!(HPETTimerMigration, VMSTATE_HPET_TIMER);
const VALIDATE_TIMERS_NAME: &CStr = c"num_timers must match";
// HPETRegistersMigration is generated by ToMigrationState macro.
impl_vmstate_struct!(
HPETRegistersMigration,
VMStateDescriptionBuilder::<HPETRegistersMigration>::new()
.name(c"hpet/regs")
.version_id(2)
.minimum_version_id(2)
.fields(vmstate_fields! {
vmstate_of!(HPETRegistersMigration, config),
vmstate_of!(HPETRegistersMigration, int_status),
vmstate_of!(HPETRegistersMigration, counter),
})
.build()
);
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, regs),
vmstate_of!(HPETState, num_timers_save),
vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers),
vmstate_of!(HPETState, timers[0 .. num_timers_save], HPETState::validate_num_timers).with_version_id(0),
})
.subsections(vmstate_subsections!(
VMSTATE_HPET_RTC_IRQ_LEVEL,
VMSTATE_HPET_OFFSET,
))
.build();
impl DeviceImpl for HPETState {
const VMSTATE: Option<VMStateDescription<Self>> = Some(VMSTATE_HPET);
const REALIZE: Option<fn(&Self) -> util::Result<()>> = Some(Self::realize);
}
impl ResettablePhasesImpl for HPETState {
const HOLD: Option<fn(&Self, ResetType)> = Some(Self::reset_hold);
}
impl SysBusDeviceImpl for HPETState {}