Skip to content

Commit 519088b

Browse files
committed
rust: hpet: decode HPET registers into enums
Generalize timer_and_addr() to decode all registers into a single enum HPETRegister, and use the TryInto derive to separate valid and invalid values. The main advantage lies in checking that all registers are enumerated in the "match" statements. Signed-off-by: Paolo Bonzini <[email protected]>
1 parent aa50bc4 commit 519088b

File tree

3 files changed

+111
-99
lines changed

3 files changed

+111
-99
lines changed

rust/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ result_unit_err = "allow"
3737
should_implement_trait = "deny"
3838
# can be for a reason, e.g. in callbacks
3939
unused_self = "allow"
40+
# common in device crates
41+
upper_case_acronyms = "allow"
4042

4143
# default-allow lints
4244
as_ptr_cast_mut = "deny"

rust/hw/char/pl011/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
//! See [`PL011State`](crate::device::PL011State) for the device model type and
1313
//! the [`registers`] module for register types.
1414
15-
#![allow(clippy::upper_case_acronyms)]
16-
1715
use qemu_api::c_str;
1816

1917
mod device;

rust/hw/timer/hpet/src/hpet.rs

Lines changed: 109 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ const RTC_ISA_IRQ: usize = 8;
4848
const HPET_CLK_PERIOD: u64 = 10; // 10 ns
4949
const FS_PER_NS: u64 = 1000000; // 1000000 femtoseconds == 1 ns
5050

51-
/// General Capabilities and ID Register
52-
const HPET_CAP_REG: u64 = 0x000;
5351
/// Revision ID (bits 0:7). Revision 1 is implemented (refer to v1.0a spec).
5452
const HPET_CAP_REV_ID_VALUE: u64 = 0x1;
5553
const HPET_CAP_REV_ID_SHIFT: usize = 0;
@@ -65,23 +63,13 @@ const HPET_CAP_VENDER_ID_SHIFT: usize = 16;
6563
/// Main Counter Tick Period (bits 32:63)
6664
const HPET_CAP_CNT_CLK_PERIOD_SHIFT: usize = 32;
6765

68-
/// General Configuration Register
69-
const HPET_CFG_REG: u64 = 0x010;
7066
/// Overall Enable (bit 0)
7167
const HPET_CFG_ENABLE_SHIFT: usize = 0;
7268
/// Legacy Replacement Route (bit 1)
7369
const HPET_CFG_LEG_RT_SHIFT: usize = 1;
7470
/// Other bits are reserved.
7571
const HPET_CFG_WRITE_MASK: u64 = 0x003;
7672

77-
/// General Interrupt Status Register
78-
const HPET_INT_STATUS_REG: u64 = 0x020;
79-
80-
/// Main Counter Value Register
81-
const HPET_COUNTER_REG: u64 = 0x0f0;
82-
83-
/// Timer N Configuration and Capability Register (masked by 0x18)
84-
const HPET_TN_CFG_REG: u64 = 0x000;
8573
/// bit 0, 7, and bits 16:31 are reserved.
8674
/// bit 4, 5, 15, and bits 32:64 are read-only.
8775
const HPET_TN_CFG_WRITE_MASK: u64 = 0x7f4e;
@@ -109,11 +97,51 @@ const HPET_TN_CFG_FSB_CAP_SHIFT: usize = 15;
10997
/// Timer N Interrupt Routing Capability (bits 32:63)
11098
const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32;
11199

112-
/// Timer N Comparator Value Register (masked by 0x18)
113-
const HPET_TN_CMP_REG: u64 = 0x008;
100+
#[derive(qemu_api_macros::TryInto)]
101+
#[repr(u64)]
102+
#[allow(non_camel_case_types)]
103+
/// Timer registers, masked by 0x18
104+
enum TimerRegister {
105+
/// Timer N Configuration and Capability Register
106+
CFG = 0,
107+
/// Timer N Comparator Value Register
108+
CMP = 8,
109+
/// Timer N FSB Interrupt Route Register
110+
ROUTE = 16,
111+
}
112+
113+
#[derive(qemu_api_macros::TryInto)]
114+
#[repr(u64)]
115+
#[allow(non_camel_case_types)]
116+
/// Global registers
117+
enum GlobalRegister {
118+
/// General Capabilities and ID Register
119+
CAP = 0,
120+
/// General Configuration Register
121+
CFG = 0x10,
122+
/// General Interrupt Status Register
123+
INT_STATUS = 0x20,
124+
/// Main Counter Value Register
125+
COUNTER = 0xF0,
126+
}
114127

