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