public inbox for qemu-devel@nongnu.org
 help / color / mirror / Atom feed
* [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers
@ 2026-01-05 21:54 Drew Fustini
  2026-01-05 21:54 ` [PATCH v4 1/6] riscv: implement Ssqosid extension and srmcfg CSR Drew Fustini
                   ` (6 more replies)
  0 siblings, 7 replies; 13+ messages in thread
From: Drew Fustini @ 2026-01-05 21:54 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-riscv, Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, 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

This series implements the RISC-V Quality-of-Service Identifiers
(Ssqosid) extension [1] which adds the srmcfg register. It also
implements the RISC-V Capacity and Bandwidth Controller QoS Register
Interface (CBQRI) specification [2]. Quality of Service (QoS) in this
context is concerned with shared resources on an SoC such as cache
capacity and memory bandwidth.

Sssqosid srmcfg CSR
-------------------
The srmcfg CSR configures a hart with two identifiers:

 - Resource Control ID (RCID)
 - Monitoring Counter ID (MCID)

These identifiers accompany each request issued by the hart to shared
resource controllers. This allows the capacity and bandwidth resources
used by a software workload (e.g. a process or a set of processes) to be
controlled and monitored.

CBQRI controllers
-----------------
CBQRI defines operations to configure resource usage limits, in the form
of capacity or bandwidth, for an RCID. CBQRI also defines operations to
configure counters to track the resource utilization per MCID.

This series implements an CBQRI capacity controller and an CBQRI
bandwidth controller which can be 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.
                           
As 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
 
Example SoC for CBQRI
---------------------
An example SoC with the following CBQRI controller configuration can be
used to test the implementation:

  - L2 cache controllers
    - Resource type: Capacity
    - Number of capacity blocks (NCBLKS): 12
    	- In the context of a set-associative cache, the number of
	  capacity blocks can be thought of as the number of ways
    - Number of access types: 2 (code and data)
    - Usage monitoring not supported
    - Capacity allocation operations: CONFIG_LIMIT, READ_LIMIT

  - Last-level cache (LLC) controller
    - Resource type: Capacity
    - Number of capacity blocks (NCBLKS): 16
    - 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

  - Memory controllers
    - Resource type: Bandwidth
    - Number of bandwidth blocks (NBWBLKS): 1024
       - Bandwidth blocks do not have a unit but instead represent a
         portion of the total bandwidth resource. For NWBLKS of 1024,
	 each block represents about 0.1% of the bandwidth resource.
    - Maximum reserved bandwidth blocks (MRBWB): 819 [80% of NBWBLKS]
    - Number of access types: 1 (no code/data differentiation)
    - 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

The memory map used for the example SoC:

  Base addr  Size
  0x4820000  4KB  Cluster 0 L2 cache controller
  0x4821000  4KB  Cluster 1 L2 cache controller
  0x4828000  4KB  Memory controller 0
  0x4829000  4KB  Memory controller 1
  0x482a000  4KB  Memory controller 2
  0x482b000  4KB  Shared LLC cache controller

This configuration is meant to provide a "concrete" example for software
(like Linux) to test against. It represents just one of many possible
ways for hardware to implement the CBQRI spec.

The example SoC configuration is created with the following:

  qemu-system-riscv64 \
        -M virt \
        -nographic \
        -smp 8 \
        -device riscv.cbqri.capacity,max_mcids=256,max_rcids=64,ncblks=12,alloc_op_flush_rcid=false,mon_op_config_event=false,mon_op_read_counter=false,mon_evt_id_none=false,mon_evt_id_occupancy=false,mmio_base=0x04820000 \
        -device riscv.cbqri.capacity,max_mcids=256,max_rcids=64,ncblks=12,alloc_op_flush_rcid=false,mon_op_config_event=false,mon_op_read_counter=false,mon_evt_id_none=false,mon_evt_id_occupancy=false,mmio_base=0x04821000 \
        -device riscv.cbqri.capacity,max_mcids=256,max_rcids=64,ncblks=16,mmio_base=0x0482B000 \
        -device riscv.cbqri.bandwidth,max_mcids=256,max_rcids=64,nbwblks=1024,mrbwb=819,mmio_base=0x04828000 \
        -device riscv.cbqri.bandwidth,max_mcids=256,max_rcids=64,nbwblks=1024,mrbwb=819,mmio_base=0x04829000 \
        -device riscv.cbqri.bandwidth,max_mcids=256,max_rcids=64,nbwblks=1024,mrbwb=819,mmio_base=0x0482a000

In addition, please note that this series only implements the register
interface that CBQRI specifies. It does not attempt to emulate the
performance impact of configuring limits on shared resources like cache
and memory bandwidth. Similarly, the code does not attempt to emulate
cache and memory bandwidth utilization, like what would be observed on a
real hardware system implementing CBQRI.

There is a branch of Linux [3] which adds support for Ssqosid and CBQRI
along with resctrl integration. I still need to do some more cleanup of
the code and intend to post the Linux patch series soon.

Open issue:
 - Support 32-bit operation as the spec states that "CBQRI registers are
   defined so that software can perform two individual 4 byte accesses."
   The MemoryRegionOps have .valid.min_access_size = 4 but the read and
   write hooks have assert(size == 8). I need to figure out how to
   correctly handle accesses of size 4.

Despite the open issue above, I am sending out this new revision as I
believe I have addressed all the other feedback.

Changes since v3:
 - NOTE: I should have used version v3 for the previous version posted
   on Nov 19, 2025 given there was already v1/v2 back in 2023. Therefore
   I am using v4 in this new revision.
 - Rebase on current master as of Dec 29 (942b0d378a1d) and update
   include paths for qdev-properties.h and sysbus.h
 - Add Rb tags from Daniel 
 - Squash the Kconfig and meson.build patches together per Daniel
 - Use riscv_cpu_cfg() instead of env_archcpu() in check_srmcfg()
 - Add check for mstateen0.SRMCFG in check_srmcfg()
 - Increase ISA_EXT_DATA_ENTRY() for ssqosid from PRIV_VERSION_1_12_0
   to PRIV_VERSION_1_13_0 as it was added in Privileged Arch 1.13
 - Calculate the size of the capacity controller MMIO space based on 
   on cc->ncblks and roundup to be aligned to the 4KB page boundary.
 - Remove rpfx, p and cunits properties that were added in the previous
   revision as I have decided to not support those optional features in
   this capacity controller implementation.
 - Update riscv_cbqri_cc_read to set 0 for RPFX, P and CUNITS in
   cc_capabilities. This indicates that this capacity controller
   implementation does not support RCID-prefixed mode and nor does it
   support capacity unit limits.
 - Add cc_cunits register after cc_block_mask register in the
   alloc_blockmasks array and adjust logic in get_blockmask_offset,
   riscv_cbqri_cc_read, riscv_cbqri_cc_write. Since capacity units is
   not supported, the cc_cunits register will always be 0.
 - Only assign capacity for rcid 0 in riscv_cbqri_cc_reset and do not
   touch allocation for other rcid values as they are unspecified
   following a reset.
 - Change CC_ALLOC_OP_FLUSH_RCID to be a no-op instead of calling
   alloc_blockmask_init() as the spec states that the configured
   capacity block allocation or the capacity unit limit is not
   changed by the flush operation.
 - Add comment to explain that, while the spec only requires the busy
   field to be reset to 0, the entire contents of the cc->cc_mon_ctl
   abd cc->cc_alloc_ctl registers are set to 0 simplify the code in
   riscv_cbqri_cc_reset().
 - Simplify the access type logic for blockmask initialization in
   riscv_cbqri_cc_reset().
 - Add static inline helpers get_bmw() and get_slots().
 - Add check to bandwidth_config() that rbwb is not greater than mrbwb.
 - Link to v3: https://lore.kernel.org/qemu-devel/20251119-riscv-ssqosid-cbqri-v1-0-3392fc760e48@kernel.org

Changes since v2:
 - NOTE: I should have used version v3 for this version posted on Nov 19
   as there were already v1 and v2 back in 2023.
 - Rebase on master which is currently at version v10.1.50
 - Add fields that were not in the draft used for the proof of concept
   and introduced in final CBQRI 1.0 spec: capacity units (cunits) and
   RCID-prefixed mode (RPFX) along with parameter P (prefixed bits)
 - Fix check_srmcfg() to check if virtualization is enabled, and if so,
   return virt instruction fault, otherwise return smode()
 - Fix indentation in read_srmcfg()
 - Add SPDX headers
 - Add MAINTAINERS enteries
 - Link to v2: https://lore.kernel.org/qemu-devel/20230425203834.1135306-1-dfustini@baylibre.com/ 

Changes since v1:
 - Rebase on current master (8.0.50) instead of 8.0.0-rc4
 - Configure CBQRI controllers based on device properties in command
   line arguments instead of a fixed example SoC configuration.
 - Move TYPE_RISCV_CBQRI_BC and TYPE_RISCV_CBQRI_CC into header so that
   machines may use it (suggested by Alistair).
 - Change 'select RISC_CBQRI' to 'imply RISCV_CBQRI' for RISCV_VIRT.
 - Patches 8/9 could be dropped as they are not needed for the CBQRI
   proof-of-concept to work. They are only meant to serve as an example
   for those implementing new machines.
 - Link to v1: https://lore.kernel.org/qemu-devel/20230416232050.4094820-1-dfustini@baylibre.com/

[1] https://github.com/riscv/riscv-ssqosid/releases/tag/v1.0
[2] https://github.com/riscv-non-isa/riscv-cbqri/releases/tag/v1.0
[3] https://git.kernel.org/pub/scm/linux/kernel/git/fustini/linux.git/log/?h=b4/ssqosid-cbqri

Signed-off-by: Drew Fustini <fustini@kernel.org>
---
Kornel Dulęba (1):
      riscv: implement Ssqosid extension and srmcfg CSR

Nicolas Pitre (5):
      hw/riscv: define capabilities of CBQRI controllers
      hw/riscv: implement CBQRI capacity controller
      hw/riscv: implement CBQRI bandwidth controller
      hw/riscv: add CBQRI to Kconfig and build if enabled
      hw/riscv: add CBQRI controllers to virt machine

 MAINTAINERS                       |   9 +
 disas/riscv.c                     |   1 +
 hw/riscv/Kconfig                  |   4 +
 hw/riscv/cbqri_bandwidth.c        | 612 +++++++++++++++++++++++++++++++++
 hw/riscv/cbqri_capacity.c         | 706 ++++++++++++++++++++++++++++++++++++++
 hw/riscv/meson.build              |   1 +
 hw/riscv/virt.c                   |   3 +
 include/hw/riscv/cbqri.h          |  82 +++++
 target/riscv/cpu.c                |   2 +
 target/riscv/cpu.h                |   3 +
 target/riscv/cpu_bits.h           |   9 +
 target/riscv/cpu_cfg_fields.h.inc |   1 +
 target/riscv/csr.c                |  37 ++
 13 files changed, 1470 insertions(+)
---
base-commit: 942b0d378a1de9649085ad6db5306d5b8cef3591
change-id: 20251229-riscv-ssqosid-cbqri-aee2271532ff

Best regards,
-- 
Drew Fustini <fustini@kernel.org>



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v4 1/6] riscv: implement Ssqosid extension and srmcfg CSR
  2026-01-05 21:54 [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
@ 2026-01-05 21:54 ` Drew Fustini
  2026-01-05 21:54 ` [PATCH v4 2/6] hw/riscv: define capabilities of CBQRI controllers Drew Fustini
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Drew Fustini @ 2026-01-05 21:54 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-riscv, Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, 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, so check mstateen0.srmcfg to determine
if s-mode is allowed to access it. In addition, accessing it when V=1
shall cause a virtual instruction exception.

Link: https://github.com/riscv-non-isa/riscv-cbqri/releases/download/v1.0/riscv-cbqri.pdf
Link: https://docs.riscv.org/reference/isa/priv/smstateen.html
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           |  9 +++++++++
 target/riscv/cpu_cfg_fields.h.inc |  1 +
 target/riscv/csr.c                | 37 +++++++++++++++++++++++++++++++++++++
 6 files changed, 53 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 8f26d8b8b076..f665308896e5 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_13_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 90b3e951053a..20c2eada1014 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..bd73f9232d70 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
@@ -356,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)
@@ -1164,4 +1168,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..298829c0239f 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -1759,6 +1759,40 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno,
     return RISCV_EXCP_NONE;
 }
 
