* [PATCH 1/7] riscv: implement Ssqosid extension and srmcfg CSR
2025-11-20 0:42 [PATCH 0/7] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
@ 2025-11-20 0:42 ` Drew Fustini
2025-11-20 11:30 ` Daniel Henrique Barboza
2025-11-20 16:07 ` Radim Krčmář
2025-11-20 0:42 ` [PATCH 2/7] hw/riscv: define capabilities of CBQRI controllers Drew Fustini
` (5 subsequent siblings)
6 siblings, 2 replies; 21+ messages in thread
From: Drew Fustini @ 2025-11-20 0:42 UTC (permalink / raw)
To: qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, qemu-riscv, Paolo Bonzini,
Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, Radim Krčmář, yunhui cui,
Chen Pei, guo.wenjia23, liu.qingtao2, Drew Fustini
From: Kornel Dulęba <mindal@semihalf.com>
Implement the srmcfg CSR defined by the Ssqosid ISA extension
(Supervisor-mode Quality of Service ID). The CSR contains two fields:
- Resource Control ID (RCID) used determine resource allocation
- Monitoring Counter ID (MCID) used to track resource usage
The CSR is defined for S-mode but accessing it when V=1 shall cause a
virtual instruction exception. Implement this behavior by calling the
hmode predicate.
Link: https://github.com/riscv-non-isa/riscv-cbqri/releases/download/v1.0/riscv-cbqri.pdf
Signed-off-by: Kornel Dulęba <mindal@semihalf.com>
[fustini: rebase on v10.1.50, fix check_srmcfg]
Signed-off-by: Drew Fustini <fustini@kernel.org>
---
disas/riscv.c | 1 +
target/riscv/cpu.c | 2 ++
target/riscv/cpu.h | 3 +++
target/riscv/cpu_bits.h | 8 ++++++++
target/riscv/cpu_cfg_fields.h.inc | 1 +
target/riscv/csr.c | 34 ++++++++++++++++++++++++++++++++++
6 files changed, 49 insertions(+)
diff --git a/disas/riscv.c b/disas/riscv.c
index 85cd2a9c2aef..86fc710528c1 100644
--- a/disas/riscv.c
+++ b/disas/riscv.c
@@ -2289,6 +2289,7 @@ static const char *csr_name(int csrno)
case 0x0143: return "stval";
case 0x0144: return "sip";
case 0x0180: return "satp";
+ case 0x0181: return "srmcfg";
case 0x0200: return "hstatus";
case 0x0202: return "hedeleg";
case 0x0203: return "hideleg";
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 73d4280d7c84..2e2e642de26b 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -216,6 +216,7 @@ const RISCVIsaExtData isa_edata_arr[] = {
ISA_EXT_DATA_ENTRY(ssdbltrp, PRIV_VERSION_1_13_0, ext_ssdbltrp),
ISA_EXT_DATA_ENTRY(ssnpm, PRIV_VERSION_1_13_0, ext_ssnpm),
ISA_EXT_DATA_ENTRY(sspm, PRIV_VERSION_1_13_0, ext_sspm),
+ ISA_EXT_DATA_ENTRY(ssqosid, PRIV_VERSION_1_12_0, ext_ssqosid),
ISA_EXT_DATA_ENTRY(ssstateen, PRIV_VERSION_1_12_0, ext_ssstateen),
ISA_EXT_DATA_ENTRY(ssstrict, PRIV_VERSION_1_12_0, has_priv_1_12),
ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc),
@@ -1268,6 +1269,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = {
MULTI_EXT_CFG_BOOL("zvfbfwma", ext_zvfbfwma, false),
MULTI_EXT_CFG_BOOL("zvfh", ext_zvfh, false),
MULTI_EXT_CFG_BOOL("zvfhmin", ext_zvfhmin, false),
+ MULTI_EXT_CFG_BOOL("ssqosid", ext_ssqosid, true),
MULTI_EXT_CFG_BOOL("sstc", ext_sstc, true),
MULTI_EXT_CFG_BOOL("ssnpm", ext_ssnpm, false),
MULTI_EXT_CFG_BOOL("sspm", ext_sspm, false),
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 36e7f100374d..21688d8ca002 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -336,6 +336,9 @@ struct CPUArchState {
uint64_t ctr_dst[16 << SCTRDEPTH_MAX];
uint64_t ctr_data[16 << SCTRDEPTH_MAX];
+ /* Ssqosid extension */
+ target_ulong srmcfg;
+
/* Machine and Supervisor interrupt priorities */
uint8_t miprio[64];
uint8_t siprio[64];
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index b62dd82fe7c0..ebb400bf6f2c 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -228,6 +228,9 @@
#define CSR_SPTBR 0x180
#define CSR_SATP 0x180
+/* Ssqosid extension */
+#define CSR_SRMCFG 0x181
+
/* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */
#define CSR_SISELECT 0x150
#define CSR_SIREG 0x151
@@ -1164,4 +1167,9 @@ typedef enum CTRType {
#define MCONTEXT64 0x0000000000001FFFULL
#define MCONTEXT32_HCONTEXT 0x0000007F
#define MCONTEXT64_HCONTEXT 0x0000000000003FFFULL
+
+/* SRMCFG CSR field masks (Ssqosid extensions) */
+#define SRMCFG_RCID 0x00000FFF
+#define SRMCFG_MCID 0x0FFF0000
+
#endif
diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc
index a154ecdc792b..6518f6f9c571 100644
--- a/target/riscv/cpu_cfg_fields.h.inc
+++ b/target/riscv/cpu_cfg_fields.h.inc
@@ -105,6 +105,7 @@ BOOL_FIELD(ext_ssaia)
BOOL_FIELD(ext_smctr)
BOOL_FIELD(ext_ssctr)
BOOL_FIELD(ext_sscofpmf)
+BOOL_FIELD(ext_ssqosid)
BOOL_FIELD(ext_smepmp)
BOOL_FIELD(ext_smrnmi)
BOOL_FIELD(ext_ssnpm)
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 5c91658c3dc4..06a6212c672d 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -1759,6 +1759,37 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
+static RISCVException check_srmcfg(CPURISCVState *env, int csrno)
+{
+ RISCVCPU *cpu = env_archcpu(env);
+
+ if (!cpu->cfg.ext_ssqosid) {
+ return RISCV_EXCP_ILLEGAL_INST;
+ }
+
+ /*
+ * Even though this is an S-mode CSR the spec says that we need to throw
+ * and virt instruction fault if a guest tries to access it.
+ */
+ return env->virt_enabled ?
+ RISCV_EXCP_VIRT_INSTRUCTION_FAULT : smode(env, csrno);
+}
+
+static RISCVException read_srmcfg(CPURISCVState *env, int csrno,
+ target_ulong *val)
+{
+ *val = env->srmcfg;
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException write_srmcfg(CPURISCVState *env, int csrno,
+ target_ulong val, uintptr_t ra)
+{
+ env->srmcfg = val & (SRMCFG_RCID | SRMCFG_MCID);
+ return RISCV_EXCP_NONE;
+}
+
+
#define VSTOPI_NUM_SRCS 5
/*
@@ -6035,6 +6066,9 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
/* Supervisor Protection and Translation */
[CSR_SATP] = { "satp", satp, read_satp, write_satp },
+ /* Supervisor-Level Quality-of-Service Identifiers (Ssqosid) */
+ [CSR_SRMCFG] = { "srmcfg", check_srmcfg, read_srmcfg, write_srmcfg },
+
/* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */
[CSR_SISELECT] = { "siselect", csrind_or_aia_smode, NULL, NULL,
rmw_xiselect },
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 1/7] riscv: implement Ssqosid extension and srmcfg CSR
2025-11-20 0:42 ` [PATCH 1/7] riscv: implement Ssqosid extension and srmcfg CSR Drew Fustini
@ 2025-11-20 11:30 ` Daniel Henrique Barboza
2025-11-20 21:34 ` Drew Fustini
2025-11-20 16:07 ` Radim Krčmář
1 sibling, 1 reply; 21+ messages in thread
From: Daniel Henrique Barboza @ 2025-11-20 11:30 UTC (permalink / raw)
To: Drew Fustini, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei,
qemu-riscv, Paolo Bonzini, Nicolas Pitre, Kornel Dulęba,
Atish Kumar Patra, Atish Patra, Vasudevan Srinivasan,
Radim Krčmář, yunhui cui, Chen Pei, guo.wenjia23,
liu.qingtao2
On 11/19/25 9:42 PM, Drew Fustini wrote:
> From: Kornel Dulęba <mindal@semihalf.com>
>
> Implement the srmcfg CSR defined by the Ssqosid ISA extension
> (Supervisor-mode Quality of Service ID). The CSR contains two fields:
>
> - Resource Control ID (RCID) used determine resource allocation
> - Monitoring Counter ID (MCID) used to track resource usage
>
> The CSR is defined for S-mode but accessing it when V=1 shall cause a
> virtual instruction exception. Implement this behavior by calling the
> hmode predicate.
>
> Link: https://github.com/riscv-non-isa/riscv-cbqri/releases/download/v1.0/riscv-cbqri.pdf
> Signed-off-by: Kornel Dulęba <mindal@semihalf.com>
> [fustini: rebase on v10.1.50, fix check_srmcfg]
> Signed-off-by: Drew Fustini <fustini@kernel.org>
> ---
> disas/riscv.c | 1 +
> target/riscv/cpu.c | 2 ++
> target/riscv/cpu.h | 3 +++
> target/riscv/cpu_bits.h | 8 ++++++++
> target/riscv/cpu_cfg_fields.h.inc | 1 +
> target/riscv/csr.c | 34 ++++++++++++++++++++++++++++++++++
> 6 files changed, 49 insertions(+)
>
> diff --git a/disas/riscv.c b/disas/riscv.c
> index 85cd2a9c2aef..86fc710528c1 100644
> --- a/disas/riscv.c
> +++ b/disas/riscv.c
> @@ -2289,6 +2289,7 @@ static const char *csr_name(int csrno)
> case 0x0143: return "stval";
> case 0x0144: return "sip";
> case 0x0180: return "satp";
> + case 0x0181: return "srmcfg";
> case 0x0200: return "hstatus";
> case 0x0202: return "hedeleg";
> case 0x0203: return "hideleg";
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index 73d4280d7c84..2e2e642de26b 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -216,6 +216,7 @@ const RISCVIsaExtData isa_edata_arr[] = {
> ISA_EXT_DATA_ENTRY(ssdbltrp, PRIV_VERSION_1_13_0, ext_ssdbltrp),
> ISA_EXT_DATA_ENTRY(ssnpm, PRIV_VERSION_1_13_0, ext_ssnpm),
> ISA_EXT_DATA_ENTRY(sspm, PRIV_VERSION_1_13_0, ext_sspm),
> + ISA_EXT_DATA_ENTRY(ssqosid, PRIV_VERSION_1_12_0, ext_ssqosid),
> ISA_EXT_DATA_ENTRY(ssstateen, PRIV_VERSION_1_12_0, ext_ssstateen),
> ISA_EXT_DATA_ENTRY(ssstrict, PRIV_VERSION_1_12_0, has_priv_1_12),
> ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc),
> @@ -1268,6 +1269,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = {
> MULTI_EXT_CFG_BOOL("zvfbfwma", ext_zvfbfwma, false),
> MULTI_EXT_CFG_BOOL("zvfh", ext_zvfh, false),
> MULTI_EXT_CFG_BOOL("zvfhmin", ext_zvfhmin, false),
> + MULTI_EXT_CFG_BOOL("ssqosid", ext_ssqosid, true),
> MULTI_EXT_CFG_BOOL("sstc", ext_sstc, true),
> MULTI_EXT_CFG_BOOL("ssnpm", ext_ssnpm, false),
> MULTI_EXT_CFG_BOOL("sspm", ext_sspm, false),
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 36e7f100374d..21688d8ca002 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -336,6 +336,9 @@ struct CPUArchState {
> uint64_t ctr_dst[16 << SCTRDEPTH_MAX];
> uint64_t ctr_data[16 << SCTRDEPTH_MAX];
>
> + /* Ssqosid extension */
> + target_ulong srmcfg;
> +
> /* Machine and Supervisor interrupt priorities */
> uint8_t miprio[64];
> uint8_t siprio[64];
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index b62dd82fe7c0..ebb400bf6f2c 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -228,6 +228,9 @@
> #define CSR_SPTBR 0x180
> #define CSR_SATP 0x180
>
> +/* Ssqosid extension */
> +#define CSR_SRMCFG 0x181
> +
> /* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */
> #define CSR_SISELECT 0x150
> #define CSR_SIREG 0x151
> @@ -1164,4 +1167,9 @@ typedef enum CTRType {
> #define MCONTEXT64 0x0000000000001FFFULL
> #define MCONTEXT32_HCONTEXT 0x0000007F
> #define MCONTEXT64_HCONTEXT 0x0000000000003FFFULL
> +
> +/* SRMCFG CSR field masks (Ssqosid extensions) */
> +#define SRMCFG_RCID 0x00000FFF
> +#define SRMCFG_MCID 0x0FFF0000
> +
> #endif
> diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc
> index a154ecdc792b..6518f6f9c571 100644
> --- a/target/riscv/cpu_cfg_fields.h.inc
> +++ b/target/riscv/cpu_cfg_fields.h.inc
> @@ -105,6 +105,7 @@ BOOL_FIELD(ext_ssaia)
> BOOL_FIELD(ext_smctr)
> BOOL_FIELD(ext_ssctr)
> BOOL_FIELD(ext_sscofpmf)
> +BOOL_FIELD(ext_ssqosid)
> BOOL_FIELD(ext_smepmp)
> BOOL_FIELD(ext_smrnmi)
> BOOL_FIELD(ext_ssnpm)
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index 5c91658c3dc4..06a6212c672d 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -1759,6 +1759,37 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno,
> return RISCV_EXCP_NONE;
> }
>
> +static RISCVException check_srmcfg(CPURISCVState *env, int csrno)
> +{
> + RISCVCPU *cpu = env_archcpu(env);
> +
> + if (!cpu->cfg.ext_ssqosid) {
> + return RISCV_EXCP_ILLEGAL_INST;
> + }
env_archcpu() is a bit heavyweight and we usually avoid it. Since you're using the 'cpu' ptr
solely to access the CFG pointer I suggest using riscv_cpu_cfg() instead:
if (!riscv_cpu_cfg(env)->ext_ssqosid) {...}
Thanks,
Daniel
> +
> + /*
> + * Even though this is an S-mode CSR the spec says that we need to throw
> + * and virt instruction fault if a guest tries to access it.
> + */
> + return env->virt_enabled ?
> + RISCV_EXCP_VIRT_INSTRUCTION_FAULT : smode(env, csrno);
> +}
> +
> +static RISCVException read_srmcfg(CPURISCVState *env, int csrno,
> + target_ulong *val)
> +{
> + *val = env->srmcfg;
> + return RISCV_EXCP_NONE;
> +}
> +
> +static RISCVException write_srmcfg(CPURISCVState *env, int csrno,
> + target_ulong val, uintptr_t ra)
> +{
> + env->srmcfg = val & (SRMCFG_RCID | SRMCFG_MCID);
> + return RISCV_EXCP_NONE;
> +}
> +
> +
> #define VSTOPI_NUM_SRCS 5
>
> /*
> @@ -6035,6 +6066,9 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
> /* Supervisor Protection and Translation */
> [CSR_SATP] = { "satp", satp, read_satp, write_satp },
>
> + /* Supervisor-Level Quality-of-Service Identifiers (Ssqosid) */
> + [CSR_SRMCFG] = { "srmcfg", check_srmcfg, read_srmcfg, write_srmcfg },
> +
> /* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */
> [CSR_SISELECT] = { "siselect", csrind_or_aia_smode, NULL, NULL,
> rmw_xiselect },
>
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 1/7] riscv: implement Ssqosid extension and srmcfg CSR
2025-11-20 11:30 ` Daniel Henrique Barboza
@ 2025-11-20 21:34 ` Drew Fustini
0 siblings, 0 replies; 21+ messages in thread
From: Drew Fustini @ 2025-11-20 21:34 UTC (permalink / raw)
To: Daniel Henrique Barboza
Cc: qemu-devel, Palmer Dabbelt, Alistair Francis, Weiwei Li,
Liu Zhiwei, qemu-riscv, Paolo Bonzini, Nicolas Pitre,
Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, Radim Krčmář, yunhui cui,
Chen Pei, guo.wenjia23, liu.qingtao2
On Thu, Nov 20, 2025 at 08:30:50AM -0300, Daniel Henrique Barboza wrote:
[snip]
> > diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> > index 5c91658c3dc4..06a6212c672d 100644
> > --- a/target/riscv/csr.c
> > +++ b/target/riscv/csr.c
> > @@ -1759,6 +1759,37 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno,
> > return RISCV_EXCP_NONE;
> > }
> > +static RISCVException check_srmcfg(CPURISCVState *env, int csrno)
> > +{
> > + RISCVCPU *cpu = env_archcpu(env);
> > +
> > + if (!cpu->cfg.ext_ssqosid) {
> > + return RISCV_EXCP_ILLEGAL_INST;
> > + }
>
> env_archcpu() is a bit heavyweight and we usually avoid it. Since you're using the 'cpu' ptr
> solely to access the CFG pointer I suggest using riscv_cpu_cfg() instead:
>
> if (!riscv_cpu_cfg(env)->ext_ssqosid) {...}
Thanks for letting me know. I will change to riscv_cpu_cfg() in the next
revision.
Drew
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/7] riscv: implement Ssqosid extension and srmcfg CSR
2025-11-20 0:42 ` [PATCH 1/7] riscv: implement Ssqosid extension and srmcfg CSR Drew Fustini
2025-11-20 11:30 ` Daniel Henrique Barboza
@ 2025-11-20 16:07 ` Radim Krčmář
2025-11-20 20:46 ` Drew Fustini
1 sibling, 1 reply; 21+ messages in thread
From: Radim Krčmář @ 2025-11-20 16:07 UTC (permalink / raw)
To: Drew Fustini, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, qemu-riscv, Paolo Bonzini,
Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, yunhui cui, Chen Pei, guo.wenjia23,
liu.qingtao2, qemu-riscv-bounces+qemu-riscv=archiver.kernel.org
2025-11-19T16:42:17-08:00, Drew Fustini <fustini@kernel.org>:
> From: Kornel Dulęba <mindal@semihalf.com>
>
> Implement the srmcfg CSR defined by the Ssqosid ISA extension
> (Supervisor-mode Quality of Service ID). The CSR contains two fields:
>
> - Resource Control ID (RCID) used determine resource allocation
> - Monitoring Counter ID (MCID) used to track resource usage
>
> The CSR is defined for S-mode but accessing it when V=1 shall cause a
> virtual instruction exception. Implement this behavior by calling the
> hmode predicate.
>
> Link: https://github.com/riscv-non-isa/riscv-cbqri/releases/download/v1.0/riscv-cbqri.pdf
> Signed-off-by: Kornel Dulęba <mindal@semihalf.com>
> [fustini: rebase on v10.1.50, fix check_srmcfg]
> Signed-off-by: Drew Fustini <fustini@kernel.org>
> ---
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> @@ -216,6 +216,7 @@ const RISCVIsaExtData isa_edata_arr[] = {
> ISA_EXT_DATA_ENTRY(ssdbltrp, PRIV_VERSION_1_13_0, ext_ssdbltrp),
> ISA_EXT_DATA_ENTRY(ssnpm, PRIV_VERSION_1_13_0, ext_ssnpm),
> ISA_EXT_DATA_ENTRY(sspm, PRIV_VERSION_1_13_0, ext_sspm),
> + ISA_EXT_DATA_ENTRY(ssqosid, PRIV_VERSION_1_12_0, ext_ssqosid),
Just out of curiosity, where does PRIV_VERSION_1_12_0 come from?
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> @@ -1759,6 +1759,37 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno,
> +static RISCVException check_srmcfg(CPURISCVState *env, int csrno)
> +{
> + RISCVCPU *cpu = env_archcpu(env);
> +
> + if (!cpu->cfg.ext_ssqosid) {
> + return RISCV_EXCP_ILLEGAL_INST;
> + }
> +
> + /*
> + * Even though this is an S-mode CSR the spec says that we need to throw
> + * and virt instruction fault if a guest tries to access it.
> + */
> + return env->virt_enabled ?
> + RISCV_EXCP_VIRT_INSTRUCTION_FAULT : smode(env, csrno);
> +}
The check is missing interaction with mstateen0.SRMCFG.
Thanks.
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 1/7] riscv: implement Ssqosid extension and srmcfg CSR
2025-11-20 16:07 ` Radim Krčmář
@ 2025-11-20 20:46 ` Drew Fustini
0 siblings, 0 replies; 21+ messages in thread
From: Drew Fustini @ 2025-11-20 20:46 UTC (permalink / raw)
To: Radim Krčmář
Cc: qemu-devel, Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, qemu-riscv, Paolo Bonzini,
Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, yunhui cui, Chen Pei, guo.wenjia23,
liu.qingtao2, qemu-riscv-bounces+qemu-riscv=archiver.kernel.org
On Thu, Nov 20, 2025 at 05:07:03PM +0100, Radim Krčmář wrote:
Thanks for the review!
> 2025-11-19T16:42:17-08:00, Drew Fustini <fustini@kernel.org>:
> > From: Kornel Dulęba <mindal@semihalf.com>
> >
> > Implement the srmcfg CSR defined by the Ssqosid ISA extension
> > (Supervisor-mode Quality of Service ID). The CSR contains two fields:
> >
> > - Resource Control ID (RCID) used determine resource allocation
> > - Monitoring Counter ID (MCID) used to track resource usage
> >
> > The CSR is defined for S-mode but accessing it when V=1 shall cause a
> > virtual instruction exception. Implement this behavior by calling the
> > hmode predicate.
> >
> > Link: https://github.com/riscv-non-isa/riscv-cbqri/releases/download/v1.0/riscv-cbqri.pdf
> > Signed-off-by: Kornel Dulęba <mindal@semihalf.com>
> > [fustini: rebase on v10.1.50, fix check_srmcfg]
> > Signed-off-by: Drew Fustini <fustini@kernel.org>
> > ---
> > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> > @@ -216,6 +216,7 @@ const RISCVIsaExtData isa_edata_arr[] = {
> > ISA_EXT_DATA_ENTRY(ssdbltrp, PRIV_VERSION_1_13_0, ext_ssdbltrp),
> > ISA_EXT_DATA_ENTRY(ssnpm, PRIV_VERSION_1_13_0, ext_ssnpm),
> > ISA_EXT_DATA_ENTRY(sspm, PRIV_VERSION_1_13_0, ext_sspm),
> > + ISA_EXT_DATA_ENTRY(ssqosid, PRIV_VERSION_1_12_0, ext_ssqosid),
>
> Just out of curiosity, where does PRIV_VERSION_1_12_0 come from?
Ssqosid was originally ratified as a separate document but it since was
add to the Privileged Architecture document [1]. However, the Supervisor
Level ISA was version 1.13 when Ssqosid was added, so I should change
the above to PRIV_VERSION_1_13_0.
>
> > diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> > @@ -1759,6 +1759,37 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno,
> > +static RISCVException check_srmcfg(CPURISCVState *env, int csrno)
> > +{
> > + RISCVCPU *cpu = env_archcpu(env);
> > +
> > + if (!cpu->cfg.ext_ssqosid) {
> > + return RISCV_EXCP_ILLEGAL_INST;
> > + }
> > +
> > + /*
> > + * Even though this is an S-mode CSR the spec says that we need to throw
> > + * and virt instruction fault if a guest tries to access it.
> > + */
> > + return env->virt_enabled ?
> > + RISCV_EXCP_VIRT_INSTRUCTION_FAULT : smode(env, csrno);
> > +}
>
> The check is missing interaction with mstateen0.SRMCFG.
Good point. It looks like I should check mstateen0.SRMCFG which it bit
55. I'll add something like the following to the next revision:
---------------------------------------------------------------------
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index ebb400bf6f2c..bd73f9232d70 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -359,6 +359,7 @@
#define SMSTATEEN0_FCSR (1ULL << 1)
#define SMSTATEEN0_JVT (1ULL << 2)
#define SMSTATEEN0_CTR (1ULL << 54)
+#define SMSTATEEN0_SRMCFG (1ULL << 55)
#define SMSTATEEN0_P1P13 (1ULL << 56)
#define SMSTATEEN0_HSCONTXT (1ULL << 57)
#define SMSTATEEN0_IMSIC (1ULL << 58)
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 06a6212c672d..8f609e826fcd 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -1767,6 +1767,11 @@ static RISCVException check_srmcfg(CPURISCVState *env, int csrno)
return RISCV_EXCP_ILLEGAL_INST;
}
+ RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_SRMCFG);
+ if (ret != RISCV_EXCP_NONE) {
+ return ret;
+ }
+
/*
* Even though this is an S-mode CSR the spec says that we need to throw
* and virt instruction fault if a guest tries to access it.
---------------------------------------------------------------------
Thanks
Drew
[1] https://docs.riscv.org/reference/isa/priv/supervisor.html#ssqosid
[2] https://docs.riscv.org/reference/isa/priv/smstateen.html#state-enable-0-registersmstateen0.SRMCFG
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 2/7] hw/riscv: define capabilities of CBQRI controllers
2025-11-20 0:42 [PATCH 0/7] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
2025-11-20 0:42 ` [PATCH 1/7] riscv: implement Ssqosid extension and srmcfg CSR Drew Fustini
@ 2025-11-20 0:42 ` Drew Fustini
2025-11-20 11:59 ` Daniel Henrique Barboza
2025-11-20 0:42 ` [PATCH 3/7] hw/riscv: implement CBQRI capacity controller Drew Fustini
` (4 subsequent siblings)
6 siblings, 1 reply; 21+ messages in thread
From: Drew Fustini @ 2025-11-20 0:42 UTC (permalink / raw)
To: qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, qemu-riscv, Paolo Bonzini,
Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, Radim Krčmář, yunhui cui,
Chen Pei, guo.wenjia23, liu.qingtao2, Drew Fustini
From: Nicolas Pitre <npitre@baylibre.com>
Define structs to represent the hardware capabilities of capacity and
bandwidth controllers according to the RISC-V Capacity and Bandwidth QoS
Register Interface (CBQRI).
Link: https://github.com/riscv-non-isa/riscv-cbqri/blob/main/riscv-cbqri.pdf
Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
[fustini: add fields introduced in the ratified spec: cunits, rpfx, p]
Signed-off-by: Drew Fustini <fustini@kernel.org>
---
MAINTAINERS | 7 ++++
include/hw/riscv/cbqri.h | 89 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 96 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 38325e0617c4..7afe80f1b17c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -357,6 +357,13 @@ F: target/riscv/XVentanaCondOps.decode
F: target/riscv/insn_trans/trans_xventanacondops.c.inc
F: disas/riscv-xventana*
+RISC-V QoS (Ssqosid ext and CBQRI spec)
+M: Nicolas Pitre <npitre@baylibre.com>
+M: Drew Fustini <fustini@kernel.org>
+L: qemu-riscv@nongnu.org
+S: Supported
+F: include/hw/riscv/cbqri.h
+
RENESAS RX CPUs
R: Yoshinori Sato <yoshinori.sato@nifty.com>
S: Orphan
diff --git a/include/hw/riscv/cbqri.h b/include/hw/riscv/cbqri.h
new file mode 100644
index 000000000000..1b4c69779c0e
--- /dev/null
+++ b/include/hw/riscv/cbqri.h
@@ -0,0 +1,89 @@
+/*
+ * RISC-V Capacity and Bandwidth QoS Register Interface
+ * URL: https://github.com/riscv-non-isa/riscv-cbqri/releases/tag/v1.0
+ *
+ * Copyright (c) 2023 BayLibre SAS
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_RISCV_CBQRI_H
+#define HW_RISCV_CBQRI_H
+
+#include "qemu/typedefs.h"
+
+#define RISCV_CBQRI_VERSION_MAJOR 0
+#define RISCV_CBQRI_VERSION_MINOR 1
+
+#define TYPE_RISCV_CBQRI_CC "riscv.cbqri.capacity"
+#define TYPE_RISCV_CBQRI_BC "riscv.cbqri.bandwidth"
+
+/* Capacity Controller hardware capabilities */
+typedef struct RiscvCbqriCapacityCaps {
+ uint16_t nb_mcids;
+ uint16_t nb_rcids;
+
+ uint16_t ncblks;
+ bool cunits:1;
+
+ bool rpfx:1;
+ uint8_t p;
+
+ bool supports_at_data:1;
+ bool supports_at_code:1;
+
+ bool supports_alloc_op_config_limit:1;
+ bool supports_alloc_op_read_limit:1;
+ bool supports_alloc_op_flush_rcid:1;
+
+ bool supports_mon_op_config_event:1;
+ bool supports_mon_op_read_counter:1;
+
+ bool supports_mon_evt_id_none:1;
+ bool supports_mon_evt_id_occupancy:1;
+} RiscvCbqriCapacityCaps;
+
+/* Bandwidth Controller hardware capabilities */
+typedef struct RiscvCbqriBandwidthCaps {
+ uint16_t nb_mcids;
+ uint16_t nb_rcids;
+
+ uint16_t nbwblks;
+ uint16_t mrbwb;
+
+ bool rpfx:1;
+ uint8_t p;
+
+ bool supports_at_data:1;
+ bool supports_at_code:1;
+
+ bool supports_alloc_op_config_limit:1;
+ bool supports_alloc_op_read_limit:1;
+
+ bool supports_mon_op_config_event:1;
+ bool supports_mon_op_read_counter:1;
+
+ bool supports_mon_evt_id_none:1;
+ bool supports_mon_evt_id_rdwr_count:1;
+ bool supports_mon_evt_id_rdonly_count:1;
+ bool supports_mon_evt_id_wronly_count:1;
+} RiscvCbqriBandwidthCaps;
+
+DeviceState *riscv_cbqri_cc_create(hwaddr addr,
+ const RiscvCbqriCapacityCaps *caps,
+ const char *target_name);
+DeviceState *riscv_cbqri_bc_create(hwaddr addr,
+ const RiscvCbqriBandwidthCaps *caps,
+ const char *target_name);
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 2/7] hw/riscv: define capabilities of CBQRI controllers
2025-11-20 0:42 ` [PATCH 2/7] hw/riscv: define capabilities of CBQRI controllers Drew Fustini
@ 2025-11-20 11:59 ` Daniel Henrique Barboza
0 siblings, 0 replies; 21+ messages in thread
From: Daniel Henrique Barboza @ 2025-11-20 11:59 UTC (permalink / raw)
To: Drew Fustini, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei,
qemu-riscv, Paolo Bonzini, Nicolas Pitre, Kornel Dulęba,
Atish Kumar Patra, Atish Patra, Vasudevan Srinivasan,
Radim Krčmář, yunhui cui, Chen Pei, guo.wenjia23,
liu.qingtao2
On 11/19/25 9:42 PM, Drew Fustini wrote:
> From: Nicolas Pitre <npitre@baylibre.com>
>
> Define structs to represent the hardware capabilities of capacity and
> bandwidth controllers according to the RISC-V Capacity and Bandwidth QoS
> Register Interface (CBQRI).
>
> Link: https://github.com/riscv-non-isa/riscv-cbqri/blob/main/riscv-cbqri.pdf
> Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
> [fustini: add fields introduced in the ratified spec: cunits, rpfx, p]
> Signed-off-by: Drew Fustini <fustini@kernel.org>
> ---
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
> MAINTAINERS | 7 ++++
> include/hw/riscv/cbqri.h | 89 ++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 96 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 38325e0617c4..7afe80f1b17c 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -357,6 +357,13 @@ F: target/riscv/XVentanaCondOps.decode
> F: target/riscv/insn_trans/trans_xventanacondops.c.inc
> F: disas/riscv-xventana*
>
> +RISC-V QoS (Ssqosid ext and CBQRI spec)
> +M: Nicolas Pitre <npitre@baylibre.com>
> +M: Drew Fustini <fustini@kernel.org>
> +L: qemu-riscv@nongnu.org
> +S: Supported
> +F: include/hw/riscv/cbqri.h
> +
> RENESAS RX CPUs
> R: Yoshinori Sato <yoshinori.sato@nifty.com>
> S: Orphan
> diff --git a/include/hw/riscv/cbqri.h b/include/hw/riscv/cbqri.h
> new file mode 100644
> index 000000000000..1b4c69779c0e
> --- /dev/null
> +++ b/include/hw/riscv/cbqri.h
> @@ -0,0 +1,89 @@
> +/*
> + * RISC-V Capacity and Bandwidth QoS Register Interface
> + * URL: https://github.com/riscv-non-isa/riscv-cbqri/releases/tag/v1.0
> + *
> + * Copyright (c) 2023 BayLibre SAS
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef HW_RISCV_CBQRI_H
> +#define HW_RISCV_CBQRI_H
> +
> +#include "qemu/typedefs.h"
> +
> +#define RISCV_CBQRI_VERSION_MAJOR 0
> +#define RISCV_CBQRI_VERSION_MINOR 1
> +
> +#define TYPE_RISCV_CBQRI_CC "riscv.cbqri.capacity"
> +#define TYPE_RISCV_CBQRI_BC "riscv.cbqri.bandwidth"
> +
> +/* Capacity Controller hardware capabilities */
> +typedef struct RiscvCbqriCapacityCaps {
> + uint16_t nb_mcids;
> + uint16_t nb_rcids;
> +
> + uint16_t ncblks;
> + bool cunits:1;
> +
> + bool rpfx:1;
> + uint8_t p;
> +
> + bool supports_at_data:1;
> + bool supports_at_code:1;
> +
> + bool supports_alloc_op_config_limit:1;
> + bool supports_alloc_op_read_limit:1;
> + bool supports_alloc_op_flush_rcid:1;
> +
> + bool supports_mon_op_config_event:1;
> + bool supports_mon_op_read_counter:1;
> +
> + bool supports_mon_evt_id_none:1;
> + bool supports_mon_evt_id_occupancy:1;
> +} RiscvCbqriCapacityCaps;
> +
> +/* Bandwidth Controller hardware capabilities */
> +typedef struct RiscvCbqriBandwidthCaps {
> + uint16_t nb_mcids;
> + uint16_t nb_rcids;
> +
> + uint16_t nbwblks;
> + uint16_t mrbwb;
> +
> + bool rpfx:1;
> + uint8_t p;
> +
> + bool supports_at_data:1;
> + bool supports_at_code:1;
> +
> + bool supports_alloc_op_config_limit:1;
> + bool supports_alloc_op_read_limit:1;
> +
> + bool supports_mon_op_config_event:1;
> + bool supports_mon_op_read_counter:1;
> +
> + bool supports_mon_evt_id_none:1;
> + bool supports_mon_evt_id_rdwr_count:1;
> + bool supports_mon_evt_id_rdonly_count:1;
> + bool supports_mon_evt_id_wronly_count:1;
> +} RiscvCbqriBandwidthCaps;
> +
> +DeviceState *riscv_cbqri_cc_create(hwaddr addr,
> + const RiscvCbqriCapacityCaps *caps,
> + const char *target_name);
> +DeviceState *riscv_cbqri_bc_create(hwaddr addr,
> + const RiscvCbqriBandwidthCaps *caps,
> + const char *target_name);
> +#endif
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 3/7] hw/riscv: implement CBQRI capacity controller
2025-11-20 0:42 [PATCH 0/7] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
2025-11-20 0:42 ` [PATCH 1/7] riscv: implement Ssqosid extension and srmcfg CSR Drew Fustini
2025-11-20 0:42 ` [PATCH 2/7] hw/riscv: define capabilities of CBQRI controllers Drew Fustini
@ 2025-11-20 0:42 ` Drew Fustini
2025-11-20 11:47 ` Daniel Henrique Barboza
2025-11-20 19:25 ` Radim Krčmář
2025-11-20 0:42 ` [PATCH 4/7] hw/riscv: implement CBQRI bandwidth controller Drew Fustini
` (3 subsequent siblings)
6 siblings, 2 replies; 21+ messages in thread
From: Drew Fustini @ 2025-11-20 0:42 UTC (permalink / raw)
To: qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, qemu-riscv, Paolo Bonzini,
Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, Radim Krčmář, yunhui cui,
Chen Pei, guo.wenjia23, liu.qingtao2, Drew Fustini
From: Nicolas Pitre <npitre@baylibre.com>
Implement a capacity controller according to the Capacity and Bandwidth
QoS Register Interface (CBQRI) which supports these capabilities:
- Number of access types: 2 (code and data)
- Usage monitoring operations: CONFIG_EVENT, READ_COUNTER
- Event IDs supported: None, Occupancy
- Capacity allocation ops: CONFIG_LIMIT, READ_LIMIT, FLUSH_RCID
Link: https://github.com/riscv-non-isa/riscv-cbqri/blob/main/riscv-cbqri.pdf
Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
[fustini: add fields introduced in the ratified spec: cunits, rpfx, p]
Signed-off-by: Drew Fustini <fustini@kernel.org>
---
MAINTAINERS | 1 +
hw/riscv/cbqri_capacity.c | 634 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 635 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 7afe80f1b17c..48cca4ac8702 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -362,6 +362,7 @@ M: Nicolas Pitre <npitre@baylibre.com>
M: Drew Fustini <fustini@kernel.org>
L: qemu-riscv@nongnu.org
S: Supported
+F: hw/riscv/cbqri_capacity.c
F: include/hw/riscv/cbqri.h
RENESAS RX CPUs
diff --git a/hw/riscv/cbqri_capacity.c b/hw/riscv/cbqri_capacity.c
new file mode 100644
index 000000000000..e70f831878ac
--- /dev/null
+++ b/hw/riscv/cbqri_capacity.c
@@ -0,0 +1,634 @@
+/*
+ * RISC-V Capacity and Bandwidth QoS Register Interface
+ * URL: https://github.com/riscv-non-isa/riscv-cbqri/releases/tag/v1.0
+ *
+ * Copyright (c) 2023 BayLibre SAS
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This file contains the Capacity-controller QoS Register Interface.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bitmap.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/cbqri.h"
+
+/* Encodings of `AT` field */
+enum {
+ CC_AT_DATA = 0,
+ CC_AT_CODE = 1,
+};
+
+/* Capabilities */
+REG64(CC_CAPABILITIES, 0);
+FIELD(CC_CAPABILITIES, VER, 0, 8);
+FIELD(CC_CAPABILITIES, VER_MINOR, 0, 4);
+FIELD(CC_CAPABILITIES, VER_MAJOR, 4, 4);
+FIELD(CC_CAPABILITIES, NCBLKS, 8, 16);
+FIELD(CC_CAPABILITIES, FRCID, 24, 1);
+FIELD(CC_CAPABILITIES, CUNITS, 25, 1);
+FIELD(CC_CAPABILITIES, RPFX, 26, 1);
+FIELD(CC_CAPABILITIES, P, 27, 4);
+
+/* Usage monitoring control */
+REG64(CC_MON_CTL, 8);
+FIELD(CC_MON_CTL, OP, 0, 5);
+FIELD(CC_MON_CTL, AT, 5, 3);
+FIELD(CC_MON_CTL, MCID, 8, 12);
+FIELD(CC_MON_CTL, EVT_ID, 20, 8);
+FIELD(CC_MON_CTL, ATV, 28, 1);
+FIELD(CC_MON_CTL, STATUS, 32, 7);
+FIELD(CC_MON_CTL, BUSY, 39, 1);
+
+/* Usage monitoring operations */
+enum {
+ CC_MON_OP_CONFIG_EVENT = 1,
+ CC_MON_OP_READ_COUNTER = 2,
+};
+
+/* Usage monitoring event ID */
+enum {
+ CC_EVT_ID_None = 0,
+ CC_EVT_ID_Occupancy = 1,
+};
+
+/* CC_MON_CTL.STATUS field encodings */
+enum {
+ CC_MON_CTL_STATUS_SUCCESS = 1,
+ CC_MON_CTL_STATUS_INVAL_OP = 2,
+ CC_MON_CTL_STATUS_INVAL_MCID = 3,
+ CC_MON_CTL_STATUS_INVAL_EVT_ID = 4,
+ CC_MON_CTL_STATUS_INVAL_AT = 5,
+};
+
+/* Monitoring counter value */
+REG64(CC_MON_CTR_VAL, 16);
+FIELD(CC_MON_CTR_VAL, CTR, 0, 63);
+FIELD(CC_MON_CTR_VAL, INVALID, 63, 1);
+
+/* Capacity allocation control */
+REG64(CC_ALLOC_CTL, 24);
+FIELD(CC_ALLOC_CTL, OP, 0, 5);
+FIELD(CC_ALLOC_CTL, AT, 5, 3);
+FIELD(CC_ALLOC_CTL, RCID, 8, 12);
+FIELD(CC_ALLOC_CTL, STATUS, 32, 7);
+FIELD(CC_ALLOC_CTL, BUSY, 39, 1);
+
+/* Capacity allocation operations */
+enum {
+ CC_ALLOC_OP_CONFIG_LIMIT = 1,
+ CC_ALLOC_OP_READ_LIMIT = 2,
+ CC_ALLOC_OP_FLUSH_RCID = 3,
+};
+
+/* CC_ALLOC_CTL.STATUS field encodings */
+enum {
+ CC_ALLOC_STATUS_SUCCESS = 1,
+ CC_ALLOC_STATUS_INVAL_OP = 2,
+ CC_ALLOC_STATUS_INVAL_RCID = 3,
+ CC_ALLOC_STATUS_INVAL_AT = 4,
+ CC_ALLOC_STATUS_INVAL_BLKMASK = 5,
+};
+
+REG64(CC_BLOCK_MASK, 32);
+
+
+typedef struct MonitorCounter {
+ uint64_t ctr_val;
+ int at;
+ int evt_id;
+ bool active;
+} MonitorCounter;
+
+typedef struct RiscvCbqriCapacityState {
+ SysBusDevice parent_obj;
+ MemoryRegion mmio;
+
+ /* cached value of some registers */
+ uint64_t cc_mon_ctl;
+ uint64_t cc_mon_ctr_val;
+ uint64_t cc_alloc_ctl;
+
+ /* monitoring counters */
+ MonitorCounter *mon_counters;
+
+ /* allocation blockmasks (1st one is the CC_BLOCK_MASK register) */
+ uint64_t *alloc_blockmasks;
+
+ /* properties */
+
+ uint64_t mmio_base;
+ char *target;
+ uint16_t nb_mcids;
+ uint16_t nb_rcids;
+
+ uint16_t ncblks;
+
+ bool cunits;
+ bool rpfx;
+ uint8_t p;
+
+ bool supports_at_data;
+ bool supports_at_code;
+
+ bool supports_alloc_op_config_limit;
+ bool supports_alloc_op_read_limit;
+ bool supports_alloc_op_flush_rcid;
+
+ bool supports_mon_op_config_event;
+ bool supports_mon_op_read_counter;
+
+ bool supports_mon_evt_id_none;
+ bool supports_mon_evt_id_occupancy;
+} RiscvCbqriCapacityState;
+
+#define RISCV_CBQRI_CC(obj) \
+ OBJECT_CHECK(RiscvCbqriCapacityState, (obj), TYPE_RISCV_CBQRI_CC)
+
+static uint64_t *get_blockmask_location(RiscvCbqriCapacityState *cc,
+ uint32_t rcid, uint32_t at)
+{
+ /*
+ * All blockmasks are contiguous to simplify allocation.
+ * The first one is used to hold the CC_BLOCK_MASK register content,
+ * followed by respective blockmasks for each AT per RCID.
+ * Each blockmask is made of one or more uint64_t "slots".
+ */
+ unsigned int nb_ats = 0;
+ nb_ats += !!cc->supports_at_data;
+ nb_ats += !!cc->supports_at_code;
+ nb_ats = MAX(nb_ats, 1);
+ assert(at < nb_ats);
+
+ unsigned int blockmask_slots = (cc->ncblks + 63) / 64;
+ unsigned int blockmask_offset = blockmask_slots * (1 + rcid * nb_ats + at);
+
+ return cc->alloc_blockmasks + blockmask_offset;
+}
+
+static uint32_t alloc_blockmask_config(RiscvCbqriCapacityState *cc,
+ uint32_t rcid, uint32_t at,
+ bool *busy)
+{
+ unsigned int blockmask_slots = (cc->ncblks + 63) / 64;
+
+ if ((cc->ncblks % 64) != 0) {
+ /* make sure provided mask isn't too large */
+ uint64_t tail = cc->alloc_blockmasks[blockmask_slots - 1];
+ if ((tail >> (cc->ncblks % 64)) != 0) {
+ return CC_ALLOC_STATUS_INVAL_BLKMASK;
+ }
+ }
+
+ /* for now we only preserve the current CC_BLOCK_MASK register content */
+ memcpy(get_blockmask_location(cc, rcid, at),
+ cc->alloc_blockmasks, blockmask_slots * 8);
+ return CC_ALLOC_STATUS_SUCCESS;
+}
+
+static uint32_t alloc_blockmask_read(RiscvCbqriCapacityState *cc,
+ uint32_t rcid, uint32_t at,
+ bool *busy)
+{
+ unsigned int blockmask_slots = (cc->ncblks + 63) / 64;
+
+ memcpy(cc->alloc_blockmasks,
+ get_blockmask_location(cc, rcid, at),
+ blockmask_slots * 8);
+ return CC_ALLOC_STATUS_SUCCESS;
+}
+
+static uint32_t alloc_blockmask_init(RiscvCbqriCapacityState *cc,
+ uint32_t rcid, uint32_t at, bool set,
+ bool *busy)
+{
+ void *blockmask = get_blockmask_location(cc, rcid, at);
+
+ if (set) {
+ bitmap_fill(blockmask, cc->ncblks);
+ } else {
+ bitmap_zero(blockmask, cc->ncblks);
+ }
+ return CC_ALLOC_STATUS_SUCCESS;
+}
+
+static bool is_valid_at(RiscvCbqriCapacityState *cc, uint32_t at)
+{
+ switch (at) {
+ case CC_AT_DATA:
+ return cc->supports_at_data;
+ case CC_AT_CODE:
+ return cc->supports_at_code;
+ default:
+ return false;
+ }
+}
+
+static void riscv_cbqri_cc_write_mon_ctl(RiscvCbqriCapacityState *cc,
+ uint64_t value)
+{
+ if (!cc->supports_mon_op_config_event &&
+ !cc->supports_mon_op_read_counter) {
+ /* monitoring not supported: leave mon_ctl set to 0 */
+ return;
+ }
+
+ /* extract writable fields */
+ uint32_t op = FIELD_EX64(value, CC_MON_CTL, OP);
+ uint32_t at = FIELD_EX64(value, CC_MON_CTL, AT);
+ uint32_t mcid = FIELD_EX64(value, CC_MON_CTL, MCID);
+ uint32_t evt_id = FIELD_EX64(value, CC_MON_CTL, EVT_ID);
+ bool atv = FIELD_EX64(value, CC_MON_CTL, ATV);
+
+ /* extract read-only fields */
+ uint32_t status = FIELD_EX64(cc->cc_mon_ctl, CC_MON_CTL, STATUS);
+ bool busy = FIELD_EX64(cc->cc_mon_ctl, CC_MON_CTL, BUSY);
+
+ if (busy) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: busy flag still set, ignored",
+ __func__);
+ return;
+ }
+
+ if (!cc->supports_at_data &&
+ !cc->supports_at_code) {
+ /* AT not supported: hardwire to 0 */
+ at = 0;
+ atv = false;
+ }
+
+ if (mcid >= cc->nb_mcids) {
+ status = CC_MON_CTL_STATUS_INVAL_MCID;
+ } else if (op == CC_MON_OP_CONFIG_EVENT &&
+ cc->supports_mon_op_config_event) {
+ if (evt_id == CC_EVT_ID_None &&
+ cc->supports_mon_evt_id_none) {
+ cc->mon_counters[mcid].active = false;
+ status = CC_MON_CTL_STATUS_SUCCESS;
+ } else if (evt_id == CC_EVT_ID_Occupancy &&
+ cc->supports_mon_evt_id_occupancy) {
+ if (atv && !is_valid_at(cc, at)) {
+ status = CC_MON_CTL_STATUS_INVAL_AT;
+ } else {
+ cc->mon_counters[mcid].ctr_val =
+ FIELD_DP64(0, CC_MON_CTR_VAL, INVALID, 1);
+ cc->mon_counters[mcid].evt_id = evt_id;
+ cc->mon_counters[mcid].at = atv ? at : -1;
+ cc->mon_counters[mcid].active = true;
+ status = CC_MON_CTL_STATUS_SUCCESS;
+ }
+ } else {
+ status = CC_MON_CTL_STATUS_INVAL_EVT_ID;
+ }
+ } else if (op == CC_MON_OP_READ_COUNTER &&
+ cc->supports_mon_op_read_counter) {
+ cc->cc_mon_ctr_val = cc->mon_counters[mcid].ctr_val;
+ status = CC_MON_CTL_STATUS_SUCCESS;
+ } else {
+ status = CC_MON_CTL_STATUS_INVAL_OP;
+ }
+
+ /* reconstruct updated register value */
+ value = 0;
+ value = FIELD_DP64(value, CC_MON_CTL, OP, op);
+ value = FIELD_DP64(value, CC_MON_CTL, AT, at);
+ value = FIELD_DP64(value, CC_MON_CTL, MCID, mcid);
+ value = FIELD_DP64(value, CC_MON_CTL, EVT_ID, evt_id);
+ value = FIELD_DP64(value, CC_MON_CTL, ATV, atv);
+ value = FIELD_DP64(value, CC_MON_CTL, STATUS, status);
+ value = FIELD_DP64(value, CC_MON_CTL, BUSY, busy);
+ cc->cc_mon_ctl = value;
+}
+
+static void riscv_cbqri_cc_write_alloc_ctl(RiscvCbqriCapacityState *cc,
+ uint64_t value)
+{
+ if (cc->ncblks == 0 ||
+ (!cc->supports_alloc_op_config_limit &&
+ !cc->supports_alloc_op_read_limit &&
+ !cc->supports_alloc_op_flush_rcid)) {
+ /* capacity allocation not supported: leave alloc_ctl set to 0 */
+ return;
+ }
+
+ /* extract writable fields */
+ uint32_t op = FIELD_EX64(value, CC_ALLOC_CTL, OP);
+ uint32_t at = FIELD_EX64(value, CC_ALLOC_CTL, AT);
+ uint32_t rcid = FIELD_EX64(value, CC_ALLOC_CTL, RCID);
+
+ /* extract read-only fields */
+ uint32_t status = FIELD_EX64(cc->cc_alloc_ctl, CC_ALLOC_CTL, STATUS);
+ bool busy = FIELD_EX64(cc->cc_alloc_ctl, CC_ALLOC_CTL, BUSY);
+
+ if (busy) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: busy flag still set, ignored",
+ __func__);
+ return;
+ }
+
+ bool atv = true;
+ if (!cc->supports_at_data &&
+ !cc->supports_at_code) {
+ /* AT not supported: hardwire to 0 */
+ at = 0;
+ atv = false;
+ }
+
+ if (rcid >= cc->nb_rcids) {
+ status = CC_ALLOC_STATUS_INVAL_RCID;
+ } else if (atv && !is_valid_at(cc, at)) {
+ status = CC_ALLOC_STATUS_INVAL_AT;
+ } else if (op == CC_ALLOC_OP_CONFIG_LIMIT &&
+ cc->supports_alloc_op_config_limit) {
+ status = alloc_blockmask_config(cc, rcid, at, &busy);
+ } else if (op == CC_ALLOC_OP_READ_LIMIT &&
+ cc->supports_alloc_op_read_limit) {
+ status = alloc_blockmask_read(cc, rcid, at, &busy);
+ } else if (op == CC_ALLOC_OP_FLUSH_RCID &&
+ cc->supports_alloc_op_flush_rcid) {
+ status = alloc_blockmask_init(cc, rcid, at, false, &busy);
+ } else {
+ status = CC_ALLOC_STATUS_INVAL_OP;
+ }
+
+ /* reconstruct updated register value */
+ value = 0;
+ value = FIELD_DP64(value, CC_ALLOC_CTL, OP, op);
+ value = FIELD_DP64(value, CC_ALLOC_CTL, AT, at);
+ value = FIELD_DP64(value, CC_ALLOC_CTL, RCID, rcid);
+ value = FIELD_DP64(value, CC_ALLOC_CTL, STATUS, status);
+ value = FIELD_DP64(value, CC_ALLOC_CTL, BUSY, busy);
+ cc->cc_alloc_ctl = value;
+}
+
+static void riscv_cbqri_cc_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ RiscvCbqriCapacityState *cc = opaque;
+
+ assert((addr % 8) == 0);
+ assert(size == 8);
+
+ switch (addr) {
+ case A_CC_CAPABILITIES:
+ /* read-only register */
+ break;
+ case A_CC_MON_CTL:
+ riscv_cbqri_cc_write_mon_ctl(cc, value);
+ break;
+ case A_CC_ALLOC_CTL:
+ riscv_cbqri_cc_write_alloc_ctl(cc, value);
+ break;
+ case A_CC_MON_CTR_VAL:
+ /* read-only register */
+ break;
+ case A_CC_BLOCK_MASK:
+ if (cc->ncblks == 0) {
+ break;
+ }
+ /* fallthrough */
+ default:
+ uint32_t blkmask_slot = (addr - A_CC_BLOCK_MASK) / 8;
+ if (blkmask_slot >= (cc->ncblks + 63) / 64) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: out of bounds (addr=0x%x)",
+ __func__, (uint32_t)addr);
+ break;
+ }
+ cc->alloc_blockmasks[blkmask_slot] = value;
+ }
+}
+
+static uint64_t riscv_cbqri_cc_read(void *opaque, hwaddr addr, unsigned size)
+{
+ RiscvCbqriCapacityState *cc = opaque;
+ uint64_t value = 0;
+
+ assert((addr % 8) == 0);
+ assert(size == 8);
+
+ switch (addr) {
+ case A_CC_CAPABILITIES:
+ value = FIELD_DP64(value, CC_CAPABILITIES, VER_MAJOR,
+ RISCV_CBQRI_VERSION_MAJOR);
+ value = FIELD_DP64(value, CC_CAPABILITIES, VER_MINOR,
+ RISCV_CBQRI_VERSION_MINOR);
+ value = FIELD_DP64(value, CC_CAPABILITIES, NCBLKS,
+ cc->ncblks);
+ value = FIELD_DP64(value, CC_CAPABILITIES, FRCID,
+ cc->supports_alloc_op_flush_rcid);
+ value = FIELD_DP64(value, CC_CAPABILITIES, CUNITS,
+ cc->cunits);
+ value = FIELD_DP64(value, CC_CAPABILITIES, RPFX,
+ cc->rpfx);
+ value = FIELD_DP64(value, CC_CAPABILITIES, P,
+ cc->p);
+
+ break;
+ case A_CC_MON_CTL:
+ value = cc->cc_mon_ctl;
+ break;
+ case A_CC_ALLOC_CTL:
+ value = cc->cc_alloc_ctl;
+ break;
+ case A_CC_MON_CTR_VAL:
+ value = cc->cc_mon_ctr_val;
+ break;
+ case A_CC_BLOCK_MASK:
+ if (cc->ncblks == 0) {
+ break;
+ }
+ /* fallthrough */
+ default:
+ unsigned int blkmask_slot = (addr - A_CC_BLOCK_MASK) / 8;
+ if (blkmask_slot >= (cc->ncblks + 63) / 64) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: out of bounds (addr=0x%x)",
+ __func__, (uint32_t)addr);
+ break;
+ }
+ value = cc->alloc_blockmasks[blkmask_slot];
+ }
+
+ return value;
+}
+
+static const MemoryRegionOps riscv_cbqri_cc_ops = {
+ .read = riscv_cbqri_cc_read,
+ .write = riscv_cbqri_cc_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 8,
+ .impl.min_access_size = 8,
+ .impl.max_access_size = 8,
+};
+
+static void riscv_cbqri_cc_realize(DeviceState *dev, Error **errp)
+{
+ RiscvCbqriCapacityState *cc = RISCV_CBQRI_CC(dev);
+
+ if (!cc->mmio_base) {
+ error_setg(errp, "mmio_base property not set");
+ return;
+ }
+
+ assert(cc->mon_counters == NULL);
+ cc->mon_counters = g_new0(MonitorCounter, cc->nb_mcids);
+
+ assert(cc->alloc_blockmasks == NULL);
+ uint64_t *end = get_blockmask_location(cc, cc->nb_rcids, 0);
+ unsigned int blockmasks_size = end - cc->alloc_blockmasks;
+ cc->alloc_blockmasks = g_new0(uint64_t, blockmasks_size);
+
+ memory_region_init_io(&cc->mmio, OBJECT(dev), &riscv_cbqri_cc_ops,
+ cc, TYPE_RISCV_CBQRI_CC".mmio", 4 * 1024);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &cc->mmio);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, cc->mmio_base);
+}
+
+static void riscv_cbqri_cc_reset(DeviceState *dev)
+{
+ RiscvCbqriCapacityState *cc = RISCV_CBQRI_CC(dev);
+
+ cc->cc_mon_ctl = 0;
+ cc->cc_alloc_ctl = 0;
+
+ /* assign all capacity only to rcid0 */
+ for (unsigned int rcid = 0; rcid < cc->nb_rcids; rcid++) {
+ bool any_at = false;
+
+ if (cc->supports_at_data) {
+ alloc_blockmask_init(cc, rcid, CC_AT_DATA,
+ rcid == 0, NULL);
+ any_at = true;
+ }
+ if (cc->supports_at_code) {
+ alloc_blockmask_init(cc, rcid, CC_AT_CODE,
+ rcid == 0, NULL);
+ any_at = true;
+ }
+ if (!any_at) {
+ alloc_blockmask_init(cc, rcid, 0,
+ rcid == 0, NULL);
+ }
+ }
+}
+
+static Property riscv_cbqri_cc_properties[] = {
+ DEFINE_PROP_UINT64("mmio_base", RiscvCbqriCapacityState, mmio_base, 0),
+ DEFINE_PROP_STRING("target", RiscvCbqriCapacityState, target),
+
+ DEFINE_PROP_UINT16("max_mcids", RiscvCbqriCapacityState, nb_mcids, 256),
+ DEFINE_PROP_UINT16("max_rcids", RiscvCbqriCapacityState, nb_rcids, 64),
+ DEFINE_PROP_UINT16("ncblks", RiscvCbqriCapacityState, ncblks, 16),
+
+ DEFINE_PROP_BOOL("cunits", RiscvCbqriCapacityState, cunits, true),
+ DEFINE_PROP_BOOL("rpfx", RiscvCbqriCapacityState, rpfx, true),
+ DEFINE_PROP_UINT8("p", RiscvCbqriCapacityState, p, 4),
+
+ DEFINE_PROP_BOOL("at_data", RiscvCbqriCapacityState,
+ supports_at_data, true),
+ DEFINE_PROP_BOOL("at_code", RiscvCbqriCapacityState,
+ supports_at_code, true),
+
+ DEFINE_PROP_BOOL("alloc_op_config_limit", RiscvCbqriCapacityState,
+ supports_alloc_op_config_limit, true),
+ DEFINE_PROP_BOOL("alloc_op_read_limit", RiscvCbqriCapacityState,
+ supports_alloc_op_read_limit, true),
+ DEFINE_PROP_BOOL("alloc_op_flush_rcid", RiscvCbqriCapacityState,
+ supports_alloc_op_flush_rcid, true),
+
+ DEFINE_PROP_BOOL("mon_op_config_event", RiscvCbqriCapacityState,
+ supports_mon_op_config_event, true),
+ DEFINE_PROP_BOOL("mon_op_read_counter", RiscvCbqriCapacityState,
+ supports_mon_op_read_counter, true),
+
+ DEFINE_PROP_BOOL("mon_evt_id_none", RiscvCbqriCapacityState,
+ supports_mon_evt_id_none, true),
+ DEFINE_PROP_BOOL("mon_evt_id_occupancy", RiscvCbqriCapacityState,
+ supports_mon_evt_id_occupancy, true),
+};
+
+static void riscv_cbqri_cc_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->realize = riscv_cbqri_cc_realize;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->desc = "RISC-V CBQRI Capacity Controller";
+ device_class_set_props(dc, riscv_cbqri_cc_properties);
+ dc->legacy_reset = riscv_cbqri_cc_reset;
+ dc->user_creatable = true;
+}
+
+static const TypeInfo riscv_cbqri_cc_info = {
+ .name = TYPE_RISCV_CBQRI_CC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RiscvCbqriCapacityState),
+ .class_init = riscv_cbqri_cc_class_init,
+};
+
+static void riscv_cbqri_cc_register_types(void)
+{
+ type_register_static(&riscv_cbqri_cc_info);
+}
+
+DeviceState *riscv_cbqri_cc_create(hwaddr addr,
+ const RiscvCbqriCapacityCaps *caps,
+ const char *target_name)
+{
+ DeviceState *dev = qdev_new(TYPE_RISCV_CBQRI_CC);
+
+ qdev_prop_set_uint64(dev, "mmio_base", addr);
+ qdev_prop_set_string(dev, "target", target_name);
+ qdev_prop_set_uint16(dev, "max_mcids", caps->nb_mcids);
+ qdev_prop_set_uint16(dev, "max_rcids", caps->nb_rcids);
+ qdev_prop_set_uint16(dev, "ncblks", caps->ncblks);
+ qdev_prop_set_bit(dev, "cunits", caps->cunits);
+ qdev_prop_set_bit(dev, "rpfx", caps->rpfx);
+ qdev_prop_set_uint8(dev, "p", caps->p);
+
+ qdev_prop_set_bit(dev, "at_data",
+ caps->supports_at_data);
+ qdev_prop_set_bit(dev, "at_code",
+ caps->supports_at_code);
+ qdev_prop_set_bit(dev, "alloc_op_config_limit",
+ caps->supports_alloc_op_config_limit);
+ qdev_prop_set_bit(dev, "alloc_op_read_limit",
+ caps->supports_alloc_op_read_limit);
+ qdev_prop_set_bit(dev, "alloc_op_flush_rcid",
+ caps->supports_alloc_op_flush_rcid);
+ qdev_prop_set_bit(dev, "mon_op_config_event",
+ caps->supports_mon_op_config_event);
+ qdev_prop_set_bit(dev, "mon_op_read_counter",
+ caps->supports_mon_op_read_counter);
+ qdev_prop_set_bit(dev, "mon_evt_id_none",
+ caps->supports_mon_evt_id_none);
+ qdev_prop_set_bit(dev, "mon_evt_id_occupancy",
+ caps->supports_mon_evt_id_occupancy);
+
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ return dev;
+}
+
+type_init(riscv_cbqri_cc_register_types)
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 3/7] hw/riscv: implement CBQRI capacity controller
2025-11-20 0:42 ` [PATCH 3/7] hw/riscv: implement CBQRI capacity controller Drew Fustini
@ 2025-11-20 11:47 ` Daniel Henrique Barboza
2025-11-21 18:57 ` Drew Fustini
2025-11-20 19:25 ` Radim Krčmář
1 sibling, 1 reply; 21+ messages in thread
From: Daniel Henrique Barboza @ 2025-11-20 11:47 UTC (permalink / raw)
To: Drew Fustini, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei,
qemu-riscv, Paolo Bonzini, Nicolas Pitre, Kornel Dulęba,
Atish Kumar Patra, Atish Patra, Vasudevan Srinivasan,
Radim Krčmář, yunhui cui, Chen Pei, guo.wenjia23,
liu.qingtao2
On 11/19/25 9:42 PM, Drew Fustini wrote:
> From: Nicolas Pitre <npitre@baylibre.com>
>
> Implement a capacity controller according to the Capacity and Bandwidth
> QoS Register Interface (CBQRI) which supports these capabilities:
>
> - Number of access types: 2 (code and data)
> - Usage monitoring operations: CONFIG_EVENT, READ_COUNTER
> - Event IDs supported: None, Occupancy
> - Capacity allocation ops: CONFIG_LIMIT, READ_LIMIT, FLUSH_RCID
>
> Link: https://github.com/riscv-non-isa/riscv-cbqri/blob/main/riscv-cbqri.pdf
> Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
> [fustini: add fields introduced in the ratified spec: cunits, rpfx, p]
> Signed-off-by: Drew Fustini <fustini@kernel.org>
> ---
> MAINTAINERS | 1 +
> hw/riscv/cbqri_capacity.c | 634 ++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 635 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7afe80f1b17c..48cca4ac8702 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -362,6 +362,7 @@ M: Nicolas Pitre <npitre@baylibre.com>
> M: Drew Fustini <fustini@kernel.org>
> L: qemu-riscv@nongnu.org
> S: Supported
> +F: hw/riscv/cbqri_capacity.c
> F: include/hw/riscv/cbqri.h
>
> RENESAS RX CPUs
> diff --git a/hw/riscv/cbqri_capacity.c b/hw/riscv/cbqri_capacity.c
> new file mode 100644
> index 000000000000..e70f831878ac
> --- /dev/null
> +++ b/hw/riscv/cbqri_capacity.c
> @@ -0,0 +1,634 @@
> +/*
> + * RISC-V Capacity and Bandwidth QoS Register Interface
> + * URL: https://github.com/riscv-non-isa/riscv-cbqri/releases/tag/v1.0
> + *
> + * Copyright (c) 2023 BayLibre SAS
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * This file contains the Capacity-controller QoS Register Interface.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/error-report.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "qemu/bitmap.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/sysbus.h"
> +#include "target/riscv/cpu.h"
> +#include "hw/riscv/cbqri.h"
> +
[ ...]
> +
> +static void riscv_cbqri_cc_write(void *opaque, hwaddr addr,
> + uint64_t value, unsigned size)
> +{
> + RiscvCbqriCapacityState *cc = opaque;
> +
> + assert((addr % 8) == 0);
> + assert(size == 8);
So here and in the read callback (riscv_cbqri_cc_read) you're doing asserts for
size == 8, while your memoryops has:
static const MemoryRegionOps riscv_cbqri_cc_ops = {
.read = riscv_cbqri_cc_read,
.write = riscv_cbqri_cc_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid.min_access_size = 4, <==========
.valid.max_access_size = 8,
.impl.min_access_size = 8,
.impl.max_access_size = 8,
};
You can get rid of assert(size == 8) in both callbacks by setting
min_access_size = 8.
Code LGTM otherwise. Thanks,
Daniel
> +
> + switch (addr) {
> + case A_CC_CAPABILITIES:
> + /* read-only register */
> + break;
> + case A_CC_MON_CTL:
> + riscv_cbqri_cc_write_mon_ctl(cc, value);
> + break;
> + case A_CC_ALLOC_CTL:
> + riscv_cbqri_cc_write_alloc_ctl(cc, value);
> + break;
> + case A_CC_MON_CTR_VAL:
> + /* read-only register */
> + break;
> + case A_CC_BLOCK_MASK:
> + if (cc->ncblks == 0) {
> + break;
> + }
> + /* fallthrough */
> + default:
> + uint32_t blkmask_slot = (addr - A_CC_BLOCK_MASK) / 8;
> + if (blkmask_slot >= (cc->ncblks + 63) / 64) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: out of bounds (addr=0x%x)",
> + __func__, (uint32_t)addr);
> + break;
> + }
> + cc->alloc_blockmasks[blkmask_slot] = value;
> + }
> +}
> +
> +static uint64_t riscv_cbqri_cc_read(void *opaque, hwaddr addr, unsigned size)
> +{
> + RiscvCbqriCapacityState *cc = opaque;
> + uint64_t value = 0;
> +
> + assert((addr % 8) == 0);
> + assert(size == 8);
> +
> + switch (addr) {
> + case A_CC_CAPABILITIES:
> + value = FIELD_DP64(value, CC_CAPABILITIES, VER_MAJOR,
> + RISCV_CBQRI_VERSION_MAJOR);
> + value = FIELD_DP64(value, CC_CAPABILITIES, VER_MINOR,
> + RISCV_CBQRI_VERSION_MINOR);
> + value = FIELD_DP64(value, CC_CAPABILITIES, NCBLKS,
> + cc->ncblks);
> + value = FIELD_DP64(value, CC_CAPABILITIES, FRCID,
> + cc->supports_alloc_op_flush_rcid);
> + value = FIELD_DP64(value, CC_CAPABILITIES, CUNITS,
> + cc->cunits);
> + value = FIELD_DP64(value, CC_CAPABILITIES, RPFX,
> + cc->rpfx);
> + value = FIELD_DP64(value, CC_CAPABILITIES, P,
> + cc->p);
> +
> + break;
> + case A_CC_MON_CTL:
> + value = cc->cc_mon_ctl;
> + break;
> + case A_CC_ALLOC_CTL:
> + value = cc->cc_alloc_ctl;
> + break;
> + case A_CC_MON_CTR_VAL:
> + value = cc->cc_mon_ctr_val;
> + break;
> + case A_CC_BLOCK_MASK:
> + if (cc->ncblks == 0) {
> + break;
> + }
> + /* fallthrough */
> + default:
> + unsigned int blkmask_slot = (addr - A_CC_BLOCK_MASK) / 8;
> + if (blkmask_slot >= (cc->ncblks + 63) / 64) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: out of bounds (addr=0x%x)",
> + __func__, (uint32_t)addr);
> + break;
> + }
> + value = cc->alloc_blockmasks[blkmask_slot];
> + }
> +
> + return value;
> +}
> +
> +static const MemoryRegionOps riscv_cbqri_cc_ops = {
> + .read = riscv_cbqri_cc_read,
> + .write = riscv_cbqri_cc_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> + .valid.min_access_size = 4,
> + .valid.max_access_size = 8,
> + .impl.min_access_size = 8,
> + .impl.max_access_size = 8,
> +};
> +
> +static void riscv_cbqri_cc_realize(DeviceState *dev, Error **errp)
> +{
> + RiscvCbqriCapacityState *cc = RISCV_CBQRI_CC(dev);
> +
> + if (!cc->mmio_base) {
> + error_setg(errp, "mmio_base property not set");
> + return;
> + }
> +
> + assert(cc->mon_counters == NULL);
> + cc->mon_counters = g_new0(MonitorCounter, cc->nb_mcids);
> +
> + assert(cc->alloc_blockmasks == NULL);
> + uint64_t *end = get_blockmask_location(cc, cc->nb_rcids, 0);
> + unsigned int blockmasks_size = end - cc->alloc_blockmasks;
> + cc->alloc_blockmasks = g_new0(uint64_t, blockmasks_size);
> +
> + memory_region_init_io(&cc->mmio, OBJECT(dev), &riscv_cbqri_cc_ops,
> + cc, TYPE_RISCV_CBQRI_CC".mmio", 4 * 1024);
> + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &cc->mmio);
> + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, cc->mmio_base);
> +}
> +
> +static void riscv_cbqri_cc_reset(DeviceState *dev)
> +{
> + RiscvCbqriCapacityState *cc = RISCV_CBQRI_CC(dev);
> +
> + cc->cc_mon_ctl = 0;
> + cc->cc_alloc_ctl = 0;
> +
> + /* assign all capacity only to rcid0 */
> + for (unsigned int rcid = 0; rcid < cc->nb_rcids; rcid++) {
> + bool any_at = false;
> +
> + if (cc->supports_at_data) {
> + alloc_blockmask_init(cc, rcid, CC_AT_DATA,
> + rcid == 0, NULL);
> + any_at = true;
> + }
> + if (cc->supports_at_code) {
> + alloc_blockmask_init(cc, rcid, CC_AT_CODE,
> + rcid == 0, NULL);
> + any_at = true;
> + }
> + if (!any_at) {
> + alloc_blockmask_init(cc, rcid, 0,
> + rcid == 0, NULL);
> + }
> + }
> +}
> +
> +static Property riscv_cbqri_cc_properties[] = {
> + DEFINE_PROP_UINT64("mmio_base", RiscvCbqriCapacityState, mmio_base, 0),
> + DEFINE_PROP_STRING("target", RiscvCbqriCapacityState, target),
> +
> + DEFINE_PROP_UINT16("max_mcids", RiscvCbqriCapacityState, nb_mcids, 256),
> + DEFINE_PROP_UINT16("max_rcids", RiscvCbqriCapacityState, nb_rcids, 64),
> + DEFINE_PROP_UINT16("ncblks", RiscvCbqriCapacityState, ncblks, 16),
> +
> + DEFINE_PROP_BOOL("cunits", RiscvCbqriCapacityState, cunits, true),
> + DEFINE_PROP_BOOL("rpfx", RiscvCbqriCapacityState, rpfx, true),
> + DEFINE_PROP_UINT8("p", RiscvCbqriCapacityState, p, 4),
> +
> + DEFINE_PROP_BOOL("at_data", RiscvCbqriCapacityState,
> + supports_at_data, true),
> + DEFINE_PROP_BOOL("at_code", RiscvCbqriCapacityState,
> + supports_at_code, true),
> +
> + DEFINE_PROP_BOOL("alloc_op_config_limit", RiscvCbqriCapacityState,
> + supports_alloc_op_config_limit, true),
> + DEFINE_PROP_BOOL("alloc_op_read_limit", RiscvCbqriCapacityState,
> + supports_alloc_op_read_limit, true),
> + DEFINE_PROP_BOOL("alloc_op_flush_rcid", RiscvCbqriCapacityState,
> + supports_alloc_op_flush_rcid, true),
> +
> + DEFINE_PROP_BOOL("mon_op_config_event", RiscvCbqriCapacityState,
> + supports_mon_op_config_event, true),
> + DEFINE_PROP_BOOL("mon_op_read_counter", RiscvCbqriCapacityState,
> + supports_mon_op_read_counter, true),
> +
> + DEFINE_PROP_BOOL("mon_evt_id_none", RiscvCbqriCapacityState,
> + supports_mon_evt_id_none, true),
> + DEFINE_PROP_BOOL("mon_evt_id_occupancy", RiscvCbqriCapacityState,
> + supports_mon_evt_id_occupancy, true),
> +};
> +
> +static void riscv_cbqri_cc_class_init(ObjectClass *klass, const void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + dc->realize = riscv_cbqri_cc_realize;
> + set_bit(DEVICE_CATEGORY_MISC, dc->categories);
> + dc->desc = "RISC-V CBQRI Capacity Controller";
> + device_class_set_props(dc, riscv_cbqri_cc_properties);
> + dc->legacy_reset = riscv_cbqri_cc_reset;
> + dc->user_creatable = true;
> +}
> +
> +static const TypeInfo riscv_cbqri_cc_info = {
> + .name = TYPE_RISCV_CBQRI_CC,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(RiscvCbqriCapacityState),
> + .class_init = riscv_cbqri_cc_class_init,
> +};
> +
> +static void riscv_cbqri_cc_register_types(void)
> +{
> + type_register_static(&riscv_cbqri_cc_info);
> +}
> +
> +DeviceState *riscv_cbqri_cc_create(hwaddr addr,
> + const RiscvCbqriCapacityCaps *caps,
> + const char *target_name)
> +{
> + DeviceState *dev = qdev_new(TYPE_RISCV_CBQRI_CC);
> +
> + qdev_prop_set_uint64(dev, "mmio_base", addr);
> + qdev_prop_set_string(dev, "target", target_name);
> + qdev_prop_set_uint16(dev, "max_mcids", caps->nb_mcids);
> + qdev_prop_set_uint16(dev, "max_rcids", caps->nb_rcids);
> + qdev_prop_set_uint16(dev, "ncblks", caps->ncblks);
> + qdev_prop_set_bit(dev, "cunits", caps->cunits);
> + qdev_prop_set_bit(dev, "rpfx", caps->rpfx);
> + qdev_prop_set_uint8(dev, "p", caps->p);
> +
> + qdev_prop_set_bit(dev, "at_data",
> + caps->supports_at_data);
> + qdev_prop_set_bit(dev, "at_code",
> + caps->supports_at_code);
> + qdev_prop_set_bit(dev, "alloc_op_config_limit",
> + caps->supports_alloc_op_config_limit);
> + qdev_prop_set_bit(dev, "alloc_op_read_limit",
> + caps->supports_alloc_op_read_limit);
> + qdev_prop_set_bit(dev, "alloc_op_flush_rcid",
> + caps->supports_alloc_op_flush_rcid);
> + qdev_prop_set_bit(dev, "mon_op_config_event",
> + caps->supports_mon_op_config_event);
> + qdev_prop_set_bit(dev, "mon_op_read_counter",
> + caps->supports_mon_op_read_counter);
> + qdev_prop_set_bit(dev, "mon_evt_id_none",
> + caps->supports_mon_evt_id_none);
> + qdev_prop_set_bit(dev, "mon_evt_id_occupancy",
> + caps->supports_mon_evt_id_occupancy);
> +
> + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +
> + return dev;
> +}
> +
> +type_init(riscv_cbqri_cc_register_types)
>
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 3/7] hw/riscv: implement CBQRI capacity controller
2025-11-20 11:47 ` Daniel Henrique Barboza
@ 2025-11-21 18:57 ` Drew Fustini
0 siblings, 0 replies; 21+ messages in thread
From: Drew Fustini @ 2025-11-21 18:57 UTC (permalink / raw)
To: Daniel Henrique Barboza
Cc: qemu-devel, Palmer Dabbelt, Alistair Francis, Weiwei Li,
Liu Zhiwei, qemu-riscv, Paolo Bonzini, Nicolas Pitre,
Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, Radim Krčmář, yunhui cui,
Chen Pei, guo.wenjia23, liu.qingtao2
On Thu, Nov 20, 2025 at 08:47:44AM -0300, Daniel Henrique Barboza wrote:
> [ ...]
>
> > +
> > +static void riscv_cbqri_cc_write(void *opaque, hwaddr addr,
> > + uint64_t value, unsigned size)
> > +{
> > + RiscvCbqriCapacityState *cc = opaque;
> > +
> > + assert((addr % 8) == 0);
> > + assert(size == 8);
>
> So here and in the read callback (riscv_cbqri_cc_read) you're doing asserts for
> size == 8, while your memoryops has:
>
> static const MemoryRegionOps riscv_cbqri_cc_ops = {
> .read = riscv_cbqri_cc_read,
> .write = riscv_cbqri_cc_write,
> .endianness = DEVICE_LITTLE_ENDIAN,
> .valid.min_access_size = 4, <==========
> .valid.max_access_size = 8,
> .impl.min_access_size = 8,
> .impl.max_access_size = 8,
> };
>
>
> You can get rid of assert(size == 8) in both callbacks by setting
> min_access_size = 8.
Thanks for the review.
I think that the assert in riscv_cbqri_cc_write maybe incorrect.
The CBQRI spec states that the registers:
- start at an 8-byte aligned physical address.
- can accessed by using naturally aligned 4-byte or 8-byte accesses
- 4-byte access to a register must be single-copy atomic
- It is UNSPECIFIED whether 8-byte access must be single-copy atomic
- 8-byte access can appear internally to the CBQRI implementation as
if two separate 4-byte accesses are performed.
The spec further notes that:
"The CBQRI registers are defined so that software can perform two
individual 4 byte accesses, or hardware can perform two independent 4
byte transactions resulting from an 8 byte access, to the high and low
halves of the register as long as the register semantics, with regards
to side-effects, are respected between the two software accesses, or two
hardware transactions, respectively."
Based on the above, I believe .valid.min_access_size does need to stay
at 4 bytes. The Qemu documentation states that otherwise "accesses
outside this range will have device and bus specific behaviour (ignored,
or machine check)".
I am confused whether ".impl.min_access_size = 8" is correct. The Qemu
documentation states that "other access sizes will be emulated using the
ones available. For example a 4-byte write will be emulated using four
1-byte writes, if .impl.max_access_size = 1."
Radim asked if 32-bit (4 byte) access would be supported, but I am
confused how other access sizes are emulated.
Do I need to add code to the read and write hooks for riscv_cbqri_cc_ops
and riscv_cbqri_bc_ops?
Or is there some aspect of MemoryRegionOps that can handle the emulation
of other sizes automatically?
Thanks,
Drew
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 3/7] hw/riscv: implement CBQRI capacity controller
2025-11-20 0:42 ` [PATCH 3/7] hw/riscv: implement CBQRI capacity controller Drew Fustini
2025-11-20 11:47 ` Daniel Henrique Barboza
@ 2025-11-20 19:25 ` Radim Krčmář
2025-11-21 19:50 ` Drew Fustini
1 sibling, 1 reply; 21+ messages in thread
From: Radim Krčmář @ 2025-11-20 19:25 UTC (permalink / raw)
To: Drew Fustini, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, qemu-riscv, Paolo Bonzini,
Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, yunhui cui, Chen Pei, guo.wenjia23,
liu.qingtao2, qemu-riscv-bounces+qemu-riscv=archiver.kernel.org
2025-11-19T16:42:19-08:00, Drew Fustini <fustini@kernel.org>:
> From: Nicolas Pitre <npitre@baylibre.com>
>
> Implement a capacity controller according to the Capacity and Bandwidth
> QoS Register Interface (CBQRI) which supports these capabilities:
>
> - Number of access types: 2 (code and data)
> - Usage monitoring operations: CONFIG_EVENT, READ_COUNTER
> - Event IDs supported: None, Occupancy
> - Capacity allocation ops: CONFIG_LIMIT, READ_LIMIT, FLUSH_RCID
>
> Link: https://github.com/riscv-non-isa/riscv-cbqri/blob/main/riscv-cbqri.pdf
> Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
> [fustini: add fields introduced in the ratified spec: cunits, rpfx, p]
> Signed-off-by: Drew Fustini <fustini@kernel.org>
> ---
> diff --git a/hw/riscv/cbqri_capacity.c b/hw/riscv/cbqri_capacity.c
> [...]
> +static void riscv_cbqri_cc_write_alloc_ctl(RiscvCbqriCapacityState *cc,
> + uint64_t value)
> +{
>[...]
> + if (rcid >= cc->nb_rcids) {
> + status = CC_ALLOC_STATUS_INVAL_RCID;
> + } else if (atv && !is_valid_at(cc, at)) {
> + status = CC_ALLOC_STATUS_INVAL_AT;
> + } else if (op == CC_ALLOC_OP_CONFIG_LIMIT &&
> + cc->supports_alloc_op_config_limit) {
> + status = alloc_blockmask_config(cc, rcid, at, &busy);
> + } else if (op == CC_ALLOC_OP_READ_LIMIT &&
> + cc->supports_alloc_op_read_limit) {
> + status = alloc_blockmask_read(cc, rcid, at, &busy);
> + } else if (op == CC_ALLOC_OP_FLUSH_RCID &&
> + cc->supports_alloc_op_flush_rcid) {
> + status = alloc_blockmask_init(cc, rcid, at, false, &busy);
The spec says the following about flush:
"The configured capacity block allocation or the capacity unit limit
is not changed by this operation."
Limits are not implemented, so I think it's supposed to be a nop.
> [...]
> +static uint64_t riscv_cbqri_cc_read(void *opaque, hwaddr addr, unsigned size)
> +{
> + RiscvCbqriCapacityState *cc = opaque;
> + uint64_t value = 0;
> +
> + assert((addr % 8) == 0);
> + assert(size == 8);
Is there a plan to extend support for 32 bit operations?
"The CBQRI registers are defined so that software can perform two
individual 4 byte accesses."
> + switch (addr) {
> [...]
> + case A_CC_BLOCK_MASK:
> + if (cc->ncblks == 0) {
> + break;
> + }
> + /* fallthrough */
> + default:
> + unsigned int blkmask_slot = (addr - A_CC_BLOCK_MASK) / 8;
> + if (blkmask_slot >= (cc->ncblks + 63) / 64) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: out of bounds (addr=0x%x)",
> + __func__, (uint32_t)addr);
> + break;
> + }
> + value = cc->alloc_blockmasks[blkmask_slot];
There is supposed to be a cc_cunits registers after the cc_block_mask.
> + }
> +
> + return value;
> +}
> [...]
> +static void riscv_cbqri_cc_realize(DeviceState *dev, Error **errp)
> +{
> + RiscvCbqriCapacityState *cc = RISCV_CBQRI_CC(dev);
> +
> + if (!cc->mmio_base) {
> + error_setg(errp, "mmio_base property not set");
> + return;
> + }
> +
> + assert(cc->mon_counters == NULL);
> + cc->mon_counters = g_new0(MonitorCounter, cc->nb_mcids);
> +
> + assert(cc->alloc_blockmasks == NULL);
> + uint64_t *end = get_blockmask_location(cc, cc->nb_rcids, 0);
> + unsigned int blockmasks_size = end - cc->alloc_blockmasks;
> + cc->alloc_blockmasks = g_new0(uint64_t, blockmasks_size);
> +
> + memory_region_init_io(&cc->mmio, OBJECT(dev), &riscv_cbqri_cc_ops,
> + cc, TYPE_RISCV_CBQRI_CC".mmio", 4 * 1024);
Shouldn't the region size take cc->ncblks into account?
(A bitmask for 2^16 ids is 8kB.)
> + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &cc->mmio);
> + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, cc->mmio_base);
> +}
> +
> +static void riscv_cbqri_cc_reset(DeviceState *dev)
> +{
> + RiscvCbqriCapacityState *cc = RISCV_CBQRI_CC(dev);
> +
> + cc->cc_mon_ctl = 0;
> + cc->cc_alloc_ctl = 0;
Only the busy field must be reset to 0. I think the decision warrants a
comment that we're zeroing more to have simpler code.
> +
> + /* assign all capacity only to rcid0 */
> + for (unsigned int rcid = 0; rcid < cc->nb_rcids; rcid++) {
rcid != 0 are unspecified on reset, so I would prefer not to touch them.
> + bool any_at = false;
> +
> + if (cc->supports_at_data) {
> + alloc_blockmask_init(cc, rcid, CC_AT_DATA,
> + rcid == 0, NULL);
> + any_at = true;
> + }
> + if (cc->supports_at_code) {
> + alloc_blockmask_init(cc, rcid, CC_AT_CODE,
> + rcid == 0, NULL);
> + any_at = true;
> + }
> + if (!any_at) {
> + alloc_blockmask_init(cc, rcid, 0,
> + rcid == 0, NULL);
> + }
> + }
I think it looks a bit better if AT values were expressed as a bitfield
of size 8: (untested)
unsigned long at = find_next_bit(&cc->supported_at_mask, 8, 0);
do {
alloc_blockmask_init(cc, rcid, at < 8 ? at : 0, rcid == 0, NULL);
at = find_next_bit(&cc->supported_at_mask, 8, at + 1);
} while (at < 8);
Thanks.
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 3/7] hw/riscv: implement CBQRI capacity controller
2025-11-20 19:25 ` Radim Krčmář
@ 2025-11-21 19:50 ` Drew Fustini
0 siblings, 0 replies; 21+ messages in thread
From: Drew Fustini @ 2025-11-21 19:50 UTC (permalink / raw)
To: Radim Krčmář
Cc: qemu-devel, Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, qemu-riscv, Paolo Bonzini,
Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, yunhui cui, Chen Pei, guo.wenjia23,
liu.qingtao2, qemu-riscv-bounces+qemu-riscv=archiver.kernel.org
On Thu, Nov 20, 2025 at 08:25:44PM +0100, Radim Krčmář wrote:
> 2025-11-19T16:42:19-08:00, Drew Fustini <fustini@kernel.org>:
> > From: Nicolas Pitre <npitre@baylibre.com>
> >
> > Implement a capacity controller according to the Capacity and Bandwidth
> > QoS Register Interface (CBQRI) which supports these capabilities:
> >
> > - Number of access types: 2 (code and data)
> > - Usage monitoring operations: CONFIG_EVENT, READ_COUNTER
> > - Event IDs supported: None, Occupancy
> > - Capacity allocation ops: CONFIG_LIMIT, READ_LIMIT, FLUSH_RCID
> >
> > Link: https://github.com/riscv-non-isa/riscv-cbqri/blob/main/riscv-cbqri.pdf
> > Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
> > [fustini: add fields introduced in the ratified spec: cunits, rpfx, p]
> > Signed-off-by: Drew Fustini <fustini@kernel.org>
> > ---
> > diff --git a/hw/riscv/cbqri_capacity.c b/hw/riscv/cbqri_capacity.c
> > [...]
> > +static void riscv_cbqri_cc_write_alloc_ctl(RiscvCbqriCapacityState *cc,
> > + uint64_t value)
> > +{
> >[...]
> > + if (rcid >= cc->nb_rcids) {
> > + status = CC_ALLOC_STATUS_INVAL_RCID;
> > + } else if (atv && !is_valid_at(cc, at)) {
> > + status = CC_ALLOC_STATUS_INVAL_AT;
> > + } else if (op == CC_ALLOC_OP_CONFIG_LIMIT &&
> > + cc->supports_alloc_op_config_limit) {
> > + status = alloc_blockmask_config(cc, rcid, at, &busy);
> > + } else if (op == CC_ALLOC_OP_READ_LIMIT &&
> > + cc->supports_alloc_op_read_limit) {
> > + status = alloc_blockmask_read(cc, rcid, at, &busy);
> > + } else if (op == CC_ALLOC_OP_FLUSH_RCID &&
> > + cc->supports_alloc_op_flush_rcid) {
> > + status = alloc_blockmask_init(cc, rcid, at, false, &busy);
>
> The spec says the following about flush:
>
> "The configured capacity block allocation or the capacity unit limit
> is not changed by this operation."
>
> Limits are not implemented, so I think it's supposed to be a nop.
Thanks, that makes sense. I'll change it for the next revision.
> > [...]
> > +static uint64_t riscv_cbqri_cc_read(void *opaque, hwaddr addr, unsigned size)
> > +{
> > + RiscvCbqriCapacityState *cc = opaque;
> > + uint64_t value = 0;
> > +
> > + assert((addr % 8) == 0);
> > + assert(size == 8);
>
> Is there a plan to extend support for 32 bit operations?
>
> "The CBQRI registers are defined so that software can perform two
> individual 4 byte accesses."
Good question. Daniel also commented about the asserts for size == 8
while your MemoryRegionOps has .valid.min_access_size = 4. It does seem
like from the spec that these CBQRI controllers do need to support 4
byte accesses.
However, I need to understand better how to accomplish that. I'm
uncertain if I need to add code to the read and write hooks for that, or
if something in MemoryRegionOps can handle it automatically.
> > [...]
> > + case A_CC_BLOCK_MASK:
> > + if (cc->ncblks == 0) {
> > + break;
> > + }
> > + /* fallthrough */
> > + default:
> > + unsigned int blkmask_slot = (addr - A_CC_BLOCK_MASK) / 8;
> > + if (blkmask_slot >= (cc->ncblks + 63) / 64) {
> > + qemu_log_mask(LOG_GUEST_ERROR,
> > + "%s: out of bounds (addr=0x%x)",
> > + __func__, (uint32_t)addr);
> > + break;
> > + }
> > + value = cc->alloc_blockmasks[blkmask_slot];
>
> There is supposed to be a cc_cunits registers after the cc_block_mask.
Good point, I need to update the code to account for cc_units.
> > [...]
> > +static void riscv_cbqri_cc_realize(DeviceState *dev, Error **errp)
> > +{
> > + RiscvCbqriCapacityState *cc = RISCV_CBQRI_CC(dev);
> > +
> > + if (!cc->mmio_base) {
> > + error_setg(errp, "mmio_base property not set");
> > + return;
> > + }
> > +
> > + assert(cc->mon_counters == NULL);
> > + cc->mon_counters = g_new0(MonitorCounter, cc->nb_mcids);
> > +
> > + assert(cc->alloc_blockmasks == NULL);
> > + uint64_t *end = get_blockmask_location(cc, cc->nb_rcids, 0);
> > + unsigned int blockmasks_size = end - cc->alloc_blockmasks;
> > + cc->alloc_blockmasks = g_new0(uint64_t, blockmasks_size);
> > +
> > + memory_region_init_io(&cc->mmio, OBJECT(dev), &riscv_cbqri_cc_ops,
> > + cc, TYPE_RISCV_CBQRI_CC".mmio", 4 * 1024);
>
> Shouldn't the region size take cc->ncblks into account?
> (A bitmask for 2^16 ids is 8kB.)
cc_block_mask field is BMW / 8. In the case of NCBLKS of 12 and NCBLKS
of 16, both end up with a BMW of 64 which would be 8 bytes. I think the
the only reason the allocation is 4KB is that is meant to be aligned to
the page size. Otherwise, the capacity controller register layout is
pretty small.
> > + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &cc->mmio);
> > + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, cc->mmio_base);
> > +}
> > +
> > +static void riscv_cbqri_cc_reset(DeviceState *dev)
> > +{
> > + RiscvCbqriCapacityState *cc = RISCV_CBQRI_CC(dev);
> > +
> > + cc->cc_mon_ctl = 0;
> > + cc->cc_alloc_ctl = 0;
>
> Only the busy field must be reset to 0. I think the decision warrants a
> comment that we're zeroing more to have simpler code.
Okay, will do.
> > +
> > + /* assign all capacity only to rcid0 */
> > + for (unsigned int rcid = 0; rcid < cc->nb_rcids; rcid++) {
>
> rcid != 0 are unspecified on reset, so I would prefer not to touch them.
Okay, that is a good point that the spec does state that. I'll fix this
in the next rev.
> > + bool any_at = false;
> > +
> > + if (cc->supports_at_data) {
> > + alloc_blockmask_init(cc, rcid, CC_AT_DATA,
> > + rcid == 0, NULL);
> > + any_at = true;
> > + }
> > + if (cc->supports_at_code) {
> > + alloc_blockmask_init(cc, rcid, CC_AT_CODE,
> > + rcid == 0, NULL);
> > + any_at = true;
> > + }
> > + if (!any_at) {
> > + alloc_blockmask_init(cc, rcid, 0,
> > + rcid == 0, NULL);
> > + }
> > + }
>
> I think it looks a bit better if AT values were expressed as a bitfield
> of size 8: (untested)
>
> unsigned long at = find_next_bit(&cc->supported_at_mask, 8, 0);
>
> do {
> alloc_blockmask_init(cc, rcid, at < 8 ? at : 0, rcid == 0, NULL);
> at = find_next_bit(&cc->supported_at_mask, 8, at + 1);
> } while (at < 8);
Thanks for the suggestion. I will give that a try.
Drew
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 4/7] hw/riscv: implement CBQRI bandwidth controller
2025-11-20 0:42 [PATCH 0/7] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
` (2 preceding siblings ...)
2025-11-20 0:42 ` [PATCH 3/7] hw/riscv: implement CBQRI capacity controller Drew Fustini
@ 2025-11-20 0:42 ` Drew Fustini
2025-11-20 11:51 ` Daniel Henrique Barboza
2025-11-20 0:42 ` [PATCH 5/7] hw/riscv: Kconfig: add CBQRI options Drew Fustini
` (2 subsequent siblings)
6 siblings, 1 reply; 21+ messages in thread
From: Drew Fustini @ 2025-11-20 0:42 UTC (permalink / raw)
To: qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, qemu-riscv, Paolo Bonzini,
Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, Radim Krčmář, yunhui cui,
Chen Pei, guo.wenjia23, liu.qingtao2, Drew Fustini
From: Nicolas Pitre <npitre@baylibre.com>
Implement a bandwidth controller according to the Capacity and Bandwidth
QoS Register Interface (CBQRI) which supports these capabilities:
- Number of access types: 2 (code and data)
- Usage monitoring operations: CONFIG_EVENT, READ_COUNTER
- Event IDs supported: None, Total read/write byte count, Total
read byte count, Total write byte count
- Bandwidth allocation operations: CONFIG_LIMIT, READ_LIMIT
Link: https://github.com/riscv-non-isa/riscv-cbqri/blob/main/riscv-cbqri.pdf
Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
[fustini: add fields introduced in the ratified spec: rpfx and p]
Signed-off-by: Drew Fustini <fustini@kernel.org>
---
MAINTAINERS | 1 +
hw/riscv/cbqri_bandwidth.c | 613 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 614 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 48cca4ac8702..16500c4e8a83 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -362,6 +362,7 @@ M: Nicolas Pitre <npitre@baylibre.com>
M: Drew Fustini <fustini@kernel.org>
L: qemu-riscv@nongnu.org
S: Supported
+F: hw/riscv/cbqri_bandwidth.c
F: hw/riscv/cbqri_capacity.c
F: include/hw/riscv/cbqri.h
diff --git a/hw/riscv/cbqri_bandwidth.c b/hw/riscv/cbqri_bandwidth.c
new file mode 100644
index 000000000000..40357a3219d8
--- /dev/null
+++ b/hw/riscv/cbqri_bandwidth.c
@@ -0,0 +1,613 @@
+/*
+ * RISC-V Capacity and Bandwidth QoS Register Interface
+ * URL: https://github.com/riscv-non-isa/riscv-cbqri/releases/tag/v1.0
+ *
+ * Copyright (c) 2023 BayLibre SAS
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This file contains the Bandwidth-controller QoS Register Interface.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/cbqri.h"
+
+/* Encodings of `AT` field */
+enum {
+ BC_AT_DATA = 0,
+ BC_AT_CODE = 1,
+};
+
+/* Capabilities */
+REG64(BC_CAPABILITIES, 0);
+FIELD(BC_CAPABILITIES, VER, 0, 8);
+FIELD(BC_CAPABILITIES, VER_MINOR, 0, 4);
+FIELD(BC_CAPABILITIES, VER_MAJOR, 4, 4);
+FIELD(BC_CAPABILITIES, NBWBLKS, 8, 16);
+FIELD(BC_CAPABILITIES, RPFX, 24, 1);
+FIELD(BC_CAPABILITIES, P, 25, 4);
+FIELD(BC_CAPABILITIES, MRBWB, 32, 16);
+
+/* Usage monitoring control */
+REG64(BC_MON_CTL, 8);
+FIELD(BC_MON_CTL, OP, 0, 5);
+FIELD(BC_MON_CTL, AT, 5, 3);
+FIELD(BC_MON_CTL, MCID, 8, 12);
+FIELD(BC_MON_CTL, EVT_ID, 20, 8);
+FIELD(BC_MON_CTL, ATV, 28, 1);
+FIELD(BC_MON_CTL, STATUS, 32, 7);
+FIELD(BC_MON_CTL, BUSY, 39, 1);
+
+/* Usage monitoring operations */
+enum {
+ BC_MON_OP_CONFIG_EVENT = 1,
+ BC_MON_OP_READ_COUNTER = 2,
+};
+
+/* Bandwidth monitoring event ID */
+enum {
+ BC_EVT_ID_None = 0,
+ BC_EVT_ID_RDWR_count = 1,
+ BC_EVT_ID_RDONLY_count = 2,
+ BC_EVT_ID_WRONLY_count = 3,
+};
+
+/* BC_MON_CTL.STATUS field encodings */
+enum {
+ BC_MON_CTL_STATUS_SUCCESS = 1,
+ BC_MON_CTL_STATUS_INVAL_OP = 2,
+ BC_MON_CTL_STATUS_INVAL_MCID = 3,
+ BC_MON_CTL_STATUS_INVAL_EVT_ID = 4,
+ BC_MON_CTL_STATUS_INVAL_AT = 5,
+};
+
+/* Monitoring counter value */
+REG64(BC_MON_CTR_VAL, 16);
+FIELD(BC_MON_CTR_VAL, CTR, 0, 62);
+FIELD(BC_MON_CTR_VAL, INVALID, 62, 1);
+FIELD(BC_MON_CTR_VAL, OVF, 63, 1);
+
+/* Bandwidth Allocation control */
+REG64(BC_ALLOC_CTL, 24);
+FIELD(BC_ALLOC_CTL, OP, 0, 5);
+FIELD(BC_ALLOC_CTL, AT, 5, 3);
+FIELD(BC_ALLOC_CTL, RCID, 8, 12);
+FIELD(BC_ALLOC_CTL, STATUS, 32, 7);
+FIELD(BC_ALLOC_CTL, BUSY, 39, 1);
+
+/* Bandwidth allocation operations */
+enum {
+ BC_ALLOC_OP_CONFIG_LIMIT = 1,
+ BC_ALLOC_OP_READ_LIMIT = 2,
+};
+
+/* BC_ALLOC_CTL.STATUS field encodings */
+enum {
+ BC_ALLOC_STATUS_SUCCESS = 1,
+ BC_ALLOC_STATUS_INVAL_OP = 2,
+ BC_ALLOC_STATUS_INVAL_RCID = 3,
+ BC_ALLOC_STATUS_INVAL_AT = 4,
+ BC_ALLOC_STATUS_INVAL_BLKS = 5,
+};
+
+/* Bandwidth allocation */
+REG64(BC_BW_ALLOC, 32);
+FIELD(BC_BW_ALLOC, Rbwb, 0, 16);
+FIELD(BC_BW_ALLOC, Mweight, 20, 8);
+FIELD(BC_BW_ALLOC, sharedAT, 28, 3);
+FIELD(BC_BW_ALLOC, useShared, 31, 1);
+
+
+typedef struct MonitorCounter {
+ uint64_t ctr_val;
+ int at;
+ int evt_id;
+ bool active;
+} MonitorCounter;
+
+typedef struct BandwidthAllocation {
+ uint32_t Rbwb:16;
+ uint32_t Mweight:8;
+ uint32_t sharedAT:3;
+ bool useShared:1;
+} BandwidthAllocation;
+
+typedef struct RiscvCbqriBandwidthState {
+ SysBusDevice parent_obj;
+ MemoryRegion mmio;
+
+ /* cached value of some registers */
+ uint64_t bc_mon_ctl;
+ uint64_t bc_mon_ctr_val;
+ uint64_t bc_alloc_ctl;
+ uint64_t bc_bw_alloc;
+
+ MonitorCounter *mon_counters;
+ BandwidthAllocation *bw_allocations;
+
+ /* properties */
+
+ uint64_t mmio_base;
+ char *target;
+ uint16_t nb_mcids;
+ uint16_t nb_rcids;
+
+ uint16_t nbwblks;
+ uint16_t mrbwb;
+
+ bool rpfx;
+ uint8_t p;
+
+ bool supports_at_data;
+ bool supports_at_code;
+
+ bool supports_alloc_op_config_limit;
+ bool supports_alloc_op_read_limit;
+
+ bool supports_mon_op_config_event;
+ bool supports_mon_op_read_counter;
+
+ bool supports_mon_evt_id_none;
+ bool supports_mon_evt_id_rdwr_count;
+ bool supports_mon_evt_id_rdonly_count;
+ bool supports_mon_evt_id_wronly_count;
+} RiscvCbqriBandwidthState;
+
+#define RISCV_CBQRI_BC(obj) \
+ OBJECT_CHECK(RiscvCbqriBandwidthState, (obj), TYPE_RISCV_CBQRI_BC)
+
+static BandwidthAllocation *get_bw_alloc(RiscvCbqriBandwidthState *bc,
+ uint32_t rcid, uint32_t at)
+{
+ /*
+ * All bandwidth allocation records are contiguous to simplify
+ * allocation. The first one is used to hold the BC_BW_ALLOC register
+ * content, followed by respective records for each AT per RCID.
+ */
+
+ unsigned int nb_ats = 0;
+ nb_ats += !!bc->supports_at_data;
+ nb_ats += !!bc->supports_at_code;
+ nb_ats = MAX(nb_ats, 1);
+ assert(at < nb_ats);
+
+ return &bc->bw_allocations[1 + rcid * nb_ats + at];
+}
+
+static uint32_t bandwidth_config(RiscvCbqriBandwidthState *bc,
+ uint32_t rcid, uint32_t at,
+ bool *busy)
+{
+ BandwidthAllocation *bw_alloc = get_bw_alloc(bc, rcid, at);
+
+ /* for now we only preserve the current BC_BW_ALLOC register content */
+ *bw_alloc = bc->bw_allocations[0];
+ return BC_ALLOC_STATUS_SUCCESS;
+}
+
+static uint32_t bandwidth_read(RiscvCbqriBandwidthState *bc,
+ uint32_t rcid, uint32_t at,
+ bool *busy)
+{
+ BandwidthAllocation *bw_alloc = get_bw_alloc(bc, rcid, at);
+
+ /* populate BC_BW_ALLOC register with selected content */
+ bc->bw_allocations[0] = *bw_alloc;
+ return BC_ALLOC_STATUS_SUCCESS;
+}
+
+static bool is_valid_at(RiscvCbqriBandwidthState *bc, uint32_t at)
+{
+ switch (at) {
+ case BC_AT_DATA:
+ return bc->supports_at_data;
+ case BC_AT_CODE:
+ return bc->supports_at_code;
+ default:
+ return false;
+ }
+}
+
+static void riscv_cbqri_bc_write_mon_ctl(RiscvCbqriBandwidthState *bc,
+ uint64_t value)
+{
+ if (!bc->supports_mon_op_config_event &&
+ !bc->supports_mon_op_read_counter) {
+ /* monitoring not supported: leave mon_ctl set to 0 */
+ return;
+ }
+
+ /* extract writable fields */
+ uint32_t op = FIELD_EX64(value, BC_MON_CTL, OP);
+ uint32_t at = FIELD_EX64(value, BC_MON_CTL, AT);
+ uint32_t mcid = FIELD_EX64(value, BC_MON_CTL, MCID);
+ uint32_t evt_id = FIELD_EX64(value, BC_MON_CTL, EVT_ID);
+ bool atv = FIELD_EX64(value, BC_MON_CTL, ATV);
+
+ /* extract read-only fields */
+ uint32_t status = FIELD_EX64(bc->bc_mon_ctl, BC_MON_CTL, STATUS);
+ bool busy = FIELD_EX64(bc->bc_mon_ctl, BC_MON_CTL, BUSY);
+
+ if (busy) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: busy flag still set, ignored",
+ __func__);
+ return;
+ }
+
+ if (!bc->supports_at_data &&
+ !bc->supports_at_code) {
+ /* AT not supported: hardwire to 0 */
+ at = 0;
+ atv = false;
+ }
+
+ if (mcid >= bc->nb_mcids) {
+ status = BC_MON_CTL_STATUS_INVAL_MCID;
+ } else if (op == BC_MON_OP_CONFIG_EVENT &&
+ bc->supports_mon_op_config_event) {
+ if (evt_id == BC_EVT_ID_None &&
+ bc->supports_mon_evt_id_none) {
+ bc->mon_counters[mcid].active = false;
+ status = BC_MON_CTL_STATUS_SUCCESS;
+ } else if ((evt_id == BC_EVT_ID_RDWR_count &&
+ bc->supports_mon_evt_id_rdwr_count) ||
+ (evt_id == BC_EVT_ID_RDONLY_count &&
+ bc->supports_mon_evt_id_rdonly_count) ||
+ (evt_id == BC_EVT_ID_WRONLY_count &&
+ bc->supports_mon_evt_id_wronly_count)) {
+ if (atv && !is_valid_at(bc, at)) {
+ status = BC_MON_CTL_STATUS_INVAL_AT;
+ } else {
+ bc->mon_counters[mcid].ctr_val =
+ FIELD_DP64(0, BC_MON_CTR_VAL, INVALID, 1);
+ bc->mon_counters[mcid].evt_id = evt_id;
+ bc->mon_counters[mcid].at = atv ? at : -1;
+ bc->mon_counters[mcid].active = true;
+ status = BC_MON_CTL_STATUS_SUCCESS;
+ }
+ } else {
+ status = BC_MON_CTL_STATUS_INVAL_EVT_ID;
+ }
+ } else if (op == BC_MON_OP_READ_COUNTER &&
+ bc->supports_mon_op_read_counter) {
+ bc->bc_mon_ctr_val = bc->mon_counters[mcid].ctr_val;
+ status = BC_MON_CTL_STATUS_SUCCESS;
+ } else {
+ status = BC_MON_CTL_STATUS_INVAL_OP;
+ }
+
+ /* reconstruct updated register value */
+ value = 0;
+ value = FIELD_DP64(value, BC_MON_CTL, OP, op);
+ value = FIELD_DP64(value, BC_MON_CTL, AT, at);
+ value = FIELD_DP64(value, BC_MON_CTL, MCID, mcid);
+ value = FIELD_DP64(value, BC_MON_CTL, EVT_ID, evt_id);
+ value = FIELD_DP64(value, BC_MON_CTL, ATV, atv);
+ value = FIELD_DP64(value, BC_MON_CTL, STATUS, status);
+ value = FIELD_DP64(value, BC_MON_CTL, BUSY, busy);
+ bc->bc_mon_ctl = value;
+}
+
+static void riscv_cbqri_bc_write_alloc_ctl(RiscvCbqriBandwidthState *bc,
+ uint64_t value)
+{
+ if (bc->nbwblks == 0 ||
+ (!bc->supports_alloc_op_config_limit &&
+ !bc->supports_alloc_op_read_limit)) {
+ /* capacity allocation not supported: leave bc_alloc_ctl set to 0 */
+ return;
+ }
+
+ /* extract writable fields */
+ uint32_t op = FIELD_EX64(value, BC_ALLOC_CTL, OP);
+ uint32_t at = FIELD_EX64(value, BC_ALLOC_CTL, AT);
+ uint32_t rcid = FIELD_EX64(value, BC_ALLOC_CTL, RCID);
+
+ /* extract read-only fields */
+ uint32_t status = FIELD_EX64(bc->bc_alloc_ctl, BC_ALLOC_CTL, STATUS);
+ bool busy = FIELD_EX64(bc->bc_alloc_ctl, BC_ALLOC_CTL, BUSY);
+
+ if (busy) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: busy flag still set, ignored",
+ __func__);
+ return;
+ }
+
+ bool atv = true;
+ if (!bc->supports_at_data &&
+ !bc->supports_at_code) {
+ /* AT not supported: hardwire to 0 */
+ at = 0;
+ atv = false;
+ }
+
+ if (rcid >= bc->nb_rcids) {
+ status = BC_ALLOC_STATUS_INVAL_RCID;
+ } else if (atv && !is_valid_at(bc, at)) {
+ status = BC_ALLOC_STATUS_INVAL_AT;
+ } else if (op == BC_ALLOC_OP_CONFIG_LIMIT &&
+ bc->supports_alloc_op_config_limit) {
+ status = bandwidth_config(bc, rcid, at, &busy);
+ } else if (op == BC_ALLOC_OP_READ_LIMIT &&
+ bc->supports_alloc_op_read_limit) {
+ status = bandwidth_read(bc, rcid, at, &busy);
+ } else {
+ status = BC_ALLOC_STATUS_INVAL_OP;
+ }
+
+ /* reconstruct updated register value */
+ value = 0;
+ value = FIELD_DP64(value, BC_ALLOC_CTL, OP, op);
+ value = FIELD_DP64(value, BC_ALLOC_CTL, AT, at);
+ value = FIELD_DP64(value, BC_ALLOC_CTL, RCID, rcid);
+ value = FIELD_DP64(value, BC_ALLOC_CTL, STATUS, status);
+ value = FIELD_DP64(value, BC_ALLOC_CTL, BUSY, busy);
+ bc->bc_alloc_ctl = value;
+}
+
+static void riscv_cbqri_bc_write_bw_alloc(RiscvCbqriBandwidthState *bc,
+ uint64_t value)
+{
+ if (bc->nbwblks == 0) {
+ /* capacity allocation not supported: leave bw_alloc set to 0 */
+ return;
+ }
+
+ BandwidthAllocation *bc_bw_alloc = &bc->bw_allocations[0];
+
+ /* extract writable fields */
+ bc_bw_alloc->Rbwb = FIELD_EX64(value, BC_BW_ALLOC, Rbwb);
+ bc_bw_alloc->Mweight = FIELD_EX64(value, BC_BW_ALLOC, Mweight);
+ bc_bw_alloc->sharedAT = FIELD_EX64(value, BC_BW_ALLOC, sharedAT);
+ bc_bw_alloc->useShared = FIELD_EX64(value, BC_BW_ALLOC, useShared);
+
+ if (!bc->supports_at_data &&
+ !bc->supports_at_code) {
+ /* AT not supported: hardwire to 0 */
+ bc_bw_alloc->sharedAT = 0;
+ bc_bw_alloc->useShared = false;
+ }
+}
+
+static void riscv_cbqri_bc_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ RiscvCbqriBandwidthState *bc = opaque;
+
+ assert((addr % 8) == 0);
+ assert(size == 8);
+
+ switch (addr) {
+ case A_BC_CAPABILITIES:
+ /* read-only register */
+ break;
+ case A_BC_MON_CTL:
+ riscv_cbqri_bc_write_mon_ctl(bc, value);
+ break;
+ case A_BC_MON_CTR_VAL:
+ /* read-only register */
+ break;
+ case A_BC_ALLOC_CTL:
+ riscv_cbqri_bc_write_alloc_ctl(bc, value);
+ break;
+ case A_BC_BW_ALLOC:
+ riscv_cbqri_bc_write_bw_alloc(bc, value);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: out of bounds (addr=0x%x)",
+ __func__, (uint32_t)addr);
+ }
+}
+
+static uint64_t riscv_cbqri_bc_read(void *opaque, hwaddr addr, unsigned size)
+{
+ RiscvCbqriBandwidthState *bc = opaque;
+ uint64_t value = 0;
+
+ assert((addr % 8) == 0);
+ assert(size == 8);
+
+ switch (addr) {
+ case A_BC_CAPABILITIES:
+ value = FIELD_DP64(value, BC_CAPABILITIES, VER_MAJOR,
+ RISCV_CBQRI_VERSION_MAJOR);
+ value = FIELD_DP64(value, BC_CAPABILITIES, VER_MINOR,
+ RISCV_CBQRI_VERSION_MINOR);
+ value = FIELD_DP64(value, BC_CAPABILITIES, RPFX,
+ bc->rpfx);
+ value = FIELD_DP64(value, BC_CAPABILITIES, P,
+ bc->p);
+ value = FIELD_DP64(value, BC_CAPABILITIES, NBWBLKS,
+ bc->nbwblks);
+ value = FIELD_DP64(value, BC_CAPABILITIES, MRBWB,
+ bc->mrbwb);
+ break;
+ case A_BC_MON_CTL:
+ value = bc->bc_mon_ctl;
+ break;
+ case A_BC_MON_CTR_VAL:
+ value = bc->bc_mon_ctr_val;
+ break;
+ case A_BC_ALLOC_CTL:
+ value = bc->bc_alloc_ctl;
+ break;
+ case A_BC_BW_ALLOC:
+ BandwidthAllocation *bc_bw_alloc = &bc->bw_allocations[0];
+ value = FIELD_DP64(value, BC_BW_ALLOC, Rbwb, bc_bw_alloc->Rbwb);
+ value = FIELD_DP64(value, BC_BW_ALLOC, Mweight, bc_bw_alloc->Mweight);
+ value = FIELD_DP64(value, BC_BW_ALLOC, sharedAT, bc_bw_alloc->sharedAT);
+ value = FIELD_DP64(value, BC_BW_ALLOC, useShared,
+ bc_bw_alloc->useShared);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: out of bounds (addr=0x%x)",
+ __func__, (uint32_t)addr);
+ }
+
+ return value;
+}
+
+static const MemoryRegionOps riscv_cbqri_bc_ops = {
+ .read = riscv_cbqri_bc_read,
+ .write = riscv_cbqri_bc_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 8,
+ .impl.min_access_size = 8,
+ .impl.max_access_size = 8,
+};
+
+static void riscv_cbqri_bc_realize(DeviceState *dev, Error **errp)
+{
+ RiscvCbqriBandwidthState *bc = RISCV_CBQRI_BC(dev);
+
+ if (!bc->mmio_base) {
+ error_setg(errp, "mmio_base property not set");
+ return;
+ }
+
+ assert(bc->mon_counters == NULL);
+ bc->mon_counters = g_new0(MonitorCounter, bc->nb_mcids);
+
+ assert(bc->bw_allocations == NULL);
+ BandwidthAllocation *bw_alloc_end = get_bw_alloc(bc, bc->nb_rcids, 0);
+ unsigned int bw_alloc_size = bw_alloc_end - bc->bw_allocations;
+ bc->bw_allocations = g_new0(BandwidthAllocation, bw_alloc_size);
+
+ memory_region_init_io(&bc->mmio, OBJECT(dev), &riscv_cbqri_bc_ops,
+ bc, TYPE_RISCV_CBQRI_BC".mmio", 4 * 1024);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &bc->mmio);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, bc->mmio_base);
+}
+
+static void riscv_cbqri_bc_reset(DeviceState *dev)
+{
+ RiscvCbqriBandwidthState *bc = RISCV_CBQRI_BC(dev);
+
+ bc->bc_mon_ctl = 0;
+ bc->bc_alloc_ctl = 0;
+}
+
+static Property riscv_cbqri_bc_properties[] = {
+ DEFINE_PROP_UINT64("mmio_base", RiscvCbqriBandwidthState, mmio_base, 0),
+ DEFINE_PROP_STRING("target", RiscvCbqriBandwidthState, target),
+
+ DEFINE_PROP_UINT16("max_mcids", RiscvCbqriBandwidthState, nb_mcids, 256),
+ DEFINE_PROP_UINT16("max_rcids", RiscvCbqriBandwidthState, nb_rcids, 64),
+ DEFINE_PROP_UINT16("nbwblks", RiscvCbqriBandwidthState, nbwblks, 1024),
+ DEFINE_PROP_UINT16("mrbwb", RiscvCbqriBandwidthState, mrbwb, 819),
+
+ DEFINE_PROP_BOOL("rpfx", RiscvCbqriBandwidthState, rpfx, true),
+ DEFINE_PROP_UINT8("p", RiscvCbqriBandwidthState, p, 4),
+
+ DEFINE_PROP_BOOL("at_data", RiscvCbqriBandwidthState,
+ supports_at_data, true),
+ DEFINE_PROP_BOOL("at_code", RiscvCbqriBandwidthState,
+ supports_at_code, true),
+
+ DEFINE_PROP_BOOL("alloc_op_config_limit", RiscvCbqriBandwidthState,
+ supports_alloc_op_config_limit, true),
+ DEFINE_PROP_BOOL("alloc_op_read_limit", RiscvCbqriBandwidthState,
+ supports_alloc_op_read_limit, true),
+
+ DEFINE_PROP_BOOL("mon_op_config_event", RiscvCbqriBandwidthState,
+ supports_mon_op_config_event, true),
+ DEFINE_PROP_BOOL("mon_op_read_counter", RiscvCbqriBandwidthState,
+ supports_mon_op_read_counter, true),
+
+ DEFINE_PROP_BOOL("mon_evt_id_none", RiscvCbqriBandwidthState,
+ supports_mon_evt_id_none, true),
+ DEFINE_PROP_BOOL("mon_evt_id_rdwr_count", RiscvCbqriBandwidthState,
+ supports_mon_evt_id_rdwr_count, true),
+ DEFINE_PROP_BOOL("mon_evt_id_rdonly_count", RiscvCbqriBandwidthState,
+ supports_mon_evt_id_rdonly_count, true),
+ DEFINE_PROP_BOOL("mon_evt_id_wronly_count", RiscvCbqriBandwidthState,
+ supports_mon_evt_id_wronly_count, true),
+};
+
+static void riscv_cbqri_bc_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->realize = riscv_cbqri_bc_realize;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->desc = "RISC-V CBQRI Bandwidth Controller";
+ device_class_set_props(dc, riscv_cbqri_bc_properties);
+ dc->legacy_reset = riscv_cbqri_bc_reset;
+ dc->user_creatable = true;
+}
+
+static const TypeInfo riscv_cbqri_bc_info = {
+ .name = TYPE_RISCV_CBQRI_BC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RiscvCbqriBandwidthState),
+ .class_init = riscv_cbqri_bc_class_init,
+};
+
+static void riscv_cbqri_bc_register_types(void)
+{
+ type_register_static(&riscv_cbqri_bc_info);
+}
+
+DeviceState *riscv_cbqri_bc_create(hwaddr addr,
+ const RiscvCbqriBandwidthCaps *caps,
+ const char *target_name)
+{
+ DeviceState *dev = qdev_new(TYPE_RISCV_CBQRI_BC);
+
+ qdev_prop_set_uint64(dev, "mmio_base", addr);
+ qdev_prop_set_string(dev, "target", target_name);
+ qdev_prop_set_uint16(dev, "max_mcids", caps->nb_mcids);
+ qdev_prop_set_uint16(dev, "max_rcids", caps->nb_rcids);
+ qdev_prop_set_uint16(dev, "nbwblks", caps->nbwblks);
+ qdev_prop_set_bit(dev, "rpfx", caps->rpfx);
+ qdev_prop_set_uint8(dev, "p", caps->p);
+
+ qdev_prop_set_bit(dev, "at_data",
+ caps->supports_at_data);
+ qdev_prop_set_bit(dev, "at_code",
+ caps->supports_at_code);
+ qdev_prop_set_bit(dev, "alloc_op_config_limit",
+ caps->supports_alloc_op_config_limit);
+ qdev_prop_set_bit(dev, "alloc_op_read_limit",
+ caps->supports_alloc_op_read_limit);
+ qdev_prop_set_bit(dev, "mon_op_config_event",
+ caps->supports_mon_op_config_event);
+ qdev_prop_set_bit(dev, "mon_op_read_counter",
+ caps->supports_mon_op_read_counter);
+ qdev_prop_set_bit(dev, "mon_evt_id_none",
+ caps->supports_mon_evt_id_none);
+ qdev_prop_set_bit(dev, "mon_evt_id_rdwr_count",
+ caps->supports_mon_evt_id_rdwr_count);
+ qdev_prop_set_bit(dev, "mon_evt_id_rdonly_count",
+ caps->supports_mon_evt_id_rdonly_count);
+ qdev_prop_set_bit(dev, "mon_evt_id_wronly_count",
+ caps->supports_mon_evt_id_wronly_count);
+
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ return dev;
+}
+
+type_init(riscv_cbqri_bc_register_types)
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 4/7] hw/riscv: implement CBQRI bandwidth controller
2025-11-20 0:42 ` [PATCH 4/7] hw/riscv: implement CBQRI bandwidth controller Drew Fustini
@ 2025-11-20 11:51 ` Daniel Henrique Barboza
0 siblings, 0 replies; 21+ messages in thread
From: Daniel Henrique Barboza @ 2025-11-20 11:51 UTC (permalink / raw)
To: Drew Fustini, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei,
qemu-riscv, Paolo Bonzini, Nicolas Pitre, Kornel Dulęba,
Atish Kumar Patra, Atish Patra, Vasudevan Srinivasan,
Radim Krčmář, yunhui cui, Chen Pei, guo.wenjia23,
liu.qingtao2
On 11/19/25 9:42 PM, Drew Fustini wrote:
> From: Nicolas Pitre <npitre@baylibre.com>
>
> Implement a bandwidth controller according to the Capacity and Bandwidth
> QoS Register Interface (CBQRI) which supports these capabilities:
>
> - Number of access types: 2 (code and data)
> - Usage monitoring operations: CONFIG_EVENT, READ_COUNTER
> - Event IDs supported: None, Total read/write byte count, Total
> read byte count, Total write byte count
> - Bandwidth allocation operations: CONFIG_LIMIT, READ_LIMIT
>
> Link: https://github.com/riscv-non-isa/riscv-cbqri/blob/main/riscv-cbqri.pdf
> Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
> [fustini: add fields introduced in the ratified spec: rpfx and p]
> Signed-off-by: Drew Fustini <fustini@kernel.org>
> ---
> MAINTAINERS | 1 +
> hw/riscv/cbqri_bandwidth.c | 613 +++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 614 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 48cca4ac8702..16500c4e8a83 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -362,6 +362,7 @@ M: Nicolas Pitre <npitre@baylibre.com>
> M: Drew Fustini <fustini@kernel.org>
> L: qemu-riscv@nongnu.org
> S: Supported
> +F: hw/riscv/cbqri_bandwidth.c
> F: hw/riscv/cbqri_capacity.c
> F: include/hw/riscv/cbqri.h
>
> diff --git a/hw/riscv/cbqri_bandwidth.c b/hw/riscv/cbqri_bandwidth.c
> new file mode 100644
> index 000000000000..40357a3219d8
> --- /dev/null
> +++ b/hw/riscv/cbqri_bandwidth.c
> @@ -0,0 +1,613 @@
> +/*
> + * RISC-V Capacity and Bandwidth QoS Register Interface
> + * URL: https://github.com/riscv-non-isa/riscv-cbqri/releases/tag/v1.0
> + *
> + * Copyright (c) 2023 BayLibre SAS
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * This file contains the Bandwidth-controller QoS Register Interface.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/error-report.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/sysbus.h"
> +#include "target/riscv/cpu.h"
> +#include "hw/riscv/cbqri.h"
> +
[...]
> +static void riscv_cbqri_bc_write(void *opaque, hwaddr addr,
> + uint64_t value, unsigned size)
> +{
> + RiscvCbqriBandwidthState *bc = opaque;
> +
> + assert((addr % 8) == 0);
> + assert(size == 8);
Same comment I made on patch 3: both callbacks are asserting if size == 8. This can be
avoided by setting the minimal access size in riscv_cbqri_bc_ops to 8.
Thanks,
Daniel
> +
> + switch (addr) {
> + case A_BC_CAPABILITIES:
> + /* read-only register */
> + break;
> + case A_BC_MON_CTL:
> + riscv_cbqri_bc_write_mon_ctl(bc, value);
> + break;
> + case A_BC_MON_CTR_VAL:
> + /* read-only register */
> + break;
> + case A_BC_ALLOC_CTL:
> + riscv_cbqri_bc_write_alloc_ctl(bc, value);
> + break;
> + case A_BC_BW_ALLOC:
> + riscv_cbqri_bc_write_bw_alloc(bc, value);
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: out of bounds (addr=0x%x)",
> + __func__, (uint32_t)addr);
> + }
> +}
> +
> +static uint64_t riscv_cbqri_bc_read(void *opaque, hwaddr addr, unsigned size)
> +{
> + RiscvCbqriBandwidthState *bc = opaque;
> + uint64_t value = 0;
> +
> + assert((addr % 8) == 0);
> + assert(size == 8);
> +
> + switch (addr) {
> + case A_BC_CAPABILITIES:
> + value = FIELD_DP64(value, BC_CAPABILITIES, VER_MAJOR,
> + RISCV_CBQRI_VERSION_MAJOR);
> + value = FIELD_DP64(value, BC_CAPABILITIES, VER_MINOR,
> + RISCV_CBQRI_VERSION_MINOR);
> + value = FIELD_DP64(value, BC_CAPABILITIES, RPFX,
> + bc->rpfx);
> + value = FIELD_DP64(value, BC_CAPABILITIES, P,
> + bc->p);
> + value = FIELD_DP64(value, BC_CAPABILITIES, NBWBLKS,
> + bc->nbwblks);
> + value = FIELD_DP64(value, BC_CAPABILITIES, MRBWB,
> + bc->mrbwb);
> + break;
> + case A_BC_MON_CTL:
> + value = bc->bc_mon_ctl;
> + break;
> + case A_BC_MON_CTR_VAL:
> + value = bc->bc_mon_ctr_val;
> + break;
> + case A_BC_ALLOC_CTL:
> + value = bc->bc_alloc_ctl;
> + break;
> + case A_BC_BW_ALLOC:
> + BandwidthAllocation *bc_bw_alloc = &bc->bw_allocations[0];
> + value = FIELD_DP64(value, BC_BW_ALLOC, Rbwb, bc_bw_alloc->Rbwb);
> + value = FIELD_DP64(value, BC_BW_ALLOC, Mweight, bc_bw_alloc->Mweight);
> + value = FIELD_DP64(value, BC_BW_ALLOC, sharedAT, bc_bw_alloc->sharedAT);
> + value = FIELD_DP64(value, BC_BW_ALLOC, useShared,
> + bc_bw_alloc->useShared);
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: out of bounds (addr=0x%x)",
> + __func__, (uint32_t)addr);
> + }
> +
> + return value;
> +}
> +
> +static const MemoryRegionOps riscv_cbqri_bc_ops = {
> + .read = riscv_cbqri_bc_read,
> + .write = riscv_cbqri_bc_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> + .valid.min_access_size = 4,
> + .valid.max_access_size = 8,
> + .impl.min_access_size = 8,
> + .impl.max_access_size = 8,
> +};
> +
> +static void riscv_cbqri_bc_realize(DeviceState *dev, Error **errp)
> +{
> + RiscvCbqriBandwidthState *bc = RISCV_CBQRI_BC(dev);
> +
> + if (!bc->mmio_base) {
> + error_setg(errp, "mmio_base property not set");
> + return;
> + }
> +
> + assert(bc->mon_counters == NULL);
> + bc->mon_counters = g_new0(MonitorCounter, bc->nb_mcids);
> +
> + assert(bc->bw_allocations == NULL);
> + BandwidthAllocation *bw_alloc_end = get_bw_alloc(bc, bc->nb_rcids, 0);
> + unsigned int bw_alloc_size = bw_alloc_end - bc->bw_allocations;
> + bc->bw_allocations = g_new0(BandwidthAllocation, bw_alloc_size);
> +
> + memory_region_init_io(&bc->mmio, OBJECT(dev), &riscv_cbqri_bc_ops,
> + bc, TYPE_RISCV_CBQRI_BC".mmio", 4 * 1024);
> + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &bc->mmio);
> + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, bc->mmio_base);
> +}
> +
> +static void riscv_cbqri_bc_reset(DeviceState *dev)
> +{
> + RiscvCbqriBandwidthState *bc = RISCV_CBQRI_BC(dev);
> +
> + bc->bc_mon_ctl = 0;
> + bc->bc_alloc_ctl = 0;
> +}
> +
> +static Property riscv_cbqri_bc_properties[] = {
> + DEFINE_PROP_UINT64("mmio_base", RiscvCbqriBandwidthState, mmio_base, 0),
> + DEFINE_PROP_STRING("target", RiscvCbqriBandwidthState, target),
> +
> + DEFINE_PROP_UINT16("max_mcids", RiscvCbqriBandwidthState, nb_mcids, 256),
> + DEFINE_PROP_UINT16("max_rcids", RiscvCbqriBandwidthState, nb_rcids, 64),
> + DEFINE_PROP_UINT16("nbwblks", RiscvCbqriBandwidthState, nbwblks, 1024),
> + DEFINE_PROP_UINT16("mrbwb", RiscvCbqriBandwidthState, mrbwb, 819),
> +
> + DEFINE_PROP_BOOL("rpfx", RiscvCbqriBandwidthState, rpfx, true),
> + DEFINE_PROP_UINT8("p", RiscvCbqriBandwidthState, p, 4),
> +
> + DEFINE_PROP_BOOL("at_data", RiscvCbqriBandwidthState,
> + supports_at_data, true),
> + DEFINE_PROP_BOOL("at_code", RiscvCbqriBandwidthState,
> + supports_at_code, true),
> +
> + DEFINE_PROP_BOOL("alloc_op_config_limit", RiscvCbqriBandwidthState,
> + supports_alloc_op_config_limit, true),
> + DEFINE_PROP_BOOL("alloc_op_read_limit", RiscvCbqriBandwidthState,
> + supports_alloc_op_read_limit, true),
> +
> + DEFINE_PROP_BOOL("mon_op_config_event", RiscvCbqriBandwidthState,
> + supports_mon_op_config_event, true),
> + DEFINE_PROP_BOOL("mon_op_read_counter", RiscvCbqriBandwidthState,
> + supports_mon_op_read_counter, true),
> +
> + DEFINE_PROP_BOOL("mon_evt_id_none", RiscvCbqriBandwidthState,
> + supports_mon_evt_id_none, true),
> + DEFINE_PROP_BOOL("mon_evt_id_rdwr_count", RiscvCbqriBandwidthState,
> + supports_mon_evt_id_rdwr_count, true),
> + DEFINE_PROP_BOOL("mon_evt_id_rdonly_count", RiscvCbqriBandwidthState,
> + supports_mon_evt_id_rdonly_count, true),
> + DEFINE_PROP_BOOL("mon_evt_id_wronly_count", RiscvCbqriBandwidthState,
> + supports_mon_evt_id_wronly_count, true),
> +};
> +
> +static void riscv_cbqri_bc_class_init(ObjectClass *klass, const void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + dc->realize = riscv_cbqri_bc_realize;
> + set_bit(DEVICE_CATEGORY_MISC, dc->categories);
> + dc->desc = "RISC-V CBQRI Bandwidth Controller";
> + device_class_set_props(dc, riscv_cbqri_bc_properties);
> + dc->legacy_reset = riscv_cbqri_bc_reset;
> + dc->user_creatable = true;
> +}
> +
> +static const TypeInfo riscv_cbqri_bc_info = {
> + .name = TYPE_RISCV_CBQRI_BC,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(RiscvCbqriBandwidthState),
> + .class_init = riscv_cbqri_bc_class_init,
> +};
> +
> +static void riscv_cbqri_bc_register_types(void)
> +{
> + type_register_static(&riscv_cbqri_bc_info);
> +}
> +
> +DeviceState *riscv_cbqri_bc_create(hwaddr addr,
> + const RiscvCbqriBandwidthCaps *caps,
> + const char *target_name)
> +{
> + DeviceState *dev = qdev_new(TYPE_RISCV_CBQRI_BC);
> +
> + qdev_prop_set_uint64(dev, "mmio_base", addr);
> + qdev_prop_set_string(dev, "target", target_name);
> + qdev_prop_set_uint16(dev, "max_mcids", caps->nb_mcids);
> + qdev_prop_set_uint16(dev, "max_rcids", caps->nb_rcids);
> + qdev_prop_set_uint16(dev, "nbwblks", caps->nbwblks);
> + qdev_prop_set_bit(dev, "rpfx", caps->rpfx);
> + qdev_prop_set_uint8(dev, "p", caps->p);
> +
> + qdev_prop_set_bit(dev, "at_data",
> + caps->supports_at_data);
> + qdev_prop_set_bit(dev, "at_code",
> + caps->supports_at_code);
> + qdev_prop_set_bit(dev, "alloc_op_config_limit",
> + caps->supports_alloc_op_config_limit);
> + qdev_prop_set_bit(dev, "alloc_op_read_limit",
> + caps->supports_alloc_op_read_limit);
> + qdev_prop_set_bit(dev, "mon_op_config_event",
> + caps->supports_mon_op_config_event);
> + qdev_prop_set_bit(dev, "mon_op_read_counter",
> + caps->supports_mon_op_read_counter);
> + qdev_prop_set_bit(dev, "mon_evt_id_none",
> + caps->supports_mon_evt_id_none);
> + qdev_prop_set_bit(dev, "mon_evt_id_rdwr_count",
> + caps->supports_mon_evt_id_rdwr_count);
> + qdev_prop_set_bit(dev, "mon_evt_id_rdonly_count",
> + caps->supports_mon_evt_id_rdonly_count);
> + qdev_prop_set_bit(dev, "mon_evt_id_wronly_count",
> + caps->supports_mon_evt_id_wronly_count);
> +
> + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +
> + return dev;
> +}
> +
> +type_init(riscv_cbqri_bc_register_types)
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 5/7] hw/riscv: Kconfig: add CBQRI options
2025-11-20 0:42 [PATCH 0/7] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
` (3 preceding siblings ...)
2025-11-20 0:42 ` [PATCH 4/7] hw/riscv: implement CBQRI bandwidth controller Drew Fustini
@ 2025-11-20 0:42 ` Drew Fustini
2025-11-20 0:42 ` [PATCH 6/7] hw/riscv: meson: add CBQRI controllers to the build Drew Fustini
2025-11-20 0:42 ` [PATCH 7/7] hw/riscv: add CBQRI controllers to virt machine Drew Fustini
6 siblings, 0 replies; 21+ messages in thread
From: Drew Fustini @ 2025-11-20 0:42 UTC (permalink / raw)
To: qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, qemu-riscv, Paolo Bonzini,
Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, Radim Krčmář, yunhui cui,
Chen Pei, guo.wenjia23, liu.qingtao2, Drew Fustini
From: Nicolas Pitre <npitre@baylibre.com>
Add boolean property for CBQRI and imply it should be enabled for the
RISC-V virt machine.
Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
Signed-off-by: Drew Fustini <fustini@kernel.org>
---
hw/riscv/Kconfig | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index fc9c35bd981e..663cb78b813c 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -1,3 +1,6 @@
+config RISCV_CBQRI
+ bool
+
config RISCV_IOMMU
bool
@@ -68,6 +71,7 @@ config RISCV_VIRT
select PLATFORM_BUS
select ACPI
select ACPI_PCI
+ imply RISCV_CBQRI
config SHAKTI_C
bool
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH 6/7] hw/riscv: meson: add CBQRI controllers to the build
2025-11-20 0:42 [PATCH 0/7] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
` (4 preceding siblings ...)
2025-11-20 0:42 ` [PATCH 5/7] hw/riscv: Kconfig: add CBQRI options Drew Fustini
@ 2025-11-20 0:42 ` Drew Fustini
2025-11-20 12:01 ` Daniel Henrique Barboza
2025-11-20 0:42 ` [PATCH 7/7] hw/riscv: add CBQRI controllers to virt machine Drew Fustini
6 siblings, 1 reply; 21+ messages in thread
From: Drew Fustini @ 2025-11-20 0:42 UTC (permalink / raw)
To: qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, qemu-riscv, Paolo Bonzini,
Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, Radim Krčmář, yunhui cui,
Chen Pei, guo.wenjia23, liu.qingtao2, Drew Fustini
From: Nicolas Pitre <npitre@baylibre.com>
Build the CBQRI controllers when RISC-V CBQRI is enabled by Kconfig.
Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
Signed-off-by: Drew Fustini <fustini@kernel.org>
---
hw/riscv/meson.build | 1 +
1 file changed, 1 insertion(+)
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index 2a8d5b136cc4..79e15514b797 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -14,5 +14,6 @@ riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files(
'riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c', 'riscv-iommu-hpm.c'))
riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c'))
riscv_ss.add(when: 'CONFIG_XIANGSHAN_KUNMINGHU', if_true: files('xiangshan_kmh.c'))
+riscv_ss.add(when: 'CONFIG_RISCV_CBQRI', if_true: files('cbqri_capacity.c', 'cbqri_bandwidth.c'))
hw_arch += {'riscv': riscv_ss}
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 6/7] hw/riscv: meson: add CBQRI controllers to the build
2025-11-20 0:42 ` [PATCH 6/7] hw/riscv: meson: add CBQRI controllers to the build Drew Fustini
@ 2025-11-20 12:01 ` Daniel Henrique Barboza
2025-11-20 20:56 ` Drew Fustini
0 siblings, 1 reply; 21+ messages in thread
From: Daniel Henrique Barboza @ 2025-11-20 12:01 UTC (permalink / raw)
To: Drew Fustini, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei,
qemu-riscv, Paolo Bonzini, Nicolas Pitre, Kornel Dulęba,
Atish Kumar Patra, Atish Patra, Vasudevan Srinivasan,
Radim Krčmář, yunhui cui, Chen Pei, guo.wenjia23,
liu.qingtao2
Hi,
I think this patch and patch 5 should be squashed together. Both LGTM otherwise.
Thanks,
Daniel
On 11/19/25 9:42 PM, Drew Fustini wrote:
> From: Nicolas Pitre <npitre@baylibre.com>
>
> Build the CBQRI controllers when RISC-V CBQRI is enabled by Kconfig.
>
> Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
> Signed-off-by: Drew Fustini <fustini@kernel.org>
> ---
> hw/riscv/meson.build | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
> index 2a8d5b136cc4..79e15514b797 100644
> --- a/hw/riscv/meson.build
> +++ b/hw/riscv/meson.build
> @@ -14,5 +14,6 @@ riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files(
> 'riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c', 'riscv-iommu-hpm.c'))
> riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c'))
> riscv_ss.add(when: 'CONFIG_XIANGSHAN_KUNMINGHU', if_true: files('xiangshan_kmh.c'))
> +riscv_ss.add(when: 'CONFIG_RISCV_CBQRI', if_true: files('cbqri_capacity.c', 'cbqri_bandwidth.c'))
>
> hw_arch += {'riscv': riscv_ss}
>
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 6/7] hw/riscv: meson: add CBQRI controllers to the build
2025-11-20 12:01 ` Daniel Henrique Barboza
@ 2025-11-20 20:56 ` Drew Fustini
0 siblings, 0 replies; 21+ messages in thread
From: Drew Fustini @ 2025-11-20 20:56 UTC (permalink / raw)
To: Daniel Henrique Barboza
Cc: qemu-devel, Palmer Dabbelt, Alistair Francis, Weiwei Li,
Liu Zhiwei, qemu-riscv, Paolo Bonzini, Nicolas Pitre,
Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, Radim Krčmář, yunhui cui,
Chen Pei, guo.wenjia23, liu.qingtao2
On Thu, Nov 20, 2025 at 09:01:03AM -0300, Daniel Henrique Barboza wrote:
> Hi,
>
> I think this patch and patch 5 should be squashed together. Both LGTM otherwise.
Thanks for the feedback. I will squash them for the next revision.
Drew
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 7/7] hw/riscv: add CBQRI controllers to virt machine
2025-11-20 0:42 [PATCH 0/7] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
` (5 preceding siblings ...)
2025-11-20 0:42 ` [PATCH 6/7] hw/riscv: meson: add CBQRI controllers to the build Drew Fustini
@ 2025-11-20 0:42 ` Drew Fustini
2025-11-20 12:01 ` Daniel Henrique Barboza
6 siblings, 1 reply; 21+ messages in thread
From: Drew Fustini @ 2025-11-20 0:42 UTC (permalink / raw)
To: qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, qemu-riscv, Paolo Bonzini,
Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
Vasudevan Srinivasan, Radim Krčmář, yunhui cui,
Chen Pei, guo.wenjia23, liu.qingtao2, Drew Fustini
From: Nicolas Pitre <npitre@baylibre.com>
Add CBQRI controllers to the RISC-V virt machine. The device properties
can be fully configured from the command line:
$ qemu-system-riscv64 -M virt ... \
-device riscv.cbqri.capacity,mmio_base=0x04828000[,...]
-device riscv.cbqri.bandwidth,mmio_base=0x04829000[,...]
The mmio_base option is mandatory, the others are optional.
Many -device arguments as wanted can be provided as long as their
mmio regions don't conflict.
To see all possible options:
$ qemu-system-riscv64 -device riscv.cbqri.capacity,help
riscv.cbqri.capacity options:
alloc_op_config_limit=<bool> - (default: true)
alloc_op_flush_rcid=<bool> - (default: true)
alloc_op_read_limit=<bool> - (default: true)
at_code=<bool> - (default: true)
at_data=<bool> - (default: true)
max_mcids=<uint16> - (default: 256)
max_rcids=<uint16> - (default: 64)
mmio_base=<uint64> - (default: 0)
mon_evt_id_none=<bool> - (default: true)
mon_evt_id_occupancy=<bool> - (default: true)
mon_op_config_event=<bool> - (default: true)
mon_op_read_counter=<bool> - (default: true)
ncblks=<uint16> - (default: 16)
target=<str>
$ qemu-system-riscv64 -device riscv.cbqri.bandwidth,help
riscv.cbqri.bandwidth options:
alloc_op_config_limit=<bool> - (default: true)
alloc_op_read_limit=<bool> - (default: true)
at_code=<bool> - (default: true)
at_data=<bool> - (default: true)
max_mcids=<uint16> - (default: 256)
max_rcids=<uint16> - (default: 64)
mmio_base=<uint64> - (default: 0)
mon_evt_id_none=<bool> - (default: true)
mon_evt_id_rdonly_count=<bool> - (default: true)
mon_evt_id_rdwr_count=<bool> - (default: true)
mon_evt_id_wronly_count=<bool> - (default: true)
mon_op_config_event=<bool> - (default: true)
mon_op_read_counter=<bool> - (default: true)
nbwblks=<uint16> - (default: 1024)
target=<str>
Boolean options correspond to hardware capabilities that can be disabled
Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
Signed-off-by: Drew Fustini <fustini@kernel.org>
---
hw/riscv/virt.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 17909206c7ef..498f606d33b1 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -55,6 +55,7 @@
#include "hw/pci-host/gpex.h"
#include "hw/display/ramfb.h"
#include "hw/acpi/aml-build.h"
+#include "hw/riscv/cbqri.h"
#include "qapi/qapi-visit-common.h"
#include "hw/virtio/virtio-iommu.h"
#include "hw/uefi/var-service-api.h"
@@ -1941,6 +1942,8 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data)
#ifdef CONFIG_TPM
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS);
#endif
+ machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RISCV_CBQRI_BC);
+ machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RISCV_CBQRI_CC);
object_class_property_add_bool(oc, "aclint", virt_get_aclint,
virt_set_aclint);
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 7/7] hw/riscv: add CBQRI controllers to virt machine
2025-11-20 0:42 ` [PATCH 7/7] hw/riscv: add CBQRI controllers to virt machine Drew Fustini
@ 2025-11-20 12:01 ` Daniel Henrique Barboza
0 siblings, 0 replies; 21+ messages in thread
From: Daniel Henrique Barboza @ 2025-11-20 12:01 UTC (permalink / raw)
To: Drew Fustini, qemu-devel
Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei,
qemu-riscv, Paolo Bonzini, Nicolas Pitre, Kornel Dulęba,
Atish Kumar Patra, Atish Patra, Vasudevan Srinivasan,
Radim Krčmář, yunhui cui, Chen Pei, guo.wenjia23,
liu.qingtao2
On 11/19/25 9:42 PM, Drew Fustini wrote:
> From: Nicolas Pitre <npitre@baylibre.com>
>
> Add CBQRI controllers to the RISC-V virt machine. The device properties
> can be fully configured from the command line:
>
> $ qemu-system-riscv64 -M virt ... \
> -device riscv.cbqri.capacity,mmio_base=0x04828000[,...]
> -device riscv.cbqri.bandwidth,mmio_base=0x04829000[,...]
>
> The mmio_base option is mandatory, the others are optional.
>
> Many -device arguments as wanted can be provided as long as their
> mmio regions don't conflict.
>
> To see all possible options:
>
> $ qemu-system-riscv64 -device riscv.cbqri.capacity,help
> riscv.cbqri.capacity options:
> alloc_op_config_limit=<bool> - (default: true)
> alloc_op_flush_rcid=<bool> - (default: true)
> alloc_op_read_limit=<bool> - (default: true)
> at_code=<bool> - (default: true)
> at_data=<bool> - (default: true)
> max_mcids=<uint16> - (default: 256)
> max_rcids=<uint16> - (default: 64)
> mmio_base=<uint64> - (default: 0)
> mon_evt_id_none=<bool> - (default: true)
> mon_evt_id_occupancy=<bool> - (default: true)
> mon_op_config_event=<bool> - (default: true)
> mon_op_read_counter=<bool> - (default: true)
> ncblks=<uint16> - (default: 16)
> target=<str>
>
> $ qemu-system-riscv64 -device riscv.cbqri.bandwidth,help
> riscv.cbqri.bandwidth options:
> alloc_op_config_limit=<bool> - (default: true)
> alloc_op_read_limit=<bool> - (default: true)
> at_code=<bool> - (default: true)
> at_data=<bool> - (default: true)
> max_mcids=<uint16> - (default: 256)
> max_rcids=<uint16> - (default: 64)
> mmio_base=<uint64> - (default: 0)
> mon_evt_id_none=<bool> - (default: true)
> mon_evt_id_rdonly_count=<bool> - (default: true)
> mon_evt_id_rdwr_count=<bool> - (default: true)
> mon_evt_id_wronly_count=<bool> - (default: true)
> mon_op_config_event=<bool> - (default: true)
> mon_op_read_counter=<bool> - (default: true)
> nbwblks=<uint16> - (default: 1024)
> target=<str>
>
> Boolean options correspond to hardware capabilities that can be disabled
>
> Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
> Signed-off-by: Drew Fustini <fustini@kernel.org>
> ---
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
> hw/riscv/virt.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
> index 17909206c7ef..498f606d33b1 100644
> --- a/hw/riscv/virt.c
> +++ b/hw/riscv/virt.c
> @@ -55,6 +55,7 @@
> #include "hw/pci-host/gpex.h"
> #include "hw/display/ramfb.h"
> #include "hw/acpi/aml-build.h"
> +#include "hw/riscv/cbqri.h"
> #include "qapi/qapi-visit-common.h"
> #include "hw/virtio/virtio-iommu.h"
> #include "hw/uefi/var-service-api.h"
> @@ -1941,6 +1942,8 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data)
> #ifdef CONFIG_TPM
> machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS);
> #endif
> + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RISCV_CBQRI_BC);
> + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RISCV_CBQRI_CC);
>
> object_class_property_add_bool(oc, "aclint", virt_get_aclint,
> virt_set_aclint);
>
^ permalink raw reply [flat|nested] 21+ messages in thread