115-
/// Timer N FSB Interrupt Route Register (masked by 0x18)
116-
const HPET_TN_FSB_ROUTE_REG: u64 = 0x010;
128+
enum HPETRegister<'a> {
129+
/// Global register in the range from `0` to `0xff`
130+
Global(GlobalRegister),
131+
132+
/// Register in the timer block `0x100`...`0x3ff`
133+
Timer(&'a BqlRefCell<HPETTimer>, TimerRegister),
134+
135+
/// Invalid address
136+
#[allow(dead_code)]
137+
Unknown(hwaddr),
138+
}
139+
140+
struct HPETAddrDecode<'a> {
141+
shift: u32,
142+
len: u32,
143+
reg: HPETRegister<'a>,
144+
}
117145

118146
const fn hpet_next_wrap(cur_tick: u64) -> u64 {
119147
(cur_tick | 0xffffffff) + 1
@@ -471,33 +499,21 @@ impl HPETTimer {
471499
self.update_irq(true);
472500
}
473501

474-
const fn read(&self, addr: hwaddr, _size: u32) -> u64 {
475-
let shift: u64 = (addr & 4) * 8;
476-
477-
match addr & !4 {
478-
HPET_TN_CFG_REG => self.config >> shift, // including interrupt capabilities
479-
HPET_TN_CMP_REG => self.cmp >> shift, // comparator register
480-
HPET_TN_FSB_ROUTE_REG => self.fsb >> shift,
481-
_ => {
482-
// TODO: Add trace point - trace_hpet_ram_read_invalid()
483-
// Reserved.
484-
0
485-
}
502+
const fn read(&self, reg: TimerRegister) -> u64 {
503+
use TimerRegister::*;
504+
match reg {
505+
CFG => self.config, // including interrupt capabilities
506+
CMP => self.cmp, // comparator register
507+
ROUTE => self.fsb,
486508
}
487509
}
488510

489-
fn write(&mut self, addr: hwaddr, value: u64, size: u32) {
490-
let shift = ((addr & 4) * 8) as u32;
491-
let len = std::cmp::min(size * 8, 64 - shift);
492-
493-
match addr & !4 {
494-
HPET_TN_CFG_REG => self.set_tn_cfg_reg(shift, len, value),
495-
HPET_TN_CMP_REG => self.set_tn_cmp_reg(shift, len, value),
496-
HPET_TN_FSB_ROUTE_REG => self.set_tn_fsb_route_reg(shift, len, value),
497-
_ => {
498-
// TODO: Add trace point - trace_hpet_ram_write_invalid()
499-
// Reserved.
500-
}
511+
fn write(&mut self, reg: TimerRegister, value: u64, shift: u32, len: u32) {
512+
use TimerRegister::*;
513+
match reg {
514+
CFG => self.set_tn_cfg_reg(shift, len, value),
515+
CMP => self.set_tn_cmp_reg(shift, len, value),
516+
ROUTE => self.set_tn_fsb_route_reg(shift, len, value),
501517
}
502518
}
503519
}
@@ -749,76 +765,72 @@ impl HPETState {
749765
self.rtc_irq_level.set(0);
750766
}
751767

752-
fn timer_and_addr(&self, addr: hwaddr) -> Option<(&BqlRefCell<HPETTimer>, hwaddr)> {
753-
let timer_id: usize = ((addr - 0x100) / 0x20) as usize;
768+
fn decode(&self, mut addr: hwaddr, size: u32) -> HPETAddrDecode {
769+
let shift = ((addr & 4) * 8) as u32;
770+
let len = std::cmp::min(size * 8, 64 - shift);
754771

755-
// TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id)
756-
if timer_id > self.num_timers.get() {
757-
// TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id)
758-
None
772+
addr &= !4;
773+
let reg = if (0..=0xff).contains(&addr) {
774+
GlobalRegister::try_from(addr).map(HPETRegister::Global)
759775
} else {
760-
// Keep the complete address so that HPETTimer's read and write could
761-
// detect the invalid access.
762-
Some((&self.timers[timer_id], addr & 0x1F))
763-
}
776+
let timer_id: usize = ((addr - 0x100) / 0x20) as usize;
777+
if timer_id <= self.num_timers.get() {
778+
// TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id)
779+
TimerRegister::try_from(addr)
780+
.map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg))
781+
} else {
782+
// TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id)
783+
Err(addr)
784+
}
785+
};
786+
787+
// reg is now a Result<HPETRegister, hwaddr>
788+
// convert the Err case into HPETRegister as well
789+
let reg = reg.unwrap_or_else(HPETRegister::Unknown);
790+
HPETAddrDecode { shift, len, reg }
764791
}
765792

766793
fn read(&self, addr: hwaddr, size: u32) -> u64 {
767-
let shift: u64 = (addr & 4) * 8;
768-
769-
// address range of all TN regs
770794
// TODO: Add trace point - trace_hpet_ram_read(addr)
771-
if (0x100..=0x3ff).contains(&addr) {
772-
match self.timer_and_addr(addr) {
773-
None => 0, // Reserved,
774-
Some((timer, tn_addr)) => timer.borrow_mut().read(tn_addr, size),
775-
}
776-
} else {
777-
match addr & !4 {
778-
HPET_CAP_REG => self.capability.get() >> shift, /* including HPET_PERIOD 0x004 */
779-
// (CNT_CLK_PERIOD field)
780-
HPET_CFG_REG => self.config.get() >> shift,
781-
HPET_COUNTER_REG => {
782-
let cur_tick: u64 = if self.is_hpet_enabled() {
783-
self.get_ticks()
784-
} else {
785-
self.counter.get()
786-
};
787-
788-
// TODO: Add trace point - trace_hpet_ram_read_reading_counter(addr & 4,
789-
// cur_tick)
790-
cur_tick >> shift
791-
}
792-
HPET_INT_STATUS_REG => self.int_status.get() >> shift,
793-
_ => {
794-
// TODO: Add trace point- trace_hpet_ram_read_invalid()
795-
// Reserved.
796-
0
795+
let HPETAddrDecode { shift, reg, .. } = self.decode(addr, size);
796+
797+
use GlobalRegister::*;
798+
use HPETRegister::*;
799+
(match reg {
800+
Timer(timer, tn_reg) => timer.borrow_mut().read(tn_reg),
801+
Global(CAP) => self.capability.get(), /* including HPET_PERIOD 0x004 */
802+
Global(CFG) => self.config.get(),
803+
Global(INT_STATUS) => self.int_status.get(),
804+
Global(COUNTER) => {
805+
// TODO: Add trace point
806+
// trace_hpet_ram_read_reading_counter(addr & 4, cur_tick)
807+
if self.is_hpet_enabled() {
808+
self.get_ticks()
809+
} else {
810+
self.counter.get()
797811
}
798812
}
799-
}
813+
Unknown(_) => {
814+
// TODO: Add trace point- trace_hpet_ram_read_invalid()
815+
0
816+
}
817+
}) >> shift
800818
}
801819

802820
fn write(&self, addr: hwaddr, value: u64, size: u32) {
803-
let shift = ((addr & 4) * 8) as u32;
804-
let len = std::cmp::min(size * 8, 64 - shift);
821+
let HPETAddrDecode { shift, len, reg } = self.decode(addr, size);
805822

806823
// TODO: Add trace point - trace_hpet_ram_write(addr, value)
807-
if (0x100..=0x3ff).contains(&addr) {
808-
match self.timer_and_addr(addr) {
809-
None => (), // Reserved.
810-
Some((timer, tn_addr)) => timer.borrow_mut().write(tn_addr, value, size),
811-
}
812-
} else {
813-
match addr & !0x4 {
814-
HPET_CAP_REG => {} // General Capabilities and ID Register: Read Only
815-
HPET_CFG_REG => self.set_cfg_reg(shift, len, value),
816-
HPET_INT_STATUS_REG => self.set_int_status_reg(shift, len, value),
817-
HPET_COUNTER_REG => self.set_counter_reg(shift, len, value),
818-
_ => {
819-
// TODO: Add trace point - trace_hpet_ram_write_invalid()
820-
// Reserved.
821-
}
824+
use GlobalRegister::*;
825+
use HPETRegister::*;
826+
match reg {
827+
Timer(timer, tn_reg) => timer.borrow_mut().write(tn_reg, value, shift, len),
828+
Global(CAP) => {} // General Capabilities and ID Register: Read Only
829+
Global(CFG) => self.set_cfg_reg(shift, len, value),
830+
Global(INT_STATUS) => self.set_int_status_reg(shift, len, value),
831+
Global(COUNTER) => self.set_counter_reg(shift, len, value),
832+
Unknown(_) => {
833+
// TODO: Add trace point - trace_hpet_ram_write_invalid()
822834
}
823835
}
824836
}

0 commit comments

Comments
 (0)