+static RISCVException check_srmcfg(CPURISCVState *env, int csrno)
+{
+    if (!riscv_cpu_cfg(env)->ext_ssqosid) {
+        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.
+     */
+    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 +6069,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] 13+ messages in thread

* [PATCH v4 2/6] hw/riscv: define capabilities of CBQRI controllers
  2026-01-05 21:54 [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
  2026-01-05 21:54 ` [PATCH v4 1/6] riscv: implement Ssqosid extension and srmcfg CSR Drew Fustini
@ 2026-01-05 21:54 ` Drew Fustini
  2026-01-05 21:54 ` [PATCH v4 3/6] hw/riscv: implement CBQRI capacity controller Drew Fustini
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Drew Fustini @ 2026-01-05 21:54 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-riscv, Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, 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/releases/tag/v1.0
Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Signed-off-by: Drew Fustini <fustini@kernel.org>
---
 MAINTAINERS              |  7 +++++
 include/hw/riscv/cbqri.h | 82 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 89 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index cbae7c26f83e..9d1b2b411010 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..a07f1e3a2dde
--- /dev/null
+++ b/include/hw/riscv/cbqri.h
@@ -0,0 +1,82 @@
+/*
+ * 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 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 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] 13+ messages in thread

* [PATCH v4 3/6] hw/riscv: implement CBQRI capacity controller
  2026-01-05 21:54 [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
  2026-01-05 21:54 ` [PATCH v4 1/6] riscv: implement Ssqosid extension and srmcfg CSR Drew Fustini
  2026-01-05 21:54 ` [PATCH v4 2/6] hw/riscv: define capabilities of CBQRI controllers Drew Fustini
@ 2026-01-05 21:54 ` Drew Fustini
  2026-01-16 17:20   ` Radim Krčmář
  2026-01-05 21:54 ` [PATCH v4 4/6] hw/riscv: implement CBQRI bandwidth controller Drew Fustini
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 13+ messages in thread
From: Drew Fustini @ 2026-01-05 21:54 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-riscv, Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, 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/releases/tag/v1.0
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 | 706 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 707 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 9d1b2b411010..99f4c12f3b92 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..9382ba5ee989
--- /dev/null
+++ b/hw/riscv/cbqri_capacity.c
@@ -0,0 +1,706 @@
+/*
+ * 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/core/qdev-properties.h"
+#include "hw/core/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 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 inline unsigned int get_bmw(RiscvCbqriCapacityState *cc)
+{
+    unsigned int bmw = ((cc->ncblks + 63) / 64) * 64;
+
+    return bmw;
+}
+
+static inline unsigned int get_slots(RiscvCbqriCapacityState *cc)
+{
+    unsigned int slots = (cc->ncblks + 63) / 64;
+
+    return slots;
+}
+
+static uint64_t get_blockmask_offset(RiscvCbqriCapacityState *cc,
+                                        uint32_t rcid, uint32_t at)
+{
+    /*
+     * Each blockmask is made of one or more uint64_t "slots".
+     *
+     * The first slot (or set of slots when BMW is great than 64)
+     * holds the CC_BLOCK_MASK register content.
+     *
+     * The following slot holds the the CC_CUNITS register content
+     * which has a fixed size of 8 bytes.
+     *
+     * The remaining slots contain the blockmask for each AT per RCID.
+     *
+     * For example, this would be the layout of the slots for a
+     * controller which has AT types Data and Code enabled and
+     * NCBLKS of 16. This results in a BMW of 64 so each blockmask
+     * can fit in 1 slot. The first 6 slots would be:
+     *
+     *   Slot
+     *   [ 0]     register: CC_BLOCK_MASK
+     *   [ 1]     register: CC_CUNITS
+     *   [ 2] RCID= 0 AT=0: cc_block_mask
+     *   [ 3] RCID= 0 AT=1: cc_block_mask
+     *   [ 4] RCID= 1 AT=0: cc_block_mask
+     *   [ 5] RCID= 1 AT=1: cc_block_mask
+     *
+     * This would be the layout for NCBLKS of 100 and AT types Data
+     * and Code. BMW would be 128 so each blockmask takes 2 slots.
+     * The first 11 slots would be:
+     *
+     *   Slot
+     *   [ 0]     register: CC_BLOCK_MASK
+     *   [ 1]     register: CC_BLOCK_MASK
+     *   [ 2]     register: CC_CUNITS
+     *   [ 3] RCID= 0 AT=0: cc_block_mask
+     *   [ 4] RCID= 0 AT=0: cc_block_mask
+     *   [ 5] RCID= 0 AT=1: cc_block_mask
+     *   [ 6] RCID= 0 AT=1: cc_block_mask
+     *   [ 7] RCID= 1 AT=0: cc_block_mask
+     *   [ 8] RCID= 1 AT=0: cc_block_mask
+     *   [ 9] RCID= 1 AT=1: cc_block_mask
+     *   [10] RCID= 1 AT=1: cc_block_mask
+     *
+     */
+    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_offset;
+    unsigned int blockmask_slots = get_slots(cc);
+    blockmask_offset = blockmask_slots * ((rcid * nb_ats) + at);
+
+    /*
+     * Add offset for cc_blockmask register in slot 0 to (blockmask_slots-1)
+     */
+    blockmask_offset += blockmask_slots;
+
+    /*
+     * Add offset to account for cc_cunits register which is always 1 slot
+     */
+    blockmask_offset += 1;
+
+    return blockmask_offset;
+}
+
+static uint64_t *get_blockmask_location(RiscvCbqriCapacityState *cc,
+                                        uint32_t rcid, uint32_t at)
+{
+    assert(cc->alloc_blockmasks != NULL);
+    unsigned int blockmask_offset = get_blockmask_offset(cc, rcid, 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 = get_slots(cc);
+
+    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 = get_slots(cc);
+
+    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) {
+        /*
+         * The flush operation is not allowed to change the configured
+         * capacity block allocation or the capacity unit limit.
+         */
+        status = CC_ALLOC_STATUS_SUCCESS;
+    } 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 == get_slots(cc)) {
+            /* this is cc_cunits register */
+            break;
+        } else if (blkmask_slot > get_slots(cc)) {
+            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, 0);
+        value = FIELD_DP64(value, CC_CAPABILITIES, RPFX, 0);
+        value = FIELD_DP64(value, CC_CAPABILITIES, P, 0);
+        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 == get_slots(cc)) {
+            /*
+             * The cc_cunits register is always the slot following the
+             * last slot of cc_blockmask register. Capacity units are
+             * not supported by this implementation so must return 0.
+             */
+            value = 0;
+            break;
+        } else if (blkmask_slot > get_slots(cc)) {
+            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);
+    unsigned int bmw = get_bmw(cc);
+    /* The size of the CC registers other than bmw is 40 bytes */
+    unsigned int mmio_size = (bmw / 8) + 40;
+
+    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);
+    unsigned int blockmasks_size = get_blockmask_offset(cc, cc->nb_rcids, 0);
+    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", mmio_size);
+    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);
+
+    /*
+     * The spec requires that the reset value is 0 for the cc_mon_ctl.BUSY
+     * and cc_alloc_ctl.BUSY fields, and that the reset value is UNSPECIFIED
+     * for all other registers fields.
+     *
+     * Therefore, it is legal to set the entire contents of cc_mon_ctl and
+     * cc_alloc_ctl to 0.
+     */
+    cc->cc_mon_ctl = 0;
+    cc->cc_alloc_ctl = 0;
+
+    /*
+     * The capacity controllers at reset must allocate all available
+     * capacity to RCID value of 0.
+     *
+     * When the capacity controller supports capacity allocation per
+     * access-type, then all available capacity is shared by all the
+     * access-type for RCID=0.
+     *
+     * For unsupported AT values the resource controller behaves as
+     * if AT was 0 (CC_AT_DATA).
+     */
+    alloc_blockmask_init(cc, 0, CC_AT_DATA, 1, NULL);
+    if (cc->supports_at_code) {
+        alloc_blockmask_init(cc, 0, CC_AT_CODE, 1, 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("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, "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] 13+ messages in thread

* [PATCH v4 4/6] hw/riscv: implement CBQRI bandwidth controller
  2026-01-05 21:54 [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
                   ` (2 preceding siblings ...)
  2026-01-05 21:54 ` [PATCH v4 3/6] hw/riscv: implement CBQRI capacity controller Drew Fustini
@ 2026-01-05 21:54 ` Drew Fustini
  2026-01-05 21:54 ` [PATCH v4 5/6] hw/riscv: add CBQRI to Kconfig and build if enabled Drew Fustini
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Drew Fustini @ 2026-01-05 21:54 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-riscv, Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, 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/releases/tag/v1.0
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 | 612 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 613 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 99f4c12f3b92..3c10cd154635 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..c5de74410169
--- /dev/null
+++ b/hw/riscv/cbqri_bandwidth.c
@@ -0,0 +1,612 @@
+/*
+ * 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/core/qdev-properties.h"
+#include "hw/core/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 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);
+
+    /*
+     * Bandwidth is allocated in multiples of bandwidth blocks, and the
+     * value in Rbwb must be at least 1 and must not exceed MRBWB value.
+     */
+    if (bc->bw_allocations[0].Rbwb < 1) {
+        return BC_ALLOC_STATUS_INVAL_OP;
+    } else if (bc->bw_allocations[0].Rbwb > bc->mrbwb) {
+        return BC_ALLOC_STATUS_INVAL_OP;
+    }
+
+    /* Save contents of BC_BW_ALLOC register for this rcid and at */
+    *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, 0);
+        value = FIELD_DP64(value, BC_CAPABILITIES, P, 0);
+        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("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, "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] 13+ messages in thread

* [PATCH v4 5/6] hw/riscv: add CBQRI to Kconfig and build if enabled
  2026-01-05 21:54 [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
                   ` (3 preceding siblings ...)
  2026-01-05 21:54 ` [PATCH v4 4/6] hw/riscv: implement CBQRI bandwidth controller Drew Fustini
@ 2026-01-05 21:54 ` Drew Fustini
  2026-01-05 21:54 ` [PATCH v4 6/6] hw/riscv: add CBQRI controllers to virt machine Drew Fustini
  2026-01-07  7:38 ` [External] [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers yunhui cui
  6 siblings, 0 replies; 13+ messages in thread
From: Drew Fustini @ 2026-01-05 21:54 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-riscv, Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, 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. Build the CBQRI controllers when RISC-V CBQRI is
enabled.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
Signed-off-by: Drew Fustini <fustini@kernel.org>
---
 hw/riscv/Kconfig     | 4 ++++
 hw/riscv/meson.build | 1 +
 2 files changed, 5 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
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] 13+ messages in thread

* [PATCH v4 6/6] hw/riscv: add CBQRI controllers to virt machine
  2026-01-05 21:54 [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
                   ` (4 preceding siblings ...)
  2026-01-05 21:54 ` [PATCH v4 5/6] hw/riscv: add CBQRI to Kconfig and build if enabled Drew Fustini
@ 2026-01-05 21:54 ` Drew Fustini
  2026-01-07  7:38 ` [External] [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers yunhui cui
  6 siblings, 0 replies; 13+ messages in thread
From: Drew Fustini @ 2026-01-05 21:54 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-riscv, Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, 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>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.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 c87c169d38cd..99871119be44 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] 13+ messages in thread

* Re: [External] [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers
  2026-01-05 21:54 [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
                   ` (5 preceding siblings ...)
  2026-01-05 21:54 ` [PATCH v4 6/6] hw/riscv: add CBQRI controllers to virt machine Drew Fustini
@ 2026-01-07  7:38 ` yunhui cui
  2026-01-08  7:33   ` Drew Fustini
  6 siblings, 1 reply; 13+ messages in thread
From: yunhui cui @ 2026-01-07  7:38 UTC (permalink / raw)
  To: Drew Fustini
  Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
	Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei, Paolo Bonzini,
	Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
	Vasudevan Srinivasan, Radim Krčmář, Chen Pei,
	guo.wenjia23, liu.qingtao2

Hi Drew,

On Tue, Jan 6, 2026 at 5:55 AM Drew Fustini <fustini@kernel.org> wrote:
>
> This series implements the RISC-V Quality-of-Service Identifiers
> (Ssqosid) extension [1] which adds the srmcfg register. It also
> implements the RISC-V Capacity and Bandwidth Controller QoS Register
> Interface (CBQRI) specification [2]. Quality of Service (QoS) in this
> context is concerned with shared resources on an SoC such as cache
> capacity and memory bandwidth.
>
> Sssqosid srmcfg CSR
> -------------------
> The srmcfg CSR configures a hart with two identifiers:
>
>  - Resource Control ID (RCID)
>  - Monitoring Counter ID (MCID)
>
> These identifiers accompany each request issued by the hart to shared
> resource controllers. This allows the capacity and bandwidth resources
> used by a software workload (e.g. a process or a set of processes) to be
> controlled and monitored.

1. The CBQRI specification does not mandate 64-byte access width for
its registers. Therefore, is it necessary to add a field in the ACPI
RQSC table?
2. In addition, although CBQRI does not require configuring a specific
number of RMIDs for each CLOSID like MPAM does, creating a control
group will by default create a monitoring group. So on RISC-V, the
number of MCIDs should be at least no less than that of RCIDs, right?
3. By the way, for Linux, could you also release a version of the
CBQRI/Ssqosid patchset with ACPI support?

>
> CBQRI controllers
> -----------------
> CBQRI defines operations to configure resource usage limits, in the form
> of capacity or bandwidth, for an RCID. CBQRI also defines operations to
> configure counters to track the resource utilization per MCID.
>
> This series implements an CBQRI capacity controller and an CBQRI
> bandwidth controller which can be 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.
>
> As 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
>
> Example SoC for CBQRI
> ---------------------
> An example SoC with the following CBQRI controller configuration can be
> used to test the implementation:
>
>   - L2 cache controllers
>     - Resource type: Capacity
>     - Number of capacity blocks (NCBLKS): 12
>         - In the context of a set-associative cache, the number of
>           capacity blocks can be thought of as the number of ways
>     - Number of access types: 2 (code and data)
>     - Usage monitoring not supported
>     - Capacity allocation operations: CONFIG_LIMIT, READ_LIMIT
>
>   - Last-level cache (LLC) controller
>     - Resource type: Capacity
>     - Number of capacity blocks (NCBLKS): 16
>     - 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
>
>   - Memory controllers
>     - Resource type: Bandwidth
>     - Number of bandwidth blocks (NBWBLKS): 1024
>        - Bandwidth blocks do not have a unit but instead represent a
>          portion of the total bandwidth resource. For NWBLKS of 1024,
>          each block represents about 0.1% of the bandwidth resource.
>     - Maximum reserved bandwidth blocks (MRBWB): 819 [80% of NBWBLKS]
>     - Number of access types: 1 (no code/data differentiation)
>     - 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
>
> The memory map used for the example SoC:
>
>   Base addr  Size
>   0x4820000  4KB  Cluster 0 L2 cache controller
>   0x4821000  4KB  Cluster 1 L2 cache controller
>   0x4828000  4KB  Memory controller 0
>   0x4829000  4KB  Memory controller 1
>   0x482a000  4KB  Memory controller 2
>   0x482b000  4KB  Shared LLC cache controller
>
> This configuration is meant to provide a "concrete" example for software
> (like Linux) to test against. It represents just one of many possible
> ways for hardware to implement the CBQRI spec.
>
> The example SoC configuration is created with the following:
>
>   qemu-system-riscv64 \
>         -M virt \
>         -nographic \
>         -smp 8 \
>         -device riscv.cbqri.capacity,max_mcids=256,max_rcids=64,ncblks=12,alloc_op_flush_rcid=false,mon_op_config_event=false,mon_op_read_counter=false,mon_evt_id_none=false,mon_evt_id_occupancy=false,mmio_base=0x04820000 \
>         -device riscv.cbqri.capacity,max_mcids=256,max_rcids=64,ncblks=12,alloc_op_flush_rcid=false,mon_op_config_event=false,mon_op_read_counter=false,mon_evt_id_none=false,mon_evt_id_occupancy=false,mmio_base=0x04821000 \
>         -device riscv.cbqri.capacity,max_mcids=256,max_rcids=64,ncblks=16,mmio_base=0x0482B000 \
>         -device riscv.cbqri.bandwidth,max_mcids=256,max_rcids=64,nbwblks=1024,mrbwb=819,mmio_base=0x04828000 \
>         -device riscv.cbqri.bandwidth,max_mcids=256,max_rcids=64,nbwblks=1024,mrbwb=819,mmio_base=0x04829000 \
>         -device riscv.cbqri.bandwidth,max_mcids=256,max_rcids=64,nbwblks=1024,mrbwb=819,mmio_base=0x0482a000
>
> In addition, please note that this series only implements the register
> interface that CBQRI specifies. It does not attempt to emulate the
> performance impact of configuring limits on shared resources like cache
> and memory bandwidth. Similarly, the code does not attempt to emulate
> cache and memory bandwidth utilization, like what would be observed on a
> real hardware system implementing CBQRI.
>
> There is a branch of Linux [3] which adds support for Ssqosid and CBQRI
> along with resctrl integration. I still need to do some more cleanup of
> the code and intend to post the Linux patch series soon.
>
> Open issue:
>  - Support 32-bit operation as the spec states that "CBQRI registers are
>    defined so that software can perform two individual 4 byte accesses."
>    The MemoryRegionOps have .valid.min_access_size = 4 but the read and
>    write hooks have assert(size == 8). I need to figure out how to
>    correctly handle accesses of size 4.
>
> Despite the open issue above, I am sending out this new revision as I
> believe I have addressed all the other feedback.
>
> Changes since v3:
>  - NOTE: I should have used version v3 for the previous version posted
>    on Nov 19, 2025 given there was already v1/v2 back in 2023. Therefore
>    I am using v4 in this new revision.
>  - Rebase on current master as of Dec 29 (942b0d378a1d) and update
>    include paths for qdev-properties.h and sysbus.h
>  - Add Rb tags from Daniel
>  - Squash the Kconfig and meson.build patches together per Daniel
>  - Use riscv_cpu_cfg() instead of env_archcpu() in check_srmcfg()
>  - Add check for mstateen0.SRMCFG in check_srmcfg()
>  - Increase ISA_EXT_DATA_ENTRY() for ssqosid from PRIV_VERSION_1_12_0
>    to PRIV_VERSION_1_13_0 as it was added in Privileged Arch 1.13
>  - Calculate the size of the capacity controller MMIO space based on
>    on cc->ncblks and roundup to be aligned to the 4KB page boundary.
>  - Remove rpfx, p and cunits properties that were added in the previous
>    revision as I have decided to not support those optional features in
>    this capacity controller implementation.
>  - Update riscv_cbqri_cc_read to set 0 for RPFX, P and CUNITS in
>    cc_capabilities. This indicates that this capacity controller
>    implementation does not support RCID-prefixed mode and nor does it
>    support capacity unit limits.
>  - Add cc_cunits register after cc_block_mask register in the
>    alloc_blockmasks array and adjust logic in get_blockmask_offset,
>    riscv_cbqri_cc_read, riscv_cbqri_cc_write. Since capacity units is
>    not supported, the cc_cunits register will always be 0.
>  - Only assign capacity for rcid 0 in riscv_cbqri_cc_reset and do not
>    touch allocation for other rcid values as they are unspecified
>    following a reset.
>  - Change CC_ALLOC_OP_FLUSH_RCID to be a no-op instead of calling
>    alloc_blockmask_init() as the spec states that the configured
>    capacity block allocation or the capacity unit limit is not
>    changed by the flush operation.
>  - Add comment to explain that, while the spec only requires the busy
>    field to be reset to 0, the entire contents of the cc->cc_mon_ctl
>    abd cc->cc_alloc_ctl registers are set to 0 simplify the code in
>    riscv_cbqri_cc_reset().
>  - Simplify the access type logic for blockmask initialization in
>    riscv_cbqri_cc_reset().
>  - Add static inline helpers get_bmw() and get_slots().
>  - Add check to bandwidth_config() that rbwb is not greater than mrbwb.
>  - Link to v3: https://lore.kernel.org/qemu-devel/20251119-riscv-ssqosid-cbqri-v1-0-3392fc760e48@kernel.org
>
> Changes since v2:
>  - NOTE: I should have used version v3 for this version posted on Nov 19
>    as there were already v1 and v2 back in 2023.
>  - Rebase on master which is currently at version v10.1.50
>  - Add fields that were not in the draft used for the proof of concept
>    and introduced in final CBQRI 1.0 spec: capacity units (cunits) and
>    RCID-prefixed mode (RPFX) along with parameter P (prefixed bits)
>  - Fix check_srmcfg() to check if virtualization is enabled, and if so,
>    return virt instruction fault, otherwise return smode()
>  - Fix indentation in read_srmcfg()
>  - Add SPDX headers
>  - Add MAINTAINERS enteries
>  - Link to v2: https://lore.kernel.org/qemu-devel/20230425203834.1135306-1-dfustini@baylibre.com/
>
> Changes since v1:
>  - Rebase on current master (8.0.50) instead of 8.0.0-rc4
>  - Configure CBQRI controllers based on device properties in command
>    line arguments instead of a fixed example SoC configuration.
>  - Move TYPE_RISCV_CBQRI_BC and TYPE_RISCV_CBQRI_CC into header so that
>    machines may use it (suggested by Alistair).
>  - Change 'select RISC_CBQRI' to 'imply RISCV_CBQRI' for RISCV_VIRT.
>  - Patches 8/9 could be dropped as they are not needed for the CBQRI
>    proof-of-concept to work. They are only meant to serve as an example
>    for those implementing new machines.
>  - Link to v1: https://lore.kernel.org/qemu-devel/20230416232050.4094820-1-dfustini@baylibre.com/
>
> [1] https://github.com/riscv/riscv-ssqosid/releases/tag/v1.0
> [2] https://github.com/riscv-non-isa/riscv-cbqri/releases/tag/v1.0
> [3] https://git.kernel.org/pub/scm/linux/kernel/git/fustini/linux.git/log/?h=b4/ssqosid-cbqri
>
> Signed-off-by: Drew Fustini <fustini@kernel.org>
> ---
> Kornel Dulęba (1):
>       riscv: implement Ssqosid extension and srmcfg CSR
>
> Nicolas Pitre (5):
>       hw/riscv: define capabilities of CBQRI controllers
>       hw/riscv: implement CBQRI capacity controller
>       hw/riscv: implement CBQRI bandwidth controller
>       hw/riscv: add CBQRI to Kconfig and build if enabled
>       hw/riscv: add CBQRI controllers to virt machine
>
>  MAINTAINERS                       |   9 +
>  disas/riscv.c                     |   1 +
>  hw/riscv/Kconfig                  |   4 +
>  hw/riscv/cbqri_bandwidth.c        | 612 +++++++++++++++++++++++++++++++++
>  hw/riscv/cbqri_capacity.c         | 706 ++++++++++++++++++++++++++++++++++++++
>  hw/riscv/meson.build              |   1 +
>  hw/riscv/virt.c                   |   3 +
>  include/hw/riscv/cbqri.h          |  82 +++++
>  target/riscv/cpu.c                |   2 +
>  target/riscv/cpu.h                |   3 +
>  target/riscv/cpu_bits.h           |   9 +
>  target/riscv/cpu_cfg_fields.h.inc |   1 +
>  target/riscv/csr.c                |  37 ++
>  13 files changed, 1470 insertions(+)
> ---
> base-commit: 942b0d378a1de9649085ad6db5306d5b8cef3591
> change-id: 20251229-riscv-ssqosid-cbqri-aee2271532ff
>
> Best regards,
> --
> Drew Fustini <fustini@kernel.org>
>

Thanks,
Yunhui


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers
  2026-01-07  7:38 ` [External] [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers yunhui cui
@ 2026-01-08  7:33   ` Drew Fustini
  2026-01-08  8:46     ` [External] " yunhui cui
  0 siblings, 1 reply; 13+ messages in thread
From: Drew Fustini @ 2026-01-08  7:33 UTC (permalink / raw)
  To: yunhui cui
  Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
	Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei, Paolo Bonzini,
	Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
	Vasudevan Srinivasan, Radim Krčmář, Chen Pei,
	guo.wenjia23, liu.qingtao2

On Wed, Jan 07, 2026 at 03:38:34PM +0800, yunhui cui wrote:
> Hi Drew,
> 
> On Tue, Jan 6, 2026 at 5:55 AM Drew Fustini <fustini@kernel.org> wrote:
> >
> > This series implements the RISC-V Quality-of-Service Identifiers
> > (Ssqosid) extension [1] which adds the srmcfg register. It also
> > implements the RISC-V Capacity and Bandwidth Controller QoS Register
> > Interface (CBQRI) specification [2]. Quality of Service (QoS) in this
> > context is concerned with shared resources on an SoC such as cache
> > capacity and memory bandwidth.
> >
> > Sssqosid srmcfg CSR
> > -------------------
> > The srmcfg CSR configures a hart with two identifiers:
> >
> >  - Resource Control ID (RCID)
> >  - Monitoring Counter ID (MCID)
> >
> > These identifiers accompany each request issued by the hart to shared
> > resource controllers. This allows the capacity and bandwidth resources
> > used by a software workload (e.g. a process or a set of processes) to be
> > controlled and monitored.
> 
> 1. The CBQRI specification does not mandate 64-byte access width for
> its registers. Therefore, is it necessary to add a field in the ACPI
> RQSC table?

Sorry, I don't follow. Why would a new field be needed in RQSC?

> 2. In addition, although CBQRI does not require configuring a specific
> number of RMIDs for each CLOSID like MPAM does, creating a control
> group will by default create a monitoring group. So on RISC-V, the
> number of MCIDs should be at least no less than that of RCIDs, right?

Yes, I have working on the assumption that the number of MCIDs (RMIDs)
will be greater than, or equal to, the number of RCIDs (e.g. CLOSIDs).

Chapter 6 of the CBQRI spec states that: "In a typical implementation,
the number of RCID bits implemented (for example, to support 10s of
RCIDs) might be smaller than the number of MCID bits implemented (for
example, to support 100s of MCIDs)."

I would be very interested if anyone can share the RCID and MCID count
for their CBQRI hardware implementation.

> 3. By the way, for Linux, could you also release a version of the
> CBQRI/Ssqosid patchset with ACPI support?

The series I am about to post for Ssqosid+CBQRI [1] just contains a
device tree example, but I will do a followup series for RQSC. The
kernel branch [2] from the RQSC proof-of-concept was based on v6.14 so I
need to rebase it. The good thing is that 6.19 now has the PPTT helpers
from the MPAM driver series.

Thanks,
Drew

[1] https://github.com/tt-fustini/linux/tree/b4/ssqosid-cbqri
[2] https://github.com/tt-fustini/linux/tree/dfustini/cbqri-mpam-snapshot-v6.14-rc1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [External] Re: [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers
  2026-01-08  7:33   ` Drew Fustini
@ 2026-01-08  8:46     ` yunhui cui
  2026-01-09  4:56       ` Drew Fustini
  0 siblings, 1 reply; 13+ messages in thread
From: yunhui cui @ 2026-01-08  8:46 UTC (permalink / raw)
  To: Drew Fustini
  Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
	Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei, Paolo Bonzini,
	Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
	Vasudevan Srinivasan, Radim Krčmář, Chen Pei,
	guo.wenjia23, liu.qingtao2

Hi Drew,

On Thu, Jan 8, 2026 at 3:33 PM Drew Fustini <fustini@kernel.org> wrote:
>
> On Wed, Jan 07, 2026 at 03:38:34PM +0800, yunhui cui wrote:
> > Hi Drew,
> >
> > On Tue, Jan 6, 2026 at 5:55 AM Drew Fustini <fustini@kernel.org> wrote:
> > >
> > > This series implements the RISC-V Quality-of-Service Identifiers
> > > (Ssqosid) extension [1] which adds the srmcfg register. It also
> > > implements the RISC-V Capacity and Bandwidth Controller QoS Register
> > > Interface (CBQRI) specification [2]. Quality of Service (QoS) in this
> > > context is concerned with shared resources on an SoC such as cache
> > > capacity and memory bandwidth.
> > >
> > > Sssqosid srmcfg CSR
> > > -------------------
> > > The srmcfg CSR configures a hart with two identifiers:
> > >
> > >  - Resource Control ID (RCID)
> > >  - Monitoring Counter ID (MCID)
> > >
> > > These identifiers accompany each request issued by the hart to shared
> > > resource controllers. This allows the capacity and bandwidth resources
> > > used by a software workload (e.g. a process or a set of processes) to be
> > > controlled and monitored.
> >
> > 1. The CBQRI specification does not mandate 64-byte access width for
> > its registers. Therefore, is it necessary to add a field in the ACPI
> > RQSC table?
>
> Sorry, I don't follow. Why would a new field be needed in RQSC?

Different manufacturers may design CBQRI registers with different
widths (32-bit or 64-bit). See:
https://github.com/riscv-non-isa/riscv-rqsc/issues/10

>
> > 2. In addition, although CBQRI does not require configuring a specific
> > number of RMIDs for each CLOSID like MPAM does, creating a control
> > group will by default create a monitoring group. So on RISC-V, the
> > number of MCIDs should be at least no less than that of RCIDs, right?
>
> Yes, I have working on the assumption that the number of MCIDs (RMIDs)
> will be greater than, or equal to, the number of RCIDs (e.g. CLOSIDs).
>
> Chapter 6 of the CBQRI spec states that: "In a typical implementation,
> the number of RCID bits implemented (for example, to support 10s of
> RCIDs) might be smaller than the number of MCID bits implemented (for
> example, to support 100s of MCIDs)."
>
> I would be very interested if anyone can share the RCID and MCID count
> for their CBQRI hardware implementation.
>
> > 3. By the way, for Linux, could you also release a version of the
> > CBQRI/Ssqosid patchset with ACPI support?
>
> The series I am about to post for Ssqosid+CBQRI [1] just contains a
> device tree example, but I will do a followup series for RQSC. The
> kernel branch [2] from the RQSC proof-of-concept was based on v6.14 so I
> need to rebase it. The good thing is that 6.19 now has the PPTT helpers
> from the MPAM driver series.

Oh, that's great! When will the RQSC-supported patchset based on 6.19
likely be posted? Looking forward to your update.

>
> Thanks,
> Drew
>
> [1] https://github.com/tt-fustini/linux/tree/b4/ssqosid-cbqri
> [2] https://github.com/tt-fustini/linux/tree/dfustini/cbqri-mpam-snapshot-v6.14-rc1


Thanks,
Yunhui


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [External] Re: [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers
  2026-01-08  8:46     ` [External] " yunhui cui
@ 2026-01-09  4:56       ` Drew Fustini
  0 siblings, 0 replies; 13+ messages in thread
From: Drew Fustini @ 2026-01-09  4:56 UTC (permalink / raw)
  To: yunhui cui
  Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
	Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei, Paolo Bonzini,
	Nicolas Pitre, Kornel Dulęba, Atish Kumar Patra, Atish Patra,
	Vasudevan Srinivasan, Radim Krčmář, Chen Pei,
	guo.wenjia23, liu.qingtao2

On Thu, Jan 08, 2026 at 04:46:10PM +0800, yunhui cui wrote:
> Hi Drew,
> 
> On Thu, Jan 8, 2026 at 3:33 PM Drew Fustini <fustini@kernel.org> wrote:
> >
> > On Wed, Jan 07, 2026 at 03:38:34PM +0800, yunhui cui wrote:
> > > Hi Drew,
> > >
> > > On Tue, Jan 6, 2026 at 5:55 AM Drew Fustini <fustini@kernel.org> wrote:
> > > >
> > > > This series implements the RISC-V Quality-of-Service Identifiers
> > > > (Ssqosid) extension [1] which adds the srmcfg register. It also
> > > > implements the RISC-V Capacity and Bandwidth Controller QoS Register
> > > > Interface (CBQRI) specification [2]. Quality of Service (QoS) in this
> > > > context is concerned with shared resources on an SoC such as cache
> > > > capacity and memory bandwidth.
> > > >
> > > > Sssqosid srmcfg CSR
> > > > -------------------
> > > > The srmcfg CSR configures a hart with two identifiers:
> > > >
> > > >  - Resource Control ID (RCID)
> > > >  - Monitoring Counter ID (MCID)
> > > >
> > > > These identifiers accompany each request issued by the hart to shared
> > > > resource controllers. This allows the capacity and bandwidth resources
> > > > used by a software workload (e.g. a process or a set of processes) to be
> > > > controlled and monitored.
> > >
> > > 1. The CBQRI specification does not mandate 64-byte access width for
> > > its registers. Therefore, is it necessary to add a field in the ACPI
> > > RQSC table?
> >
> > Sorry, I don't follow. Why would a new field be needed in RQSC?
> 
> Different manufacturers may design CBQRI registers with different
> widths (32-bit or 64-bit). See:
> https://github.com/riscv-non-isa/riscv-rqsc/issues/10

Thanks for the link. I now understand that you are talking about a field 
to indicate the supported access width. I think Radim's comments
illustrate that software should not have to worry about about 32-bit or
64-bit accesses as the CBQRI spec requires a controller to support both.
I think we will have to wait and see if there are any real hardware
implementations where that is not the case.

> > > 2. In addition, although CBQRI does not require configuring a specific
> > > number of RMIDs for each CLOSID like MPAM does, creating a control
> > > group will by default create a monitoring group. So on RISC-V, the
> > > number of MCIDs should be at least no less than that of RCIDs, right?
> >
> > Yes, I have working on the assumption that the number of MCIDs (RMIDs)
> > will be greater than, or equal to, the number of RCIDs (e.g. CLOSIDs).
> >
> > Chapter 6 of the CBQRI spec states that: "In a typical implementation,
> > the number of RCID bits implemented (for example, to support 10s of
> > RCIDs) might be smaller than the number of MCID bits implemented (for
> > example, to support 100s of MCIDs)."
> >
> > I would be very interested if anyone can share the RCID and MCID count
> > for their CBQRI hardware implementation.
> >
> > > 3. By the way, for Linux, could you also release a version of the
> > > CBQRI/Ssqosid patchset with ACPI support?
> >
> > The series I am about to post for Ssqosid+CBQRI [1] just contains a
> > device tree example, but I will do a followup series for RQSC. The
> > kernel branch [2] from the RQSC proof-of-concept was based on v6.14 so I
> > need to rebase it. The good thing is that 6.19 now has the PPTT helpers
> > from the MPAM driver series.
> 
> Oh, that's great! When will the RQSC-supported patchset based on 6.19
> likely be posted? Looking forward to your update.

I don't think the kernel patches for RQSC will be that complicated to
rebase. However, I think the Qemu branch [1] will take more work as it
was based on an old PPTT patch series [2]. I think I will have the
Ssqosid+CBQRI kernel patch series ready to post tomorrow, so I think it
is reasonable to target posting the rebased RQSC series for Linux and
Qemu 1 week later.

Thanks,
Drew

[1] https://github.com/tt-fustini/qemu/commits/riscv-cbqri-rqsc-pptt/
[2] https://lore.kernel.org/all/20240129104039.117671-1-jeeheng.sia@starfivetech.com/`


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 3/6] hw/riscv: implement CBQRI capacity controller
  2026-01-05 21:54 ` [PATCH v4 3/6] hw/riscv: implement CBQRI capacity controller Drew Fustini
@ 2026-01-16 17:20   ` Radim Krčmář
  2026-01-24 17:55     ` Drew Fustini
  0 siblings, 1 reply; 13+ messages in thread
From: Radim Krčmář @ 2026-01-16 17:20 UTC (permalink / raw)
  To: Drew Fustini, qemu-devel
  Cc: qemu-riscv, Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, 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

Sorry, I changed emails, and this series flew under my radar, since
qemu-riscv lore public inbox has been silent for a month and I was
happily oblivious that it's a bug on their side.

I'm addressing only the access size issue in this mail and I'll look at
the rest next week.

2026-01-05T13:54:21-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/releases/tag/v1.0
> 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 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,

The .impl.min_access_size should be 4, since access_with_adjusted_size
doesn't seem to do what we want.

> +    .impl.max_access_size = 8,

I think we can reuse the current 8 byte iplementation by adding
something like the following hack for 4 byte accesses:

  static uint64_t riscv_cbqri_cc_read_wrapper(void *opaque, hwaddr addr,
                                              unsigned size)
  {
      uint64_t value = riscv_cbqri_br_read(opaque, addr & ~0x7UL, 8);
      if (size == 4) {
          if (addr & 0x7) {
              return value >> 32;
          } else {
              return value & 0xffffffff;
          }
      }
      return value;
  }

read has no side-effects, so the wrapper should be conceptually correct.


  static uint64_t riscv_cbqri_cc_write_wrapper(void *opaque, hwaddr addr,
                                               uint64_t value, unsigned size)
  {
      if (size == 4) {
          uint64_t reg = riscv_cbqri_br_read(opaque, addr & ~0x7UL, 8);
          if (addr & 0x7) {
              value = value << 32 | reg & 0xffffffff;
          } else {
              value = value | reg & ~0xffffffffUL;
          }
      }

      riscv_cbqri_cc_write(opaque, addr & ~0x7UL, value, 8);
  }
 
The control registers will execute an operation on a write to the upper
half as well, which doesn't seem desirable, but it's unspecified afaik.
The upper half of control registers has no fields that software would
ever want to write, so I think it's not really a practical issue,
especially since we're basically a skeleton implementation.

Another solution would be to implement just 4 byte registers, and handle
8 byte writes as a combination of two 4 byte -- the spec hints that this
is an expected implementation, but with all registers being defined as 8
byte, I think my hack would need "clarifications" to make it incorrect.

Thanks.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 3/6] hw/riscv: implement CBQRI capacity controller
  2026-01-16 17:20   ` Radim Krčmář
@ 2026-01-24 17:55     ` Drew Fustini
  0 siblings, 0 replies; 13+ messages in thread
From: Drew Fustini @ 2026-01-24 17:55 UTC (permalink / raw)
  To: Radim Krčmář
  Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
	Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei, 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 Fri, Jan 16, 2026 at 05:20:00PM +0000, Radim Krčmář wrote:
> Sorry, I changed emails, and this series flew under my radar, since
> qemu-riscv lore public inbox has been silent for a month and I was
> happily oblivious that it's a bug on their side.
> 
> I'm addressing only the access size issue in this mail and I'll look at
> the rest next week.
> 
> 2026-01-05T13:54:21-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/releases/tag/v1.0
> > 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 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,
> 
> The .impl.min_access_size should be 4, since access_with_adjusted_size
> doesn't seem to do what we want.

Ok, thanks.

> > +    .impl.max_access_size = 8,
> 
> I think we can reuse the current 8 byte iplementation by adding
> something like the following hack for 4 byte accesses:
> 
>   static uint64_t riscv_cbqri_cc_read_wrapper(void *opaque, hwaddr addr,
>                                               unsigned size)
>   {
>       uint64_t value = riscv_cbqri_br_read(opaque, addr & ~0x7UL, 8);
>       if (size == 4) {
>           if (addr & 0x7) {
>               return value >> 32;
>           } else {
>               return value & 0xffffffff;
>           }
>       }
>       return value;
>   }
> 
> read has no side-effects, so the wrapper should be conceptually correct.

Thank you very much for the suggestion. I'll try it out and get a new
revision ready.

> 
> 
>   static uint64_t riscv_cbqri_cc_write_wrapper(void *opaque, hwaddr addr,
>                                                uint64_t value, unsigned size)
>   {
>       if (size == 4) {
>           uint64_t reg = riscv_cbqri_br_read(opaque, addr & ~0x7UL, 8);
>           if (addr & 0x7) {
>               value = value << 32 | reg & 0xffffffff;
>           } else {
>               value = value | reg & ~0xffffffffUL;
>           }
>       }
> 
>       riscv_cbqri_cc_write(opaque, addr & ~0x7UL, value, 8);
>   }
>  
> The control registers will execute an operation on a write to the upper
> half as well, which doesn't seem desirable, but it's unspecified afaik.
> The upper half of control registers has no fields that software would
> ever want to write, so I think it's not really a practical issue,
> especially since we're basically a skeleton implementation.

Thanks for the suggestion, I think that is an acceptable solution. The
main point of the Qemu implementation is to exercise the Linux kernel
code ahead of silicon that implements CBQRI.

> Another solution would be to implement just 4 byte registers, and handle
> 8 byte writes as a combination of two 4 byte -- the spec hints that this
> is an expected implementation, but with all registers being defined as 8
> byte, I think my hack would need "clarifications" to make it incorrect.

Thanks for also suggesting this. I agree that the previous suggestion is
sufficent.

-Drew


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-01-24 17:56 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-05 21:54 [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers Drew Fustini
2026-01-05 21:54 ` [PATCH v4 1/6] riscv: implement Ssqosid extension and srmcfg CSR Drew Fustini
2026-01-05 21:54 ` [PATCH v4 2/6] hw/riscv: define capabilities of CBQRI controllers Drew Fustini
2026-01-05 21:54 ` [PATCH v4 3/6] hw/riscv: implement CBQRI capacity controller Drew Fustini
2026-01-16 17:20   ` Radim Krčmář
2026-01-24 17:55     ` Drew Fustini
2026-01-05 21:54 ` [PATCH v4 4/6] hw/riscv: implement CBQRI bandwidth controller Drew Fustini
2026-01-05 21:54 ` [PATCH v4 5/6] hw/riscv: add CBQRI to Kconfig and build if enabled Drew Fustini
2026-01-05 21:54 ` [PATCH v4 6/6] hw/riscv: add CBQRI controllers to virt machine Drew Fustini
2026-01-07  7:38 ` [External] [PATCH v4 0/6] riscv: implement Ssqosid extension and CBQRI controllers yunhui cui
2026-01-08  7:33   ` Drew Fustini
2026-01-08  8:46     ` [External] " yunhui cui
2026-01-09  4:56       ` Drew Fustini

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox