* [PATCH] hw/timer/hpet: Detect invalid access to TN registers
@ 2025-02-18 7:37 Zhao Liu
2025-02-18 8:53 ` Paolo Bonzini
0 siblings, 1 reply; 7+ messages in thread
From: Zhao Liu @ 2025-02-18 7:37 UTC (permalink / raw)
To: Paolo Bonzini, Michael S . Tsirkin; +Cc: qemu-devel, Zhao Liu
"addr & 0x18" ignores invalid address, so that the trace in default
branch (trace_hpet_ram_{read|write}_invalid()) doesn't work.
Mask addr by "0x1f & ~4", in which 0x1f means to get the complete TN
registers access and ~4 means to keep any invalid address offset.
Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
---
* Note: This patch applies the changes from Rust HPET [*] to C HPET to
ensure logic consistency.
[*]: The original comment should use "(0x1f & ~4)":
https://lore.kernel.org/qemu-devel/Z6edKxYFzA6suDlj@intel.com/
---
hw/timer/hpet.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c
index dcff18a9871d..0f786047e54f 100644
--- a/hw/timer/hpet.c
+++ b/hw/timer/hpet.c
@@ -455,7 +455,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
return 0;
}
- switch (addr & 0x18) {
+ switch (addr & (0x1f & ~4)) {
case HPET_TN_CFG: // including interrupt capabilities
return timer->config >> shift;
case HPET_TN_CMP: // comparator register
@@ -511,7 +511,8 @@ static void hpet_ram_write(void *opaque, hwaddr addr,
trace_hpet_timer_id_out_of_range(timer_id);
return;
}
- switch (addr & 0x18) {
+
+ switch (addr & (0x1f & ~4)) {
case HPET_TN_CFG:
trace_hpet_ram_write_tn_cfg(addr & 4);
old_val = timer->config;
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH] hw/timer/hpet: Detect invalid access to TN registers
2025-02-18 7:37 [PATCH] hw/timer/hpet: Detect invalid access to TN registers Zhao Liu
@ 2025-02-18 8:53 ` Paolo Bonzini
2025-02-18 9:07 ` Philippe Mathieu-Daudé
2025-02-19 6:12 ` Zhao Liu
0 siblings, 2 replies; 7+ messages in thread
From: Paolo Bonzini @ 2025-02-18 8:53 UTC (permalink / raw)
To: Zhao Liu, Michael S . Tsirkin; +Cc: qemu-devel
On 2/18/25 08:37, Zhao Liu wrote:
> "addr & 0x18" ignores invalid address, so that the trace in default
> branch (trace_hpet_ram_{read|write}_invalid()) doesn't work.
>
> Mask addr by "0x1f & ~4", in which 0x1f means to get the complete TN
> registers access and ~4 means to keep any invalid address offset.
I think this is less readable.
The reason to use !4 in the Rust code is because the initial AND is done
in a separate function, timer_and_addr(). In C you don't have the same
thing. If anything you could do something like this:
diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c
index ccb97b68066..7c011204971 100644
--- a/hw/timer/hpet.c
+++ b/hw/timer/hpet.c
@@ -425,6 +425,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
int shift = (addr & 4) * 8;
uint64_t cur_tick;
+ addr &= ~4;
trace_hpet_ram_read(addr);
/*address range of all TN regs*/
@@ -437,7 +438,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
return 0;
}
- switch (addr & 0x18) {
+ switch (addr & 0x1f) {
case HPET_TN_CFG: // including interrupt capabilities
return timer->config >> shift;
case HPET_TN_CMP: // comparator register
@@ -449,7 +450,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
break;
}
} else {
- switch (addr & ~4) {
+ switch (addr) {
case HPET_ID: // including HPET_PERIOD
return s->capability >> shift;
case HPET_CFG:
and the same in the write function, but that's also different from the Rust
code.
Paolo
> Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
> ---
> * Note: This patch applies the changes from Rust HPET [*] to C HPET to
> ensure logic consistency.
> [*]: The original comment should use "(0x1f & ~4)":
> https://lore.kernel.org/qemu-devel/Z6edKxYFzA6suDlj@intel.com/
> ---
> hw/timer/hpet.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c
> index dcff18a9871d..0f786047e54f 100644
> --- a/hw/timer/hpet.c
> +++ b/hw/timer/hpet.c
> @@ -455,7 +455,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
> return 0;
> }
>
> - switch (addr & 0x18) {
> + switch (addr & (0x1f & ~4)) {
> case HPET_TN_CFG: // including interrupt capabilities
> return timer->config >> shift;
> case HPET_TN_CMP: // comparator register
> @@ -511,7 +511,8 @@ static void hpet_ram_write(void *opaque, hwaddr addr,
> trace_hpet_timer_id_out_of_range(timer_id);
> return;
> }
> - switch (addr & 0x18) {
> +
> + switch (addr & (0x1f & ~4)) {
> case HPET_TN_CFG:
> trace_hpet_ram_write_tn_cfg(addr & 4);
> old_val = timer->config;
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH] hw/timer/hpet: Detect invalid access to TN registers
2025-02-18 8:53 ` Paolo Bonzini
@ 2025-02-18 9:07 ` Philippe Mathieu-Daudé
2025-02-19 3:34 ` Zhao Liu
2025-02-19 9:25 ` Paolo Bonzini
2025-02-19 6:12 ` Zhao Liu
1 sibling, 2 replies; 7+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-02-18 9:07 UTC (permalink / raw)
To: Paolo Bonzini, Zhao Liu, Michael S . Tsirkin; +Cc: qemu-devel
On 18/2/25 09:53, Paolo Bonzini wrote:
> On 2/18/25 08:37, Zhao Liu wrote:
>> "addr & 0x18" ignores invalid address, so that the trace in default
>> branch (trace_hpet_ram_{read|write}_invalid()) doesn't work.
>>
>> Mask addr by "0x1f & ~4", in which 0x1f means to get the complete TN
>> registers access and ~4 means to keep any invalid address offset.
>
> I think this is less readable.
>
> The reason to use !4 in the Rust code is because the initial AND is done
> in a separate function, timer_and_addr().
Having a quick look at the model without looking at the specs:
include/hw/timer/hpet.h:20:#define HPET_LEN 0x400
hw/timer/hpet.c:439:static uint64_t hpet_ram_read(...,
hw/timer/hpet.c-441-{
hw/timer/hpet.c-448- /*address range of all TN regs*/
hw/timer/hpet.c-449- if (addr >= 0x100 && addr <= 0x3ff) {
hw/timer/hpet.c-450- uint8_t timer_id = (addr - 0x100) / 0x20;
...
hw/timer/hpet.c-469- } else {
hw/timer/hpet.c-470- switch (addr & ~4) {
...
hw/timer/hpet.c-488- }
hw/timer/hpet.c-489- }
hw/timer/hpet.c-490- return 0;
hw/timer/hpet.c-491-}
hw/timer/hpet.c:699: memory_region_init_io(&s->iomem, obj,
&hpet_ram_ops, s,
"hpet", HPET_LEN);
I suppose we want to register multiple timers of I/O size 0x20 at 0x100,
and the I/O size of 0x20 at 0x000 is a generic control region.
Maybe split hpet_ram_ops in 2 (hpet_cfg_ops and hpet_tmr_ops), mapping
the first one once at 0x000 and the other 24 times at 0x100-0x3ff?
No clue what is between 0x020-0x0ff.
My 2 cents looking at QDev modelling to avoid these address
manipulations.
Regards,
Phil.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] hw/timer/hpet: Detect invalid access to TN registers
2025-02-18 9:07 ` Philippe Mathieu-Daudé
@ 2025-02-19 3:34 ` Zhao Liu
2025-02-19 9:25 ` Paolo Bonzini
1 sibling, 0 replies; 7+ messages in thread
From: Zhao Liu @ 2025-02-19 3:34 UTC (permalink / raw)
To: Philippe Mathieu-Daudé
Cc: Paolo Bonzini, Michael S . Tsirkin, qemu-devel
On Tue, Feb 18, 2025 at 10:07:18AM +0100, Philippe Mathieu-Daudé wrote:
> Date: Tue, 18 Feb 2025 10:07:18 +0100
> From: Philippe Mathieu-Daudé <philmd@linaro.org>
> Subject: Re: [PATCH] hw/timer/hpet: Detect invalid access to TN registers
>
> On 18/2/25 09:53, Paolo Bonzini wrote:
> > On 2/18/25 08:37, Zhao Liu wrote:
> > > "addr & 0x18" ignores invalid address, so that the trace in default
> > > branch (trace_hpet_ram_{read|write}_invalid()) doesn't work.
> > >
> > > Mask addr by "0x1f & ~4", in which 0x1f means to get the complete TN
> > > registers access and ~4 means to keep any invalid address offset.
> >
> > I think this is less readable.
> >
> > The reason to use !4 in the Rust code is because the initial AND is done
> > in a separate function, timer_and_addr().
>
> Having a quick look at the model without looking at the specs:
>
> include/hw/timer/hpet.h:20:#define HPET_LEN 0x400
>
> hw/timer/hpet.c:439:static uint64_t hpet_ram_read(...,
> hw/timer/hpet.c-441-{
> hw/timer/hpet.c-448- /*address range of all TN regs*/
> hw/timer/hpet.c-449- if (addr >= 0x100 && addr <= 0x3ff) {
> hw/timer/hpet.c-450- uint8_t timer_id = (addr - 0x100) / 0x20;
> ...
> hw/timer/hpet.c-469- } else {
> hw/timer/hpet.c-470- switch (addr & ~4) {
> ...
> hw/timer/hpet.c-488- }
> hw/timer/hpet.c-489- }
> hw/timer/hpet.c-490- return 0;
> hw/timer/hpet.c-491-}
>
> hw/timer/hpet.c:699: memory_region_init_io(&s->iomem, obj,
> &hpet_ram_ops, s,
> "hpet", HPET_LEN);
>
> I suppose we want to register multiple timers of I/O size 0x20 at 0x100,
> and the I/O size of 0x20 at 0x000 is a generic control region.
Range of general control region is from 0x00 to 0xff.
> Maybe split hpet_ram_ops in 2 (hpet_cfg_ops and hpet_tmr_ops), mapping
> the first one once at 0x000 and the other 24 times at 0x100-0x3ff?
>
> My 2 cents looking at QDev modelling to avoid these address
> manipulations.
I think it's a good idea! Pls give me some time to try applying
memory_region_add_subregion() to this HPET case. :-)
Thanks,
Zhao
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] hw/timer/hpet: Detect invalid access to TN registers
2025-02-18 8:53 ` Paolo Bonzini
2025-02-18 9:07 ` Philippe Mathieu-Daudé
@ 2025-02-19 6:12 ` Zhao Liu
1 sibling, 0 replies; 7+ messages in thread
From: Zhao Liu @ 2025-02-19 6:12 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: Michael S . Tsirkin, qemu-devel
On Tue, Feb 18, 2025 at 09:53:00AM +0100, Paolo Bonzini wrote:
> Date: Tue, 18 Feb 2025 09:53:00 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: Re: [PATCH] hw/timer/hpet: Detect invalid access to TN registers
>
> On 2/18/25 08:37, Zhao Liu wrote:
> > "addr & 0x18" ignores invalid address, so that the trace in default
> > branch (trace_hpet_ram_{read|write}_invalid()) doesn't work.
> >
> > Mask addr by "0x1f & ~4", in which 0x1f means to get the complete TN
> > registers access and ~4 means to keep any invalid address offset.
>
> I think this is less readable.
>
> The reason to use !4 in the Rust code is because the initial AND is done
> in a separate function, timer_and_addr(). In C you don't have the same thing.
Yes.
> If anything you could do something like this:
> diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c
> index ccb97b68066..7c011204971 100644
> --- a/hw/timer/hpet.c
> +++ b/hw/timer/hpet.c
> @@ -425,6 +425,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
> int shift = (addr & 4) * 8;
> uint64_t cur_tick;
> + addr &= ~4;
> trace_hpet_ram_read(addr);
> /*address range of all TN regs*/
> @@ -437,7 +438,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
> return 0;
> }
> - switch (addr & 0x18) {
> + switch (addr & 0x1f) {
> case HPET_TN_CFG: // including interrupt capabilities
> return timer->config >> shift;
> case HPET_TN_CMP: // comparator register
> @@ -449,7 +450,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
> break;
> }
> } else {
> - switch (addr & ~4) {
> + switch (addr) {
> case HPET_ID: // including HPET_PERIOD
> return s->capability >> shift;
> case HPET_CFG:
>
> and the same in the write function,
Thanks! Your example is clearer!
> but that's also different from the Rust code.
At least, user could know the invalid access by trace in C side. Rust
hasn't supported trace, but it will be in the future.
There will be some differences in code between the two, and we can make
sure that the debug ability to be as consistent as possible.
Thanks,
Zhao
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] hw/timer/hpet: Detect invalid access to TN registers
2025-02-18 9:07 ` Philippe Mathieu-Daudé
2025-02-19 3:34 ` Zhao Liu
@ 2025-02-19 9:25 ` Paolo Bonzini
2025-02-19 14:38 ` Zhao Liu
1 sibling, 1 reply; 7+ messages in thread
From: Paolo Bonzini @ 2025-02-19 9:25 UTC (permalink / raw)
To: Philippe Mathieu-Daudé, Zhao Liu, Michael S . Tsirkin; +Cc: qemu-devel
On 2/18/25 10:07, Philippe Mathieu-Daudé wrote:
> On 18/2/25 09:53, Paolo Bonzini wrote:
>> On 2/18/25 08:37, Zhao Liu wrote:
>>> "addr & 0x18" ignores invalid address, so that the trace in default
>>> branch (trace_hpet_ram_{read|write}_invalid()) doesn't work.
>>>
>>> Mask addr by "0x1f & ~4", in which 0x1f means to get the complete TN
>>> registers access and ~4 means to keep any invalid address offset.
>>
>> I think this is less readable.
>>
>> The reason to use !4 in the Rust code is because the initial AND is done
>> in a separate function, timer_and_addr().
>
> Having a quick look at the model without looking at the specs:
>
> include/hw/timer/hpet.h:20:#define HPET_LEN 0x400
>
> hw/timer/hpet.c:439:static uint64_t hpet_ram_read(...,
> hw/timer/hpet.c-441-{
> hw/timer/hpet.c-448- /*address range of all TN regs*/
> hw/timer/hpet.c-449- if (addr >= 0x100 && addr <= 0x3ff) {
> hw/timer/hpet.c-450- uint8_t timer_id = (addr - 0x100) / 0x20;
> ...
> hw/timer/hpet.c-469- } else {
> hw/timer/hpet.c-470- switch (addr & ~4) {
> ...
> hw/timer/hpet.c-488- }
> hw/timer/hpet.c-489- }
> hw/timer/hpet.c-490- return 0;
> hw/timer/hpet.c-491-}
>
> hw/timer/hpet.c:699: memory_region_init_io(&s->iomem, obj,
> &hpet_ram_ops, s,
> "hpet", HPET_LEN);
>
> I suppose we want to register multiple timers of I/O size 0x20 at 0x100,
> and the I/O size of 0x20 at 0x000 is a generic control region.
>
> Maybe split hpet_ram_ops in 2 (hpet_cfg_ops and hpet_tmr_ops), mapping
> the first one once at 0x000 and the other 24 times at 0x100-0x3ff?
You would have to come up with a way to get the index though. It seems
to be adding churn for no particular reason.
I'd rather look into how to make decoding code *easy* without making
everything MemoryRegions. As I explained yesterday, while I'm not yet
sure that Rust is going to stay in QEMU, I'd like to have as many
examples as possible to help tilting the balance one way or the other.
And indeed in the Rust version of HPET, timer_and_addr() could be
extended to something like this:
// Start with the same "enum for registers" pattern that PL011 uses:
#[derive(qemu_api_macros::TryInto)]
#[repr(u64)]
enum TimerRegister {
CFG = 0,
CMP = 8,
ROUTE = 16,
}
#[derive(qemu_api_macros::TryInto)]
#[repr(u64)]
enum GlobalRegister {
CAP = 0,
CFG = 0x10,
INT_STATUS = 0x20,
COUNTER = 0xF0,
}
// Go one step further and define types for all possible outcomes:
#[derive(Copy)]
enum HPETRegister {
Timer(&BqlRefCell<HPETTimer>, TimerRegister),
Global(GlobalRegister),
Unknown(hwaddr),
}
struct HPETAddrDecode {
u32 shift,
u32 len,
HPETRegister reg,
}
fn decode(&self, addr: hwaddr, size: u32) -> HPETAddrDecode {
let shift = ((addr & 4) * 8) as u32;
let len = std::cmp::min(size * 8, 64 - shift);
addr &= !4;
let reg = if (0x100..=0x3ff).contains(&addr) {
let timer_id: usize = ((addr - 0x100) / 0x20) as usize;
TimerRegister::try_from(addr)
.map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg))
} else {
GlobalRegister::try_from(addr)
.map(HPETRegister::Global)
}
// reg is now a Result<HPETRegister, hwaddr>
// convert the Err case into HPETRegister as well
let reg = reg.unwrap_or_else(HPETRegister::Unknown);
HPETAddrDecode { shift, len, reg }
}
(untested). The read and write functions then can do something like
let val = match decoded.reg {
Timer(timer, reg) => timer.borrow_mut().read(decoded),
Global(GlobalRegister::CAP) => self.capability.get(),
Global(GlobalRegister::CFG) => self.config.get(),
...
}
val >> decoded.shift
and for write:
match decoded.reg {
Timer(timer, reg) => timer.borrow_mut().write(decoded, value),
Global(GlobalRegister::CAP) => {}, // read-only
Global(GlobalRegister::CFG) => self.set_cfg_reg(decoded, value),
...
}
The above could be a scheme that new devices could copy. Overall I
think it would be shorter code than what is there in rust/hw/timer/hpet
(which is IMO already better than C, mind!).
The honest question for people with less experience is whether this is
readable at all; whether seeing it helps you learn the language or
discourages you.
Paolo
> No clue what is between 0x020-0x0ff.
>
> My 2 cents looking at QDev modelling to avoid these address
> manipulations.
>
> Regards,
>
> Phil.
>
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] hw/timer/hpet: Detect invalid access to TN registers
2025-02-19 9:25 ` Paolo Bonzini
@ 2025-02-19 14:38 ` Zhao Liu
0 siblings, 0 replies; 7+ messages in thread
From: Zhao Liu @ 2025-02-19 14:38 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Philippe Mathieu-Daudé, Michael S . Tsirkin, qemu-devel
On Wed, Feb 19, 2025 at 10:25:40AM +0100, Paolo Bonzini wrote:
> Date: Wed, 19 Feb 2025 10:25:40 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: Re: [PATCH] hw/timer/hpet: Detect invalid access to TN registers
>
> On 2/18/25 10:07, Philippe Mathieu-Daudé wrote:
> > On 18/2/25 09:53, Paolo Bonzini wrote:
> > > On 2/18/25 08:37, Zhao Liu wrote:
> > > > "addr & 0x18" ignores invalid address, so that the trace in default
> > > > branch (trace_hpet_ram_{read|write}_invalid()) doesn't work.
> > > >
> > > > Mask addr by "0x1f & ~4", in which 0x1f means to get the complete TN
> > > > registers access and ~4 means to keep any invalid address offset.
> > >
> > > I think this is less readable.
> > >
> > > The reason to use !4 in the Rust code is because the initial AND is done
> > > in a separate function, timer_and_addr().
> >
> > Having a quick look at the model without looking at the specs:
> >
> > include/hw/timer/hpet.h:20:#define HPET_LEN 0x400
> >
> > hw/timer/hpet.c:439:static uint64_t hpet_ram_read(...,
> > hw/timer/hpet.c-441-{
> > hw/timer/hpet.c-448- /*address range of all TN regs*/
> > hw/timer/hpet.c-449- if (addr >= 0x100 && addr <= 0x3ff) {
> > hw/timer/hpet.c-450- uint8_t timer_id = (addr - 0x100) / 0x20;
> > ...
> > hw/timer/hpet.c-469- } else {
> > hw/timer/hpet.c-470- switch (addr & ~4) {
> > ...
> > hw/timer/hpet.c-488- }
> > hw/timer/hpet.c-489- }
> > hw/timer/hpet.c-490- return 0;
> > hw/timer/hpet.c-491-}
> >
> > hw/timer/hpet.c:699: memory_region_init_io(&s->iomem, obj,
> > &hpet_ram_ops, s,
> > "hpet", HPET_LEN);
> >
> > I suppose we want to register multiple timers of I/O size 0x20 at 0x100,
> > and the I/O size of 0x20 at 0x000 is a generic control region.
> >
> > Maybe split hpet_ram_ops in 2 (hpet_cfg_ops and hpet_tmr_ops), mapping
> > the first one once at 0x000 and the other 24 times at 0x100-0x3ff?
>
> You would have to come up with a way to get the index though. It seems to
> be adding churn for no particular reason.
>
> I'd rather look into how to make decoding code *easy* without making
> everything MemoryRegions.
I aslo met an register space implementation [1] which stores registers
with range (I guess for QEMU, it could map each register to a memory
region) and register specific callbacks.
I didn't choose this way since it's too complex to quickly develop...
[1]: https://github.com/google/crosvm/blob/main/devices/src/register_space/mod.rs
> As I explained yesterday, while I'm not yet sure
> that Rust is going to stay in QEMU, I'd like to have as many examples as
> possible to help tilting the balance one way or the other. And indeed in the
> Rust version of HPET, timer_and_addr() could be extended to something like
> this:
>
> // Start with the same "enum for registers" pattern that PL011 uses:
> #[derive(qemu_api_macros::TryInto)]
> #[repr(u64)]
> enum TimerRegister {
> CFG = 0,
> CMP = 8,
> ROUTE = 16,
> }
>
> #[derive(qemu_api_macros::TryInto)]
> #[repr(u64)]
> enum GlobalRegister {
> CAP = 0,
> CFG = 0x10,
> INT_STATUS = 0x20,
> COUNTER = 0xF0,
> }
>
> // Go one step further and define types for all possible outcomes:
> #[derive(Copy)]
> enum HPETRegister {
> Timer(&BqlRefCell<HPETTimer>, TimerRegister),
> Global(GlobalRegister),
> Unknown(hwaddr),
> }
>
> struct HPETAddrDecode {
> u32 shift,
> u32 len,
> HPETRegister reg,
> }
>
> fn decode(&self, addr: hwaddr, size: u32) -> HPETAddrDecode {
> let shift = ((addr & 4) * 8) as u32;
> let len = std::cmp::min(size * 8, 64 - shift);
>
> addr &= !4;
> let reg = if (0x100..=0x3ff).contains(&addr) {
> let timer_id: usize = ((addr - 0x100) / 0x20) as usize;
> TimerRegister::try_from(addr)
> .map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg))
> } else {
> GlobalRegister::try_from(addr)
> .map(HPETRegister::Global)
> }
>
> // reg is now a Result<HPETRegister, hwaddr>
> // convert the Err case into HPETRegister as well
> let reg = reg.unwrap_or_else(HPETRegister::Unknown);
> HPETAddrDecode { shift, len, reg }
> }
>
> (untested). The read and write functions then can do something like
>
> let val = match decoded.reg {
> Timer(timer, reg) => timer.borrow_mut().read(decoded),
> Global(GlobalRegister::CAP) => self.capability.get(),
> Global(GlobalRegister::CFG) => self.config.get(),
> ...
> }
> val >> decoded.shift
>
> and for write:
>
> match decoded.reg {
> Timer(timer, reg) => timer.borrow_mut().write(decoded, value),
> Global(GlobalRegister::CAP) => {}, // read-only
> Global(GlobalRegister::CFG) => self.set_cfg_reg(decoded, value),
> ...
> }
>
>
> The above could be a scheme that new devices could copy. Overall I think it
> would be shorter code than what is there in rust/hw/timer/hpet (which is IMO
> already better than C, mind!).
Yes, I also like your way. It's a bit more abstract than current hpet,
but is also more simple than pl011 without bilge.
I also found that there's another example to abstract register fields
without bilge [2]. However, examples such as hpet without any abstraction
also exit [3].
From the experiences of other Rust VMMs, the handling of registers varies
greatly :-(, and there doesn't seem to be a unified approach. Developers
create abstractions according to their preferences, which I think is quite
different from C devices (in C, most people will hardcode everything
like hpet).
[2]: https://github.com/cloud-hypervisor/cloud-hypervisor/blob/main/devices/src/tpm.rs
[3]: https://github.com/google/crosvm/blob/main/devices/src/cmos.rs
> The honest question for people with less experience is whether this is
> readable at all; whether seeing it helps you learn the language or
> discourages you.
If you think this conversion is not urgent, perhaps this case could
become as a "good first issue" to encourage people practice and get
familiar with RustInQemu.
Regards,
Zhao
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-02-19 14:19 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-18 7:37 [PATCH] hw/timer/hpet: Detect invalid access to TN registers Zhao Liu
2025-02-18 8:53 ` Paolo Bonzini
2025-02-18 9:07 ` Philippe Mathieu-Daudé
2025-02-19 3:34 ` Zhao Liu
2025-02-19 9:25 ` Paolo Bonzini
2025-02-19 14:38 ` Zhao Liu
2025-02-19 6:12 ` Zhao Liu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).