:p
atchew
Login
From: Alistair Francis <alistair.francis@wdc.com> The following changes since commit f5a2438405d4ae8b62de7c9b39fac0b2155ee544: Merge tag 'pull-qapi-2026-05-21' of https://repo.or.cz/qemu/armbru into staging (2026-05-21 09:00:22 -0400) are available in the Git repository at: https://github.com/alistair23/qemu.git tags/pull-riscv-to-apply-20260522 for you to fetch changes up to b7d87fb10d53a918e9c4a3dfe1fb06ce42c52526: hw/riscv: riscv-iommu: Re-process command queue after clearing CMD_ILL (2026-05-22 09:59:21 +1000) ---------------------------------------------------------------- RISC-V PR for 11.1. * Remove spike as default machine * Deprecate the shakti_c machine * Set MISA.[C|X] based on the selected extensions * Update Maintainers for OpenSBI Firmware * Update OpenSBI to v1.8.1 * Avoid RISCVCPU copy in PMU FDT setup * A collection of specification compliance improvements * Fix Svnapot 64KB pages * Handle source overlap of vector widening reduction instructions * Check interrupt in SiFive UART after txctrl register is written * Fix medeleg[11] read-only zero bit for M-mode ECALL * Fix tail handling for vmv.s.x and vfmv.s.f * Update the local AIA interrupt mask * Add KVM support for Zicbop and BFloat16 extensions * Fix the IOMMU FSC SV32 capability check * Avoid caching PCI device IDs in the IOMMU * Implement Microchip mpfs ioscb PLLs and sysreg clock dividers * Remove the internal CPU riscv_cpu_* arrays * Fix IOCOUNTINH.CY toggle detection * Fix the read of pmpaddr(0-63) CSRs * Make hpmcounterh return the upper 32-bits * Minor fixes and enhancements of RISC-V AIA devices * Re-process IOMMU command queue after clearing CMD_ILL ---------------------------------------------------------------- Abhigyan Kumar (1): target/riscv: Fix medeleg[11] read-only zero bit for M-mode ECALL Alistair Francis (4): target/riscv: Remove spike as default machine target/riscv: Deprecate the shakti_c machine MAINTAINERS: Maintain OpenSBI Firmware target/riscv: Update the local interrupt mask Andrew Jones (1): hw/riscv/riscv-iommu: Fix Svnapot 64KB pages Anton Blanchard (1): target/riscv: rvv: Handle source overlap of vector widening reduction instructions Chengbo Gao (1): hw/riscv/riscv-iommu: Avoid caching PCI device IDs Daniel Henrique Barboza (16): roms/opensbi: Update to v1.8.1 target/riscv/cpu.c: add xlrbr isa_edata_arr[] entry target/riscv/cpu.c: fix smctr/ssctr isa_edata_arr[] order target/riscv: make riscv-qmp-cmds use isa_data_arr[] target/riscv/tcg: treat all exts equally in cpu_disable_priv_spec_isa_exts target/riscv/tcg: use only isa_edata_arr[] in cpu_cfg_ext_get_name() target/riscv/cpu.c: remove riscv_cpu_enable_named_feat() target/riscv: remove riscv_cpu_named_features[] target/riscv/tcg: use isa_edata_arr[] in riscv_cpu_update_misa_x() target/riscv/kvm: use isa_edata_arr[] for unavailable props target/riscv/tcg: use isa_edata_arr[] to enable max exts target/riscv/tcg: use cfg_offset as cpu_set_multi_ext cb opaque target/riscv: do not set defaults in cpu prop callback target/riscv/tcg: use isa_edata_arr[] to create user props target/riscv/cpu: remove riscv_cpu_* arrays target/riscv/csr.c: fix read of pmpaddr(0-63) CSRs Fangyu Yu (1): hw/riscv/riscv-iommu: Fix IOCOUNTINH.CY toggle detection Frank Chang (3): target/riscv: Update MISA.C for Zc* extensions target/riscv: Update MISA.X for non-standard extensions hw/char: Check interrupt after txctrl register is written Guenter Roeck (1): hw: misc: Implement Microchip mpfs ioscb PLLs and sysreg clock dividers Jay Chang (1): hw/riscv: riscv-iommu: Re-process command queue after clearing CMD_ILL Jim Shu (4): hw/intc: riscv_aplic: Fix level trigger IRQ in direct delivery mode hw/intc: riscv_aplic: Add reset API to APLIC hw/intc: riscv_imsic: Add reset API to IMSIC hw/intc: riscv_aplic: add trace events of APLIC read/write function Mohamed Ayman (1): riscv: virt: avoid RISCVCPU copy in PMU FDT setup Portia Stephens (1): target/riscv: Make hpmcounterh return the upper 32-bits Zishun Yi (9): target/riscv: Allow mseccfg access based on ext_zicfilp target/riscv: add Zvknha as an implied extension for Zvknhb target/riscv: Remove unconditional MENVCFG_CDE from mask target/riscv: Fix missing CDE check for scountinhibit target/riscv/pmp: Fix integer overflow in TOR and NA4 address computation target/riscv: Add mseccfg to VMStateDescription target/riscv: clear mseccfg on reset for all dependent extensions hw/riscv/riscv-iommu: fix FSC SV32 capability check hw/riscv/virt-acpi-build: Fix off-by-one error in RIMT ID mapping Zongmin Zhou (2): target/riscv/kvm: add KVM support for Zicbop extension target/riscv/kvm: Add BFloat16 extensions support chen.zhongyao@zte.com.cn (1): target/riscv: Fix tail handling for vmv.s.x and vfmv.s.f MAINTAINERS | 2 + docs/about/deprecated.rst | 21 +- docs/about/removed-features.rst | 14 ++ include/hw/misc/mchp_pfsoc_ioscb.h | 2 + target/riscv/cpu.h | 13 +- target/riscv/helper.h | 4 + hw/char/sifive_uart.c | 1 + hw/intc/riscv_aplic.c | 116 ++++++--- hw/intc/riscv_imsic.c | 19 ++ hw/misc/mchp_pfsoc_ioscb.c | 47 +++- hw/misc/mchp_pfsoc_sysreg.c | 13 + hw/riscv/riscv-iommu-hpm.c | 3 +- hw/riscv/riscv-iommu.c | 96 ++++++-- hw/riscv/shakti_c.c | 1 + hw/riscv/spike.c | 1 - hw/riscv/virt-acpi-build.c | 3 +- hw/riscv/virt.c | 4 +- target/riscv/cpu.c | 327 +++++++++---------------- target/riscv/csr.c | 36 ++- target/riscv/kvm/kvm-cpu.c | 62 +++-- target/riscv/machine.c | 20 ++ target/riscv/pmp.c | 4 +- target/riscv/riscv-qmp-cmds.c | 30 +-- target/riscv/tcg/tcg-cpu.c | 192 +++++++-------- target/riscv/vector_helper.c | 18 ++ target/riscv/insn_trans/trans_rvv.c.inc | 45 ++-- hw/intc/trace-events | 4 + pc-bios/opensbi-riscv32-generic-fw_dynamic.bin | Bin 268752 -> 270384 bytes pc-bios/opensbi-riscv64-generic-fw_dynamic.bin | Bin 273048 -> 275928 bytes roms/opensbi | 2 +- 30 files changed, 613 insertions(+), 487 deletions(-)
From: Alistair Francis <alistair.francis@wdc.com> In QEMU 10.0 we deprecated the default spike machine, let's finally remove spike as the default and require users to specify the machine. Signed-off-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Message-ID: <20260430052218.771358-2-alistair.francis@wdc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- docs/about/deprecated.rst | 18 ------------------ docs/about/removed-features.rst | 14 ++++++++++++++ hw/riscv/spike.c | 1 - 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -XXX,XX +XXX,XX @@ better reflects the way this property affects all random data within the device tree blob, not just the ``kaslr-seed`` node. -RISC-V default machine option (since 10.0) -'''''''''''''''''''''''''''''''''''''''''' - -RISC-V defines ``spike`` as the default machine if no machine option is -given in the command line. This happens because ``spike`` is the first -RISC-V machine implemented in QEMU and setting it as default was -convenient at that time. Now we have 7 riscv64 and 6 riscv32 machines -and having ``spike`` as a default is no longer justified. This default -will also promote situations where users think they're running ``virt`` -(the most used RISC-V machine type in 10.0) when in fact they're -running ``spike``. - -Removing the default machine option forces users to always set the machine -they want to use and avoids confusion. Existing users of the ``spike`` -machine must ensure that they're setting the ``spike`` machine in the -command line (``-M spike``). - - Backend options --------------- diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -XXX,XX +XXX,XX @@ firmware is available to boot it. It can be replaced by the ``ast2700fc``, another multi-SoC machine based on the newer AST2700 SoCs which are excepted to receive better support in the future. +RISC-V default machine (removed in 11.1) +'''''''''''''''''''''''''''''''''''''''' + +RISC-V used to define ``spike`` as the default machine if no machine option +was given via the command line. This happend because ``spike`` was the first +RISC-V machine implemented in QEMU and setting it as default was +convenient at that time. Now we have 7 riscv64 and 6 riscv32 machines +and having ``spike`` as a default is no longer justified. + +The default machine option has been removed, forcing users to always set the +machine they want to use to avoid confusion. Existing users of the ``spike`` +machine must ensure that they're setting the ``spike`` machine in the +command line (``-M spike``). + linux-user mode CPUs -------------------- diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -XXX,XX +XXX,XX @@ static void spike_machine_class_init(ObjectClass *oc, const void *data) mc->desc = "RISC-V Spike board"; mc->init = spike_board_init; mc->max_cpus = SPIKE_CPUS_MAX; - mc->is_default = true; mc->default_cpu_type = TYPE_RISCV_CPU_BASE; mc->possible_cpu_arch_ids = riscv_numa_possible_cpu_arch_ids; mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props; -- 2.53.0
From: Alistair Francis <alistair.francis@wdc.com> The RISC-V shakti_c machine hasn't had meaningful contributions since 2021 and is currently unmaintained. The machine is scheduled to be removed as it appears to have no users. Signed-off-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Message-ID: <20260430052218.771358-3-alistair.francis@wdc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- docs/about/deprecated.rst | 7 +++++++ hw/riscv/shakti_c.c | 1 + 2 files changed, 8 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -XXX,XX +XXX,XX @@ deprecated; use the new name ``dtb-randomness`` instead. The new name better reflects the way this property affects all random data within the device tree blob, not just the ``kaslr-seed`` node. +RISC-V Shakti machine (since 11.1) +'''''''''''''''''''''''''''''''''' + +The RISC-V ``shakti_c`` machine hasn't had meaningful contributions since 2021 +and is currently unmaintained. The machine is scheduled to be removed as it +appears to have no users. + Backend options --------------- diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/shakti_c.c +++ b/hw/riscv/shakti_c.c @@ -XXX,XX +XXX,XX @@ static void shakti_c_machine_class_init(ObjectClass *klass, const void *data) mc->init = shakti_c_machine_state_init; mc->default_cpu_type = TYPE_RISCV_CPU_SHAKTI_C; mc->valid_cpu_types = valid_cpu_types; + mc->deprecation_reason = "Currently unmaintained with no known users"; mc->default_ram_id = "riscv.shakti.c.ram"; } -- 2.53.0
From: Frank Chang <frank.chang@sifive.com> MISA.C is set if the following extensions are selected: * Zca and not F. * Zca, Zcf and F (but not D) is specified (RV32 only). * Zca, Zcf and Zcd if D is specified (RV32 only). * Zca, Zcd if D is specified (RV64 only). Therefore, MISA.C must be set according to the Zc* extension rules. Warn the user if RVC is explicitly disabled but MISA.C is required by the rules above. Signed-off-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Max Chou <max.chou@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260424050509.3935180-2-frank.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/tcg/tcg-cpu.c | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_enable_implied_rules(RISCVCPU *cpu) } } +/* + * MISA.C is set if the following extensions are selected: + * - Zca and not F. + * - Zca, Zcf and F (but not D) is specified on RV32. + * - Zca, Zcf and Zcd if D is specified on RV32. + * - Zca, Zcd if D is specified on RV64. + */ +static void riscv_cpu_update_misa_c(RISCVCPU *cpu) +{ + CPURISCVState *env = &cpu->env; + bool set_misa_c = false; + + if (riscv_has_ext(env, RVC)) { + return; + } + + if (cpu->cfg.ext_zca && !riscv_has_ext(env, RVF)) { + set_misa_c = true; + } else if (riscv_cpu_mxl(env) == MXL_RV32 && + cpu->cfg.ext_zca && cpu->cfg.ext_zcf && + (riscv_has_ext(env, RVD) ? cpu->cfg.ext_zcd : + riscv_has_ext(env, RVF))) { + set_misa_c = true; + } else if (riscv_cpu_mxl(env) == MXL_RV64 && + cpu->cfg.ext_zca && cpu->cfg.ext_zcd) { + set_misa_c = true; + } + + if (set_misa_c) { + if (cpu_misa_ext_is_user_set(RVC)) { + warn_report("RVC mandated by Zca/Zcf/Zcd extensions"); + return; + } + + riscv_cpu_set_misa_ext(env, env->misa_ext | RVC); + } +} + void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp) { CPURISCVState *env = &cpu->env; @@ -XXX,XX +XXX,XX @@ void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp) riscv_cpu_init_implied_exts_rules(); riscv_cpu_enable_implied_rules(cpu); + riscv_cpu_update_misa_c(cpu); riscv_cpu_validate_misa_priv(env, &local_err); if (local_err != NULL) { -- 2.53.0
From: Frank Chang <frank.chang@sifive.com> MISA.X is set if there are any non-standard extensions. We should set MISA.X when any of the vendor extensions is enabled. Signed-off-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Max Chou <max.chou@sifive.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260424050509.3935180-3-frank.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 1 + target/riscv/tcg/tcg-cpu.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ typedef struct CPUArchState CPURISCVState; #define RVH RV('H') #define RVG RV('G') #define RVB RV('B') +#define RVX RV('X') extern const uint32_t misa_bits[]; const char *riscv_get_misa_ext_name(uint32_t bit); diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_update_misa_c(RISCVCPU *cpu) } } +/* MISA.X is set when any of the non-standard extensions is enabled. */ +static void riscv_cpu_update_misa_x(RISCVCPU *cpu) +{ + CPURISCVState *env = &cpu->env; + const RISCVCPUMultiExtConfig *arr = riscv_cpu_vendor_exts; + + for (int i = 0; arr[i].name != NULL; i++) { + if (isa_ext_is_enabled(cpu, arr[i].offset)) { + riscv_cpu_set_misa_ext(env, env->misa_ext | RVX); + break; + } + } +} + void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp) { CPURISCVState *env = &cpu->env; @@ -XXX,XX +XXX,XX @@ void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp) riscv_cpu_init_implied_exts_rules(); riscv_cpu_enable_implied_rules(cpu); riscv_cpu_update_misa_c(cpu); + riscv_cpu_update_misa_x(cpu); riscv_cpu_validate_misa_priv(env, &local_err); if (local_err != NULL) { -- 2.53.0
From: Alistair Francis <alistair.francis@wdc.com> Mark the OpenSBI Firmware as maintained by me (Alistair Francis) and add Daniel Henrique Barboza as a reviewer. Signed-off-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20260506234243.1873782-1-alistair.francis@wdc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ S: Supported F: hw/i386/amd_iommu* OpenSBI Firmware +M: Alistair Francis <Alistair.Francis@wdc.com> +R: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> L: qemu-riscv@nongnu.org S: Supported F: pc-bios/opensbi-* -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Update OpenSBI and the pre-built opensbi32 and opensbi64 images to version 1.8.1. We're going for the intermediate/bug fix release 1.8.1 directly due to a PMP related fix that was impacting QEMU profile CPUs. Changelog for v1.8.1: * Typo fix in comments of sbi_hartindex_to_hartid() * Fix hart protection abstraction for platforms without PMP Changelog for v1.8 includes, among other things: - IPI device ratings - SiFive CLINT v2 support - SiFive PL2 cache controller driver - SiFive Extensible Cache (EC) driver - SiFive TMC0 based HSM driver - SiFive SMC0 based system suspend driver - MPXY RPMI mailbox driver for voltage service group - MPXY RPMI mailbox driver for device power service group - MPXY RPMI mailbox driver for performance service group Check out the full release log at [1] for more info. [1] https://github.com/riscv-software-src/opensbi/releases/tag/v1.8 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260108160825.171794-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- .../opensbi-riscv32-generic-fw_dynamic.bin | Bin 268752 -> 270384 bytes .../opensbi-riscv64-generic-fw_dynamic.bin | Bin 273048 -> 275928 bytes roms/opensbi | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin index XXXXXXX..XXXXXXX 100644 Binary files a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin and b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin differ diff --git a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin index XXXXXXX..XXXXXXX 100644 Binary files a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin and b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin differ diff --git a/roms/opensbi b/roms/opensbi index XXXXXXX..XXXXXXX 160000 --- a/roms/opensbi +++ b/roms/opensbi @@ -1 +1 @@ -Subproject commit a32a91069119e7a5aa31e6bc51d5e00860be3d80 +Subproject commit 74434f255873d74e56cc50aa762d1caf24c099f8 -- 2.53.0
From: Mohamed Ayman <mohamedaymanworkspace@gmail.com> Use a pointer to the hart instead of copying RISCVCPU when passing pmu_avail_ctrs to riscv_pmu_generate_fdt_node(). Signed-off-by: Mohamed Ayman <mohamedaymanworkspace@gmail.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260503180252.1122776-1-mohamedaymanworkspace@gmail.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -XXX,XX +XXX,XX @@ static void create_fdt_pmu(RISCVVirtState *s) { g_autofree char *pmu_name = g_strdup_printf("/pmu"); MachineState *ms = MACHINE(s); - RISCVCPU hart = s->soc[0].harts[0]; + RISCVCPU *hart = &s->soc[0].harts[0]; qemu_fdt_add_subnode(ms->fdt, pmu_name); qemu_fdt_setprop_string(ms->fdt, pmu_name, "compatible", "riscv,pmu"); - riscv_pmu_generate_fdt_node(ms->fdt, hart.pmu_avail_ctrs, pmu_name); + riscv_pmu_generate_fdt_node(ms->fdt, hart->pmu_avail_ctrs, pmu_name); } static void create_fdt_sockets(RISCVVirtState *s, -- 2.53.0
From: Zishun Yi <vulab@iscas.ac.cn> The Zicfilp extension adds the MLPE field to the mseccfg CSR. According to the RISC-V Privileged Specification, mseccfg exists if any extension that adds a field to it is implemented. Currently, the `have_mseccfg()` predicate function checks for Smepmp, Zkr, and Smmpm, but misses Zicfilp. As a result, if a CPU is configured with `zicfilp=true` but without the other extensions, accessing the mseccfg CSR will incorrectly raise an illegal instruction exception. This patch adds the missing check for `ext_zicfilp` to ensure the CSR is properly accessible when the Zicfilp extension is enabled. This issue was discovered and reported by SpecHunter, an AI-driven architecture specification analysis tool. Link: https://github.com/yizishun/rv-isa-sec/blob/master/output/riscv-isa-manual/pr-2561/qemu.txt Signed-off-by: Zishun Yi <vulab@iscas.ac.cn> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260511072705.3015986-1-vulab@iscas.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException have_mseccfg(CPURISCVState *env, int csrno) if (riscv_cpu_cfg(env)->ext_smmpm) { return RISCV_EXCP_NONE; } + if (riscv_cpu_cfg(env)->ext_zicfilp) { + return RISCV_EXCP_NONE; + } return RISCV_EXCP_ILLEGAL_INST; } -- 2.53.0
From: Andrew Jones <andrew.jones@oss.qualcomm.com> The Svnapot extension encodes a 64KB leaf PTE by setting PTE_N and storing bits [3:0] of the PPN as a NAPOT size indicator. The IOMMU model wasn't checking PTE_N and therefore was using the raw (NAPOT- encoded) PPN directly in the physical address, yielding an address 32 KB above the correct base. Fix both riscv_iommu_spa_fetch() and pdt_memory_read() by mirroring the Svnapot handling already present in target/riscv/cpu_helper.c: napot_bits = ctz64(ppn) + 1 /* 4 for 64KB */ napot_mask = (1 << napot_bits) - 1 /* 0xF */ phys_base = PPN_PHYS(ppn & ~napot_mask) page_offset = addr & (PPN_PHYS(napot_mask) | (TARGET_PAGE_SIZE - 1)) The spec only defines napot_bits == 4 (64KB); any other value is treated as a reserved encoding. This is a fix, rather than new feature support, because the spec says "IOMMU implementations must support the Svnapot standard extension for NAPOT Translation Contiguity." Fixes: 0c54acb8243d ("hw/riscv: add RISC-V IOMMU base emulation") Cc: qemu-stable@nongnu.org Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Reviewed-by: Tomasz Jeznach <tjeznach@rivosinc.com> Message-ID: <20260508205129.377032-1-andrew.jones@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu.c | 44 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -XXX,XX +XXX,XX @@ static bool riscv_iommu_msi_check(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, return true; } +/* Returns the NAPOT page mask, or 0 for reserved encodings. */ +static hwaddr riscv_iommu_napot_page_mask(hwaddr ppn, hwaddr addr, hwaddr *out) +{ + int napot_bits = ctz64(ppn) + 1; + hwaddr napot_mask, page_mask; + + /* The spec only defines 64KB (napot_bits == 4) */ + if (napot_bits != 4) { + return 0; + } + + napot_mask = (1ULL << napot_bits) - 1; + page_mask = PPN_PHYS(napot_mask) | (TARGET_PAGE_SIZE - 1); + + *out = PPN_PHYS(ppn & ~napot_mask) | (addr & page_mask); + + return page_mask; +} + /* * RISCV IOMMU Address Translation Lookup - Page Table Walk * @@ -XXX,XX +XXX,XX @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, } else { /* Leaf PTE, translation completed. */ sc[pass].step = sc[pass].levels; - base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1)); - /* Update address mask based on smallest translation granularity */ - iotlb->addr_mask &= (1ULL << va_skip) - 1; + + if (pte & PTE_N) { + hwaddr mask = riscv_iommu_napot_page_mask(ppn, addr, &base); + + if (!mask) { + break; + } + iotlb->addr_mask &= mask; + } else { + base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1)); + /* Update address mask based on smallest translation granularity */ + iotlb->addr_mask &= (1ULL << va_skip) - 1; + } + /* Continue with S-Stage translation? */ if (pass && sc[0].step != sc[0].levels) { pass = S_STAGE; @@ -XXX,XX +XXX,XX @@ static MemTxResult pdt_memory_read(RISCVIOMMUState *s, return MEMTX_ACCESS_ERROR; /* Misaligned PPN */ } else { /* Leaf PTE, translation completed. */ - base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1)); + if (pte & PTE_N) { + if (!riscv_iommu_napot_page_mask(ppn, addr, &base)) { + return MEMTX_ACCESS_ERROR; + } + } else { + base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1)); + } break; } -- 2.53.0
From: Anton Blanchard <antonb@tenstorrent.com> Widening reductions read vs2 as a vector of SEW elements and vs1[0] as a scalar of 2*SEW. The ISA does not allow the same vector register to be read with different EEWs, so they must not overlap. vs1 is read as a scalar from element 0, so it is treated as a single vector register (independent of LMUL) when checking overlap. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3208 Signed-off-by: Anton Blanchard <antonb@tenstorrent.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260417080328.31918-1-antonb@tenstorrent.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/insn_trans/trans_rvv.c.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -XXX,XX +XXX,XX @@ GEN_OPIVV_TRANS(vredxor_vs, reduction_check) static bool reduction_widen_check(DisasContext *s, arg_rmrr *a) { return reduction_check(s, a) && (s->sew < MO_64) && + !is_overlapped(a->rs1, 1, a->rs2, 1 << MAX(s->lmul, 0)) && ((s->sew + 1) <= (s->cfg_ptr->elen >> 4)); } -- 2.53.0
From: Frank Chang <frank.chang@sifive.com> It's possible that the transmit watermark level (txctrl.txcnt) is updated when the user writes to txctrl register, which may decrease the transmit watermark level to less than the number of entries in the transmit FIFO. In such a case, the interrupt should be raised so we need to call sifive_uart_update_irq() to check and update interrupt when txctrl register is written. Otherwise, the interrupt will have to be delayed until next TX FIFO transmission is processed. Suggested-by: Chao Liu <chao.liu.zevorn@gmail.com> Signed-off-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260513030503.3665414-1-frank.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/char/sifive_uart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index XXXXXXX..XXXXXXX 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -XXX,XX +XXX,XX @@ sifive_uart_write(void *opaque, hwaddr addr, if (SIFIVE_UART_TXEN(s->txctrl) && !fifo8_is_empty(&s->tx_fifo)) { sifive_uart_trigger_tx_fifo(s); } + sifive_uart_update_irq(s); return; case SIFIVE_UART_RXCTRL: s->rxctrl = val64; -- 2.53.0
From: Abhigyan Kumar <314abh@gmail.com> RISC-V Privileged Specification 3.1.8 (Machine Trap Delegation Registers (medeleg and mideleg)) mentions: "For exceptions that cannot occur in less privileged modes, the corresponding medeleg bits should be read-only zero. In particular, medeleg[11] is read-only zero." QEMU incorrectly included RISCV_EXCP_M_ECALL in DELEGABLE_EXCPS. It allowed the 11th bit to be written and read as set. Fixed by removing it from the DELEGABLE_EXCPS mask, adhering to the specification. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3438 Signed-off-by: Abhigyan Kumar <314abh@gmail.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260427060849.749179-2-314abh@gmail.com> [ Changes by AF: - Remove comment ] Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static const uint64_t all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS | (1ULL << (RISCV_EXCP_U_ECALL)) | \ (1ULL << (RISCV_EXCP_S_ECALL)) | \ (1ULL << (RISCV_EXCP_VS_ECALL)) | \ - (1ULL << (RISCV_EXCP_M_ECALL)) | \ (1ULL << (RISCV_EXCP_INST_PAGE_FAULT)) | \ (1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT)) | \ (1ULL << (RISCV_EXCP_STORE_PAGE_FAULT)) | \ -- 2.53.0
From: "chen.zhongyao@zte.com.cn" <chen.zhongyao@zte.com.cn> The risc-v vector spec defines vmv.s.x and vfmv.s.f as writing element 0 of the destination register while the remaining destination elements follow the current tail policy. When QEMU runs with rvv_ta_all_1s enabled, those elements must therefore become all 1s. QEMU handled both instructions as translation-time special cases that directly wrote vd[0] and skipped the usual tail processing. As a result, vmv.s.x and vfmv.s.f left the remaining destination elements unchanged instead of applying the configured tail policy. Fix this by routing both instructions through a helper that writes vd[0] and then treats the rest of the destination register as tail, reusing the existing agnostic-element fill logic. Signed-off-by: Zhongyao Chen <chen.zhongyao@zte.com.cn> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <202605061005.646A537u037157@mse-fl1.zte.com.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/helper.h | 4 +++ target/riscv/vector_helper.c | 18 ++++++++++ target/riscv/insn_trans/trans_rvv.c.inc | 44 ++++++++----------------- 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/target/riscv/helper.h b/target/riscv/helper.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -XXX,XX +XXX,XX @@ DEF_HELPER_4(vmv_v_x_b, void, ptr, i64, env, i32) DEF_HELPER_4(vmv_v_x_h, void, ptr, i64, env, i32) DEF_HELPER_4(vmv_v_x_w, void, ptr, i64, env, i32) DEF_HELPER_4(vmv_v_x_d, void, ptr, i64, env, i32) +DEF_HELPER_4(vset_velem0_b, void, ptr, i64, env, i32) +DEF_HELPER_4(vset_velem0_h, void, ptr, i64, env, i32) +DEF_HELPER_4(vset_velem0_w, void, ptr, i64, env, i32) +DEF_HELPER_4(vset_velem0_d, void, ptr, i64, env, i32) DEF_HELPER_6(vsaddu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vsaddu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VMV_VX(vmv_v_x_h, int16_t, H2) GEN_VEXT_VMV_VX(vmv_v_x_w, int32_t, H4) GEN_VEXT_VMV_VX(vmv_v_x_d, int64_t, H8) +#define GEN_VEXT_SET_VELEM0(NAME, ETYPE, H) \ +void HELPER(NAME)(void *vd, uint64_t s1, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t vlenb = riscv_cpu_cfg(env)->vlenb; \ + uint32_t vta = vext_vta(desc); \ + \ + *((ETYPE *)vd + H(0)) = (ETYPE)s1; \ + /* Treat every element past vd[0] as tail for scalar-to-vector moves. */ \ + vext_set_elems_1s(vd, vta, esz, vlenb); \ +} + +GEN_VEXT_SET_VELEM0(vset_velem0_b, int8_t, H1) +GEN_VEXT_SET_VELEM0(vset_velem0_h, int16_t, H2) +GEN_VEXT_SET_VELEM0(vset_velem0_w, int32_t, H4) +GEN_VEXT_SET_VELEM0(vset_velem0_d, int64_t, H8) + #define GEN_VEXT_VMERGE_VV(NAME, ETYPE, H) \ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ CPURISCVState *env, uint32_t desc) \ diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -XXX,XX +XXX,XX @@ static void vec_element_loadi(DisasContext *s, TCGv_i64 dest, load_element(dest, tcg_env, endian_ofs(s, vreg, idx), s->sew, sign); } -/* Integer Scalar Move Instruction */ +typedef void gen_helper_vset_velem0(TCGv_ptr, TCGv_i64, TCGv_env, TCGv_i32); -static void store_element(TCGv_i64 val, TCGv_ptr base, - int ofs, int sew) +static void vec_element_storei_tail(DisasContext *s, int vreg, TCGv_i64 val) { - switch (sew) { - case MO_8: - tcg_gen_st8_i64(val, base, ofs); - break; - case MO_16: - tcg_gen_st16_i64(val, base, ofs); - break; - case MO_32: - tcg_gen_st32_i64(val, base, ofs); - break; - case MO_64: - tcg_gen_st_i64(val, base, ofs); - break; - default: - g_assert_not_reached(); - } -} + static gen_helper_vset_velem0 * const fns[4] = { + gen_helper_vset_velem0_b, gen_helper_vset_velem0_h, + gen_helper_vset_velem0_w, gen_helper_vset_velem0_d, + }; + TCGv_ptr dest = tcg_temp_new_ptr(); + uint32_t data = FIELD_DP32(0, VDATA, VTA, s->vta); + TCGv_i32 desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); -/* - * Store vreg[idx] = val. - * The index must be in range of VLMAX. - */ -static void vec_element_storei(DisasContext *s, int vreg, - int idx, TCGv_i64 val) -{ - store_element(val, tcg_env, endian_ofs(s, vreg, idx), s->sew); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vreg)); + fns[s->sew](dest, val, tcg_env, desc); } /* vmv.x.s rd, vs2 # x[rd] = vs2[0] */ @@ -XXX,XX +XXX,XX @@ static bool trans_vmv_s_x(DisasContext *s, arg_vmv_s_x *a) */ s1 = get_gpr(s, a->rs1, EXT_NONE); tcg_gen_ext_tl_i64(t1, s1); - vec_element_storei(s, a->rd, 0, t1); + vec_element_storei_tail(s, a->rd, t1); gen_set_label(over); tcg_gen_movi_tl(cpu_vstart, 0); finalize_rvv_inst(s); @@ -XXX,XX +XXX,XX @@ static bool trans_vfmv_s_f(DisasContext *s, arg_vfmv_s_f *a) t1 = tcg_temp_new_i64(); do_nanbox(s, t1, cpu_fpr[a->rs1]); - vec_element_storei(s, a->rd, 0, t1); + vec_element_storei_tail(s, a->rd, t1); gen_set_label(over); tcg_gen_movi_tl(cpu_vstart, 0); -- 2.53.0
From: Zishun Yi <vulab@iscas.ac.cn> The RISC-V unpriv specification (zvk.adoc) states that "Zvknhb implies Zvknha". This means that enabling Zvknhb should automatically enable Zvknha. Add Zvknha to ZVKNHB_IMPLIED to ensure the dependency is correctly enforced. This issue was discovered and reported by SpecHunter, an AI-driven architecture specification analysis tool. Link:https://github.com/yizishun/rv-isa-sec/blob/master/output/riscv-isa-manual/pr-2635/qemu.txt Signed-off-by: Zishun Yi <vulab@iscas.ac.cn> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260511051736.2916225-1-vulab@iscas.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static RISCVCPUImpliedExtsRule ZVKNG_IMPLIED = { static RISCVCPUImpliedExtsRule ZVKNHB_IMPLIED = { .ext = CPU_CFG_OFFSET(ext_zvknhb), .implied_multi_exts = { - CPU_CFG_OFFSET(ext_zve64x), + CPU_CFG_OFFSET(ext_zve64x), CPU_CFG_OFFSET(ext_zvknha), RISCV_IMPLIED_EXTS_RULE_END }, -- 2.53.0
From: Zishun Yi <vulab@iscas.ac.cn> Currently, the MENVCFG_CDE (Counter Delegation Enable) bit is unconditionally included in the base write mask for CSR_MENVCFG. This make the subsequent conditional check `(cfg->ext_smcdeleg ? MENVCFG_CDE : 0)` completely ineffective, as a bitwise OR cannot clear a bit that is already set. Fix this by removing MENVCFG_CDE from the initial base mask. The bit will now only be writable when explicitly granted by the `ext_smcdeleg` configuration. This issue was discovered and reported by SpecHunter, an AI-driven architecture specification analysis tool. Link: https://github.com/yizishun/rv-isa-sec/blob/master/output/riscv-isa-manual/pr-2601/qemu.txt Signed-off-by: Zishun Yi <vulab@iscas.ac.cn> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260511040534.2862443-1-vulab@iscas.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, { const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | - MENVCFG_CBZE | MENVCFG_CDE; + MENVCFG_CBZE; bool stce_changed = false; if (riscv_cpu_mxl(env) == MXL_RV64) { -- 2.53.0
From: Zishun Yi <vulab@iscas.ac.cn> According to the RISC-V smcdeleg specification: "When menvcfg.CDE=0, attempts to access scountinhibit raise an illegal-instruction exception." The current implementation of scountinhibit_pred() only checks the hardware extensions (ext_ssccfg, ext_smcdeleg) and virtualization status, but completely misses the runtime environment configuration check (menvcfg.CDE). This allows S-mode to access scountinhibit even when the M-mode has explicitly disabled counter delegation. This issue was discovered by the SpecHunter tool (https://github.com/yizishun/rv-isa-sec/blob/master/output/riscv-isa-manual/pr-2571/qemu.txt). Fixes: 6247dc2ef70b ("target/riscv: Add counter delegation/configuration support") Signed-off-by: Zishun Yi <vulab@iscas.ac.cn> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260508174917.371667-1-vulab@iscas.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException scountinhibit_pred(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } + if (!get_field(env->menvcfg, MENVCFG_CDE)) { + return RISCV_EXCP_ILLEGAL_INST; + } + if (env->virt_enabled) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } -- 2.53.0
From: Zishun Yi <vulab@iscas.ac.cn> According to the RISC-V Privileged Manual: "The Sv32 page-based virtual-memory scheme described in sv32 supports 34-bit physical addresses for RV32, so the PMP scheme must support addresses wider than XLEN for RV32." However, the current QEMU implementation uses `target_ulong` (which resolves to `uint32_t` on RV32) for PMP address variables. When shifting these addresses left (e.g., `this_addr << 2`), an integer overflow occurs, truncating the high bits of the 34-bit physical address. Fix this issue by changing the types of PMP address variables (`this_addr` and `prev_addr`) to `hwaddr`. This issue was discovered and reported by SpecHunter, an AI-driven architecture specification analysis tool. Link: https://github.com/yizishun/rv-isa-sec/blob/master/output/riscv-isa-manual/pr-2472/qemu.txt Signed-off-by: Zishun Yi <vulab@iscas.ac.cn> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260511102627.3120140-1-vulab@iscas.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/pmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -XXX,XX +XXX,XX @@ static void pmp_decode_napot(hwaddr a, hwaddr *sa, hwaddr *ea) void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index) { uint8_t this_cfg = env->pmp_state.pmp[pmp_index].cfg_reg; - target_ulong this_addr = env->pmp_state.pmp[pmp_index].addr_reg; - target_ulong prev_addr = 0u; + hwaddr this_addr = env->pmp_state.pmp[pmp_index].addr_reg; + hwaddr prev_addr = 0u; hwaddr sa = 0u; hwaddr ea = 0u; int g = pmp_get_granularity_g(env); -- 2.53.0
From: Zishun Yi <vulab@iscas.ac.cn> Currently, the Machine Security Configuration Register (mseccfg) was missing from the live migration state. This omission causes the register to be reset to zero on the destination host after migration. Fixed by adding vmstate_mseccfg subsection This vulnerability was discovered and reported by SpecHunter, an AI-driven architecture specification analysis tool. Link: https://github.com/yizishun/rv-isa-sec/blob/a22e4459cd026ae970791dfbd9cfe5d110fbd46b/output/riscv-isa-manual/pr-1879/qemu.txt#L121 Signed-off-by: Zishun Yi <vulab@iscas.ac.cn> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260511124828.3210477-1-vulab@iscas.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/machine.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_sstc = { } }; +static bool mseccfg_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + + return cpu->cfg.ext_smepmp || cpu->cfg.ext_zkr + || cpu->cfg.ext_smmpm || cpu->cfg.ext_zicfilp; +} + +static const VMStateDescription vmstate_mseccfg = { + .name = "cpu/mseccfg", + .version_id = 1, + .minimum_version_id = 1, + .needed = mseccfg_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINTTL(env.mseccfg, RISCVCPU), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", .version_id = 11, @@ -XXX,XX +XXX,XX @@ const VMStateDescription vmstate_riscv_cpu = { &vmstate_ssp, &vmstate_ctr, &vmstate_sstc, + &vmstate_mseccfg, NULL } }; -- 2.53.0
From: Alistair Francis <alistair.francis@wdc.com> The RISC-V spec describes bits 0-15 as standard fixed interrupts. The AIA spec on the other hand describes bits 0-12 as standard fixed interrupts. This conflict causes issues for us as we don't dynamically determine if AIA is enabled when setting the *delegable_ints consts. This means currently we incorrectly treat the LCOFIP bit as delegable, even if AIA is disabled, which is incorrect (see the issues mentioned below). The AIA spec indicates that implementations can determine which bits of 13-63 in mvien are writable, so let's just make it bits 15-63 to match the main spec. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3133 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3134 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3135 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3138 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3140 Signed-off-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260513051841.1671987-1-alistair.francis@wdc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno, #define VSTOPI_NUM_SRCS 5 /* - * All core local interrupts except the fixed ones 0:12. This macro is for + * All core local interrupts except the fixed ones 0:15. This macro is for * virtual interrupts logic so please don't change this to avoid messing up * the whole support, For reference see AIA spec: `5.3 Interrupt filtering and * virtual interrupts for supervisor level` and `6.3.2 Virtual interrupts for * VS level`. */ -#define LOCAL_INTERRUPTS (~0x1FFFULL) +#define LOCAL_INTERRUPTS (~0xFFFFULL) static const uint64_t delegable_ints = S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS | MIP_LCOFIP; -- 2.53.0
From: Zishun Yi <vulab@iscas.ac.cn> Currently, the `mseccfg` CSR is only cleared to 0 during reset if the `ext_smepmp` is enabled. However, this register is now shared by several other extensions such as `zkr`, `smmpm`, and `zicfilp`. Fix by clearing `mseccfg` if any dependent extension is present, and adjusting the relevant comments. This vulnerability was discovered and reported by SpecHunter, an AI-driven architecture specification analysis tool. Link: https://github.com/yizishun/rv-isa-sec/blob/master/output/riscv-svvptc/pr-134/qemu.txt Signed-off-by: Zishun Yi <vulab@iscas.ac.cn> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260512052240.330815-1-vulab@iscas.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type) /* * Clear mseccfg and unlock all the PMP entries upon reset. - * This is allowed as per the priv and smepmp specifications - * and is needed to clear stale entries across reboots. + * This is required as per the priv, smepmp, and other security + * extension specifications that share this CSR, and is needed + * to clear stale entries across reboots. */ - if (riscv_cpu_cfg(env)->ext_smepmp) { + if (riscv_cpu_cfg(env)->ext_smepmp || + riscv_cpu_cfg(env)->ext_zkr || + riscv_cpu_cfg(env)->ext_smmpm || + riscv_cpu_cfg(env)->ext_zicfilp) { env->mseccfg = 0; } -- 2.53.0
From: Zongmin Zhou <zhouzongmin@kylinos.cn> When the Zicbop extension is available expose it to the KVM guest. Also read and validate cbop_blocksize from the host via KVM interface. Signed-off-by: Zongmin Zhou <zhouzongmin@kylinos.cn> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <9e388aa9984ea9d3d0c85e1630c9195023938952.1778653159.git.zhouzongmin@kylinos.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/kvm/kvm-cpu.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -XXX,XX +XXX,XX @@ static void kvm_cpu_csr_set_u64(RISCVCPU *cpu, KVMCPUConfig *csr_cfg, static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("zicbom", ext_zicbom, KVM_RISCV_ISA_EXT_ZICBOM), + KVM_EXT_CFG("zicbop", ext_zicbop, KVM_RISCV_ISA_EXT_ZICBOP), KVM_EXT_CFG("zicboz", ext_zicboz, KVM_RISCV_ISA_EXT_ZICBOZ), KVM_EXT_CFG("ziccrse", ext_ziccrse, KVM_RISCV_ISA_EXT_ZICCRSE), KVM_EXT_CFG("zicntr", ext_zicntr, KVM_RISCV_ISA_EXT_ZICNTR), @@ -XXX,XX +XXX,XX @@ static KVMCPUConfig kvm_cboz_blocksize = { .kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicboz_block_size) }; +static KVMCPUConfig kvm_cbop_blocksize = { + .name = "cbop_blocksize", + .offset = CPU_CFG_OFFSET(cbop_blocksize), + .kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicbop_block_size) +}; + static KVMCPUConfig kvm_v_vlenb = { .name = "vlenb", .offset = CPU_CFG_OFFSET(vlenb), @@ -XXX,XX +XXX,XX @@ static void kvm_riscv_init_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cbom_blocksize); } + if (cpu->cfg.ext_zicbop) { + kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cbop_blocksize); + } + if (cpu->cfg.ext_zicboz) { kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cboz_blocksize); } @@ -XXX,XX +XXX,XX @@ void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp) int ret; /* short-circuit without spinning the scratch CPU */ - if (!cpu->cfg.ext_zicbom && !cpu->cfg.ext_zicboz && - !riscv_has_ext(env, RVV)) { + if (!cpu->cfg.ext_zicbom && !cpu->cfg.ext_zicbop && + !cpu->cfg.ext_zicboz && !riscv_has_ext(env, RVV)) { return; } @@ -XXX,XX +XXX,XX @@ void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp) } } + if (cpu->cfg.ext_zicbop && + riscv_cpu_option_set(kvm_cbop_blocksize.name)) { + + reg.id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CONFIG, + kvm_cbop_blocksize.kvm_reg_id); + reg.addr = (uint64_t)&val; + ret = ioctl(kvmcpu.cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_setg_errno(errp, errno, "Unable to read cbop_blocksize"); + return; + } + + if (cpu->cfg.cbop_blocksize != val) { + error_setg(errp, "Unable to set cbop_blocksize to a different " + "value than the host (%lu)", val); + return; + } + } + /* Users are setting vlen, not vlenb */ if (riscv_has_ext(env, RVV) && riscv_cpu_option_set("vlen")) { if (!kvm_v_vlenb.supported) { -- 2.53.0
From: Zongmin Zhou <zhouzongmin@kylinos.cn> Expose the BFloat16 extensions (Zfbfmin, Zvfbfmin, Zvfbfwma) to KVM guests when available in hardware. Signed-off-by: Zongmin Zhou <zhouzongmin@kylinos.cn> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <1258febb0eeb4ee286eb88f70b077a7f1ece507f.1778653159.git.zhouzongmin@kylinos.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/kvm/kvm-cpu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -XXX,XX +XXX,XX @@ static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("zacas", ext_zacas, KVM_RISCV_ISA_EXT_ZACAS), KVM_EXT_CFG("zawrs", ext_zawrs, KVM_RISCV_ISA_EXT_ZAWRS), KVM_EXT_CFG("zfa", ext_zfa, KVM_RISCV_ISA_EXT_ZFA), + KVM_EXT_CFG("zfbfmin", ext_zfbfmin, KVM_RISCV_ISA_EXT_ZFBFMIN), KVM_EXT_CFG("zfh", ext_zfh, KVM_RISCV_ISA_EXT_ZFH), KVM_EXT_CFG("zfhmin", ext_zfhmin, KVM_RISCV_ISA_EXT_ZFHMIN), KVM_EXT_CFG("zba", ext_zba, KVM_RISCV_ISA_EXT_ZBA), @@ -XXX,XX +XXX,XX @@ static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("zvbc", ext_zvbc, KVM_RISCV_ISA_EXT_ZVBC), KVM_EXT_CFG("zvfh", ext_zvfh, KVM_RISCV_ISA_EXT_ZVFH), KVM_EXT_CFG("zvfhmin", ext_zvfhmin, KVM_RISCV_ISA_EXT_ZVFHMIN), + KVM_EXT_CFG("zvfbfmin", ext_zvfbfmin, KVM_RISCV_ISA_EXT_ZVFBFMIN), + KVM_EXT_CFG("zvfbfwma", ext_zvfbfwma, KVM_RISCV_ISA_EXT_ZVFBFWMA), KVM_EXT_CFG("zvkb", ext_zvkb, KVM_RISCV_ISA_EXT_ZVKB), KVM_EXT_CFG("zvkg", ext_zvkg, KVM_RISCV_ISA_EXT_ZVKG), KVM_EXT_CFG("zvkned", ext_zvkned, KVM_RISCV_ISA_EXT_ZVKNED), -- 2.53.0
From: Zishun Yi <vulab@iscas.ac.cn> Fix a mode-to-capability comparison error in riscv_iommu_validate_device_ctx. The code was comparing fsc_mode (a value) against a capability bitmask, making the SV32 support check ineffective. This issue was discovered and reported by SpecHunter, an AI-driven architecture specification analysis tool. Link: https://github.com/yizishun/rv-isa-sec/blob/master/output/riscv-iommu/pr-694/qemu.txt Signed-off-by: Zishun Yi <vulab@iscas.ac.cn> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260511080904.3049446-1-vulab@iscas.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -XXX,XX +XXX,XX @@ static bool riscv_iommu_validate_device_ctx(RISCVIOMMUState *s, } if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) { - if (fsc_mode == RISCV_IOMMU_CAP_SV32 && + if (fsc_mode == RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV32 && !(s->cap & RISCV_IOMMU_CAP_SV32)) { return false; } -- 2.53.0
From: Chengbo Gao <gaochengbo@bosc.ac.cn> PCI bus numbers may still be unassigned when QEMU initializes a PCI device's bus-master address space. For devices behind bridges, pci_bus_num() can return 0 at that point because the guest has not yet programmed the bridge Secondary Bus Number register. The RISC-V IOMMU currently stores a fixed device_id in RISCVIOMMUSpace when the address space is created. If the guest later enumerates the device on a non-zero bus, DMA translation still uses the stale device_id and may look up the wrong device context in the DDT. Store the stable PCIBus pointer and devfn in RISCVIOMMUSpace instead, and compute the device_id from the current bus number when it is needed. This keeps DMA translation and ATS invalidation in sync with guest PCI bus enumeration. Signed-off-by: Chengbo Gao <gaochengbo@bosc.ac.cn> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260514020637.2819308-1-gaochengbo@bosc.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -XXX,XX +XXX,XX @@ struct RISCVIOMMUSpace { IOMMUMemoryRegion iova_mr; /* IOVA memory region for attached device */ AddressSpace iova_as; /* IOVA address space for attached device */ RISCVIOMMUState *iommu; /* Managing IOMMU device state */ - uint32_t devid; /* Requester identifier, AKA device_id */ + PCIBus *bus; /* PCI bus of the requester */ + uint8_t devfn; /* Requester identifier, AKA device_id */ bool notifier; /* IOMMU unmap notifier enabled */ QLIST_ENTRY(RISCVIOMMUSpace) list; }; @@ -XXX,XX +XXX,XX @@ struct RISCVIOMMUEntry { /* IOMMU index for transactions without process_id specified. */ #define RISCV_IOMMU_NOPROCID 0 +static uint32_t riscv_iommu_space_devid(RISCVIOMMUSpace *as) +{ + uint32_t devid = PCI_BUILD_BDF(pci_bus_num(as->bus), as->devfn); + + /* FIXME: PCIe bus remapping for attached endpoints. */ + devid |= as->iommu->bus << 8; + return devid; +} + static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type) { switch (vec_type) { @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_ctx_put(RISCVIOMMUState *s, void *ref) } /* Find or allocate address space for a given device */ -static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid) +static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, PCIBus *bus, + int devfn) { RISCVIOMMUSpace *as; - /* FIXME: PCIe bus remapping for attached endpoints. */ - devid |= s->bus << 8; - QLIST_FOREACH(as, &s->spaces, list) { - if (as->devid == devid) { + if (as->bus == bus && as->devfn == devfn) { break; } } @@ -XXX,XX +XXX,XX @@ static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid) as = g_new0(RISCVIOMMUSpace, 1); as->iommu = s; - as->devid = devid; + as->bus = bus; + as->devfn = devfn; snprintf(name, sizeof(name), "riscv-iommu-%04x:%02x.%d-iova", - PCI_BUS_NUM(as->devid), PCI_SLOT(as->devid), PCI_FUNC(as->devid)); + pci_bus_num(bus), PCI_SLOT(devfn), PCI_FUNC(devfn)); /* IOVA address space, untranslated addresses */ memory_region_init_iommu(&as->iova_mr, sizeof(as->iova_mr), @@ -XXX,XX +XXX,XX @@ static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid) QLIST_INSERT_HEAD(&s->spaces, as, list); - trace_riscv_iommu_new(s->parent_obj.id, PCI_BUS_NUM(as->devid), - PCI_SLOT(as->devid), PCI_FUNC(as->devid)); + trace_riscv_iommu_new(s->parent_obj.id, pci_bus_num(bus), + PCI_SLOT(devfn), PCI_FUNC(devfn)); } return &as->iova_as; } @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_ats(RISCVIOMMUState *s, pid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_PID); QLIST_FOREACH(as, &s->spaces, list) { - if (as->devid == devid) { + if (riscv_iommu_space_devid(as) == devid) { break; } } @@ -XXX,XX +XXX,XX @@ static IOMMUTLBEntry riscv_iommu_memory_region_translate( .addr_mask = ~0ULL, .perm = flag, }; + uint32_t devid = riscv_iommu_space_devid(as); - ctx = riscv_iommu_ctx(as->iommu, as->devid, iommu_idx, &ref); + ctx = riscv_iommu_ctx(as->iommu, devid, iommu_idx, &ref); if (ctx == NULL) { /* Translation disabled or invalid. */ iotlb.addr_mask = 0; @@ -XXX,XX +XXX,XX @@ static IOMMUTLBEntry riscv_iommu_memory_region_translate( } /* Trace all dma translations with original access flags. */ - trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(as->devid), - PCI_SLOT(as->devid), PCI_FUNC(as->devid), iommu_idx, + trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(devid), + PCI_SLOT(devid), PCI_FUNC(devid), iommu_idx, IOMMU_FLAG_STR[flag & IOMMU_RW], iotlb.iova, iotlb.translated_addr); @@ -XXX,XX +XXX,XX @@ static AddressSpace *riscv_iommu_find_as(PCIBus *bus, void *opaque, int devfn) /* Find first matching IOMMU */ while (s != NULL && as == NULL) { - as = riscv_iommu_space(s, PCI_BUILD_BDF(pci_bus_num(bus), devfn)); + as = riscv_iommu_space(s, bus, devfn); s = s->iommus.le_next; } -- 2.53.0
From: Guenter Roeck <linux@roeck-us.net> Minimal clock register configuration required to boot Linux without backtraces in the clock code. Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Conor Dooley <conor.dooley@microchip.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260514-reference-overhand-749149e36a88@spud> [ Changes by AF: - Fixup checkpatch errors ] Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/misc/mchp_pfsoc_ioscb.h | 2 ++ hw/misc/mchp_pfsoc_ioscb.c | 47 +++++++++++++++++++++++++++++- hw/misc/mchp_pfsoc_sysreg.c | 13 +++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/include/hw/misc/mchp_pfsoc_ioscb.h b/include/hw/misc/mchp_pfsoc_ioscb.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/misc/mchp_pfsoc_ioscb.h +++ b/include/hw/misc/mchp_pfsoc_ioscb.h @@ -XXX,XX +XXX,XX @@ typedef struct MchpPfSoCIoscbState { MemoryRegion mailbox; MemoryRegion cfg; MemoryRegion ccc; + MemoryRegion pll_nw_0; + MemoryRegion pll_nw_1; MemoryRegion pll_mss; MemoryRegion cfm_mss; MemoryRegion pll_ddr; diff --git a/hw/misc/mchp_pfsoc_ioscb.c b/hw/misc/mchp_pfsoc_ioscb.c index XXXXXXX..XXXXXXX 100644 --- a/hw/misc/mchp_pfsoc_ioscb.c +++ b/hw/misc/mchp_pfsoc_ioscb.c @@ -XXX,XX +XXX,XX @@ #define IOSCB_MAILBOX_BASE 0x07020800 #define IOSCB_CFG_BASE 0x07080000 #define IOSCB_CCC_BASE 0x08000000 +#define IOSCB_PLL_NW0_BASE 0x08100000 +#define IOSCB_PLL_NW1_BASE 0x08200000 #define IOSCB_PLL_MSS_BASE 0x0E001000 #define IOSCB_CFM_MSS_BASE 0x0E002000 #define IOSCB_PLL_DDR_BASE 0x0E010000 @@ -XXX,XX +XXX,XX @@ static const MemoryRegionOps mchp_pfsoc_dummy_ops = { /* All PLL modules in IOSCB have the same register layout */ #define PLL_CTRL 0x04 +#define PLL_REF_FB 0x08 +#define PLL_DIV_0_1 0x10 +#define PLL_DIV_2_3 0x14 +#define PLL_CTRL2 0x18 +#define PLL_CAL 0x1c +#define PLL_PHADJ 0x20 +#define SSCG_REG_0 0x24 +#define SSCG_REG_1 0x28 +#define SSCG_REG_2 0x2c +#define SSCG_REG_3 0x30 static uint64_t mchp_pfsoc_pll_read(void *opaque, hwaddr offset, unsigned size) @@ -XXX,XX +XXX,XX @@ static uint64_t mchp_pfsoc_pll_read(void *opaque, hwaddr offset, /* PLL is locked */ val = BIT(25); break; + case PLL_DIV_0_1: + case PLL_DIV_2_3: + val = 0x01000100; /* return valid post divider values */ + break; + case PLL_CTRL2: + val = 0x00001110; + break; + case PLL_REF_FB: + val = 0x00000100; /* RFDIV := 1 */ + break; + case SSCG_REG_2: + val = 0x00000001; /* INTIN := 1 */ + break; + case PLL_PHADJ: + val = 0x00000401; + break; default: qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " "(size %d, offset 0x%" HWADDR_PRIx ")\n", @@ -XXX,XX +XXX,XX @@ static uint64_t mchp_pfsoc_pll_read(void *opaque, hwaddr offset, return val; } +static void mchp_pfsoc_pll_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, value, offset); +} + static const MemoryRegionOps mchp_pfsoc_pll_ops = { .read = mchp_pfsoc_pll_read, - .write = mchp_pfsoc_dummy_write, + .write = mchp_pfsoc_pll_write, .endianness = DEVICE_LITTLE_ENDIAN, }; @@ -XXX,XX +XXX,XX @@ static void mchp_pfsoc_ioscb_realize(DeviceState *dev, Error **errp) "mchp.pfsoc.ioscb.ccc", IOSCB_CCC_REG_SIZE); memory_region_add_subregion(&s->container, IOSCB_CCC_BASE, &s->ccc); + memory_region_init_io(&s->pll_nw_0, OBJECT(s), &mchp_pfsoc_pll_ops, s, + "mchp.pfsoc.ioscb.pll_nw_0", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_PLL_NW0_BASE, &s->pll_nw_0); + + memory_region_init_io(&s->pll_nw_1, OBJECT(s), &mchp_pfsoc_pll_ops, s, + "mchp.pfsoc.ioscb.pll_nw_1", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_PLL_NW1_BASE, &s->pll_nw_1); + memory_region_init_io(&s->pll_mss, OBJECT(s), &mchp_pfsoc_pll_ops, s, "mchp.pfsoc.ioscb.pll_mss", IOSCB_SUBMOD_REG_SIZE); memory_region_add_subregion(&s->container, IOSCB_PLL_MSS_BASE, &s->pll_mss); diff --git a/hw/misc/mchp_pfsoc_sysreg.c b/hw/misc/mchp_pfsoc_sysreg.c index XXXXXXX..XXXXXXX 100644 --- a/hw/misc/mchp_pfsoc_sysreg.c +++ b/hw/misc/mchp_pfsoc_sysreg.c @@ -XXX,XX +XXX,XX @@ #include "hw/misc/mchp_pfsoc_sysreg.h" #include "system/runstate.h" +#define CLOCK_CONFIG_CR 0x8 +#define RTC_CLOCK_CR 0xc #define MSS_RESET_CR 0x18 #define ENVM_CR 0xb8 #define MESSAGE_INT 0x118c @@ -XXX,XX +XXX,XX @@ static uint64_t mchp_pfsoc_sysreg_read(void *opaque, hwaddr offset, uint32_t val = 0; switch (offset) { + case CLOCK_CONFIG_CR: + /* Icicle kit reference design cpu/axi/ahb divider setting */ + val = 0x24; + break; + case RTC_CLOCK_CR: + /* + * Bit 16 enables the RTC clock, 0x7d is the required divider + * setting for a 125 MHz reference. + */ + val = BIT(16) | 0x7d; + break; case ENVM_CR: /* Indicate the eNVM is running at the configured divider rate */ val = BIT(6); -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> The entry was missing from isa_edata_arr[]. Fixes: 32bbab666 ("target/riscv: add draft RISC-V Zbr ext as xbr0p93") Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-2-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(svrsw60t59b, PRIV_VERSION_1_13_0, ext_svrsw60t59b), ISA_EXT_DATA_ENTRY(svukte, PRIV_VERSION_1_13_0, ext_svukte), ISA_EXT_DATA_ENTRY(svvptc, PRIV_VERSION_1_13_0, ext_svvptc), + ISA_EXT_DATA_ENTRY(xlrbr, PRIV_VERSION_1_13_0, ext_xlrbr), ISA_EXT_DATA_ENTRY(xmipscbop, PRIV_VERSION_1_12_0, ext_xmipscbop), ISA_EXT_DATA_ENTRY(xmipscmov, PRIV_VERSION_1_12_0, ext_xmipscmov), ISA_EXT_DATA_ENTRY(xmipslsp, PRIV_VERSION_1_12_0, ext_xmipslsp), -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Noticed when realizing that 'svadu' was too far apart from 'svade'. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-3-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(smcdeleg, PRIV_VERSION_1_13_0, ext_smcdeleg), ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), ISA_EXT_DATA_ENTRY(smcsrind, PRIV_VERSION_1_13_0, ext_smcsrind), + ISA_EXT_DATA_ENTRY(smctr, PRIV_VERSION_1_12_0, ext_smctr), ISA_EXT_DATA_ENTRY(smdbltrp, PRIV_VERSION_1_13_0, ext_smdbltrp), ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp), ISA_EXT_DATA_ENTRY(smpmpmt, PRIV_VERSION_1_12_0, ext_smpmpmt), @@ -XXX,XX +XXX,XX @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf), ISA_EXT_DATA_ENTRY(sscounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(sscsrind, PRIV_VERSION_1_12_0, ext_sscsrind), + ISA_EXT_DATA_ENTRY(ssctr, PRIV_VERSION_1_12_0, ext_ssctr), 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), @@ -XXX,XX +XXX,XX @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(ssu64xl, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(supm, PRIV_VERSION_1_13_0, ext_supm), ISA_EXT_DATA_ENTRY(svade, PRIV_VERSION_1_11_0, ext_svade), - ISA_EXT_DATA_ENTRY(smctr, PRIV_VERSION_1_12_0, ext_smctr), - ISA_EXT_DATA_ENTRY(ssctr, PRIV_VERSION_1_12_0, ext_ssctr), ISA_EXT_DATA_ENTRY(svadu, PRIV_VERSION_1_12_0, ext_svadu), ISA_EXT_DATA_ENTRY(svinval, PRIV_VERSION_1_12_0, ext_svinval), ISA_EXT_DATA_ENTRY(svnapot, PRIV_VERSION_1_12_0, ext_svnapot), -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We want to reduce the usage of the riscv_cpu_* arrays in target/riscv/cpu.c, centering everything in isa_edata_arr[] instead, to reduce data duplication and make our lives a bit easier when adding new extensions. To do that we'll have to make isa_edata_arr[] do what the other arrays are currently doing, not breaking existing semantics in the process. The riscv_cpu_* arrays have a few exclusive uses: - set default extensions for rv32 and rv64; - create user CPU properties; - misc uses around tcg-cpu.c and kvm-cpu.c. We'll slowly make isa_edata_arr[] supersede these arrays. Start by adding a new 'prop_name' field in RISCVIsaExtData. This field is needed because not all extensions have properties and not all properties match the riscv,isa string name. Create two additional macros and reclassify existing isa_edata_arr[] entries as follows: - ISA_EXT_DATA_ENTRY creates an entry where prop_name == riscv,isa. Most of our regular extensions fall into this category; - ISA_EXPERIMENTAL_EXT_DATA_ENTRY: same as above but add a "x-" to the prop name, i.e. prop_name == "x-" + riscv,isa; - ISA_INTERNAL_EXT_DATA_ENTRY: used to declared internal extensions. By 'internal' we mean users/management can't set them on or off. With these new macros we can start our simplification by changing riscv-qmp-cmds to use isa_edata_arr[]. We'll just scroll through it once and gather what we need. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-4-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 1 + target/riscv/cpu.c | 60 +++++++++++++++++++++++------------ target/riscv/riscv-qmp-cmds.c | 30 ++++-------------- 3 files changed, 47 insertions(+), 44 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ extern const RISCVCPUMultiExtConfig riscv_cpu_named_features[]; typedef struct isa_ext_data { const char *name; + const char *prop_name; int min_version; int ext_enable_offset; } RISCVIsaExtData; diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_cfg_merge(RISCVCPUConfig *dest, const RISCVCPUConfig *src) #include "cpu_cfg_fields.h.inc" } +/* Use this for regular user facing extensions */ #define ISA_EXT_DATA_ENTRY(_name, _min_ver, _prop) \ - {#_name, _min_ver, CPU_CFG_OFFSET(_prop)} + {#_name, #_name, _min_ver, CPU_CFG_OFFSET(_prop)} + +/* + * Same as above but for experimental extensions. We'll add a + * "x-" right after "_name" when creating the user property. + */ +#define ISA_EXPERIMENTAL_EXT_DATA_ENTRY(_name, _min_ver, _prop) \ + {#_name, "x-" #_name, _min_ver, CPU_CFG_OFFSET(_prop)} + +/* + * Internal extensions are extensions we will declare in the + * riscv,isa DT but they don't have an user property, i.e. + * users/management can't enable/disable them. + */ +#define ISA_INTERNAL_EXT_DATA_ENTRY(_name, _min_ver, _prop) \ + {#_name, NULL, _min_ver, CPU_CFG_OFFSET(_prop)} /* * Here are the ordering rules of extension naming defined by RISC-V @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_cfg_merge(RISCVCPUConfig *dest, const RISCVCPUConfig *src) * instead. */ const RISCVIsaExtData isa_edata_arr[] = { - ISA_EXT_DATA_ENTRY(zic64b, PRIV_VERSION_1_12_0, ext_zic64b), + ISA_INTERNAL_EXT_DATA_ENTRY(zic64b, PRIV_VERSION_1_12_0, ext_zic64b), ISA_EXT_DATA_ENTRY(zicbom, PRIV_VERSION_1_12_0, ext_zicbom), ISA_EXT_DATA_ENTRY(zicbop, PRIV_VERSION_1_12_0, ext_zicbop), ISA_EXT_DATA_ENTRY(zicboz, PRIV_VERSION_1_12_0, ext_zicboz), - ISA_EXT_DATA_ENTRY(ziccamoa, PRIV_VERSION_1_11_0, has_priv_1_11), - ISA_EXT_DATA_ENTRY(ziccif, PRIV_VERSION_1_11_0, has_priv_1_11), - ISA_EXT_DATA_ENTRY(zicclsm, PRIV_VERSION_1_11_0, has_priv_1_11), + ISA_INTERNAL_EXT_DATA_ENTRY(ziccamoa, PRIV_VERSION_1_11_0, has_priv_1_11), + ISA_INTERNAL_EXT_DATA_ENTRY(ziccif, PRIV_VERSION_1_11_0, has_priv_1_11), + ISA_INTERNAL_EXT_DATA_ENTRY(zicclsm, PRIV_VERSION_1_11_0, has_priv_1_11), ISA_EXT_DATA_ENTRY(ziccrse, PRIV_VERSION_1_11_0, ext_ziccrse), ISA_EXT_DATA_ENTRY(zicfilp, PRIV_VERSION_1_12_0, ext_zicfilp), ISA_EXT_DATA_ENTRY(zicfiss, PRIV_VERSION_1_13_0, ext_zicfiss), @@ -XXX,XX +XXX,XX @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zilsd, PRIV_VERSION_1_12_0, ext_zilsd), ISA_EXT_DATA_ENTRY(zimop, PRIV_VERSION_1_13_0, ext_zimop), ISA_EXT_DATA_ENTRY(zmmul, PRIV_VERSION_1_12_0, ext_zmmul), - ISA_EXT_DATA_ENTRY(za64rs, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_INTERNAL_EXT_DATA_ENTRY(za64rs, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(zaamo, PRIV_VERSION_1_12_0, ext_zaamo), ISA_EXT_DATA_ENTRY(zabha, PRIV_VERSION_1_13_0, ext_zabha), ISA_EXT_DATA_ENTRY(zacas, PRIV_VERSION_1_12_0, ext_zacas), @@ -XXX,XX +XXX,XX @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), ISA_EXT_DATA_ENTRY(sdtrig, PRIV_VERSION_1_12_0, debug), - ISA_EXT_DATA_ENTRY(shcounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), - ISA_EXT_DATA_ENTRY(sha, PRIV_VERSION_1_12_0, ext_sha), - ISA_EXT_DATA_ENTRY(shgatpa, PRIV_VERSION_1_12_0, has_priv_1_12), - ISA_EXT_DATA_ENTRY(shtvala, PRIV_VERSION_1_12_0, has_priv_1_12), - ISA_EXT_DATA_ENTRY(shvsatpa, PRIV_VERSION_1_12_0, has_priv_1_12), - ISA_EXT_DATA_ENTRY(shvstvala, PRIV_VERSION_1_12_0, has_priv_1_12), - ISA_EXT_DATA_ENTRY(shvstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_INTERNAL_EXT_DATA_ENTRY(shcounterenw, PRIV_VERSION_1_12_0, + has_priv_1_12), + ISA_INTERNAL_EXT_DATA_ENTRY(sha, PRIV_VERSION_1_12_0, ext_sha), + ISA_INTERNAL_EXT_DATA_ENTRY(shgatpa, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_INTERNAL_EXT_DATA_ENTRY(shtvala, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_INTERNAL_EXT_DATA_ENTRY(shvsatpa, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_INTERNAL_EXT_DATA_ENTRY(shvstvala, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_INTERNAL_EXT_DATA_ENTRY(shvstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), ISA_EXT_DATA_ENTRY(smcdeleg, PRIV_VERSION_1_13_0, ext_smcdeleg), ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), @@ -XXX,XX +XXX,XX @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(smstateen, PRIV_VERSION_1_12_0, ext_smstateen), ISA_EXT_DATA_ENTRY(ssaia, PRIV_VERSION_1_12_0, ext_ssaia), ISA_EXT_DATA_ENTRY(ssccfg, PRIV_VERSION_1_13_0, ext_ssccfg), - ISA_EXT_DATA_ENTRY(ssccptr, PRIV_VERSION_1_11_0, has_priv_1_11), + ISA_INTERNAL_EXT_DATA_ENTRY(ssccptr, PRIV_VERSION_1_11_0, has_priv_1_11), ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf), - ISA_EXT_DATA_ENTRY(sscounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_INTERNAL_EXT_DATA_ENTRY(sscounterenw, PRIV_VERSION_1_12_0, + has_priv_1_12), ISA_EXT_DATA_ENTRY(sscsrind, PRIV_VERSION_1_12_0, ext_sscsrind), ISA_EXT_DATA_ENTRY(ssctr, PRIV_VERSION_1_12_0, ext_ssctr), 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(ssstateen, PRIV_VERSION_1_12_0, ext_ssstateen), - ISA_EXT_DATA_ENTRY(ssstrict, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_INTERNAL_EXT_DATA_ENTRY(ssstateen, PRIV_VERSION_1_12_0, ext_ssstateen), + ISA_INTERNAL_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), - ISA_EXT_DATA_ENTRY(sstvala, PRIV_VERSION_1_12_0, has_priv_1_12), - ISA_EXT_DATA_ENTRY(sstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), - ISA_EXT_DATA_ENTRY(ssu64xl, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_INTERNAL_EXT_DATA_ENTRY(sstvala, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_INTERNAL_EXT_DATA_ENTRY(sstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_INTERNAL_EXT_DATA_ENTRY(ssu64xl, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(supm, PRIV_VERSION_1_13_0, ext_supm), ISA_EXT_DATA_ENTRY(svade, PRIV_VERSION_1_11_0, ext_svade), ISA_EXT_DATA_ENTRY(svadu, PRIV_VERSION_1_12_0, ext_svadu), @@ -XXX,XX +XXX,XX @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(svnapot, PRIV_VERSION_1_12_0, ext_svnapot), ISA_EXT_DATA_ENTRY(svpbmt, PRIV_VERSION_1_12_0, ext_svpbmt), ISA_EXT_DATA_ENTRY(svrsw60t59b, PRIV_VERSION_1_13_0, ext_svrsw60t59b), - ISA_EXT_DATA_ENTRY(svukte, PRIV_VERSION_1_13_0, ext_svukte), + ISA_EXPERIMENTAL_EXT_DATA_ENTRY(svukte, PRIV_VERSION_1_13_0, ext_svukte), ISA_EXT_DATA_ENTRY(svvptc, PRIV_VERSION_1_13_0, ext_svvptc), ISA_EXT_DATA_ENTRY(xlrbr, PRIV_VERSION_1_13_0, ext_xlrbr), ISA_EXT_DATA_ENTRY(xmipscbop, PRIV_VERSION_1_12_0, ext_xmipscbop), diff --git a/target/riscv/riscv-qmp-cmds.c b/target/riscv/riscv-qmp-cmds.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/riscv-qmp-cmds.c +++ b/target/riscv/riscv-qmp-cmds.c @@ -XXX,XX +XXX,XX @@ static void riscv_obj_add_qdict_prop(Object *obj, QDict *qdict_out, } } -static void riscv_obj_add_multiext_props(Object *obj, QDict *qdict_out, - const RISCVCPUMultiExtConfig *arr) +static void riscv_obj_add_multiext_props(Object *obj, QDict *qdict_out) { - for (int i = 0; arr[i].name != NULL; i++) { - riscv_obj_add_qdict_prop(obj, qdict_out, arr[i].name); - } -} + const RISCVIsaExtData *edata; -static void riscv_obj_add_named_feats_qdict(Object *obj, QDict *qdict_out) -{ - const RISCVCPUMultiExtConfig *named_cfg; - RISCVCPU *cpu = RISCV_CPU(obj); - QObject *value; - bool flag_val; - - for (int i = 0; riscv_cpu_named_features[i].name != NULL; i++) { - named_cfg = &riscv_cpu_named_features[i]; - flag_val = isa_ext_is_enabled(cpu, named_cfg->offset); - value = QOBJECT(qbool_from_bool(flag_val)); - - qdict_put_obj(qdict_out, named_cfg->name, value); + for (edata = isa_edata_arr; edata && edata->name; edata++) { + if (edata->prop_name) { + riscv_obj_add_qdict_prop(obj, qdict_out, edata->prop_name); + } } } @@ -XXX,XX +XXX,XX @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, qdict_out = qdict_new(); - riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_extensions); - riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_experimental_exts); - riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_vendor_exts); - riscv_obj_add_named_feats_qdict(obj, qdict_out); + riscv_obj_add_multiext_props(obj, qdict_out); riscv_obj_add_profiles_qdict(obj, qdict_out); /* Add our CPU boolean options too */ -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Commit 7e4f75cadf ("target/riscv/tcg: hide warn for named feats when disabling via priv_ver") changed the design of disable_priv_spec_isa_exts to not warn when disabling what we were calling "named features" at that time. Back in that time we were adding RVA22 profile support and we were dealing with a RVI innovation: named features. These can be define as cute names for pre-existing behavior/parameters, e.g. 'zic64b' means all cache blocks must have 64 bytes. They do get inserted in the riscv,isa but they are not "real" extensions because users shouldn't be setting them in the command line, or so we thought back then, and we attempted to model that by calling it "named features". This didn't age that well: ziccrse is a so called named feature as far as TCG goes, but then KVM had to treat it as a regular extension that can be user set and we made an exception: ziccrse is a named feature that is user set by KVM. "Named features" are regular extensions that might or might not be user set, and might or might not warrant special handling during realize() by setting other parameters. And we'll being treating them as such, which will culminate in the end of riscv_cpu_named_features[] array in the end. This reverts commit 7e4f75cadf44ee67809c7ca82645a289a5268966. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-5-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/tcg/tcg-cpu.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu) } isa_ext_update_enabled(cpu, edata->ext_enable_offset, false); - - /* - * Do not show user warnings for named features that users - * can't enable/disable in the command line. See commit - * 68c9e54bea for more info. - */ - if (cpu_cfg_offset_is_named_feat(edata->ext_enable_offset)) { - continue; - } #ifndef CONFIG_USER_ONLY warn_report("disabling %s extension for hart 0x%" PRIx64 " because privilege spec version does not match", @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_update_named_features(RISCVCPU *cpu) cpu->cfg.has_priv_1_13 = true; } + /* zic64b is 1.12 or later */ cpu->cfg.ext_zic64b = cpu->cfg.cbom_blocksize == 64 && cpu->cfg.cbop_blocksize == 64 && - cpu->cfg.cboz_blocksize == 64; + cpu->cfg.cboz_blocksize == 64 && + cpu->cfg.has_priv_1_12; cpu->cfg.ext_ssstateen = cpu->cfg.ext_smstateen; -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We're using both isa_edata_arr[] and riscv_cpu_named_features[] to find an extension name based on a cfg offset. This is reminiscent of a past where isa_edata_arr[] didn't include entries for internal extensions. At this point we can use just isa_edata_arr[]. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-6-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/tcg/tcg-cpu.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static int cpu_cfg_ext_get_min_version(uint32_t ext_offset) static const char *cpu_cfg_ext_get_name(uint32_t ext_offset) { - const RISCVCPUMultiExtConfig *feat; const RISCVIsaExtData *edata; for (edata = isa_edata_arr; edata->name != NULL; edata++) { @@ -XXX,XX +XXX,XX @@ static const char *cpu_cfg_ext_get_name(uint32_t ext_offset) } } - for (feat = riscv_cpu_named_features; feat->name != NULL; feat++) { - if (feat->offset == ext_offset) { - return feat->name; - } - } - g_assert_not_reached(); } -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Deprecate riscv_cpu_enable_named_feat() by creating implied rules for 'sha' and 'ssstateen'. 'zic64' is always true given that our default block size is set to 64. With this change we homogeneize extension handling when enabling profiles. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-7-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 25 ++++++++++++++++++++++-- target/riscv/tcg/tcg-cpu.c | 40 -------------------------------------- 2 files changed, 23 insertions(+), 42 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static RISCVCPUImpliedExtsRule ZVKSG_IMPLIED = { }, }; +static RISCVCPUImpliedExtsRule SHA_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_sha), + .implied_misa_exts = RVH, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_smstateen), + CPU_CFG_OFFSET(ext_ssstateen), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + static RISCVCPUImpliedExtsRule SSCFG_IMPLIED = { .ext = CPU_CFG_OFFSET(ext_ssccfg), .implied_multi_exts = { @@ -XXX,XX +XXX,XX @@ static RISCVCPUImpliedExtsRule SSCTR_IMPLIED = { }, }; +static RISCVCPUImpliedExtsRule SSSTATEEN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_ssstateen), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_smstateen), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + static RISCVCPUImpliedExtsRule ZVFBFA_IMPLIED = { .ext = CPU_CFG_OFFSET(ext_zvfbfa), .implied_multi_exts = { @@ -XXX,XX +XXX,XX @@ RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = { &ZVFBFA_IMPLIED, &ZVFBFMIN_IMPLIED, &ZVFBFWMA_IMPLIED, &ZVFH_IMPLIED, &ZVFHMIN_IMPLIED, &ZVKN_IMPLIED, &ZVKNC_IMPLIED, &ZVKNG_IMPLIED, &ZVKNHB_IMPLIED, - &ZVKS_IMPLIED, &ZVKSC_IMPLIED, &ZVKSG_IMPLIED, &SSCFG_IMPLIED, - &SUPM_IMPLIED, &SSPM_IMPLIED, &SMCTR_IMPLIED, &SSCTR_IMPLIED, + &ZVKS_IMPLIED, &ZVKSC_IMPLIED, &ZVKSG_IMPLIED, &SHA_IMPLIED, + &SSCFG_IMPLIED, &SUPM_IMPLIED, &SSPM_IMPLIED, &SMCTR_IMPLIED, + &SSCTR_IMPLIED, &SSSTATEEN_IMPLIED, NULL }; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static const char *cpu_cfg_ext_get_name(uint32_t ext_offset) g_assert_not_reached(); } -static bool cpu_cfg_offset_is_named_feat(uint32_t ext_offset) -{ - const RISCVCPUMultiExtConfig *feat; - - for (feat = riscv_cpu_named_features; feat->name != NULL; feat++) { - if (feat->offset == ext_offset) { - return true; - } - } - - return false; -} - -static void riscv_cpu_enable_named_feat(RISCVCPU *cpu, uint32_t feat_offset) -{ - /* - * All other named features are already enabled - * in riscv_tcg_cpu_instance_init(). - */ - switch (feat_offset) { - case CPU_CFG_OFFSET(ext_zic64b): - cpu->cfg.cbom_blocksize = 64; - cpu->cfg.cbop_blocksize = 64; - cpu->cfg.cboz_blocksize = 64; - break; - case CPU_CFG_OFFSET(ext_sha): - if (!cpu_misa_ext_is_user_set(RVH)) { - riscv_cpu_write_misa_bit(cpu, RVH, true); - } - /* fallthrough */ - case CPU_CFG_OFFSET(ext_ssstateen): - cpu->cfg.ext_smstateen = true; - break; - } -} - static void cpu_bump_multi_ext_priv_ver(CPURISCVState *env, uint32_t ext_offset) { @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_set_profile(RISCVCPU *cpu, ext_offset = profile->ext_offsets[i]; if (profile->enabled) { - if (cpu_cfg_offset_is_named_feat(ext_offset)) { - riscv_cpu_enable_named_feat(cpu, ext_offset); - } - cpu_bump_multi_ext_priv_ver(&cpu->env, ext_offset); } -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> This array has no uses left. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-8-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 1 - target/riscv/cpu.c | 26 -------------------------- target/riscv/tcg/tcg-cpu.c | 4 ++-- 3 files changed, 2 insertions(+), 29 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ typedef struct RISCVCPUMultiExtConfig { extern const RISCVCPUMultiExtConfig riscv_cpu_extensions[]; extern const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[]; extern const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[]; -extern const RISCVCPUMultiExtConfig riscv_cpu_named_features[]; typedef struct isa_ext_data { const char *name; diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { { }, }; -/* - * 'Named features' is the name we give to extensions that we - * don't want to expose to users. They are either immutable - * (always enabled/disable) or they'll vary depending on - * the resulting CPU state. - * - * Some of them are always enabled depending on priv version - * of the CPU and are declared directly in isa_edata_arr[]. - * The ones listed here have special checks during finalize() - * time and require their own flags like regular extensions. - * See riscv_cpu_update_named_features() for more info. - */ -const RISCVCPUMultiExtConfig riscv_cpu_named_features[] = { - MULTI_EXT_CFG_BOOL("zic64b", ext_zic64b, true), - MULTI_EXT_CFG_BOOL("ssstateen", ext_ssstateen, true), - MULTI_EXT_CFG_BOOL("sha", ext_sha, true), - - /* - * 'ziccrse' has its own flag because the KVM driver - * wants to enable/disable it on its own accord. - */ - MULTI_EXT_CFG_BOOL("ziccrse", ext_ziccrse, true), - - { }, -}; - static void cpu_set_prop_err(RISCVCPU *cpu, const char *propname, Error **errp) { diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu) } } -static void riscv_cpu_update_named_features(RISCVCPU *cpu) +static void riscv_cpu_update_cfg(RISCVCPU *cpu) { if (cpu->env.priv_ver >= PRIV_VERSION_1_11_0) { cpu->cfg.has_priv_1_11 = true; @@ -XXX,XX +XXX,XX @@ void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp) return; } - riscv_cpu_update_named_features(cpu); + riscv_cpu_update_cfg(cpu); riscv_cpu_validate_profiles(cpu); if (cpu->cfg.ext_smepmp && !cpu->cfg.pmp) { -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> The non-standard extensions, in this particular function the vendor extensions, are all riscv,isa names that starts with 'x'. In theory this is a bit slower than using riscv_cpu_vendor_exts (isa_edata_arr is longer) but riscv_cpu_update_misa_x() is executed only once during finalize(), i.e. not a hot path. We're accepting a tiny performance hit as as a tradeoff for the code simplication we'll have later by removing all riscv_cpu_* arrays. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-9-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/tcg/tcg-cpu.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_update_misa_c(RISCVCPU *cpu) static void riscv_cpu_update_misa_x(RISCVCPU *cpu) { CPURISCVState *env = &cpu->env; - const RISCVCPUMultiExtConfig *arr = riscv_cpu_vendor_exts; + const RISCVIsaExtData *edata; - for (int i = 0; arr[i].name != NULL; i++) { - if (isa_ext_is_enabled(cpu, arr[i].offset)) { + for (edata = isa_edata_arr; edata && edata->name; edata++) { + if (edata->name[0] == 'x' + && isa_ext_is_enabled(cpu, edata->ext_enable_offset)) { riscv_cpu_set_misa_ext(env, env->misa_ext | RVX); break; } -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Use isa_edata_arr[] directly instead of going through 3 different arrays to populate the unavailable KVM properties. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-10-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/kvm/kvm-cpu.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_add_kvm_unavail_prop(Object *obj, const char *prop_name) NULL, (void *)prop_name); } -static void riscv_cpu_add_kvm_unavail_prop_array(Object *obj, - const RISCVCPUMultiExtConfig *array) -{ - const RISCVCPUMultiExtConfig *prop; - - g_assert(array); - - for (prop = array; prop && prop->name; prop++) { - riscv_cpu_add_kvm_unavail_prop(obj, prop->name); - } -} - static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj) { + const RISCVIsaExtData *edata; int i; riscv_add_satp_mode_properties(cpu_obj); @@ -XXX,XX +XXX,XX @@ static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj) NULL, multi_cfg); } - riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_extensions); - riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_vendor_exts); - riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_experimental_exts); + /* + * Mark all isa_edata_arr properties that collides with + * a KVM property as unavailable. + */ + for (edata = isa_edata_arr; edata && edata->name; edata++) { + if (edata->prop_name) { + riscv_cpu_add_kvm_unavail_prop(cpu_obj, edata->prop_name); + } + } /* We don't have the needed KVM support for profiles */ for (i = 0; riscv_profiles[i] != NULL; i++) { -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Use isa_edata_arr[] to enable all feasible extensions. Filter experimental and vendor extensions by checking if the riscv,isa or the prop name starts with 'x'. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-11-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/tcg/tcg-cpu.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_init_max_cpu_extensions(Object *obj) { RISCVCPU *cpu = RISCV_CPU(obj); CPURISCVState *env = &cpu->env; - const RISCVCPUMultiExtConfig *prop; + const RISCVIsaExtData *edata; /* Enable RVG and RVV that are disabled by default */ riscv_cpu_set_misa_ext(env, env->misa_ext | RVB | RVG | RVV); - for (prop = riscv_cpu_extensions; prop && prop->name; prop++) { - isa_ext_update_enabled(cpu, prop->offset, true); + for (edata = isa_edata_arr; edata && edata->name; edata++) { + if (edata->name[0] == 'x' + || (edata->prop_name && edata->prop_name[0] == 'x')) { + continue; + } + + isa_ext_update_enabled(cpu, edata->ext_enable_offset, true); } /* -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> The cpu_get_multi_ext_cfg/cpu_set_multi_ext_cfg callbacks are using a RISCVCPUMultiExtConfig pointer as opaque, but internally we're just using multi_ext_cfg->offset. Use multi_ext_cfg->offset as opaque instead. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-12-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/tcg/tcg-cpu.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_add_profiles(Object *cpu_obj) static void cpu_set_multi_ext_cfg(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - const RISCVCPUMultiExtConfig *multi_ext_cfg = opaque; RISCVCPU *cpu = RISCV_CPU(obj); + uint32_t cfg_offset = *(uint32_t *)opaque; bool vendor_cpu = riscv_cpu_is_vendor(obj); bool prev_val, value; @@ -XXX,XX +XXX,XX @@ static void cpu_set_multi_ext_cfg(Object *obj, Visitor *v, const char *name, return; } - cpu_cfg_ext_add_user_opt(multi_ext_cfg->offset, value); + cpu_cfg_ext_add_user_opt(cfg_offset, value); - prev_val = isa_ext_is_enabled(cpu, multi_ext_cfg->offset); + prev_val = isa_ext_is_enabled(cpu, cfg_offset); if (value == prev_val) { return; @@ -XXX,XX +XXX,XX @@ static void cpu_set_multi_ext_cfg(Object *obj, Visitor *v, const char *name, } if (value) { - cpu_bump_multi_ext_priv_ver(&cpu->env, multi_ext_cfg->offset); + cpu_bump_multi_ext_priv_ver(&cpu->env, cfg_offset); } - isa_ext_update_enabled(cpu, multi_ext_cfg->offset, value); + isa_ext_update_enabled(cpu, cfg_offset, value); } static void cpu_get_multi_ext_cfg(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - const RISCVCPUMultiExtConfig *multi_ext_cfg = opaque; - bool value = isa_ext_is_enabled(RISCV_CPU(obj), multi_ext_cfg->offset); + uint32_t cfg_offset = *(uint32_t *)opaque; + bool value = isa_ext_is_enabled(RISCV_CPU(obj), cfg_offset); visit_type_bool(v, name, &value, errp); } @@ -XXX,XX +XXX,XX @@ static void cpu_add_multi_ext_prop(Object *cpu_obj, object_property_add(cpu_obj, multi_cfg->name, "bool", cpu_get_multi_ext_cfg, cpu_set_multi_ext_cfg, - NULL, (void *)multi_cfg); + NULL, (void *)&multi_cfg->offset); if (!generic_cpu) { return; -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Do not rely on riscv_cpu_* arrays to set rv32/rv64 CPU defaults - add them to the CPU definition like we do with all other CPUs (aside from 'max'). Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-13-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 40 ++++++++++++++++++++++++++++++++++++++ target/riscv/tcg/tcg-cpu.c | 14 ------------- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE32, TYPE_RISCV_DYNAMIC_CPU, .cfg.max_satp_mode = VM_1_10_SV32, .misa_mxl_max = MXL_RV32, + + /* Default extensions as of QEMU 11.1. */ + .cfg.ext_zicbom = true, + .cfg.ext_zicbop = true, + .cfg.ext_zicboz = true, + .cfg.ext_zicntr = true, + .cfg.ext_zicsr = true, + .cfg.ext_zifencei = true, + .cfg.ext_zihintntl = true, + .cfg.ext_zihintpause = true, + .cfg.ext_zihpm = true, + .cfg.ext_zawrs = true, + .cfg.ext_zfa = true, + .cfg.ext_zba = true, + .cfg.ext_zbb = true, + .cfg.ext_zbc = true, + .cfg.ext_zbs = true, + .cfg.ext_sstc = true, + .cfg.ext_svadu = true, + .cfg.ext_svvptc = true, ), DEFINE_RISCV_CPU(TYPE_RISCV_CPU_IBEX, TYPE_RISCV_VENDOR_CPU, @@ -XXX,XX +XXX,XX @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE64, TYPE_RISCV_DYNAMIC_CPU, .cfg.max_satp_mode = VM_1_10_SV57, .misa_mxl_max = MXL_RV64, + + /* Default extensions as of QEMU 11.1. */ + .cfg.ext_zicbom = true, + .cfg.ext_zicbop = true, + .cfg.ext_zicboz = true, + .cfg.ext_zicntr = true, + .cfg.ext_zicsr = true, + .cfg.ext_zifencei = true, + .cfg.ext_zihintntl = true, + .cfg.ext_zihintpause = true, + .cfg.ext_zihpm = true, + .cfg.ext_zawrs = true, + .cfg.ext_zfa = true, + .cfg.ext_zba = true, + .cfg.ext_zbb = true, + .cfg.ext_zbc = true, + .cfg.ext_zbs = true, + .cfg.ext_sstc = true, + .cfg.ext_svadu = true, + .cfg.ext_svvptc = true, ), DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E51, TYPE_RISCV_CPU_SIFIVE_E, diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static void cpu_get_multi_ext_cfg(Object *obj, Visitor *v, const char *name, static void cpu_add_multi_ext_prop(Object *cpu_obj, const RISCVCPUMultiExtConfig *multi_cfg) { - bool generic_cpu = riscv_cpu_is_generic(cpu_obj); - object_property_add(cpu_obj, multi_cfg->name, "bool", cpu_get_multi_ext_cfg, cpu_set_multi_ext_cfg, NULL, (void *)&multi_cfg->offset); - - if (!generic_cpu) { - return; - } - - /* - * Set def val directly instead of using - * object_property_set_bool() to save the set() - * callback hash for user inputs. - */ - isa_ext_update_enabled(RISCV_CPU(cpu_obj), multi_cfg->offset, - multi_cfg->enabled); } static void riscv_cpu_add_multiext_prop_array(Object *obj, -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> This change has been done twice now: one in riscv-qmp-cmds, another in the KVM. We're now ready to do it for TCG. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-14-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/tcg/tcg-cpu.c | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static void cpu_get_multi_ext_cfg(Object *obj, Visitor *v, const char *name, visit_type_bool(v, name, &value, errp); } -static void cpu_add_multi_ext_prop(Object *cpu_obj, - const RISCVCPUMultiExtConfig *multi_cfg) -{ - object_property_add(cpu_obj, multi_cfg->name, "bool", - cpu_get_multi_ext_cfg, - cpu_set_multi_ext_cfg, - NULL, (void *)&multi_cfg->offset); -} - -static void riscv_cpu_add_multiext_prop_array(Object *obj, - const RISCVCPUMultiExtConfig *array) -{ - const RISCVCPUMultiExtConfig *prop; - - g_assert(array); - - for (prop = array; prop && prop->name; prop++) { - cpu_add_multi_ext_prop(obj, prop); - } -} - /* * Add CPU properties with user-facing flags. * @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_add_multiext_prop_array(Object *obj, */ static void riscv_cpu_add_user_properties(Object *obj) { + const RISCVIsaExtData *edata; + #ifndef CONFIG_USER_ONLY riscv_add_satp_mode_properties(obj); #endif riscv_cpu_add_misa_properties(obj); - riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_extensions); - riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_vendor_exts); - riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_experimental_exts); + for (edata = isa_edata_arr; edata && edata->name; edata++) { + if (edata->prop_name) { + object_property_add(obj, edata->prop_name, "bool", + cpu_get_multi_ext_cfg, + cpu_set_multi_ext_cfg, + NULL, (void *)&edata->ext_enable_offset); + } + } riscv_cpu_add_profiles(obj); } -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> There is no more uses left for these arrays. isa_edata_arr[] is doing everything we need, making our lives a little easier when adding new extensions. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512032926.1978818-15-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 10 --- target/riscv/cpu.c | 159 --------------------------------------------- 2 files changed, 169 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ bool isa_ext_is_enabled(RISCVCPU *cpu, uint32_t ext_offset); void riscv_cpu_set_misa_ext(CPURISCVState *env, uint32_t ext); bool riscv_cpu_is_vendor(Object *cpu_obj); -typedef struct RISCVCPUMultiExtConfig { - const char *name; - uint32_t offset; - bool enabled; -} RISCVCPUMultiExtConfig; - -extern const RISCVCPUMultiExtConfig riscv_cpu_extensions[]; -extern const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[]; -extern const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[]; - typedef struct isa_ext_data { const char *name; const char *prop_name; diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ const char *riscv_get_misa_ext_description(uint32_t bit) return val; } -#define MULTI_EXT_CFG_BOOL(_name, _prop, _defval) \ - {.name = _name, .offset = CPU_CFG_OFFSET(_prop), \ - .enabled = _defval} - -const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { - /* Defaults for standard extensions */ - MULTI_EXT_CFG_BOOL("sscofpmf", ext_sscofpmf, false), - MULTI_EXT_CFG_BOOL("smcntrpmf", ext_smcntrpmf, false), - MULTI_EXT_CFG_BOOL("smcsrind", ext_smcsrind, false), - MULTI_EXT_CFG_BOOL("smcdeleg", ext_smcdeleg, false), - MULTI_EXT_CFG_BOOL("sscsrind", ext_sscsrind, false), - MULTI_EXT_CFG_BOOL("ssccfg", ext_ssccfg, false), - MULTI_EXT_CFG_BOOL("smctr", ext_smctr, false), - MULTI_EXT_CFG_BOOL("ssctr", ext_ssctr, false), - MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true), - MULTI_EXT_CFG_BOOL("zicfilp", ext_zicfilp, false), - MULTI_EXT_CFG_BOOL("zicfiss", ext_zicfiss, false), - MULTI_EXT_CFG_BOOL("zicsr", ext_zicsr, true), - MULTI_EXT_CFG_BOOL("zihintntl", ext_zihintntl, true), - MULTI_EXT_CFG_BOOL("zihintpause", ext_zihintpause, true), - MULTI_EXT_CFG_BOOL("zimop", ext_zimop, false), - MULTI_EXT_CFG_BOOL("zcmop", ext_zcmop, false), - MULTI_EXT_CFG_BOOL("zacas", ext_zacas, false), - MULTI_EXT_CFG_BOOL("zama16b", ext_zama16b, false), - MULTI_EXT_CFG_BOOL("zabha", ext_zabha, false), - MULTI_EXT_CFG_BOOL("zaamo", ext_zaamo, false), - MULTI_EXT_CFG_BOOL("zalasr", ext_zalasr, false), - MULTI_EXT_CFG_BOOL("zalrsc", ext_zalrsc, false), - MULTI_EXT_CFG_BOOL("zawrs", ext_zawrs, true), - MULTI_EXT_CFG_BOOL("zfa", ext_zfa, true), - MULTI_EXT_CFG_BOOL("zfbfmin", ext_zfbfmin, false), - MULTI_EXT_CFG_BOOL("zfh", ext_zfh, false), - MULTI_EXT_CFG_BOOL("zfhmin", ext_zfhmin, false), - MULTI_EXT_CFG_BOOL("zve32f", ext_zve32f, false), - MULTI_EXT_CFG_BOOL("zve32x", ext_zve32x, false), - MULTI_EXT_CFG_BOOL("zve64f", ext_zve64f, false), - MULTI_EXT_CFG_BOOL("zve64d", ext_zve64d, false), - MULTI_EXT_CFG_BOOL("zve64x", ext_zve64x, false), - MULTI_EXT_CFG_BOOL("zvfbfa", ext_zvfbfa, false), - MULTI_EXT_CFG_BOOL("zvfbfmin", ext_zvfbfmin, false), - 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("sstc", ext_sstc, true), - MULTI_EXT_CFG_BOOL("ssnpm", ext_ssnpm, false), - MULTI_EXT_CFG_BOOL("sspm", ext_sspm, false), - MULTI_EXT_CFG_BOOL("supm", ext_supm, false), - - MULTI_EXT_CFG_BOOL("smaia", ext_smaia, false), - MULTI_EXT_CFG_BOOL("smdbltrp", ext_smdbltrp, false), - MULTI_EXT_CFG_BOOL("smepmp", ext_smepmp, false), - MULTI_EXT_CFG_BOOL("smpmpmt", ext_smpmpmt, false), - MULTI_EXT_CFG_BOOL("smrnmi", ext_smrnmi, false), - MULTI_EXT_CFG_BOOL("smmpm", ext_smmpm, false), - MULTI_EXT_CFG_BOOL("smnpm", ext_smnpm, false), - MULTI_EXT_CFG_BOOL("smstateen", ext_smstateen, false), - MULTI_EXT_CFG_BOOL("ssaia", ext_ssaia, false), - MULTI_EXT_CFG_BOOL("ssdbltrp", ext_ssdbltrp, false), - MULTI_EXT_CFG_BOOL("svade", ext_svade, false), - MULTI_EXT_CFG_BOOL("svadu", ext_svadu, true), - MULTI_EXT_CFG_BOOL("svinval", ext_svinval, false), - MULTI_EXT_CFG_BOOL("svnapot", ext_svnapot, false), - MULTI_EXT_CFG_BOOL("svpbmt", ext_svpbmt, false), - MULTI_EXT_CFG_BOOL("svrsw60t59b", ext_svrsw60t59b, false), - MULTI_EXT_CFG_BOOL("svvptc", ext_svvptc, true), - - MULTI_EXT_CFG_BOOL("zicntr", ext_zicntr, true), - MULTI_EXT_CFG_BOOL("zihpm", ext_zihpm, true), - MULTI_EXT_CFG_BOOL("zilsd", ext_zilsd, false), - - MULTI_EXT_CFG_BOOL("zba", ext_zba, true), - MULTI_EXT_CFG_BOOL("zbb", ext_zbb, true), - MULTI_EXT_CFG_BOOL("zbc", ext_zbc, true), - MULTI_EXT_CFG_BOOL("zbkb", ext_zbkb, false), - MULTI_EXT_CFG_BOOL("zbkc", ext_zbkc, false), - MULTI_EXT_CFG_BOOL("zbkx", ext_zbkx, false), - MULTI_EXT_CFG_BOOL("zbs", ext_zbs, true), - MULTI_EXT_CFG_BOOL("zk", ext_zk, false), - MULTI_EXT_CFG_BOOL("zkn", ext_zkn, false), - MULTI_EXT_CFG_BOOL("zknd", ext_zknd, false), - MULTI_EXT_CFG_BOOL("zkne", ext_zkne, false), - MULTI_EXT_CFG_BOOL("zknh", ext_zknh, false), - MULTI_EXT_CFG_BOOL("zkr", ext_zkr, false), - MULTI_EXT_CFG_BOOL("zks", ext_zks, false), - MULTI_EXT_CFG_BOOL("zksed", ext_zksed, false), - MULTI_EXT_CFG_BOOL("zksh", ext_zksh, false), - MULTI_EXT_CFG_BOOL("zkt", ext_zkt, false), - MULTI_EXT_CFG_BOOL("ztso", ext_ztso, false), - - MULTI_EXT_CFG_BOOL("zdinx", ext_zdinx, false), - MULTI_EXT_CFG_BOOL("zfinx", ext_zfinx, false), - MULTI_EXT_CFG_BOOL("zhinx", ext_zhinx, false), - MULTI_EXT_CFG_BOOL("zhinxmin", ext_zhinxmin, false), - - MULTI_EXT_CFG_BOOL("zicbom", ext_zicbom, true), - MULTI_EXT_CFG_BOOL("zicbop", ext_zicbop, true), - MULTI_EXT_CFG_BOOL("zicboz", ext_zicboz, true), - - MULTI_EXT_CFG_BOOL("zmmul", ext_zmmul, false), - - MULTI_EXT_CFG_BOOL("zca", ext_zca, false), - MULTI_EXT_CFG_BOOL("zcb", ext_zcb, false), - MULTI_EXT_CFG_BOOL("zcd", ext_zcd, false), - MULTI_EXT_CFG_BOOL("zce", ext_zce, false), - MULTI_EXT_CFG_BOOL("zcf", ext_zcf, false), - MULTI_EXT_CFG_BOOL("zcmp", ext_zcmp, false), - MULTI_EXT_CFG_BOOL("zcmt", ext_zcmt, false), - MULTI_EXT_CFG_BOOL("zicond", ext_zicond, false), - MULTI_EXT_CFG_BOOL("zclsd", ext_zclsd, false), - - /* Vector cryptography extensions */ - MULTI_EXT_CFG_BOOL("zvbb", ext_zvbb, false), - MULTI_EXT_CFG_BOOL("zvbc", ext_zvbc, false), - MULTI_EXT_CFG_BOOL("zvkb", ext_zvkb, false), - MULTI_EXT_CFG_BOOL("zvkg", ext_zvkg, false), - MULTI_EXT_CFG_BOOL("zvkned", ext_zvkned, false), - MULTI_EXT_CFG_BOOL("zvknha", ext_zvknha, false), - MULTI_EXT_CFG_BOOL("zvknhb", ext_zvknhb, false), - MULTI_EXT_CFG_BOOL("zvksed", ext_zvksed, false), - MULTI_EXT_CFG_BOOL("zvksh", ext_zvksh, false), - MULTI_EXT_CFG_BOOL("zvkt", ext_zvkt, false), - MULTI_EXT_CFG_BOOL("zvkn", ext_zvkn, false), - MULTI_EXT_CFG_BOOL("zvknc", ext_zvknc, false), - MULTI_EXT_CFG_BOOL("zvkng", ext_zvkng, false), - MULTI_EXT_CFG_BOOL("zvks", ext_zvks, false), - MULTI_EXT_CFG_BOOL("zvksc", ext_zvksc, false), - MULTI_EXT_CFG_BOOL("zvksg", ext_zvksg, false), - - { }, -}; - -const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[] = { - MULTI_EXT_CFG_BOOL("xtheadba", ext_xtheadba, false), - MULTI_EXT_CFG_BOOL("xtheadbb", ext_xtheadbb, false), - MULTI_EXT_CFG_BOOL("xtheadbs", ext_xtheadbs, false), - MULTI_EXT_CFG_BOOL("xtheadcmo", ext_xtheadcmo, false), - MULTI_EXT_CFG_BOOL("xtheadcondmov", ext_xtheadcondmov, false), - MULTI_EXT_CFG_BOOL("xtheadfmemidx", ext_xtheadfmemidx, false), - MULTI_EXT_CFG_BOOL("xtheadfmv", ext_xtheadfmv, false), - MULTI_EXT_CFG_BOOL("xtheadmac", ext_xtheadmac, false), - MULTI_EXT_CFG_BOOL("xtheadmemidx", ext_xtheadmemidx, false), - MULTI_EXT_CFG_BOOL("xtheadmempair", ext_xtheadmempair, false), - MULTI_EXT_CFG_BOOL("xtheadsync", ext_xtheadsync, false), - MULTI_EXT_CFG_BOOL("xventanacondops", ext_XVentanaCondOps, false), - MULTI_EXT_CFG_BOOL("xmipscbop", ext_xmipscbop, false), - MULTI_EXT_CFG_BOOL("xmipscmov", ext_xmipscmov, false), - MULTI_EXT_CFG_BOOL("xmipslsp", ext_xmipslsp, false), - MULTI_EXT_CFG_BOOL("xlrbr", ext_xlrbr, false), - - { }, -}; - -/* These are experimental so mark with 'x-' */ -const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { - MULTI_EXT_CFG_BOOL("x-svukte", ext_svukte, false), - - { }, -}; - static void cpu_set_prop_err(RISCVCPU *cpu, const char *propname, Error **errp) { -- 2.53.0
From: Fangyu Yu <fangyu.yu@linux.alibaba.com> Fix IOCOUNTINH.CY toggle detection by comparing normalized CY bit rather than XORing whole register with boolean. Fixes: 2cf2a6c027ba ("hw/riscv/riscv-iommu: add IOCOUNTINH mmio writes") Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Message-ID: <20260509074941.99295-1-fangyu.yu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu-hpm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/riscv/riscv-iommu-hpm.c b/hw/riscv/riscv-iommu-hpm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu-hpm.c +++ b/hw/riscv/riscv-iommu-hpm.c @@ -XXX,XX +XXX,XX @@ void riscv_iommu_process_iocntinh_cy(RISCVIOMMUState *s, bool prev_cy_inh) s, RISCV_IOMMU_REG_IOCOUNTINH); /* We only need to process CY bit toggle. */ - if (!(inhibit ^ prev_cy_inh)) { + bool cy_inh = !!(inhibit & RISCV_IOMMU_IOCOUNTINH_CY); + if (cy_inh == prev_cy_inh) { return; } -- 2.53.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> The priv spec defines, for RV64, that the upper 10 bits of pmpaddr0-pmpaddr63 are WARL and are supposed to be cleared. After this patch, using the bug reproducer in [1], writing ffffffffffffffff in pmpaddr0 and reading it back now results in 003fffffffffffff. Here's the 'diff -cp' dump before and after this change: *************** IN: *** 5272,5278 **** pmpcfg10 0000000000000000 pmpcfg12 0000000000000000 pmpcfg14 0000000000000000 ! pmpaddr0 ffffffffffffffff pmpaddr1 0000000000000000 pmpaddr2 0000000000000000 pmpaddr3 0000000000000000 Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260514123342.2139464-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException read_pmpaddr(CPURISCVState *env, int csrno, target_ulong *val) { *val = pmpaddr_csr_read(env, csrno - CSR_PMPADDR0); + + /* + * For RV64, bits 54-63 of the address registers + * PMPAADDR(0-63) is a WARL zero field (priv spec, + * section "Physical Memory Protection CSRs"). + * + * We'll have to add an annoying TARGET_RISCV64 gate + * here to avoid complaints about masking bits 0-53 + * of a potential 32 bit target_ulong '*var'. + */ +#ifdef TARGET_RISCV64 + if (env->misa_mxl == MXL_RV64 + && csrno >= CSR_PMPADDR0 && csrno <= CSR_PMPADDR63) { + target_ulong read_mask = MAKE_64BIT_MASK(0, 54); + *val &= read_mask; + } +#endif return RISCV_EXCP_NONE; } -- 2.53.0
From: Zishun Yi <vulab@iscas.ac.cn> In build_rimt(), the calculation of `num_ids` for the ID mapping array incorrectly uses the same formula (`0xffff - s->pci_iommu_bdf`) for both System IOMMU and PCI IOMMU topologies. For a System IOMMU, `s->pci_iommu_bdf` is 0. This results in a `num_ids` value of 0xffff. Since the source ID base starts at 0, the mapping only covers Requester IDs from 0 to 0xfffe. The final valid PCI Requester ID (0xffff) is erroneously omitted from the RIMT table. Fix this by decoupling the `num_ids` calculation. For System IOMMUs, explicitly set `num_ids` to 0x10000 to cover the entire PCI Requester ID space. This issue was discovered and reported by SpecHunter, an AI-driven architecture specification analysis tool. Link: https://github.com/yizishun/rv-isa-sec/blob/c78dacf66c8acd677b3538c837fde310bb71a97b/output/riscv-server-platform/pr-102/qemu.txt#L32 Signed-off-by: Zishun Yi <vulab@iscas.ac.cn> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260512062310.348208-1-vulab@iscas.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt-acpi-build.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -XXX,XX +XXX,XX @@ static void build_rimt(GArray *table_data, BIOSLinker *linker, range = &g_array_index(iommu_idmaps, AcpiRimtIdMapping, i); if (virt_is_iommu_sys_enabled(s)) { range->source_id_base = 0; + range->num_ids = 0x10000; } else { range->source_id_base = s->pci_iommu_bdf + 1; + range->num_ids = 0xffff - s->pci_iommu_bdf; } - range->num_ids = 0xffff - s->pci_iommu_bdf; build_rimt_id_mapping(table_data, range->source_id_base, range->num_ids, iommu_offset); } -- 2.53.0
From: Portia Stephens <portias@oss.tenstorrent.com> The counter value was not being bitshifted for a hpmcounterh read resulting in hpmcounterh returning the bottom 32-bits. Fixes: cfc96df65e01 ("target/riscv: Remove upper_half from riscv_pmu_ctr_get_fixed_counters_val") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3498 Cc: qemu-stable@nongnu.org Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260519043352.3685866-1-stephensportia@gmail.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, */ if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { - *val = riscv_pmu_ctr_get_fixed_counters_val(env, ctr_idx) - - ctr_prev + ctr_val; + uint64_t cntr = riscv_pmu_ctr_get_fixed_counters_val(env, ctr_idx) - + ctr_prev + ctr_val; + *val = extract64(cntr, start, length); } else { *val = ctr_val; } -- 2.53.0
From: Jim Shu <jim.shu@sifive.com> According to the AIA spec ch4.7 ("Precise effects on interrupt-pending bits"), pending bit of APLIC should be set/cleared whenever the rectified input value is high/low in the both level-trigger mode and direct delivery mode. Currently, QEMU APLIC only clears the pending bit when interrupt is claimed in APLIC, but not clears it when the rectified input value is low. (e.g. IRQ source signal is low in the LEVEL_HIGH/Level1 mode). The software may receive an additional IRQ if the peripheral triggers one after the software clears the APLIC IRQ but before it clears the peripheral's IRQ. Thus, we also clear the pending bit via the rectified input value in the level-trigger mode. This change doesn't affect MSI delivery mode. Calling riscv_aplic_msi_irq_update() when IRQ pending is low will do nothing. Signed-off-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260428160103.3551125-2-jim.shu@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/intc/riscv_aplic.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -XXX,XX +XXX,XX @@ static void riscv_aplic_request(void *opaque, int irq, int level) } break; case APLIC_SOURCECFG_SM_LEVEL_HIGH: - if ((level > 0) && !(state & APLIC_ISTATE_PENDING)) { - riscv_aplic_set_pending_raw(aplic, irq, true); + if ((level > 0) != !!(state & APLIC_ISTATE_PENDING)) { + riscv_aplic_set_pending_raw(aplic, irq, level > 0); update = true; } break; case APLIC_SOURCECFG_SM_LEVEL_LOW: - if ((level <= 0) && !(state & APLIC_ISTATE_PENDING)) { - riscv_aplic_set_pending_raw(aplic, irq, true); + if ((level <= 0) != !!(state & APLIC_ISTATE_PENDING)) { + riscv_aplic_set_pending_raw(aplic, irq, level <= 0); update = true; } break; -- 2.53.0
From: Jim Shu <jim.shu@sifive.com> Clearing APLIC registers and qemu_irq in the reset function Signed-off-by: Jim Shu <jim.shu@sifive.com> Signed-off-by: Fea.Wang <fea.wang@sifive.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260428160103.3551125-3-jim.shu@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/intc/riscv_aplic.c | 45 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -XXX,XX +XXX,XX @@ static const MemoryRegionOps riscv_aplic_ops = { } }; +static void riscv_aplic_reset_enter(Object *obj, ResetType type) +{ + RISCVAPLICState *aplic = RISCV_APLIC(obj); + int i; + + aplic->domaincfg = 0; + memset(aplic->sourcecfg, 0, sizeof(uint32_t) * aplic->num_irqs); + memset(aplic->target, 0, sizeof(uint32_t) * aplic->num_irqs); + if (!aplic->msimode) { + for (i = 0; i < aplic->num_irqs; i++) { + aplic->target[i] = 1; + } + } + + for (i = 0; i < aplic->num_irqs ; i++) { + riscv_aplic_set_enabled_raw(aplic, i, false); + } + + /* Need to unlock [ms]msicfgaddrh.L */ + aplic->mmsicfgaddr = 0; + aplic->mmsicfgaddrH = 0; + aplic->smsicfgaddr = 0; + aplic->smsicfgaddrH = 0; + + if (!aplic->msimode) { + /* Reset IDC registers only in non-MSI mode */ + for (i = 0; i < aplic->num_harts; i++) { + aplic->idelivery[i] = 0; + aplic->iforce[i] = 0; + aplic->ithreshold[i] = 0; + } + + for (i = 0; i < aplic->num_harts; i++) { + qemu_irq_lower(aplic->external_irqs[i]); + } + } +} + static void riscv_aplic_realize(DeviceState *dev, Error **errp) { uint32_t i; @@ -XXX,XX +XXX,XX @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) aplic->sourcecfg = g_new0(uint32_t, aplic->num_irqs); aplic->state = g_new0(uint32_t, aplic->num_irqs); aplic->target = g_new0(uint32_t, aplic->num_irqs); - if (!aplic->msimode) { - for (i = 0; i < aplic->num_irqs; i++) { - aplic->target[i] = 1; - } - } aplic->idelivery = g_new0(uint32_t, aplic->num_harts); aplic->iforce = g_new0(uint32_t, aplic->num_harts); aplic->ithreshold = g_new0(uint32_t, aplic->num_harts); @@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_riscv_aplic = { static void riscv_aplic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); device_class_set_props(dc, riscv_aplic_properties); dc->realize = riscv_aplic_realize; + rc->phases.enter = riscv_aplic_reset_enter; dc->vmsd = &vmstate_riscv_aplic; } -- 2.53.0
From: Jim Shu <jim.shu@sifive.com> Clearing IMSIC registers and qemu_irq in the reset function Signed-off-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260428160103.3551125-4-jim.shu@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/intc/riscv_imsic.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/riscv_imsic.c +++ b/hw/intc/riscv_imsic.c @@ -XXX,XX +XXX,XX @@ static const MemoryRegionOps riscv_imsic_ops = { } }; +static void riscv_imsic_reset_enter(Object *obj, ResetType type) +{ + RISCVIMSICState *imsic = RISCV_IMSIC(obj); + int i; + + memset(imsic->eidelivery, 0, sizeof(uint32_t) * imsic->num_pages); + memset(imsic->eithreshold, 0, sizeof(uint32_t) * imsic->num_pages); + + for (i = 0; i < imsic->num_eistate; i++) { + imsic->eistate[i] &= ~IMSIC_EISTATE_ENABLED; + } + + for (i = 0; i < imsic->num_pages; i++) { + qemu_irq_lower(imsic->external_irqs[i]); + } +} + static void riscv_imsic_realize(DeviceState *dev, Error **errp) { RISCVIMSICState *imsic = RISCV_IMSIC(dev); @@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_riscv_imsic = { static void riscv_imsic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); device_class_set_props(dc, riscv_imsic_properties); dc->realize = riscv_imsic_realize; + rc->phases.enter = riscv_imsic_reset_enter; dc->vmsd = &vmstate_riscv_imsic; } -- 2.53.0
From: Jim Shu <jim.shu@sifive.com> Add the trace events for APLIC read/write functions. Signed-off-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260428160103.3551125-5-jim.shu@sifive.com> [ Changes by AF: - Init val to zero to avoid uninitialised build errors ] Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/intc/riscv_aplic.c | 63 ++++++++++++++++++++++++++----------------- hw/intc/trace-events | 4 +++ 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -XXX,XX +XXX,XX @@ #include "system/tcg.h" #include "kvm/kvm_riscv.h" #include "migration/vmstate.h" +#include "trace.h" #define APLIC_MAX_IDC (1UL << 14) #define APLIC_MAX_SOURCE 1024 @@ -XXX,XX +XXX,XX @@ static void riscv_aplic_request(void *opaque, int irq, int level) static uint64_t riscv_aplic_read(void *opaque, hwaddr addr, unsigned size) { uint32_t irq, word, idc; + uint64_t val = 0; RISCVAPLICState *aplic = opaque; /* Reads must be 4 byte words */ @@ -XXX,XX +XXX,XX @@ static uint64_t riscv_aplic_read(void *opaque, hwaddr addr, unsigned size) } if (addr == APLIC_DOMAINCFG) { - return APLIC_DOMAINCFG_RDONLY | aplic->domaincfg | - (aplic->msimode ? APLIC_DOMAINCFG_DM : 0); + val = APLIC_DOMAINCFG_RDONLY | aplic->domaincfg | + (aplic->msimode ? APLIC_DOMAINCFG_DM : 0); } else if ((APLIC_SOURCECFG_BASE <= addr) && (addr < (APLIC_SOURCECFG_BASE + (aplic->num_irqs - 1) * 4))) { irq = ((addr - APLIC_SOURCECFG_BASE) >> 2) + 1; - return aplic->sourcecfg[irq]; + val = aplic->sourcecfg[irq]; } else if (aplic->mmode && aplic->msimode && (addr == APLIC_MMSICFGADDR)) { - return aplic->mmsicfgaddr; + val = aplic->mmsicfgaddr; } else if (aplic->mmode && aplic->msimode && (addr == APLIC_MMSICFGADDRH)) { - return aplic->mmsicfgaddrH; + val = aplic->mmsicfgaddrH; } else if (aplic->mmode && aplic->msimode && (addr == APLIC_SMSICFGADDR)) { /* @@ -XXX,XX +XXX,XX @@ static uint64_t riscv_aplic_read(void *opaque, hwaddr addr, unsigned size) * only zero in at least one of the supervisor-level child * domains). */ - return (aplic->num_children) ? aplic->smsicfgaddr : 0; + val = (aplic->num_children) ? aplic->smsicfgaddr : 0; } else if (aplic->mmode && aplic->msimode && (addr == APLIC_SMSICFGADDRH)) { - return (aplic->num_children) ? aplic->smsicfgaddrH : 0; + val = (aplic->num_children) ? aplic->smsicfgaddrH : 0; } else if ((APLIC_SETIP_BASE <= addr) && (addr < (APLIC_SETIP_BASE + aplic->bitfield_words * 4))) { word = (addr - APLIC_SETIP_BASE) >> 2; - return riscv_aplic_read_pending_word(aplic, word); + val = riscv_aplic_read_pending_word(aplic, word); } else if (addr == APLIC_SETIPNUM) { - return 0; + val = 0; } else if ((APLIC_CLRIP_BASE <= addr) && (addr < (APLIC_CLRIP_BASE + aplic->bitfield_words * 4))) { word = (addr - APLIC_CLRIP_BASE) >> 2; - return riscv_aplic_read_input_word(aplic, word); + val = riscv_aplic_read_input_word(aplic, word); } else if (addr == APLIC_CLRIPNUM) { - return 0; + val = 0; } else if ((APLIC_SETIE_BASE <= addr) && (addr < (APLIC_SETIE_BASE + aplic->bitfield_words * 4))) { word = (addr - APLIC_SETIE_BASE) >> 2; - return riscv_aplic_read_enabled_word(aplic, word); + val = riscv_aplic_read_enabled_word(aplic, word); } else if (addr == APLIC_SETIENUM) { - return 0; + val = 0; } else if ((APLIC_CLRIE_BASE <= addr) && (addr < (APLIC_CLRIE_BASE + aplic->bitfield_words * 4))) { - return 0; + val = 0; } else if (addr == APLIC_CLRIENUM) { - return 0; + val = 0; } else if (addr == APLIC_SETIPNUM_LE) { - return 0; + val = 0; } else if (addr == APLIC_SETIPNUM_BE) { - return 0; + val = 0; } else if (addr == APLIC_GENMSI) { - return (aplic->msimode) ? aplic->genmsi : 0; + val = (aplic->msimode) ? aplic->genmsi : 0; } else if ((APLIC_TARGET_BASE <= addr) && (addr < (APLIC_TARGET_BASE + (aplic->num_irqs - 1) * 4))) { irq = ((addr - APLIC_TARGET_BASE) >> 2) + 1; if (!riscv_aplic_source_active(aplic, irq)) { - return 0; + val = 0; + } else { + val = aplic->target[irq]; } - return aplic->target[irq]; } else if (!aplic->msimode && (APLIC_IDC_BASE <= addr) && (addr < (APLIC_IDC_BASE + aplic->num_harts * APLIC_IDC_SIZE))) { idc = (addr - APLIC_IDC_BASE) / APLIC_IDC_SIZE; switch (addr - (APLIC_IDC_BASE + idc * APLIC_IDC_SIZE)) { case APLIC_IDC_IDELIVERY: - return aplic->idelivery[idc]; + val = aplic->idelivery[idc]; + break; case APLIC_IDC_IFORCE: - return aplic->iforce[idc]; + val = aplic->iforce[idc]; + break; case APLIC_IDC_ITHRESHOLD: - return aplic->ithreshold[idc]; + val = aplic->ithreshold[idc]; + break; case APLIC_IDC_TOPI: - return riscv_aplic_idc_topi(aplic, idc); + val = riscv_aplic_idc_topi(aplic, idc); + break; case APLIC_IDC_CLAIMI: - return riscv_aplic_idc_claimi(aplic, idc); + val = riscv_aplic_idc_claimi(aplic, idc); + break; default: goto err; }; } + trace_riscv_aplic_read(addr, size, val); + return val; + err: qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid register read 0x%" HWADDR_PRIx "\n", @@ -XXX,XX +XXX,XX @@ static void riscv_aplic_write(void *opaque, hwaddr addr, uint64_t value, goto err; } + trace_riscv_aplic_write(addr, size, value); + if (addr == APLIC_DOMAINCFG) { /* Only IE bit writable at the moment */ value &= APLIC_DOMAINCFG_IE; diff --git a/hw/intc/trace-events b/hw/intc/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -XXX,XX +XXX,XX @@ loongarch_msi_set_irq(int irq_num) "set msi irq %d" loongarch_extioi_setirq(int irq, int level) "set extirq irq %d level %d" loongarch_extioi_readw(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64 "val: 0x%" PRIx64 loongarch_extioi_writew(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64 "val: 0x%" PRIx64 + +# riscv_aplic.c +riscv_aplic_read(uint64_t offset, unsigned size, uint64_t value) "offset: 0x%" PRIx64 ", size: %u, value: 0x%" PRIx64 +riscv_aplic_write(uint64_t offset, unsigned size, uint64_t value) "offset: 0x%" PRIx64 ", size: %u, value: 0x%" PRIx64 -- 2.53.0
From: Jay Chang <jay.chang@sifive.com> When software clears CMD_ILL or CQMF error bits by writing 1 to CQCSR, the IOMMU should re-check the command queue and continue processing pending commands if head != tail. Per RISC-V IOMMU specification : If command-queue access leads to a memory fault then the command-queue-memory-fault bit is set to 1 and the commandqueue stalls until this bit is cleared. To re-enable command processing, software should clear this bit by writing 1. Signed-off-by: Jay Chang <jay.chang@sifive.com> Reviewed-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Message-ID: <20260518070143.8405-1-jay.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_process_cq_control(RISCVIOMMUState *s) } riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, ctrl_set, ctrl_clr); + + /* + * After clearing error bits (CMD_ILL, CQMF), if queue is still active, + * re-process pending command. + */ + ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR); + if ((ctrl_set & RISCV_IOMMU_CQCSR_CQON) && + !(ctrl_set & (RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CQMF))) { + riscv_iommu_process_cq_tail(s); + } } static void riscv_iommu_process_fq_control(RISCVIOMMUState *s) -- 2.53.0
From: Alistair Francis <alistair.francis@wdc.com> The following changes since commit 2f28d34ea0aead9830478cd1d3d0dd9d9191d82e: Merge tag 'pull-tcg-20260612' of https://gitlab.com/rth7680/qemu into staging (2026-06-13 14:02:34 -0400) are available in the Git repository at: https://github.com/alistair23/qemu.git tags/pull-riscv-to-apply-20260616 for you to fetch changes up to e968e487acf082279118667f495a3f4275a59d59: hw/riscv: add create_fdt_socket_cpu_sifive() (2026-06-16 20:01:11 +1000) ---------------------------------------------------------------- RISC-V PR for 11.1 * Disable svpbmt if satp_mode is less then sv39 * Fix PMP address alignment * Mstatus write bug fixes * Add 'cbo' insns to disassembler * Do not hide Sstc CSRs from gdbstub * Reject Svinval instructions in U-mode * Save opcode before zicbo helpers * Fault with reserved PTE.PBMT val * Allow LOAD_ADDR_MIS promotion to AMO fault * Make riscv cpu.h target independent * Add PMA access fault * Disable svnapot if satp_mode is less then sv39 * Fix disassembler inst_length calculation * Add RISC-V big-endian target support * Add the implied rules for G and B extensions * Print privilege level and ELP in riscv_cpu_dump_state * Improve alignment in riscv_cpu_dump_state * Mask vxrm csrw write to the low 2 bits * Reorder Smrnmi CPU fields above CPU reset line * Supplement cpu topology arguments * Don't insert DDT cache in Bare mode * Fix 'iommu-map' FDT entry * Fix mstatus.FS dirty tracking for FP exception-raising instructions * Enable `mnret` disassembly * Add support for K230 board * FDT creation helpers ---------------------------------------------------------------- Abhigyan Kumar (1): target/riscv: mask vxrm csrw write to the low 2 bits Anton Blanchard (2): target/riscv: Print privilege level and ELP in riscv_cpu_dump_state target/riscv: Improve alignment in riscv_cpu_dump_state Anton Johansson (27): target/riscv: Fix size of gpr and gprh target/riscv: Fix size of vector CSRs target/riscv: Fix size of pc, load_[val|res] target/riscv: Fix size of frm and fflags target/riscv: Fix size of badaddr and bins target/riscv: Fix size of guest_phys_fault_addr target/riscv: Fix size of priv_ver and vext_ver target/riscv: Fix size of retxh target/riscv: Fix size of ssp target/riscv: Fix size of excp_uw2 target/riscv: Fix size of sw_check_code target/riscv: Fix size of priv target/riscv: Fix size of gei fields target/riscv: Fix size of [m|s|vs]iselect fields target/riscv: Fix arguments to board IMSIC emulation callbacks target/riscv: Fix size of irq_overflow_left target/riscv: Indent PMUFixedCtrState correctly target/riscv: Replace target_ulong in riscv_cpu_get_trap_name() target/riscv: Replace target_ulong in riscv_ctr_add_entry() target/riscv: Fix size of trigger data target/riscv: Fix size of mseccfg target/riscv: Move debug.h include away from cpu.h target/riscv: Move CSR declarations to separate csr.h header target/riscv: Introduce externally facing CSR access functions target/riscv: Make pmp.h target_ulong agnostic target/riscv: Pass address as uint64_t in cpu_set_exception_base() target/riscv: Fix pmp.h/cpu.h circular inclusion Chao Liu (5): target/riscv: add thead-c908 cpu support hw/riscv: add k230 board initial support hw/watchdog: add k230 watchdog initial support tests/qtest: add test for K230 watchdog docs/system/riscv: add documentation for k230 machine Daniel Henrique Barboza (24): target/riscv/tcg: disable svpbmt if satp_mode < sv39 target/riscv/csr.c: do not allow mstatus MPV/GVA writes target/riscv/csr.c: fix mstatus.UXL reserved value disas/riscv.c: add 'cbo' insns to disassembler target/riscv/insn_trans/trans_rvzicbo.c.inc: save opcode before helpers target/riscv/cpu_helper.c: fault with reserved PTE.PBMT val target/riscv/cpu_helper.c: allow LOAD_ADDR_MIS promotion to AMO fault target/riscv/cpu_helper.c: add PMA access fault target/riscv/tcg: disable svnapot if satp_mode < sv39 disas/riscv.c: fix inst_length() hw/riscv/virt.c: fix 'iommu-map' FDT entry hw/riscv/sifive_u.c: add a FDT phandle to cpu-intc hw/riscv: add fdt-common helper hw/riscv/numa: make numa_enabled() public hw/riscv: add create_fdt_socket_memory() helper hw/riscv/sifive_u.c: add intc_phandles array hw/riscv/spike.c: add intc_phandles array hw/riscv: add create_fdt_clint() helper hw/riscv/sifive_u.c: add cpu-map, cluster and core DTs hw/riscv: add fdt_create_cpu_socket_subnode() helper hw/riscv: add create_fdt_socket_cpus() hw/riscv/spike.c: use create_fdt_socket_cpus() hw/riscv/fdt_common.c: create create_fdt_socket_cpu_internal() hw/riscv: add create_fdt_socket_cpu_sifive() Djordje Todorovic (6): target/riscv: Implement runtime data endianness via MSTATUS bits target/riscv: Fix page table walk endianness for big-endian harts target/riscv: Add big-endian CPU configuration field and reset logic hw/riscv/boot: Honour data endianness target/riscv: Expose and document the CPU 'big-endian' property tests/functional: Add RISC-V endianness test Frank Chang (2): target/riscv: Add standard B extension implied rule target/riscv: Reorder Smrnmi CPU fields above CPU reset line Jay Chang (4): target/riscv: Align pmp size to pmp-granularity target/riscv: Improve PMP address alignment readability hw/riscv: Don't insert DDT cache in Bare mode hw/riscv: Refactor riscv_iommu_ctx_put() for Bare mode handling Jim Shu (1): target/riscv: Add the implied rule for G extension Max Chou (2): target/riscv: Set mstatus.FS dirty when scalar FP raises exceptions target/riscv: rvv: Set mstatus.FS dirty when vector FP raises exceptions Philippe Mathieu-Daudé (5): target/riscv: Initialize DisasContext::mo_endian once target/riscv: De-indent some code in get_physical_address() target/riscv: Remove target_ulong use in get_physical_address() hw/riscv/boot: Rewrite setup_rom_reset_vec() using load/store API hw/riscv/boot: Replace cpu_to_le32() -> const_le32() Xuemei Liu (1): hw/riscv/numa.c: Supplement cpu topology arguments Zephyr Li (2): target/riscv: Do not hide Sstc CSRs from gdbstub target/riscv: Reject Svinval instructions in U-mode imaginos (1): disas/riscv: enable `mnret` disassembly MAINTAINERS | 11 + docs/system/riscv/k230.rst | 113 ++++++ docs/system/target-riscv.rst | 30 ++ include/hw/riscv/fdt-common.h | 33 ++ include/hw/riscv/k230.h | 149 +++++++ include/hw/riscv/numa.h | 8 + include/hw/watchdog/k230_wdt.h | 121 ++++++ target/riscv/cpu-qom.h | 2 + target/riscv/cpu.h | 287 +++++--------- target/riscv/csr.h | 102 +++++ target/riscv/debug.h | 2 - target/riscv/internals.h | 33 +- target/riscv/pmp.h | 23 +- target/riscv/pmu.h | 2 +- target/riscv/cpu_cfg_fields.h.inc | 1 + target/riscv/insn32.decode | 2 +- disas/riscv.c | 56 ++- hw/intc/riscv_imsic.c | 34 +- hw/riscv/boot.c | 95 ++--- hw/riscv/fdt-common.c | 202 ++++++++++ hw/riscv/k230.c | 526 +++++++++++++++++++++++++ hw/riscv/numa.c | 32 +- hw/riscv/riscv-iommu.c | 34 +- hw/riscv/riscv_hart.c | 7 +- hw/riscv/sifive_u.c | 88 +---- hw/riscv/spike.c | 123 ++---- hw/riscv/virt.c | 185 ++------- hw/watchdog/k230_wdt.c | 296 ++++++++++++++ linux-user/riscv/signal.c | 5 +- target/riscv/cpu.c | 140 ++++++- target/riscv/cpu_helper.c | 180 ++++++--- target/riscv/csr.c | 116 ++++-- target/riscv/debug.c | 1 + target/riscv/fpu_helper.c | 167 ++++++-- target/riscv/gdbstub.c | 3 +- target/riscv/kvm/kvm-cpu.c | 1 + target/riscv/machine.c | 81 ++-- target/riscv/mips_csr.c | 1 + target/riscv/monitor.c | 3 +- target/riscv/op_helper.c | 28 +- target/riscv/pmp.c | 34 +- target/riscv/pmu.c | 9 +- target/riscv/riscv-qmp-cmds.c | 1 + target/riscv/tcg/tcg-cpu.c | 63 ++- target/riscv/th_csr.c | 377 +++++++++++++++++- target/riscv/translate.c | 82 ++-- target/riscv/vector_helper.c | 366 +++++++++-------- tests/qtest/k230-wdt-test.c | 189 +++++++++ target/riscv/insn_trans/trans_privileged.c.inc | 2 +- target/riscv/insn_trans/trans_rva.c.inc | 4 +- target/riscv/insn_trans/trans_rvd.c.inc | 4 +- target/riscv/insn_trans/trans_rvf.c.inc | 4 +- target/riscv/insn_trans/trans_rvi.c.inc | 24 +- target/riscv/insn_trans/trans_rvm.c.inc | 16 +- target/riscv/insn_trans/trans_rvv.c.inc | 22 +- target/riscv/insn_trans/trans_rvzacas.c.inc | 4 +- target/riscv/insn_trans/trans_rvzalasr.c.inc | 4 +- target/riscv/insn_trans/trans_rvzce.c.inc | 4 +- target/riscv/insn_trans/trans_rvzfh.c.inc | 4 +- target/riscv/insn_trans/trans_rvzicbo.c.inc | 8 + target/riscv/insn_trans/trans_rvzicfiss.c.inc | 26 +- target/riscv/insn_trans/trans_svinval.c.inc | 12 + target/riscv/insn_trans/trans_xmips.c.inc | 8 +- target/riscv/insn_trans/trans_xthead.c.inc | 16 +- target/riscv/insn_trans/trans_zilsd.c.inc | 4 +- hw/riscv/Kconfig | 11 + hw/riscv/meson.build | 3 +- hw/watchdog/Kconfig | 4 + hw/watchdog/meson.build | 1 + hw/watchdog/trace-events | 9 + tests/functional/riscv64/meson.build | 1 + tests/functional/riscv64/test_endianness.py | 57 +++ tests/qtest/meson.build | 3 +- 73 files changed, 3544 insertions(+), 1155 deletions(-) create mode 100644 docs/system/riscv/k230.rst create mode 100644 include/hw/riscv/fdt-common.h create mode 100644 include/hw/riscv/k230.h create mode 100644 include/hw/watchdog/k230_wdt.h create mode 100644 target/riscv/csr.h create mode 100644 hw/riscv/fdt-common.c create mode 100644 hw/riscv/k230.c create mode 100644 hw/watchdog/k230_wdt.c create mode 100644 tests/qtest/k230-wdt-test.c create mode 100644 tests/functional/riscv64/test_endianness.py
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Priv spec chapter "Svpbmt Extension for Page-Based Memory Types, Version 1.0" mentions that "The Svpbmt extension depends on the Sv39 extension.". We're not doing any satp checks when enabling svpbmt. This causes problems with the riscv32 'max' CPU that happens to be enabling svpbmt even though it doesn't support the required satp mode. In fact all rv32 CPUs are allowing menvcfg.PBMTE writes, which doesn't make sense for them in any circunstance since svpbmt is not possible for rv32 at this moment [1]. This also impacts rv64 CPUs that are running in satp 'bare' mode and are reporting svpbmt in the riscv,isa. All these problems can be solved by disabling svpbmt if satp_mode is not at least sv39. The problem reported in [1] goes away because we'll never enable MENVCFG_PBMTE write mask in write_menvcfgh(). We're also become consistent with how svpbmt is enabled for rv64. In case the user enables svpbmt in the command line using an invalid setup, not just disable svpbmt but also throw a warning: $ ./build/qemu-system-riscv64 -M virt,dumpdtb=fdt.dtb \ -cpu max,sv39=off,sv48=off,sv57=off,sv64=off,svpbmt=on qemu-system-riscv64: warning: svpbmt requires at least satp sv39, current satp mode: none [1] https://gitlab.com/qemu-project/qemu/-/work_items/3473 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3473 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Message-ID: <20260519114858.316532-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/tcg/tcg-cpu.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } +#ifndef CONFIG_USER_ONLY + if (cpu->cfg.ext_svpbmt && cpu->cfg.max_satp_mode < VM_1_10_SV39) { + cpu->cfg.ext_svpbmt = false; + if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_svpbmt))) { + warn_report("svpbmt requires at least satp sv39, " + "current satp mode: %s", + satp_mode_str(cpu->cfg.max_satp_mode, + riscv_cpu_is_32bit(cpu))); + } + } +#endif /* * Disable isa extensions based on priv spec after we * validated and set everything we need. -- 2.54.0
From: Jay Chang <jay.chang@sifive.com> When configuring pmpcfg (TOR, NA4, or NAPOT) and pmpaddr, if the value is smaller than the PMP granularity, it needs to be aligned to the PMP granularity. Signed-off-by: Jay Chang <jay.chang@sifive.com> Reviewed-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Message-ID: <20260520063606.36600-2-jay.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/pmp.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -XXX,XX +XXX,XX @@ static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) } /* * When granularity g >= 1 (i.e., granularity > 4 bytes), - * the NA4 (Naturally Aligned 4-byte) mode is not selectable + * the NA4 (Naturally Aligned 4-byte) mode is not selectable. + * In this case, an NA4 setting is reinterpreted as a NAPOT mode. */ if ((riscv_cpu_cfg(env)->pmp_granularity > MIN_RISCV_PMP_GRANULARITY) && (a_field == PMP_AMATCH_NA4)) { - return false; + val |= PMP_AMATCH; } env->pmp_state.pmp[pmp_index].cfg_reg = val; pmp_update_rule_addr(env, pmp_index); @@ -XXX,XX +XXX,XX @@ void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index) break; case PMP_AMATCH_NAPOT: + /* Bits [g-2:0] need to be all one to align pmp granularity */ + if (g >= 2) { + this_addr = deposit64(this_addr, 0, g - 1, -1ULL); + } + pmp_decode_napot(this_addr, &sa, &ea); break; -- 2.54.0
From: Jay Chang <jay.chang@sifive.com> Replace manual bit manipulation for better readability: - TOR: Use ROUND_DOWN() to clear lower bits - NAPOT: Use deposit64() to set lower bits Signed-off-by: Jay Chang <jay.chang@sifive.com> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Message-ID: <20260520063606.36600-3-jay.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/pmp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -XXX,XX +XXX,XX @@ void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index) case PMP_AMATCH_TOR: /* Bits pmpaddr[G-1:0] do not affect the TOR address-matching logic. */ if (g >= 1) { - prev_addr &= ~((1ULL << g) - 1ULL); - this_addr &= ~((1ULL << g) - 1ULL); + uint64_t granule = 1ULL << g; + prev_addr = ROUND_DOWN(prev_addr, granule); + this_addr = ROUND_DOWN(this_addr, granule); } if (prev_addr >= this_addr) { sa = ea = 0u; @@ -XXX,XX +XXX,XX @@ target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index) case PMP_AMATCH_TOR: /* Bit [g-1:0] read all zero */ if (g >= 1 && g < TARGET_LONG_BITS) { - val &= ~((1ULL << g) - 1ULL); + uint64_t granule = 1ULL << g; + val = ROUND_DOWN(val, granule); } break; case PMP_AMATCH_NAPOT: /* Bit [g-2:0] read all one */ if (g >= 2 && g < TARGET_LONG_BITS) { - val |= ((1ULL << (g - 1)) - 1ULL); + val = deposit64(val, 0, g - 1, -1ULL); } break; default: -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> The priv spec states the following about mstatus.MPV: "The MPV bit (Machine Previous Virtualization Mode) is written by the implementation whenever a trap is taken into M-mode." And, about mstatus.GVA: "Field GVA (Guest Virtual Address) is written by the implementation whenever a trap is taken into M-mode." Both are written during riscv_cpu_do_interrupt(). They're not supposed to be written by userspace. As far as write_mstatus goes these fields are read only. The same applies for mstatush.MPV/mstatush.GVA. Fixes: 03dd405dd5 ("target/riscv: Support MSTATUS.MPV/GVA only when RVH is enabled") Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260514194537.2416243-2-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, } if (xl != MXL_RV32 || env->debugger) { - if (riscv_has_ext(env, RVH)) { - mask |= MSTATUS_MPV | MSTATUS_GVA; - } if ((val & MSTATUS64_UXL) != 0) { mask |= MSTATUS64_UXL; } @@ -XXX,XX +XXX,XX @@ static RISCVException write_mstatush(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { uint64_t valh = (uint64_t)val << 32; - uint64_t mask = riscv_has_ext(env, RVH) ? MSTATUS_MPV | MSTATUS_GVA : 0; + uint64_t mask = 0; if (riscv_cpu_cfg(env)->ext_smdbltrp) { mask |= MSTATUS_MDT; -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> By the priv spec the value "3" is marked as 'Reserved' for mstatus.UXL. Handle a mstatus.UXL = 3 write by writing the current 'xl' instead. Fixes: https://gitlab.com/qemu-project/qemu/-/work_items/3367 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260514194537.2416243-3-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, if (xl != MXL_RV32 || env->debugger) { if ((val & MSTATUS64_UXL) != 0) { + uint64_t uxl = val & MSTATUS64_UXL >> 32; mask |= MSTATUS64_UXL; + + /* + * uxl = 3 is reserved so write the current xl instead. + * In case xl = MXL_RV128 (3) write MXL_RV64. + */ + if (uxl == 3) { + uxl = xl == MXL_RV128 ? MXL_RV64 : xl; + val = deposit64(val, 32, 2, uxl); + } } } -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We forgot to add 'cbo' insns to disas/riscv.c. The result is that the disassembler recognizes all of them as 'lq', an insn that happens to share the same opcode space. While we're at it reorder cbo_* entries in insn32.decode using opcode order instead of insn name. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3480 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20260519204714.1376551-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/insn32.decode | 2 +- disas/riscv.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -XXX,XX +XXX,XX @@ ldu ............ ..... 111 ..... 0000011 @i { [ # *** RV32 Zicbom Standard Extension *** + cbo_inval 0000000 00000 ..... 010 00000 0001111 @sfence_vm cbo_clean 0000000 00001 ..... 010 00000 0001111 @sfence_vm cbo_flush 0000000 00010 ..... 010 00000 0001111 @sfence_vm - cbo_inval 0000000 00000 ..... 010 00000 0001111 @sfence_vm # *** RV32 Zicboz Standard Extension *** cbo_zero 0000000 00100 ..... 010 00000 0001111 @sfence_vm diff --git a/disas/riscv.c b/disas/riscv.c index XXXXXXX..XXXXXXX 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -XXX,XX +XXX,XX @@ typedef enum { rv_op_ssamoswap_d = 953, rv_op_c_sspush = 954, rv_op_c_sspopchk = 955, + rv_op_cbo_inval = 956, + rv_op_cbo_clean = 957, + rv_op_cbo_flush = 958, + rv_op_cbo_zero = 959, } rv_op; /* register names */ @@ -XXX,XX +XXX,XX @@ const rv_opcode_data rvi_opcode_data[] = { rv_op_sspush, 0 }, { "c.sspopchk", rv_codec_cmop_ss, rv_fmt_rs1, NULL, rv_op_sspopchk, rv_op_sspopchk, 0 }, + { "cbo.inval", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "cbo.clean", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "cbo.flush", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "cbo.zero", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, }; /* CSR names */ @@ -XXX,XX +XXX,XX @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fence; break; case 1: op = rv_op_fence_i; break; - case 2: op = rv_op_lq; break; + case 2: + /* + * 'lq' shares the "(...) 010 ..... 0001111" opcode space + * with 'cbo' insns. Check the next 5 bits to select + * what we want: + * + * cbo_inval 0000000 00000 ..... 010 00000 0001111 + * cbo_clean 0000000 00001 ..... 010 00000 0001111 + * cbo_flush 0000000 00010 ..... 010 00000 0001111 + * cbo_zero 0000000 00100 ..... 010 00000 0001111 + * + * Anything that doesn't match these will default to 'lq'. + */ + switch ((inst >> 17) & 0b11111) { + case 0: op = rv_op_cbo_inval; break; + case 1: op = rv_op_cbo_clean; break; + case 2: op = rv_op_cbo_flush; break; + case 4: op = rv_op_cbo_zero; break; + default: op = rv_op_lq; break; + } } break; case 4: -- 2.54.0
From: Zephyr Li <fritchleybohrer@gmail.com> The Sstc predicate currently checks both ext_sstc and rdtime_fn. This causes the gdbstub CSR XML generation to skip Sstc CSRs when rdtime_fn has not been initialized yet, even if the CPU supports Sstc. As a result, GDB reports $stimecmp as void with a CPU that exposes the sstc extension. Only use ext_sstc for the early existence check, and keep the rdtime_fn check for non-debugger accesses. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3496 Signed-off-by: Zephyr Li <fritchleybohrer@gmail.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Message-ID: <20260525024220.39027-1-fritchleybohrer@gmail.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException sstc(CPURISCVState *env, int csrno) { bool hmode_check = false; - if (!riscv_cpu_cfg(env)->ext_sstc || !env->rdtime_fn) { + if (!riscv_cpu_cfg(env)->ext_sstc) { return RISCV_EXCP_ILLEGAL_INST; } @@ -XXX,XX +XXX,XX @@ static RISCVException sstc(CPURISCVState *env, int csrno) return RISCV_EXCP_NONE; } + if (!env->rdtime_fn) { + return RISCV_EXCP_ILLEGAL_INST; + } + if (env->priv == PRV_M) { return RISCV_EXCP_NONE; } -- 2.54.0
From: Zephyr Li <fritchleybohrer@gmail.com> The RISC-V privileged specification requires SFENCE.W.INVAL and SFENCE.INVAL.IR to raise an illegal instruction exception when executed in U-mode. Check the current privilege mode during translation and reject these instructions in U-mode, so they are reported as illegal instructions. Add a helper to reject these instructions in U-mode during translation. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3493 Suggested-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Signed-off-by: Zephyr Li <fritchleybohrer@gmail.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260522033145.17850-1-fritchleybohrer@gmail.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/insn_trans/trans_svinval.c.inc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/riscv/insn_trans/trans_svinval.c.inc b/target/riscv/insn_trans/trans_svinval.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_svinval.c.inc +++ b/target/riscv/insn_trans/trans_svinval.c.inc @@ -XXX,XX +XXX,XX @@ } \ } while (0) +/* Test if priv level is M or S. */ +#define REQUIRE_PRIV_MS(ctx) do { \ + if (ctx->priv == PRV_U) { \ + return false; \ + } \ +} while (0) + static bool trans_sinval_vma(DisasContext *ctx, arg_sinval_vma *a) { REQUIRE_SVINVAL(ctx); /* Do the same as sfence.vma currently */ REQUIRE_EXT(ctx, RVS); + REQUIRE_PRIV_MS(ctx); #ifndef CONFIG_USER_ONLY decode_save_opc(ctx, 0); gen_helper_tlb_flush(tcg_env); @@ -XXX,XX +XXX,XX @@ static bool trans_sfence_w_inval(DisasContext *ctx, arg_sfence_w_inval *a) { REQUIRE_SVINVAL(ctx); REQUIRE_EXT(ctx, RVS); + REQUIRE_PRIV_MS(ctx); /* Do nothing currently */ return true; } @@ -XXX,XX +XXX,XX @@ static bool trans_sfence_inval_ir(DisasContext *ctx, arg_sfence_inval_ir *a) { REQUIRE_SVINVAL(ctx); REQUIRE_EXT(ctx, RVS); + REQUIRE_PRIV_MS(ctx); /* Do nothing currently */ return true; } @@ -XXX,XX +XXX,XX @@ static bool trans_hinval_vvma(DisasContext *ctx, arg_hinval_vvma *a) REQUIRE_SVINVAL(ctx); /* Do the same as hfence.vvma currently */ REQUIRE_EXT(ctx, RVH); + REQUIRE_PRIV_MS(ctx); #ifndef CONFIG_USER_ONLY decode_save_opc(ctx, 0); gen_helper_hyp_tlb_flush(tcg_env); @@ -XXX,XX +XXX,XX @@ static bool trans_hinval_gvma(DisasContext *ctx, arg_hinval_gvma *a) REQUIRE_SVINVAL(ctx); /* Do the same as hfence.gvma currently */ REQUIRE_EXT(ctx, RVH); + REQUIRE_PRIV_MS(ctx); #ifndef CONFIG_USER_ONLY decode_save_opc(ctx, 0); gen_helper_hyp_gvma_tlb_flush(tcg_env); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> All helpers from this file can trigger ILLEGAL_INSN exceptions via check_zicbo_envcfg() directly, bypassing the usual exception code from translate.c. If we don't save the opcode before each helper, riscv_raise_exception() is triggered and env->bins won't be unwind during cpu_loop_exit_restore() (code path cpu_restore_state -> cpu_restore_state_from_tb() -> restore_state_to_opc()). And finally, in riscv_cpu_do_interrupt(), we will set (m)tval = 0 when we can, instead, set it to the cbo opcode that generated the exception. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3380 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520214704.1943652-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/insn_trans/trans_rvzicbo.c.inc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/riscv/insn_trans/trans_rvzicbo.c.inc b/target/riscv/insn_trans/trans_rvzicbo.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvzicbo.c.inc +++ b/target/riscv/insn_trans/trans_rvzicbo.c.inc @@ -XXX,XX +XXX,XX @@ static bool trans_cbo_clean(DisasContext *ctx, arg_cbo_clean *a) REQUIRE_ZICBOM(ctx); TCGv src = get_address(ctx, a->rs1, 0); + /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ + decode_save_opc(ctx, 0); gen_helper_cbo_clean_flush(tcg_env, src); return true; } @@ -XXX,XX +XXX,XX @@ static bool trans_cbo_flush(DisasContext *ctx, arg_cbo_flush *a) REQUIRE_ZICBOM(ctx); TCGv src = get_address(ctx, a->rs1, 0); + /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ + decode_save_opc(ctx, 0); gen_helper_cbo_clean_flush(tcg_env, src); return true; } @@ -XXX,XX +XXX,XX @@ static bool trans_cbo_inval(DisasContext *ctx, arg_cbo_inval *a) REQUIRE_ZICBOM(ctx); TCGv src = get_address(ctx, a->rs1, 0); + /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ + decode_save_opc(ctx, 0); gen_helper_cbo_inval(tcg_env, src); return true; } @@ -XXX,XX +XXX,XX @@ static bool trans_cbo_zero(DisasContext *ctx, arg_cbo_zero *a) REQUIRE_ZICBOZ(ctx); TCGv src = get_address(ctx, a->rs1, 0); + /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ + decode_save_opc(ctx, 0); gen_helper_cbo_zero(tcg_env, src); return true; } -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We need to fault during any access done while PTE bits 62-61 are both set, according to the RISC-V priv spec. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3494 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260521130727.2311629-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_helper.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -XXX,XX +XXX,XX @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, return TRANSLATE_FAIL; } + /* + * priv spec, "Svpbmt" chapter: + * "For non-leaf PTEs, bits 62-61 are reserved for future + * standard use. Until their use is defined by a standard + * extension, they must be cleared by software for forward + * compatibility, or else a page-fault exception is raised." + * + * For leaf PTEs the same bits are also reserved but in that + * case the page-fault is mandatory. Make both cases consistent + * by also page faulting here. + */ + if ((pte & PTE_PBMT) == PTE_PBMT) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: PBMT bits 62 and 61 are " + "reserved but are set in PTE: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); + return TRANSLATE_FAIL; + } + if (!riscv_cpu_cfg(env)->ext_svnapot && (pte & PTE_N)) { /* Reserved without Svnapot extension */ qemu_log_mask(LOG_GUEST_ERROR, "%s: N bit set in PTE, " @@ -XXX,XX +XXX,XX @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, return TRANSLATE_FAIL; } + /* + * priv spec, "Svpbmt" chapter: + * "For leaf PTEs, setting bits 62-61 to the value 3 is reserved + * for future standard use. Until this value is defined by a + * standard extension, using this reserved value in a leaf PTE + * raises a page-fault exception. " + * + * Raise a fault if 62-61 (i.e. PTE_PBMT) are set. + */ + if ((pte & PTE_PBMT) == PTE_PBMT) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: PBMT bits 62 and 61 are " + "reserved but are set in leaf PTE: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); + return TRANSLATE_FAIL; + } + target_ulong rwx = pte & (PTE_R | PTE_W | PTE_X); /* Check for reserved combinations of RWX flags. */ switch (rwx) { -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> promote_load_fault() is missing the promotion of misaligned AMO load addresses, i.e. RISCV_EXCP_LOAD_ADDR_MIS should be promoted to RISCV_EXCP_STORE_AMO_ADDR_MIS when RISCV_UW2_ALWAYS_STORE_AMO (i.e. always_storeamo is true). All other load AMO faults are already being covered. Cc: qemu-stable@nongnu.org Fixes: 98f21c30f5 ("target/riscv: AMO operations always raise store/AMO fault") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3503 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Message-ID: <20260522181353.429782-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_helper.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -XXX,XX +XXX,XX @@ static target_ulong promote_load_fault(target_ulong orig_cause) case RISCV_EXCP_LOAD_PAGE_FAULT: return RISCV_EXCP_STORE_PAGE_FAULT; + + case RISCV_EXCP_LOAD_ADDR_MIS: + return RISCV_EXCP_STORE_AMO_ADDR_MIS; } /* if no promotion, return original cause */ -- 2.54.0
From: Anton Johansson <anjo@rev.ng> gprh is only needed for TARGET_RISCV64 when modeling 128-bit registers, fixing their size to 64 bits makes sense. gpr is also fixed to 64 bits since all direct uses of env->gpr correctly zero extend/truncate to/from target_ulong, meaning !TARGET_RISCV64 will behave as expected. We do however need to be a bit careful when mapping 64-bit fields to 32-bit TCGv globals on big endian hosts. Note, the cpu/rv128 VMSTATE version is bumped, breaking migration from older versions. Signed-off-by: Anton Johansson <anjo@rev.ng> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Message-ID: <20260520125406.28693-2-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 4 ++-- target/riscv/cpu.c | 2 +- target/riscv/machine.c | 8 ++++---- target/riscv/monitor.c | 2 +- target/riscv/translate.c | 16 ++++++++++++++-- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ typedef struct PMUFixedCtrState { } PMUFixedCtrState; struct CPUArchState { - target_ulong gpr[32]; - target_ulong gprh[32]; /* 64 top bits of the 128-bit registers */ + uint64_t gpr[32]; + uint64_t gprh[32]; /* 64 top bits of the 128-bit registers */ /* vector coprocessor state. */ uint64_t vreg[32 * RV_VLEN_MAX / 64] QEMU_ALIGNED(16); diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) #endif for (i = 0; i < 32; i++) { - qemu_fprintf(f, " %-8s " TARGET_FMT_lx, + qemu_fprintf(f, " %-8s %" PRIx64, riscv_int_regnames[i], env->gpr[i]); if ((i & 3) == 3) { qemu_fprintf(f, "\n"); diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ static bool rv128_needed(void *opaque) static const VMStateDescription vmstate_rv128 = { .name = "cpu/rv128", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .needed = rv128_needed, .fields = (const VMStateField[]) { - VMSTATE_UINTTL_ARRAY(env.gprh, RISCVCPU, 32), + VMSTATE_UINT64_ARRAY(env.gprh, RISCVCPU, 32), VMSTATE_UINT64(env.mscratchh, RISCVCPU), VMSTATE_UINT64(env.sscratchh, RISCVCPU), VMSTATE_END_OF_LIST() @@ -XXX,XX +XXX,XX @@ const VMStateDescription vmstate_riscv_cpu = { .minimum_version_id = 11, .post_load = riscv_cpu_post_load, .fields = (const VMStateField[]) { - VMSTATE_UINTTL_ARRAY(env.gpr, RISCVCPU, 32), + VMSTATE_UINT64_ARRAY(env.gpr, RISCVCPU, 32), VMSTATE_UINT64_ARRAY(env.fpr, RISCVCPU, 32), VMSTATE_UINT8_ARRAY(env.miprio, RISCVCPU, 64), VMSTATE_UINT8_ARRAY(env.siprio, RISCVCPU, 64), diff --git a/target/riscv/monitor.c b/target/riscv/monitor.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/monitor.c +++ b/target/riscv/monitor.c @@ -XXX,XX +XXX,XX @@ static bool reg_is_ulong_integer(CPURISCVState *env, const char *name, target_ulong *val, bool is_gprh) { const char * const *reg_names; - target_ulong *vals; + uint64_t *vals; if (is_gprh) { reg_names = riscv_int_regnamesh; diff --git a/target/riscv/translate.c b/target/riscv/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -XXX,XX +XXX,XX @@ void riscv_translate_init(void) */ cpu_gpr[0] = NULL; cpu_gprh[0] = NULL; + /* + * Be careful with big endian hosts when mapping 64-bit CPUArchState fields + * to 32-bit TCGv globals. An offset of 4 bytes is applied so the least + * significant bytes are correctly written to. + */ +#if HOST_BIG_ENDIAN && !defined(TARGET_RISCV64) + size_t field_offset = 4; +#else + size_t field_offset = 0; +#endif for (i = 1; i < 32; i++) { cpu_gpr[i] = tcg_global_mem_new(tcg_env, - offsetof(CPURISCVState, gpr[i]), riscv_int_regnames[i]); + offsetof(CPURISCVState, gpr[i]) + field_offset, + riscv_int_regnames[i]); cpu_gprh[i] = tcg_global_mem_new(tcg_env, - offsetof(CPURISCVState, gprh[i]), riscv_int_regnamesh[i]); + offsetof(CPURISCVState, gprh[i]) + field_offset, + riscv_int_regnamesh[i]); } for (i = 0; i < 32; i++) { -- 2.54.0
From: Anton Johansson <anjo@rev.ng> According to version 20250508 of the unprivileged specification: - vtype: bits 0..7 used, bit XLEN-1 illegal, rest reserved => fix to 64-bits. - vxsat: bit 0 used, vxrm which would occupy bits 1..2 is stored separately, and bits 3..31 are set to 0 => fix to 8-bits. - vxrm: 2 lowest bits are used for rounding mode, rest set to 0 => fix to 8-bits. - vstart: maximum value of VLMAX-1, where VLMAX is at most 2^16 => fix to 32-bits as vstart is mapped to a TCG global. - vl: maximum value of VLEN which is at most 2^16 => fix to 32-bits as vl is mapped to a TCG global. Fields are shuffled for reduced padding. Note, the cpu/vector VMSTATE version is bumped, breaking migration from older versions. Signed-off-by: Anton Johansson <anjo@rev.ng> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20260520125406.28693-3-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 12 +-- target/riscv/machine.c | 14 +-- target/riscv/translate.c | 12 ++- target/riscv/vector_helper.c | 125 ++++++++++++++---------- target/riscv/insn_trans/trans_rvv.c.inc | 22 ++--- 5 files changed, 103 insertions(+), 82 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ FIELD(VTYPE, VSEW, 3, 3) FIELD(VTYPE, VTA, 6, 1) FIELD(VTYPE, VMA, 7, 1) FIELD(VTYPE, ALTFMT, 8, 1) -FIELD(VTYPE, RESERVED, 9, sizeof(target_ulong) * 8 - 10) +FIELD(VTYPE, RESERVED, 9, sizeof(uint64_t) * 8 - 10) typedef struct PMUCTRState { /* Current value of a counter */ @@ -XXX,XX +XXX,XX @@ struct CPUArchState { /* vector coprocessor state. */ uint64_t vreg[32 * RV_VLEN_MAX / 64] QEMU_ALIGNED(16); - target_ulong vxrm; - target_ulong vxsat; - target_ulong vl; - target_ulong vstart; - target_ulong vtype; + uint64_t vtype; + uint32_t vl; + uint32_t vstart; + uint8_t vxrm; + uint8_t vxsat; bool vill; target_ulong pc; diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ static bool vector_needed(void *opaque) static const VMStateDescription vmstate_vector = { .name = "cpu/vector", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .needed = vector_needed, .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.vreg, RISCVCPU, 32 * RV_VLEN_MAX / 64), - VMSTATE_UINTTL(env.vxrm, RISCVCPU), - VMSTATE_UINTTL(env.vxsat, RISCVCPU), - VMSTATE_UINTTL(env.vl, RISCVCPU), - VMSTATE_UINTTL(env.vstart, RISCVCPU), - VMSTATE_UINTTL(env.vtype, RISCVCPU), + VMSTATE_UINT64(env.vtype, RISCVCPU), + VMSTATE_UINT32(env.vl, RISCVCPU), + VMSTATE_UINT32(env.vstart, RISCVCPU), + VMSTATE_UINT8(env.vxrm, RISCVCPU), + VMSTATE_UINT8(env.vxsat, RISCVCPU), VMSTATE_BOOL(env.vill, RISCVCPU), VMSTATE_END_OF_LIST() } diff --git a/target/riscv/translate.c b/target/riscv/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -XXX,XX +XXX,XX @@ #include "tcg/tcg-cpu.h" /* global register indices */ -static TCGv cpu_gpr[32], cpu_gprh[32], cpu_pc, cpu_vl, cpu_vstart; +static TCGv cpu_gpr[32], cpu_gprh[32], cpu_pc; static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */ +static TCGv_i32 cpu_vl, cpu_vstart; static TCGv load_res; static TCGv load_val; @@ -XXX,XX +XXX,XX @@ void riscv_translate_init(void) size_t field_offset = 0; #endif + /* 32 bits in size, no offset needed */ + size_t vl_offset = offsetof(CPURISCVState, vl); + size_t vstart_offset = offsetof(CPURISCVState, vstart); + for (i = 1; i < 32; i++) { cpu_gpr[i] = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, gpr[i]) + field_offset, @@ -XXX,XX +XXX,XX @@ void riscv_translate_init(void) } cpu_pc = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, pc), "pc"); - cpu_vl = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, vl), "vl"); - cpu_vstart = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, vstart), - "vstart"); + cpu_vl = tcg_global_mem_new_i32(tcg_env, vl_offset, "vl"); + cpu_vstart = tcg_global_mem_new_i32(tcg_env, vstart_offset, "vstart"); load_res = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, load_res), "load_res"); load_val = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, load_val), diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -XXX,XX +XXX,XX @@ vext_continuous_ldst_host(CPURISCVState *env, vext_ldst_elem_fn_host *ldst_host, } } -static void vext_set_tail_elems_1s(target_ulong vl, void *vd, +static void vext_set_tail_elems_1s(uint32_t vl, void *vd, uint32_t desc, uint32_t nf, uint32_t esz, uint32_t max_elems) { @@ -XXX,XX +XXX,XX @@ vext_page_ldst_us(CPURISCVState *env, void *vd, target_ulong addr, uint32_t evl = env->vstart + elems; MMUAccessType access_type = is_load ? MMU_DATA_LOAD : MMU_DATA_STORE; + /* + * Maximum vector length is VLMAX == 2^16 == LMUL * VL / SEW, and + * occurs for LMUL == 8, SEW == 8, VL == 2^16. + */ + g_assert(env->vstart < UINT16_MAX && UINT16_MAX - env->vstart >= elems); + /* Check page permission/pmp/watchpoint/etc. */ probe_pages(env, addr, size, ra, access_type, mmu_index, &host, &flags, true); @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VMERGE_VX(vmerge_vxm_d, int64_t, H8) * define common macros for fixed point here. */ typedef void opivv2_rm_fn(void *vd, void *vs1, void *vs2, int i, - CPURISCVState *env, int vxrm); + CPURISCVState *env, uint8_t vxrm); #define OPIVV2_RM(NAME, TD, T1, T2, TX1, TX2, HD, HS1, HS2, OP) \ static inline void \ do_##NAME(void *vd, void *vs1, void *vs2, int i, \ - CPURISCVState *env, int vxrm) \ + CPURISCVState *env, uint8_t vxrm) \ { \ TX1 s1 = *((T1 *)vs1 + HS1(i)); \ TX2 s2 = *((T2 *)vs2 + HS2(i)); \ @@ -XXX,XX +XXX,XX @@ do_##NAME(void *vd, void *vs1, void *vs2, int i, \ static inline void vext_vv_rm_1(void *vd, void *v0, void *vs1, void *vs2, CPURISCVState *env, - uint32_t vl, uint32_t vm, int vxrm, + uint32_t vl, uint32_t vm, uint8_t vxrm, opivv2_rm_fn *fn, uint32_t vma, uint32_t esz) { for (uint32_t i = env->vstart; i < vl; i++) { @@ -XXX,XX +XXX,XX @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ do_##NAME, ESZ); \ } -static inline uint8_t saddu8(CPURISCVState *env, int vxrm, uint8_t a, +static inline uint8_t saddu8(CPURISCVState *env, uint8_t vxrm, uint8_t a, uint8_t b) { uint8_t res = a + b; @@ -XXX,XX +XXX,XX @@ static inline uint8_t saddu8(CPURISCVState *env, int vxrm, uint8_t a, return res; } -static inline uint16_t saddu16(CPURISCVState *env, int vxrm, uint16_t a, +static inline uint16_t saddu16(CPURISCVState *env, uint8_t vxrm, uint16_t a, uint16_t b) { uint16_t res = a + b; @@ -XXX,XX +XXX,XX @@ static inline uint16_t saddu16(CPURISCVState *env, int vxrm, uint16_t a, return res; } -static inline uint32_t saddu32(CPURISCVState *env, int vxrm, uint32_t a, +static inline uint32_t saddu32(CPURISCVState *env, uint8_t vxrm, uint32_t a, uint32_t b) { uint32_t res = a + b; @@ -XXX,XX +XXX,XX @@ static inline uint32_t saddu32(CPURISCVState *env, int vxrm, uint32_t a, return res; } -static inline uint64_t saddu64(CPURISCVState *env, int vxrm, uint64_t a, +static inline uint64_t saddu64(CPURISCVState *env, uint8_t vxrm, uint64_t a, uint64_t b) { uint64_t res = a + b; @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VV_RM(vsaddu_vv_w, 4) GEN_VEXT_VV_RM(vsaddu_vv_d, 8) typedef void opivx2_rm_fn(void *vd, target_long s1, void *vs2, int i, - CPURISCVState *env, int vxrm); + CPURISCVState *env, uint8_t vxrm); #define OPIVX2_RM(NAME, TD, T1, T2, TX1, TX2, HD, HS2, OP) \ static inline void \ do_##NAME(void *vd, target_long s1, void *vs2, int i, \ - CPURISCVState *env, int vxrm) \ + CPURISCVState *env, uint8_t vxrm) \ { \ TX2 s2 = *((T2 *)vs2 + HS2(i)); \ *((TD *)vd + HD(i)) = OP(env, vxrm, s2, (TX1)(T1)s1); \ @@ -XXX,XX +XXX,XX @@ do_##NAME(void *vd, target_long s1, void *vs2, int i, \ static inline void vext_vx_rm_1(void *vd, void *v0, target_long s1, void *vs2, CPURISCVState *env, - uint32_t vl, uint32_t vm, int vxrm, + uint32_t vl, uint32_t vm, uint8_t vxrm, opivx2_rm_fn *fn, uint32_t vma, uint32_t esz) { for (uint32_t i = env->vstart; i < vl; i++) { @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VX_RM(vsaddu_vx_h, 2) GEN_VEXT_VX_RM(vsaddu_vx_w, 4) GEN_VEXT_VX_RM(vsaddu_vx_d, 8) -static inline int8_t sadd8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) +static inline int8_t sadd8(CPURISCVState *env, uint8_t vxrm, int8_t a, + int8_t b) { int8_t res = a + b; if ((res ^ a) & (res ^ b) & INT8_MIN) { @@ -XXX,XX +XXX,XX @@ static inline int8_t sadd8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) return res; } -static inline int16_t sadd16(CPURISCVState *env, int vxrm, int16_t a, +static inline int16_t sadd16(CPURISCVState *env, uint8_t vxrm, int16_t a, int16_t b) { int16_t res = a + b; @@ -XXX,XX +XXX,XX @@ static inline int16_t sadd16(CPURISCVState *env, int vxrm, int16_t a, return res; } -static inline int32_t sadd32(CPURISCVState *env, int vxrm, int32_t a, +static inline int32_t sadd32(CPURISCVState *env, uint8_t vxrm, int32_t a, int32_t b) { int32_t res = a + b; @@ -XXX,XX +XXX,XX @@ static inline int32_t sadd32(CPURISCVState *env, int vxrm, int32_t a, return res; } -static inline int64_t sadd64(CPURISCVState *env, int vxrm, int64_t a, +static inline int64_t sadd64(CPURISCVState *env, uint8_t vxrm, int64_t a, int64_t b) { int64_t res = a + b; @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VX_RM(vsadd_vx_h, 2) GEN_VEXT_VX_RM(vsadd_vx_w, 4) GEN_VEXT_VX_RM(vsadd_vx_d, 8) -static inline uint8_t ssubu8(CPURISCVState *env, int vxrm, uint8_t a, +static inline uint8_t ssubu8(CPURISCVState *env, uint8_t vxrm, uint8_t a, uint8_t b) { uint8_t res = a - b; @@ -XXX,XX +XXX,XX @@ static inline uint8_t ssubu8(CPURISCVState *env, int vxrm, uint8_t a, return res; } -static inline uint16_t ssubu16(CPURISCVState *env, int vxrm, uint16_t a, +static inline uint16_t ssubu16(CPURISCVState *env, uint8_t vxrm, uint16_t a, uint16_t b) { uint16_t res = a - b; @@ -XXX,XX +XXX,XX @@ static inline uint16_t ssubu16(CPURISCVState *env, int vxrm, uint16_t a, return res; } -static inline uint32_t ssubu32(CPURISCVState *env, int vxrm, uint32_t a, +static inline uint32_t ssubu32(CPURISCVState *env, uint8_t vxrm, uint32_t a, uint32_t b) { uint32_t res = a - b; @@ -XXX,XX +XXX,XX @@ static inline uint32_t ssubu32(CPURISCVState *env, int vxrm, uint32_t a, return res; } -static inline uint64_t ssubu64(CPURISCVState *env, int vxrm, uint64_t a, +static inline uint64_t ssubu64(CPURISCVState *env, uint8_t vxrm, uint64_t a, uint64_t b) { uint64_t res = a - b; @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VX_RM(vssubu_vx_h, 2) GEN_VEXT_VX_RM(vssubu_vx_w, 4) GEN_VEXT_VX_RM(vssubu_vx_d, 8) -static inline int8_t ssub8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) +static inline int8_t ssub8(CPURISCVState *env, uint8_t vxrm, int8_t a, + int8_t b) { int8_t res = a - b; if ((res ^ a) & (a ^ b) & INT8_MIN) { @@ -XXX,XX +XXX,XX @@ static inline int8_t ssub8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) return res; } -static inline int16_t ssub16(CPURISCVState *env, int vxrm, int16_t a, +static inline int16_t ssub16(CPURISCVState *env, uint8_t vxrm, int16_t a, int16_t b) { int16_t res = a - b; @@ -XXX,XX +XXX,XX @@ static inline int16_t ssub16(CPURISCVState *env, int vxrm, int16_t a, return res; } -static inline int32_t ssub32(CPURISCVState *env, int vxrm, int32_t a, +static inline int32_t ssub32(CPURISCVState *env, uint8_t vxrm, int32_t a, int32_t b) { int32_t res = a - b; @@ -XXX,XX +XXX,XX @@ static inline int32_t ssub32(CPURISCVState *env, int vxrm, int32_t a, return res; } -static inline int64_t ssub64(CPURISCVState *env, int vxrm, int64_t a, +static inline int64_t ssub64(CPURISCVState *env, uint8_t vxrm, int64_t a, int64_t b) { int64_t res = a - b; @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VX_RM(vssub_vx_w, 4) GEN_VEXT_VX_RM(vssub_vx_d, 8) /* Vector Single-Width Averaging Add and Subtract */ -static inline uint8_t get_round(int vxrm, uint64_t v, uint8_t shift) +static inline uint8_t get_round(uint8_t vxrm, uint64_t v, uint8_t shift) { uint8_t d = extract64(v, shift, 1); uint8_t d1; @@ -XXX,XX +XXX,XX @@ static inline uint8_t get_round(int vxrm, uint64_t v, uint8_t shift) d1 = extract64(v, shift - 1, 1); D1 = extract64(v, 0, shift); - if (vxrm == 0) { /* round-to-nearest-up (add +0.5 LSB) */ + switch (vxrm) { + case 0: + /* round-to-nearest-up (add +0.5 LSB) */ return d1; - } else if (vxrm == 1) { /* round-to-nearest-even */ + case 1: + /* round-to-nearest-even */ if (shift > 1) { D2 = extract64(v, 0, shift - 1); return d1 & ((D2 != 0) | d); } else { return d1 & d; } - } else if (vxrm == 3) { /* round-to-odd (OR bits into LSB, aka "jam") */ + case 2: + /* round-down (truncate) */ + return 0; + case 3: + /* round-to-odd (OR bits into LSB, aka "jam") */ return !d & (D1 != 0); + default: + g_assert_not_reached(); } - return 0; /* round-down (truncate) */ } -static inline int32_t aadd32(CPURISCVState *env, int vxrm, int32_t a, +static inline int32_t aadd32(CPURISCVState *env, uint8_t vxrm, int32_t a, int32_t b) { int64_t res = (int64_t)a + b; @@ -XXX,XX +XXX,XX @@ static inline int32_t aadd32(CPURISCVState *env, int vxrm, int32_t a, return (res >> 1) + round; } -static inline int64_t aadd64(CPURISCVState *env, int vxrm, int64_t a, +static inline int64_t aadd64(CPURISCVState *env, uint8_t vxrm, int64_t a, int64_t b) { int64_t res = a + b; @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VX_RM(vaadd_vx_h, 2) GEN_VEXT_VX_RM(vaadd_vx_w, 4) GEN_VEXT_VX_RM(vaadd_vx_d, 8) -static inline uint32_t aaddu32(CPURISCVState *env, int vxrm, +static inline uint32_t aaddu32(CPURISCVState *env, uint8_t vxrm, uint32_t a, uint32_t b) { uint64_t res = (uint64_t)a + b; @@ -XXX,XX +XXX,XX @@ static inline uint32_t aaddu32(CPURISCVState *env, int vxrm, return (res >> 1) + round; } -static inline uint64_t aaddu64(CPURISCVState *env, int vxrm, +static inline uint64_t aaddu64(CPURISCVState *env, uint8_t vxrm, uint64_t a, uint64_t b) { uint64_t res = a + b; @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VX_RM(vaaddu_vx_h, 2) GEN_VEXT_VX_RM(vaaddu_vx_w, 4) GEN_VEXT_VX_RM(vaaddu_vx_d, 8) -static inline int32_t asub32(CPURISCVState *env, int vxrm, int32_t a, +static inline int32_t asub32(CPURISCVState *env, uint8_t vxrm, int32_t a, int32_t b) { int64_t res = (int64_t)a - b; @@ -XXX,XX +XXX,XX @@ static inline int32_t asub32(CPURISCVState *env, int vxrm, int32_t a, return (res >> 1) + round; } -static inline int64_t asub64(CPURISCVState *env, int vxrm, int64_t a, +static inline int64_t asub64(CPURISCVState *env, uint8_t vxrm, int64_t a, int64_t b) { int64_t res = (int64_t)a - b; @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VX_RM(vasub_vx_h, 2) GEN_VEXT_VX_RM(vasub_vx_w, 4) GEN_VEXT_VX_RM(vasub_vx_d, 8) -static inline uint32_t asubu32(CPURISCVState *env, int vxrm, +static inline uint32_t asubu32(CPURISCVState *env, uint8_t vxrm, uint32_t a, uint32_t b) { int64_t res = (int64_t)a - b; @@ -XXX,XX +XXX,XX @@ static inline uint32_t asubu32(CPURISCVState *env, int vxrm, return (res >> 1) + round; } -static inline uint64_t asubu64(CPURISCVState *env, int vxrm, +static inline uint64_t asubu64(CPURISCVState *env, uint8_t vxrm, uint64_t a, uint64_t b) { uint64_t res = (uint64_t)a - b; @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VX_RM(vasubu_vx_w, 4) GEN_VEXT_VX_RM(vasubu_vx_d, 8) /* Vector Single-Width Fractional Multiply with Rounding and Saturation */ -static inline int8_t vsmul8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) +static inline int8_t vsmul8(CPURISCVState *env, uint8_t vxrm, int8_t a, + int8_t b) { uint8_t round; int16_t res; @@ -XXX,XX +XXX,XX @@ static inline int8_t vsmul8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) } } -static int16_t vsmul16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) +static int16_t vsmul16(CPURISCVState *env, uint8_t vxrm, int16_t a, int16_t b) { uint8_t round; int32_t res; @@ -XXX,XX +XXX,XX @@ static int16_t vsmul16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) } } -static int32_t vsmul32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) +static int32_t vsmul32(CPURISCVState *env, uint8_t vxrm, int32_t a, int32_t b) { uint8_t round; int64_t res; @@ -XXX,XX +XXX,XX @@ static int32_t vsmul32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) } } -static int64_t vsmul64(CPURISCVState *env, int vxrm, int64_t a, int64_t b) +static int64_t vsmul64(CPURISCVState *env, uint8_t vxrm, int64_t a, int64_t b) { uint8_t round; uint64_t hi_64, lo_64; @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VX_RM(vsmul_vx_d, 8) /* Vector Single-Width Scaling Shift Instructions */ static inline uint8_t -vssrl8(CPURISCVState *env, int vxrm, uint8_t a, uint8_t b) +vssrl8(CPURISCVState *env, uint8_t vxrm, uint8_t a, uint8_t b) { uint8_t round, shift = b & 0x7; uint8_t res; @@ -XXX,XX +XXX,XX @@ vssrl8(CPURISCVState *env, int vxrm, uint8_t a, uint8_t b) return res; } static inline uint16_t -vssrl16(CPURISCVState *env, int vxrm, uint16_t a, uint16_t b) +vssrl16(CPURISCVState *env, uint8_t vxrm, uint16_t a, uint16_t b) { uint8_t round, shift = b & 0xf; @@ -XXX,XX +XXX,XX @@ vssrl16(CPURISCVState *env, int vxrm, uint16_t a, uint16_t b) return (a >> shift) + round; } static inline uint32_t -vssrl32(CPURISCVState *env, int vxrm, uint32_t a, uint32_t b) +vssrl32(CPURISCVState *env, uint8_t vxrm, uint32_t a, uint32_t b) { uint8_t round, shift = b & 0x1f; @@ -XXX,XX +XXX,XX @@ vssrl32(CPURISCVState *env, int vxrm, uint32_t a, uint32_t b) return (a >> shift) + round; } static inline uint64_t -vssrl64(CPURISCVState *env, int vxrm, uint64_t a, uint64_t b) +vssrl64(CPURISCVState *env, uint8_t vxrm, uint64_t a, uint64_t b) { uint8_t round, shift = b & 0x3f; @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VX_RM(vssrl_vx_w, 4) GEN_VEXT_VX_RM(vssrl_vx_d, 8) static inline int8_t -vssra8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) +vssra8(CPURISCVState *env, uint8_t vxrm, int8_t a, int8_t b) { uint8_t round, shift = b & 0x7; @@ -XXX,XX +XXX,XX @@ vssra8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) return (a >> shift) + round; } static inline int16_t -vssra16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) +vssra16(CPURISCVState *env, uint8_t vxrm, int16_t a, int16_t b) { uint8_t round, shift = b & 0xf; @@ -XXX,XX +XXX,XX @@ vssra16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) return (a >> shift) + round; } static inline int32_t -vssra32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) +vssra32(CPURISCVState *env, uint8_t vxrm, int32_t a, int32_t b) { uint8_t round, shift = b & 0x1f; @@ -XXX,XX +XXX,XX @@ vssra32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) return (a >> shift) + round; } static inline int64_t -vssra64(CPURISCVState *env, int vxrm, int64_t a, int64_t b) +vssra64(CPURISCVState *env, uint8_t vxrm, int64_t a, int64_t b) { uint8_t round, shift = b & 0x3f; @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VX_RM(vssra_vx_d, 8) /* Vector Narrowing Fixed-Point Clip Instructions */ static inline int8_t -vnclip8(CPURISCVState *env, int vxrm, int16_t a, int8_t b) +vnclip8(CPURISCVState *env, uint8_t vxrm, int16_t a, int8_t b) { uint8_t round, shift = b & 0xf; int16_t res; @@ -XXX,XX +XXX,XX @@ vnclip8(CPURISCVState *env, int vxrm, int16_t a, int8_t b) } static inline int16_t -vnclip16(CPURISCVState *env, int vxrm, int32_t a, int16_t b) +vnclip16(CPURISCVState *env, uint8_t vxrm, int32_t a, int16_t b) { uint8_t round, shift = b & 0x1f; int32_t res; @@ -XXX,XX +XXX,XX @@ vnclip16(CPURISCVState *env, int vxrm, int32_t a, int16_t b) } static inline int32_t -vnclip32(CPURISCVState *env, int vxrm, int64_t a, int32_t b) +vnclip32(CPURISCVState *env, uint8_t vxrm, int64_t a, int32_t b) { uint8_t round, shift = b & 0x3f; int64_t res; @@ -XXX,XX +XXX,XX @@ GEN_VEXT_VX_RM(vnclip_wx_h, 2) GEN_VEXT_VX_RM(vnclip_wx_w, 4) static inline uint8_t -vnclipu8(CPURISCVState *env, int vxrm, uint16_t a, uint8_t b) +vnclipu8(CPURISCVState *env, uint8_t vxrm, uint16_t a, uint8_t b) { uint8_t round, shift = b & 0xf; uint16_t res; @@ -XXX,XX +XXX,XX @@ vnclipu8(CPURISCVState *env, int vxrm, uint16_t a, uint8_t b) } static inline uint16_t -vnclipu16(CPURISCVState *env, int vxrm, uint32_t a, uint16_t b) +vnclipu16(CPURISCVState *env, uint8_t vxrm, uint32_t a, uint16_t b) { uint8_t round, shift = b & 0x1f; uint32_t res; @@ -XXX,XX +XXX,XX @@ vnclipu16(CPURISCVState *env, int vxrm, uint32_t a, uint16_t b) } static inline uint32_t -vnclipu32(CPURISCVState *env, int vxrm, uint64_t a, uint32_t b) +vnclipu32(CPURISCVState *env, uint8_t vxrm, uint64_t a, uint32_t b) { uint8_t round, shift = b & 0x3f; uint64_t res; diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -XXX,XX +XXX,XX @@ static bool do_vsetvl(DisasContext *s, int rd, int rs1, TCGv s2) if (rd == 0 && rs1 == 0) { s1 = tcg_temp_new(); - tcg_gen_mov_tl(s1, cpu_vl); + tcg_gen_ext_i32_tl(s1, cpu_vl); } else if (rs1 == 0) { /* As the mask is at least one bit, RV_VLEN_MAX is >= VLMAX */ s1 = tcg_constant_tl(RV_VLEN_MAX); @@ -XXX,XX +XXX,XX @@ static bool ldst_whole_trans(uint32_t vd, uint32_t rs1, uint32_t nf, tcg_gen_qemu_st_i64(t8, addr, s->mem_idx, MO_LEUQ | atomicity); } if (i == size - 8) { - tcg_gen_movi_tl(cpu_vstart, 0); + tcg_gen_movi_i32(cpu_vstart, 0); } else { - tcg_gen_addi_tl(cpu_vstart, cpu_vstart, 8 >> log2_esz); + tcg_gen_addi_i32(cpu_vstart, cpu_vstart, 8 >> log2_esz); } } } else { @@ -XXX,XX +XXX,XX @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ s->cfg_ptr->vlenb, data, \ (s->altfmt ? gen_helper_##BFA_HELPER : \ fns[s->sew - 1])); \ - tcg_gen_movi_tl(cpu_vstart, 0); \ + tcg_gen_movi_i32(cpu_vstart, 0); \ finalize_rvv_inst(s); \ \ return true; \ @@ -XXX,XX +XXX,XX @@ static bool trans_vmv_x_s(DisasContext *s, arg_vmv_x_s *a) vec_element_loadi(s, t1, a->rs2, 0, true); tcg_gen_trunc_i64_tl(dest, t1); gen_set_gpr(s, a->rd, dest); - tcg_gen_movi_tl(cpu_vstart, 0); + tcg_gen_movi_i32(cpu_vstart, 0); finalize_rvv_inst(s); return true; } @@ -XXX,XX +XXX,XX @@ static bool trans_vmv_s_x(DisasContext *s, arg_vmv_s_x *a) TCGv s1; TCGLabel *over = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); + tcg_gen_brcond_i32(TCG_COND_GEU, cpu_vstart, cpu_vl, over); t1 = tcg_temp_new_i64(); @@ -XXX,XX +XXX,XX @@ static bool trans_vmv_s_x(DisasContext *s, arg_vmv_s_x *a) tcg_gen_ext_tl_i64(t1, s1); vec_element_storei_tail(s, a->rd, t1); gen_set_label(over); - tcg_gen_movi_tl(cpu_vstart, 0); + tcg_gen_movi_i32(cpu_vstart, 0); finalize_rvv_inst(s); return true; } @@ -XXX,XX +XXX,XX @@ static bool trans_vfmv_f_s(DisasContext *s, arg_vfmv_f_s *a) } mark_fs_dirty(s); - tcg_gen_movi_tl(cpu_vstart, 0); + tcg_gen_movi_i32(cpu_vstart, 0); finalize_rvv_inst(s); return true; } @@ -XXX,XX +XXX,XX @@ static bool trans_vfmv_s_f(DisasContext *s, arg_vfmv_s_f *a) TCGLabel *over = gen_new_label(); /* if vstart >= vl, skip vector register write back */ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); + tcg_gen_brcond_i32(TCG_COND_GEU, cpu_vstart, cpu_vl, over); /* NaN-box f[rs1] */ t1 = tcg_temp_new_i64(); @@ -XXX,XX +XXX,XX @@ static bool trans_vfmv_s_f(DisasContext *s, arg_vfmv_s_f *a) vec_element_storei_tail(s, a->rd, t1); gen_set_label(over); - tcg_gen_movi_tl(cpu_vstart, 0); + tcg_gen_movi_i32(cpu_vstart, 0); finalize_rvv_inst(s); return true; } @@ -XXX,XX +XXX,XX @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ \ fns[s->sew](dest, mask, src1, src2, tcg_env, desc); \ \ - tcg_gen_movi_tl(cpu_vstart, 0); \ + tcg_gen_movi_i32(cpu_vstart, 0); \ finalize_rvv_inst(s); \ \ return true; \ -- 2.54.0
From: Anton Johansson <anjo@rev.ng> Fix to 64 bits in size and as these are mapped to TCG globals, be careful with host endianness when allocating globals. Casts are added to logging expressions to retain the correct size for TARGET_RISCV32. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-4-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 6 +++--- target/riscv/cpu.c | 2 +- target/riscv/cpu_helper.c | 6 +++--- target/riscv/machine.c | 6 +++--- target/riscv/translate.c | 12 +++++++----- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { uint8_t vxsat; bool vill; - target_ulong pc; - target_ulong load_res; - target_ulong load_val; + uint64_t pc; + uint64_t load_res; + uint64_t load_val; /* Floating-Point state */ uint64_t fpr[32]; /* assume both F and D extensions */ diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, " %s %d\n", "V = ", env->virt_enabled); } #endif - qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc ", env->pc); + qemu_fprintf(f, " %s %" PRIx64 "\n", "pc ", env->pc); #ifndef CONFIG_USER_ONLY for (i = 0; i < ARRAY_SIZE(csr_ops); i++) { int csrno = i; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -XXX,XX +XXX,XX @@ void riscv_cpu_do_interrupt(CPUState *cs) qemu_log_mask(CPU_LOG_INT, "%s: hart:%"PRIu64", async:%d, cause:"TARGET_FMT_lx", " - "epc:0x"TARGET_FMT_lx", tval:0x"TARGET_FMT_lx", desc=%s\n", - __func__, env->mhartid, async, cause, env->pc, tval, - riscv_cpu_get_trap_name(cause, async)); + "epc:0x%"PRIx64", tval:0x"TARGET_FMT_lx", desc=%s\n", + __func__, env->mhartid, async, cause, env->pc, + tval, riscv_cpu_get_trap_name(cause, async)); mode = env->priv <= PRV_S && cause < 64 && (((deleg >> cause) & 1) || s_injected || vs_injected) ? PRV_S : PRV_M; diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINT64_ARRAY(env.fpr, RISCVCPU, 32), VMSTATE_UINT8_ARRAY(env.miprio, RISCVCPU, 64), VMSTATE_UINT8_ARRAY(env.siprio, RISCVCPU, 64), - VMSTATE_UINTTL(env.pc, RISCVCPU), - VMSTATE_UINTTL(env.load_res, RISCVCPU), - VMSTATE_UINTTL(env.load_val, RISCVCPU), + VMSTATE_UINT64(env.pc, RISCVCPU), + VMSTATE_UINT64(env.load_res, RISCVCPU), + VMSTATE_UINT64(env.load_val, RISCVCPU), VMSTATE_UINTTL(env.frm, RISCVCPU), VMSTATE_UINTTL(env.badaddr, RISCVCPU), VMSTATE_UINTTL(env.guest_phys_fault_addr, RISCVCPU), diff --git a/target/riscv/translate.c b/target/riscv/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -XXX,XX +XXX,XX @@ void riscv_translate_init(void) /* 32 bits in size, no offset needed */ size_t vl_offset = offsetof(CPURISCVState, vl); size_t vstart_offset = offsetof(CPURISCVState, vstart); + /* 64 bits in size mapped to TCGv, needs offset */ + size_t pc_offset = offsetof(CPURISCVState, pc) + field_offset; + size_t res_offset = offsetof(CPURISCVState, load_res) + field_offset; + size_t val_offset = offsetof(CPURISCVState, load_val) + field_offset; for (i = 1; i < 32; i++) { cpu_gpr[i] = tcg_global_mem_new(tcg_env, @@ -XXX,XX +XXX,XX @@ void riscv_translate_init(void) offsetof(CPURISCVState, fpr[i]), riscv_fpr_regnames[i]); } - cpu_pc = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, pc), "pc"); + cpu_pc = tcg_global_mem_new(tcg_env, pc_offset, "pc"); cpu_vl = tcg_global_mem_new_i32(tcg_env, vl_offset, "vl"); cpu_vstart = tcg_global_mem_new_i32(tcg_env, vstart_offset, "vstart"); - load_res = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, load_res), - "load_res"); - load_val = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, load_val), - "load_val"); + load_res = tcg_global_mem_new(tcg_env, res_offset, "load_res"); + load_val = tcg_global_mem_new(tcg_env, val_offset, "load_val"); } -- 2.54.0
From: Anton Johansson <anjo@rev.ng> According to version 20250508 of the unprivileged specification the frm field of fcsr is 3-bits in size, fix it to 8-bits. Similarly fflags is 5 bits, fix to 8. Uses of frm is restricted to uint8_t where sensible, helpers still need 32-bit arguments and the DisasContext field is kept as int to represent -1 for an unknown rm. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-5-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 6 +++--- target/riscv/csr.c | 4 ++++ target/riscv/fpu_helper.c | 10 +++++----- target/riscv/machine.c | 2 +- target/riscv/translate.c | 4 ++-- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { /* Floating-Point state */ uint64_t fpr[32]; /* assume both F and D extensions */ - target_ulong frm; + uint8_t frm; float_status fp_status; target_ulong badaddr; @@ -XXX,XX +XXX,XX @@ G_NORETURN void riscv_raise_exception(CPURISCVState *env, RISCVException exception, uintptr_t pc); -target_ulong riscv_cpu_get_fflags(CPURISCVState *env); -void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong); +uint8_t riscv_cpu_get_fflags(CPURISCVState *env); +void riscv_cpu_set_fflags(CPURISCVState *env, uint8_t); #ifndef CONFIG_USER_ONLY void cpu_set_exception_base(int vp_index, target_ulong address); diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException write_frm(CPURISCVState *env, int csrno, static RISCVException read_fcsr(CPURISCVState *env, int csrno, target_ulong *val) { + /* + * This is an 8-bit operation, fflags make up the lower 5 bits and + * frm the upper 3 bits of fcsr. + */ *val = (riscv_cpu_get_fflags(env) << FSR_AEXC_SHIFT) | (env->frm << FSR_RD_SHIFT); return RISCV_EXCP_NONE; diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -XXX,XX +XXX,XX @@ #include "fpu/softfloat.h" #include "internals.h" -target_ulong riscv_cpu_get_fflags(CPURISCVState *env) +uint8_t riscv_cpu_get_fflags(CPURISCVState *env) { int soft = get_float_exception_flags(&env->fp_status); - target_ulong hard = 0; + uint8_t hard = 0; hard |= (soft & float_flag_inexact) ? FPEXC_NX : 0; hard |= (soft & float_flag_underflow) ? FPEXC_UF : 0; @@ -XXX,XX +XXX,XX @@ target_ulong riscv_cpu_get_fflags(CPURISCVState *env) return hard; } -void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong hard) +void riscv_cpu_set_fflags(CPURISCVState *env, uint8_t hard) { int soft = 0; @@ -XXX,XX +XXX,XX @@ void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong hard) void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm) { - int softrm; + FloatRoundMode softrm; if (rm == RISCV_FRM_DYN) { rm = env->frm; @@ -XXX,XX +XXX,XX @@ void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm) void helper_set_rounding_mode_chkfrm(CPURISCVState *env, uint32_t rm) { - int softrm; + FloatRoundMode softrm; /* Always validate frm, even if rm != DYN. */ if (unlikely(env->frm >= 5)) { diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINT64(env.pc, RISCVCPU), VMSTATE_UINT64(env.load_res, RISCVCPU), VMSTATE_UINT64(env.load_val, RISCVCPU), - VMSTATE_UINTTL(env.frm, RISCVCPU), + VMSTATE_UINT8(env.frm, RISCVCPU), VMSTATE_UINTTL(env.badaddr, RISCVCPU), VMSTATE_UINTTL(env.guest_phys_fault_addr, RISCVCPU), VMSTATE_UINTTL(env.priv_ver, RISCVCPU), diff --git a/target/riscv/translate.c b/target/riscv/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -XXX,XX +XXX,XX @@ static void finalize_rvv_inst(DisasContext *ctx) ctx->vstart_eq_zero = true; } -static void gen_set_rm(DisasContext *ctx, int rm) +static void gen_set_rm(DisasContext *ctx, uint8_t rm) { if (ctx->frm == rm) { return; @@ -XXX,XX +XXX,XX @@ static void gen_set_rm(DisasContext *ctx, int rm) gen_helper_set_rounding_mode(tcg_env, tcg_constant_i32(rm)); } -static void gen_set_rm_chkfrm(DisasContext *ctx, int rm) +static void gen_set_rm_chkfrm(DisasContext *ctx, uint8_t rm) { if (ctx->frm == rm && ctx->frm_valid) { return; -- 2.54.0
From: Anton Johansson <anjo@rev.ng> Fix these fields to 64 bits as they cannot be made smaller. Also make sure stores to these fields from TCG are 64 bits in size to avoid incorrect values on big endian hosts. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-6-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 4 ++-- target/riscv/machine.c | 2 +- target/riscv/translate.c | 6 ++++-- target/riscv/insn_trans/trans_privileged.c.inc | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { uint8_t frm; float_status fp_status; - target_ulong badaddr; - target_ulong bins; + uint64_t badaddr; + uint64_t bins; target_ulong guest_phys_fault_addr; diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINT64(env.load_res, RISCVCPU), VMSTATE_UINT64(env.load_val, RISCVCPU), VMSTATE_UINT8(env.frm, RISCVCPU), - VMSTATE_UINTTL(env.badaddr, RISCVCPU), + VMSTATE_UINT64(env.badaddr, RISCVCPU), VMSTATE_UINTTL(env.guest_phys_fault_addr, RISCVCPU), VMSTATE_UINTTL(env.priv_ver, RISCVCPU), VMSTATE_UINTTL(env.vext_ver, RISCVCPU), diff --git a/target/riscv/translate.c b/target/riscv/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -XXX,XX +XXX,XX @@ static void generate_exception(DisasContext *ctx, RISCVException excp) static void gen_exception_illegal(DisasContext *ctx) { - tcg_gen_st_i32(tcg_constant_i32(ctx->opcode), tcg_env, + tcg_gen_st_i64(tcg_constant_i64(ctx->opcode), tcg_env, offsetof(CPURISCVState, bins)); if (ctx->virt_inst_excp) { generate_exception(ctx, RISCV_EXCP_VIRT_INSTRUCTION_FAULT); @@ -XXX,XX +XXX,XX @@ static void gen_exception_illegal(DisasContext *ctx) static void gen_exception_inst_addr_mis(DisasContext *ctx, TCGv target) { - tcg_gen_st_tl(target, tcg_env, offsetof(CPURISCVState, badaddr)); + TCGv_i64 ext = tcg_temp_new_i64(); + tcg_gen_extu_tl_i64(ext, target); + tcg_gen_st_i64(ext, tcg_env, offsetof(CPURISCVState, badaddr)); generate_exception(ctx, RISCV_EXCP_INST_ADDR_MIS); } diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -XXX,XX +XXX,XX @@ static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a) if (pre == 0x01f01013 && ebreak == 0x00100073 && post == 0x40705013) { generate_exception(ctx, RISCV_EXCP_SEMIHOST); } else { - tcg_gen_st_tl(tcg_constant_tl(ebreak_addr), tcg_env, + tcg_gen_st_i64(tcg_constant_i64(ebreak_addr), tcg_env, offsetof(CPURISCVState, badaddr)); generate_exception(ctx, RISCV_EXCP_BREAKPOINT); } -- 2.54.0
From: Anton Johansson <anjo@rev.ng> Widen to 64 bits, and use hwaddr as argument to get_physical_address(). Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-7-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 2 +- target/riscv/cpu_helper.c | 3 +-- target/riscv/machine.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { uint64_t badaddr; uint64_t bins; - target_ulong guest_phys_fault_addr; + uint64_t guest_phys_fault_addr; target_ulong priv_ver; target_ulong vext_ver; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -XXX,XX +XXX,XX @@ static bool check_svukte_addr(CPURISCVState *env, vaddr addr) */ static int get_physical_address(CPURISCVState *env, hwaddr *physical, int *ret_prot, vaddr addr, - target_ulong *fault_pte_addr, + hwaddr *fault_pte_addr, int access_type, int mmu_idx, bool first_stage, bool two_stage, bool is_debug, bool is_probe) @@ -XXX,XX +XXX,XX @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, ret = get_physical_address(env, &pa, &prot, address, &env->guest_phys_fault_addr, access_type, mmu_idx, true, true, false, probe); - /* * A G-stage exception may be triggered during two state lookup. * And the env->guest_phys_fault_addr has already been set in diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINT64(env.load_val, RISCVCPU), VMSTATE_UINT8(env.frm, RISCVCPU), VMSTATE_UINT64(env.badaddr, RISCVCPU), - VMSTATE_UINTTL(env.guest_phys_fault_addr, RISCVCPU), + VMSTATE_UINT64(env.guest_phys_fault_addr, RISCVCPU), VMSTATE_UINTTL(env.priv_ver, RISCVCPU), VMSTATE_UINTTL(env.vext_ver, RISCVCPU), VMSTATE_UINT32(env.misa_mxl, RISCVCPU), -- 2.54.0
From: Anton Johansson <anjo@rev.ng> Fix these fields to 32 bits, also update corresponding priv_ver field in DisasContext as well as function arguments. 32 bits was chosen since it's large enough to fit all stored values and int/int32_t is used in RISCVCPUDef and a few functions. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-8-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 6 +++--- target/riscv/machine.c | 4 ++-- target/riscv/translate.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { uint64_t guest_phys_fault_addr; - target_ulong priv_ver; - target_ulong vext_ver; + uint32_t priv_ver; + uint32_t vext_ver; /* RISCVMXL, but uint32_t for vmstate migration */ uint32_t misa_mxl; /* current mxl */ @@ -XXX,XX +XXX,XX @@ bool riscv_cpu_eff_priv(CPURISCVState *env, int *priv, bool *virt) } static inline bool riscv_cpu_allow_16bit_insn(const RISCVCPUConfig *cfg, - target_long priv_ver, + uint32_t priv_ver, uint32_t misa_ext) { /* In priv spec version 1.12 or newer, C always implies Zca */ diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINT8(env.frm, RISCVCPU), VMSTATE_UINT64(env.badaddr, RISCVCPU), VMSTATE_UINT64(env.guest_phys_fault_addr, RISCVCPU), - VMSTATE_UINTTL(env.priv_ver, RISCVCPU), - VMSTATE_UINTTL(env.vext_ver, RISCVCPU), + VMSTATE_UINT32(env.priv_ver, RISCVCPU), + VMSTATE_UINT32(env.vext_ver, RISCVCPU), VMSTATE_UINT32(env.misa_mxl, RISCVCPU), VMSTATE_UINT32(env.misa_ext, RISCVCPU), VMSTATE_UNUSED(4), diff --git a/target/riscv/translate.c b/target/riscv/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -XXX,XX +XXX,XX @@ typedef struct DisasContext { DisasContextBase base; target_ulong cur_insn_len; target_ulong pc_save; - target_ulong priv_ver; + uint32_t priv_ver; RISCVMXL misa_mxl_max; RISCVMXL xl; RISCVMXL address_xl; -- 2.54.0
From: Anton Johansson <anjo@rev.ng> 128-bit helpers only make sense for MXL_RV128, TARGET_RISCV64, and TCGv == TCGv_i64, therefore fix retxh to 64 bits. For the sake of being pedandic, update 128-bit instructions to access retxh via 64 bit TCG ops, even if they only make sense when TCGv == TCGv_i64. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-9-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 2 +- target/riscv/insn_trans/trans_rvi.c.inc | 8 ++++++-- target/riscv/insn_trans/trans_rvm.c.inc | 16 ++++++++++++---- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { uint32_t xl; /* current xlen */ /* 128-bit helpers upper part return value */ - target_ulong retxh; + uint64_t retxh; uint64_t jvt; diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -XXX,XX +XXX,XX @@ static bool do_csrr_i128(DisasContext *ctx, int rd, int rc) TCGv destl = dest_gpr(ctx, rd); TCGv desth = dest_gprh(ctx, rd); TCGv_i32 csr = tcg_constant_i32(rc); + TCGv_i64 wide_desth = tcg_temp_new_i64(); translator_io_start(&ctx->base); gen_helper_csrr_i128(destl, tcg_env, csr); - tcg_gen_ld_tl(desth, tcg_env, offsetof(CPURISCVState, retxh)); + tcg_gen_ld_i64(wide_desth, tcg_env, offsetof(CPURISCVState, retxh)); + tcg_gen_trunc_i64_tl(desth, wide_desth); gen_set_gpr128(ctx, rd, destl, desth); return do_csr_post(ctx); } @@ -XXX,XX +XXX,XX @@ static bool do_csrrw_i128(DisasContext *ctx, int rd, int rc, TCGv destl = dest_gpr(ctx, rd); TCGv desth = dest_gprh(ctx, rd); TCGv_i32 csr = tcg_constant_i32(rc); + TCGv_i64 wide_desth = tcg_temp_new_i64(); translator_io_start(&ctx->base); gen_helper_csrrw_i128(destl, tcg_env, csr, srcl, srch, maskl, maskh); - tcg_gen_ld_tl(desth, tcg_env, offsetof(CPURISCVState, retxh)); + tcg_gen_ld_i64(wide_desth, tcg_env, offsetof(CPURISCVState, retxh)); + tcg_gen_trunc_i64_tl(desth, wide_desth); gen_set_gpr128(ctx, rd, destl, desth); return do_csr_post(ctx); } diff --git a/target/riscv/insn_trans/trans_rvm.c.inc b/target/riscv/insn_trans/trans_rvm.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvm.c.inc +++ b/target/riscv/insn_trans/trans_rvm.c.inc @@ -XXX,XX +XXX,XX @@ static bool trans_mulhu(DisasContext *ctx, arg_mulhu *a) static void gen_div_i128(TCGv rdl, TCGv rdh, TCGv rs1l, TCGv rs1h, TCGv rs2l, TCGv rs2h) { + TCGv_i64 wide_rdh = tcg_temp_new_i64(); gen_helper_divs_i128(rdl, tcg_env, rs1l, rs1h, rs2l, rs2h); - tcg_gen_ld_tl(rdh, tcg_env, offsetof(CPURISCVState, retxh)); + tcg_gen_ld_i64(wide_rdh, tcg_env, offsetof(CPURISCVState, retxh)); + tcg_gen_trunc_i64_tl(rdh, wide_rdh); } static void gen_div(TCGv ret, TCGv source1, TCGv source2) @@ -XXX,XX +XXX,XX @@ static bool trans_div(DisasContext *ctx, arg_div *a) static void gen_divu_i128(TCGv rdl, TCGv rdh, TCGv rs1l, TCGv rs1h, TCGv rs2l, TCGv rs2h) { + TCGv_i64 wide_rdh = tcg_temp_new_i64(); gen_helper_divu_i128(rdl, tcg_env, rs1l, rs1h, rs2l, rs2h); - tcg_gen_ld_tl(rdh, tcg_env, offsetof(CPURISCVState, retxh)); + tcg_gen_ld_i64(wide_rdh, tcg_env, offsetof(CPURISCVState, retxh)); + tcg_gen_trunc_i64_tl(rdh, wide_rdh); } static void gen_divu(TCGv ret, TCGv source1, TCGv source2) @@ -XXX,XX +XXX,XX @@ static bool trans_divu(DisasContext *ctx, arg_divu *a) static void gen_rem_i128(TCGv rdl, TCGv rdh, TCGv rs1l, TCGv rs1h, TCGv rs2l, TCGv rs2h) { + TCGv_i64 wide_rdh = tcg_temp_new_i64(); gen_helper_rems_i128(rdl, tcg_env, rs1l, rs1h, rs2l, rs2h); - tcg_gen_ld_tl(rdh, tcg_env, offsetof(CPURISCVState, retxh)); + tcg_gen_ld_i64(wide_rdh, tcg_env, offsetof(CPURISCVState, retxh)); + tcg_gen_trunc_i64_tl(rdh, wide_rdh); } static void gen_rem(TCGv ret, TCGv source1, TCGv source2) @@ -XXX,XX +XXX,XX @@ static bool trans_rem(DisasContext *ctx, arg_rem *a) static void gen_remu_i128(TCGv rdl, TCGv rdh, TCGv rs1l, TCGv rs1h, TCGv rs2l, TCGv rs2h) { + TCGv_i64 wide_rdh = tcg_temp_new_i64(); gen_helper_remu_i128(rdl, tcg_env, rs1l, rs1h, rs2l, rs2h); - tcg_gen_ld_tl(rdh, tcg_env, offsetof(CPURISCVState, retxh)); + tcg_gen_ld_i64(wide_rdh, tcg_env, offsetof(CPURISCVState, retxh)); + tcg_gen_trunc_i64_tl(rdh, wide_rdh); } static void gen_remu(TCGv ret, TCGv source1, TCGv source2) -- 2.54.0
From: Anton Johansson <anjo@rev.ng> As ssp holds a pointer, fix to 64 bits in size and make sure stores from TCG use the correct size to avoid problems on big endian hosts. Note, the cpu/ssp VMSTATE version is bumped, breaking migration from older versions. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-10-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 2 +- target/riscv/machine.c | 6 +++--- target/riscv/insn_trans/trans_rvzicfiss.c.inc | 18 +++++++++++++----- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { /* elp state for zicfilp extension */ bool elp; /* shadow stack register for zicfiss extension */ - target_ulong ssp; + uint64_t ssp; /* env place holder for extra word 2 during unwind */ target_ulong excp_uw2; /* sw check code for sw check exception */ diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ static bool ssp_needed(void *opaque) static const VMStateDescription vmstate_ssp = { .name = "cpu/ssp", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .needed = ssp_needed, .fields = (const VMStateField[]) { - VMSTATE_UINTTL(env.ssp, RISCVCPU), + VMSTATE_UINT64(env.ssp, RISCVCPU), VMSTATE_END_OF_LIST() } }; diff --git a/target/riscv/insn_trans/trans_rvzicfiss.c.inc b/target/riscv/insn_trans/trans_rvzicfiss.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvzicfiss.c.inc +++ b/target/riscv/insn_trans/trans_rvzicfiss.c.inc @@ -XXX,XX +XXX,XX @@ static bool trans_sspopchk(DisasContext *ctx, arg_sspopchk *a) TCGLabel *skip = gen_new_label(); uint32_t tmp = (get_xl(ctx) == MXL_RV64) ? 8 : 4; TCGv data = tcg_temp_new(); - tcg_gen_ld_tl(addr, tcg_env, offsetof(CPURISCVState, ssp)); + TCGv_i64 wide_addr = tcg_temp_new_i64(); + tcg_gen_ld_i64(wide_addr, tcg_env, offsetof(CPURISCVState, ssp)); + tcg_gen_trunc_i64_tl(addr, wide_addr); decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); tcg_gen_qemu_ld_tl(data, addr, SS_MMU_INDEX(ctx), mxl_memop(ctx) | MO_ALIGN); @@ -XXX,XX +XXX,XX @@ static bool trans_sspopchk(DisasContext *ctx, arg_sspopchk *a) tcg_constant_i32(RISCV_EXCP_SW_CHECK)); gen_set_label(skip); tcg_gen_addi_tl(addr, addr, tmp); - tcg_gen_st_tl(addr, tcg_env, offsetof(CPURISCVState, ssp)); + tcg_gen_ext_tl_i64(wide_addr, addr); + tcg_gen_st_i64(wide_addr, tcg_env, offsetof(CPURISCVState, ssp)); return true; } @@ -XXX,XX +XXX,XX @@ static bool trans_sspush(DisasContext *ctx, arg_sspush *a) TCGv addr = tcg_temp_new(); int tmp = (get_xl(ctx) == MXL_RV64) ? -8 : -4; TCGv data = get_gpr(ctx, a->rs2, EXT_NONE); + TCGv_i64 wide_addr = tcg_temp_new_i64(); decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); - tcg_gen_ld_tl(addr, tcg_env, offsetof(CPURISCVState, ssp)); + tcg_gen_ld_i64(wide_addr, tcg_env, offsetof(CPURISCVState, ssp)); + tcg_gen_trunc_i64_tl(addr, wide_addr); tcg_gen_addi_tl(addr, addr, tmp); tcg_gen_qemu_st_tl(data, addr, SS_MMU_INDEX(ctx), mxl_memop(ctx) | MO_ALIGN); - tcg_gen_st_tl(addr, tcg_env, offsetof(CPURISCVState, ssp)); + tcg_gen_ext_tl_i64(wide_addr, addr); + tcg_gen_st_i64(wide_addr, tcg_env, offsetof(CPURISCVState, ssp)); return true; } @@ -XXX,XX +XXX,XX @@ static bool trans_ssrdp(DisasContext *ctx, arg_ssrdp *a) } TCGv dest = dest_gpr(ctx, a->rd); - tcg_gen_ld_tl(dest, tcg_env, offsetof(CPURISCVState, ssp)); + TCGv_i64 wide_addr = tcg_temp_new_i64(); + tcg_gen_ld_i64(wide_addr, tcg_env, offsetof(CPURISCVState, ssp)); + tcg_gen_trunc_i64_tl(dest, wide_addr); gen_set_gpr(ctx, a->rd, dest); return true; -- 2.54.0
From: Anton Johansson <anjo@rev.ng> Fix to 64 bits to match size of instruction start words. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-11-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 2 +- target/riscv/translate.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { /* shadow stack register for zicfiss extension */ uint64_t ssp; /* env place holder for extra word 2 during unwind */ - target_ulong excp_uw2; + uint64_t excp_uw2; /* sw check code for sw check exception */ target_ulong sw_check_code; #ifdef CONFIG_USER_ONLY diff --git a/target/riscv/translate.c b/target/riscv/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -XXX,XX +XXX,XX @@ static void gen_check_nanbox_s(TCGv_i64 out, TCGv_i64 in) tcg_gen_movcond_i64(TCG_COND_GEU, out, in, t_max, in, t_nan); } -static void decode_save_opc(DisasContext *ctx, target_ulong excp_uw2) +static void decode_save_opc(DisasContext *ctx, uint64_t excp_uw2) { assert(!ctx->insn_start_updated); ctx->insn_start_updated = true; -- 2.54.0
From: Anton Johansson <anjo@rev.ng> The field only holds values of 2 and 3, fix its size to 8 bits and update stores from TCG. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-12-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 2 +- target/riscv/translate.c | 4 ++-- target/riscv/insn_trans/trans_rvi.c.inc | 8 ++++---- target/riscv/insn_trans/trans_rvzicfiss.c.inc | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { /* env place holder for extra word 2 during unwind */ uint64_t excp_uw2; /* sw check code for sw check exception */ - target_ulong sw_check_code; + uint8_t sw_check_code; #ifdef CONFIG_USER_ONLY uint32_t elf_flags; #endif diff --git a/target/riscv/translate.c b/target/riscv/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -XXX,XX +XXX,XX @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) if (ctx->fcfi_lp_expected) { /* Emit after insn_start, i.e. before the op following insn_start. */ tcg_ctx->emit_before_op = QTAILQ_NEXT(ctx->base.insn_start, link); - tcg_gen_st_tl(tcg_constant_tl(RISCV_EXCP_SW_CHECK_FCFI_TVAL), - tcg_env, offsetof(CPURISCVState, sw_check_code)); + tcg_gen_st8_i32(tcg_constant_i32(RISCV_EXCP_SW_CHECK_FCFI_TVAL), + tcg_env, offsetof(CPURISCVState, sw_check_code)); gen_helper_raise_exception(tcg_env, tcg_constant_i32(RISCV_EXCP_SW_CHECK)); tcg_ctx->emit_before_op = NULL; diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -XXX,XX +XXX,XX @@ static bool trans_lpad(DisasContext *ctx, arg_lpad *a) /* * misaligned, according to spec we should raise sw check exception */ - tcg_gen_st_tl(tcg_constant_tl(RISCV_EXCP_SW_CHECK_FCFI_TVAL), - tcg_env, offsetof(CPURISCVState, sw_check_code)); + tcg_gen_st8_i32(tcg_constant_i32(RISCV_EXCP_SW_CHECK_FCFI_TVAL), + tcg_env, offsetof(CPURISCVState, sw_check_code)); gen_helper_raise_exception(tcg_env, tcg_constant_i32(RISCV_EXCP_SW_CHECK)); return true; @@ -XXX,XX +XXX,XX @@ static bool trans_lpad(DisasContext *ctx, arg_lpad *a) TCGv tmp = tcg_temp_new(); tcg_gen_extract_tl(tmp, get_gpr(ctx, xT2, EXT_NONE), 12, 20); tcg_gen_brcondi_tl(TCG_COND_EQ, tmp, a->label, skip); - tcg_gen_st_tl(tcg_constant_tl(RISCV_EXCP_SW_CHECK_FCFI_TVAL), - tcg_env, offsetof(CPURISCVState, sw_check_code)); + tcg_gen_st8_i32(tcg_constant_i32(RISCV_EXCP_SW_CHECK_FCFI_TVAL), + tcg_env, offsetof(CPURISCVState, sw_check_code)); gen_helper_raise_exception(tcg_env, tcg_constant_i32(RISCV_EXCP_SW_CHECK)); gen_set_label(skip); diff --git a/target/riscv/insn_trans/trans_rvzicfiss.c.inc b/target/riscv/insn_trans/trans_rvzicfiss.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvzicfiss.c.inc +++ b/target/riscv/insn_trans/trans_rvzicfiss.c.inc @@ -XXX,XX +XXX,XX @@ static bool trans_sspopchk(DisasContext *ctx, arg_sspopchk *a) mxl_memop(ctx) | MO_ALIGN); TCGv rs1 = get_gpr(ctx, a->rs1, EXT_NONE); tcg_gen_brcond_tl(TCG_COND_EQ, data, rs1, skip); - tcg_gen_st_tl(tcg_constant_tl(RISCV_EXCP_SW_CHECK_BCFI_TVAL), - tcg_env, offsetof(CPURISCVState, sw_check_code)); + tcg_gen_st8_i32(tcg_constant_i32(RISCV_EXCP_SW_CHECK_BCFI_TVAL), + tcg_env, offsetof(CPURISCVState, sw_check_code)); gen_update_pc(ctx, 0); gen_helper_raise_exception(tcg_env, tcg_constant_i32(RISCV_EXCP_SW_CHECK)); -- 2.54.0
From: Anton Johansson <anjo@rev.ng> The priv field of CPUArchState only stores values in the range [0,3], fix to 8 bits in size and update relevant function arguments. Introduce a new privilege_mode_t typedef for passing around the privilege mode. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-13-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 27 +++++++++++++++++---------- target/riscv/internals.h | 4 ++-- target/riscv/pmu.h | 2 +- target/riscv/cpu_helper.c | 37 ++++++++++++++++++++----------------- target/riscv/csr.c | 10 ++++++---- target/riscv/gdbstub.c | 2 +- target/riscv/machine.c | 2 +- target/riscv/op_helper.c | 27 ++++++++++++++------------- target/riscv/pmu.c | 9 ++++++--- target/riscv/translate.c | 2 +- 10 files changed, 69 insertions(+), 53 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ typedef struct riscv_cpu_profile { extern RISCVCPUProfile *riscv_profiles[]; +/* + * Type large enough to hold all PRV_* fields, update CPUArchState::priv + * migration field if changing this type. + */ +typedef uint8_t privilege_mode_t; + /* Privileged specification version */ #define PRIV_VER_1_10_0_STR "v1.10.0" #define PRIV_VER_1_11_0_STR "v1.11.0" @@ -XXX,XX +XXX,XX @@ struct CPUArchState { uint32_t elf_flags; #endif - target_ulong priv; + privilege_mode_t priv; /* CSRs for execution environment configuration */ uint64_t menvcfg; uint64_t senvcfg; @@ -XXX,XX +XXX,XX @@ void riscv_cpu_interrupt(CPURISCVState *env); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *), void *arg); -void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, +void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, privilege_mode_t priv, int (*rmw_fn)(void *arg, target_ulong reg, target_ulong *val, @@ -XXX,XX +XXX,XX @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit); #endif /* !CONFIG_USER_ONLY */ -void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en); +void riscv_cpu_set_mode(CPURISCVState *env, privilege_mode_t newpriv, + bool virt_en); void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst, - enum CTRType type, target_ulong prev_priv, bool prev_virt); + enum CTRType type, privilege_mode_t prev_priv, bool prev_virt); void riscv_ctr_clear(CPURISCVState *env); void riscv_translate_init(void); @@ -XXX,XX +XXX,XX @@ static inline const RISCVCPUConfig *riscv_cpu_cfg(CPURISCVState *env) } #if !defined(CONFIG_USER_ONLY) -static inline int cpu_address_mode(CPURISCVState *env) +static inline privilege_mode_t cpu_address_mode(CPURISCVState *env) { - int mode = env->priv; + privilege_mode_t mode = env->priv; if (mode == PRV_M && get_field(env->mstatus, MSTATUS_MPRV)) { mode = get_field(env->mstatus, MSTATUS_MPP); @@ -XXX,XX +XXX,XX @@ static inline int cpu_address_mode(CPURISCVState *env) return mode; } -static inline RISCVMXL cpu_get_xl(CPURISCVState *env, target_ulong mode) +static inline RISCVMXL cpu_get_xl(CPURISCVState *env, privilege_mode_t mode) { RISCVMXL xl = env->misa_mxl; /* @@ -XXX,XX +XXX,XX @@ static inline RISCVMXL cpu_address_xl(CPURISCVState *env) #ifdef CONFIG_USER_ONLY return env->xl; #else - int mode = cpu_address_mode(env); + privilege_mode_t mode = cpu_address_mode(env); return cpu_get_xl(env, mode); #endif @@ -XXX,XX +XXX,XX @@ static inline RISCVMXL riscv_cpu_sxl(CPURISCVState *env) * Returns true if the effective privilege mode is modified. */ static inline QEMU_ALWAYS_INLINE -bool riscv_cpu_eff_priv(CPURISCVState *env, int *priv, bool *virt) +bool riscv_cpu_eff_priv(CPURISCVState *env, privilege_mode_t *priv, bool *virt) { - int mode = env->priv; + privilege_mode_t mode = env->priv; bool virt_enabled = false; bool mode_modified = false; diff --git a/target/riscv/internals.h b/target/riscv/internals.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -XXX,XX +XXX,XX @@ #define MMU_2STAGE_BIT (1 << 2) #define MMU_IDX_SS_WRITE (1 << 3) -static inline int mmuidx_priv(int mmu_idx) +static inline privilege_mode_t mmuidx_priv(int mmu_idx) { - int ret = mmu_idx & 3; + privilege_mode_t ret = mmu_idx & 3; if (ret == MMUIdx_S_SUM) { ret = PRV_S; } diff --git a/target/riscv/pmu.h b/target/riscv/pmu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/pmu.h +++ b/target/riscv/pmu.h @@ -XXX,XX +XXX,XX @@ int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx); void riscv_pmu_generate_fdt_node(void *fdt, uint32_t cmask, char *pmu_name); int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx); -void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, target_ulong newpriv, +void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, privilege_mode_t newpriv, bool new_virt); RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, bool upper_half, uint32_t ctr_idx); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -XXX,XX +XXX,XX @@ int riscv_env_mmu_index(CPURISCVState *env, bool ifetch) return 0; #else bool virt = env->virt_enabled; - int mode = env->priv; + privilege_mode_t mode = env->priv; bool mode_modified = false; /* All priv -> mmu_idx mapping are here */ @@ -XXX,XX +XXX,XX @@ bool riscv_env_smode_dbltrp_enabled(CPURISCVState *env, bool virt) RISCVPmPmm riscv_pm_get_pmm(CPURISCVState *env) { #ifndef CONFIG_USER_ONLY - int priv_mode; + privilege_mode_t priv_mode; bool virt; riscv_cpu_eff_priv(env, &priv_mode, &virt); @@ -XXX,XX +XXX,XX @@ RISCVPmPmm riscv_pm_get_pmm(CPURISCVState *env) RISCVPmPmm riscv_pm_get_vm_ldst_pmm(CPURISCVState *env) { #ifndef CONFIG_USER_ONLY - int priv_mode; + privilege_mode_t priv_mode; if (!riscv_cpu_cfg(env)->ext_ssnpm || get_field(env->mstatus, MSTATUS_MXR) || @@ -XXX,XX +XXX,XX @@ bool riscv_cpu_virt_mem_enabled(CPURISCVState *env, bool is_vm_ldst) #ifndef CONFIG_USER_ONLY int satp_mode = 0; uint64_t satp; - int priv_mode; + privilege_mode_t priv_mode; bool virt = false; if (!is_vm_ldst) { @@ -XXX,XX +XXX,XX @@ void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *), env->rdtime_fn_arg = arg; } -void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, +void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, privilege_mode_t priv, int (*rmw_fn)(void *arg, target_ulong reg, target_ulong *val, @@ -XXX,XX +XXX,XX @@ void riscv_ctr_clear(CPURISCVState *env) memset(env->ctr_data, 0x0, sizeof(env->ctr_data)); } -static uint64_t riscv_ctr_priv_to_mask(target_ulong priv, bool virt) +static uint64_t riscv_ctr_priv_to_mask(privilege_mode_t priv, bool virt) { switch (priv) { case PRV_M: @@ -XXX,XX +XXX,XX @@ static uint64_t riscv_ctr_priv_to_mask(target_ulong priv, bool virt) g_assert_not_reached(); } -static uint64_t riscv_ctr_get_control(CPURISCVState *env, target_long priv, +static uint64_t riscv_ctr_get_control(CPURISCVState *env, + privilege_mode_t priv, bool virt) { switch (priv) { @@ -XXX,XX +XXX,XX @@ static uint64_t riscv_ctr_get_control(CPURISCVState *env, target_long priv, * and src privilege is less than target privilege. This includes the virtual * state as well. */ -static bool riscv_ctr_check_xte(CPURISCVState *env, target_long src_prv, +static bool riscv_ctr_check_xte(CPURISCVState *env, + privilege_mode_t src_prv, bool src_virt) { - target_long tgt_prv = env->priv; + privilege_mode_t tgt_prv = env->priv; bool res = true; /* @@ -XXX,XX +XXX,XX @@ static bool riscv_ctr_check_xte(CPURISCVState *env, target_long src_prv, * idx = (sctrstatus.WRPTR - entry - 1) & (depth - 1); */ void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst, - enum CTRType type, target_ulong src_priv, bool src_virt) + enum CTRType type, privilege_mode_t src_priv, bool src_virt) { bool tgt_virt = env->virt_enabled; uint64_t src_mask = riscv_ctr_priv_to_mask(src_priv, src_virt); @@ -XXX,XX +XXX,XX @@ void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst, env->sctrstatus = set_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK, head); } -void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en) +void riscv_cpu_set_mode(CPURISCVState *env, privilege_mode_t newpriv, + bool virt_en) { g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED); @@ -XXX,XX +XXX,XX @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en) */ static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr, int size, MMUAccessType access_type, - int mode) + privilege_mode_t mode) { pmp_priv_t pmp_priv; bool pmp_has_privs; @@ -XXX,XX +XXX,XX @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr, /* Returns 'true' if a svukte address check is needed */ static bool do_svukte_check(CPURISCVState *env, bool first_stage, - int mode, bool virt) + privilege_mode_t mode, bool virt) { /* Svukte extension depends on Sv39. */ if (!(env_archcpu(env)->cfg.ext_svukte || @@ -XXX,XX +XXX,XX @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, */ MemTxResult res; MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; - int mode = mmuidx_priv(mmu_idx); + privilege_mode_t mode = mmuidx_priv(mmu_idx); bool virt = mmuidx_2stage(mmu_idx); bool use_background = false; hwaddr ppn; @@ -XXX,XX +XXX,XX @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, bool two_stage_lookup = mmuidx_2stage(mmu_idx); bool two_stage_indirect_error = false; int ret = TRANSLATE_FAIL; - int mode = mmuidx_priv(mmu_idx); + privilege_mode_t mode = mmuidx_priv(mmu_idx); /* default TLB page size */ hwaddr tlb_size = TARGET_PAGE_SIZE; @@ -XXX,XX +XXX,XX @@ void riscv_cpu_do_interrupt(CPUState *cs) bool always_storeamo = (env->excp_uw2 & RISCV_UW2_ALWAYS_STORE_AMO); bool vsmode_exc; uint64_t s; - int mode; + privilege_mode_t mode; /* * cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide @@ -XXX,XX +XXX,XX @@ void riscv_cpu_do_interrupt(CPUState *cs) bool smode_double_trap = false; uint64_t hdeleg = async ? env->hideleg : env->hedeleg; const bool prev_virt = env->virt_enabled; - const target_ulong prev_priv = env->priv; + const privilege_mode_t prev_priv = env->priv; uint64_t last_pc = env->pc; target_ulong tval = 0; target_ulong tinst = 0; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException aia_smode(CPURISCVState *env, int csrno) static RISCVException aia_smode32(CPURISCVState *env, int csrno) { int ret; - int csr_priv = get_field(csrno, 0x300); + privilege_mode_t csr_priv = get_field(csrno, 0x300); if (csr_priv == PRV_M && !riscv_cpu_cfg(env)->ext_smaia) { return RISCV_EXCP_ILLEGAL_INST; @@ -XXX,XX +XXX,XX @@ static RISCVException rmw_xireg_aia(CPURISCVState *env, int csrno, bool virt = false, isel_reserved = false; int ret = -EINVAL; uint8_t *iprio; - target_ulong priv, vgein; + privilege_mode_t priv; + uint32_t vgein; /* VS-mode CSR number passed in has already been translated */ switch (csrno) { @@ -XXX,XX +XXX,XX @@ static RISCVException rmw_xtopei(CPURISCVState *env, int csrno, { bool virt; int ret = -EINVAL; - target_ulong priv, vgein; + privilege_mode_t priv; + uint32_t vgein; /* Translate CSR number for VS-mode */ csrno = aia_xlate_vs_csrno(env, csrno); @@ -XXX,XX +XXX,XX @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, } #if !defined(CONFIG_USER_ONLY) - int csr_priv, effective_priv = env->priv; + privilege_mode_t csr_priv, effective_priv = env->priv; if (riscv_has_ext(env, RVH) && env->priv == PRV_S && !env->virt_enabled) { diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -XXX,XX +XXX,XX @@ static int riscv_gdb_set_virtual(CPUState *cs, uint8_t *mem_buf, int n) const unsigned regsz = riscv_cpu_is_32bit(cpu) ? 4 : 8; #ifndef CONFIG_USER_ONLY CPURISCVState *env = &cpu->env; - uint64_t new_priv = ldn(env, mem_buf, regsz) & 0x3; + privilege_mode_t new_priv = ldn(env, mem_buf, regsz) & 0x3; bool new_virt = 0; if (new_priv == PRV_RESERVED) { diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINT32(env.misa_ext, RISCVCPU), VMSTATE_UNUSED(4), VMSTATE_UINT32(env.misa_ext_mask, RISCVCPU), - VMSTATE_UINTTL(env.priv, RISCVCPU), + VMSTATE_UINT8(env.priv, RISCVCPU), VMSTATE_BOOL(env.virt_enabled, RISCVCPU), VMSTATE_UINT64(env.resetvec, RISCVCPU), VMSTATE_UINT64(env.mhartid, RISCVCPU), diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -XXX,XX +XXX,XX @@ void helper_sc_probe_write(CPURISCVState *env, target_ulong addr, target_ulong helper_sret(CPURISCVState *env) { uint64_t mstatus; - target_ulong prev_priv, prev_virt = env->virt_enabled; - const target_ulong src_priv = env->priv; + privilege_mode_t prev_priv; + bool prev_virt = env->virt_enabled; + const privilege_mode_t src_priv = env->priv; const bool src_virt = env->virt_enabled; if (!(env->priv >= PRV_S)) { @@ -XXX,XX +XXX,XX @@ target_ulong helper_sret(CPURISCVState *env) /* We support Hypervisor extensions and virtulisation is disabled */ target_ulong hstatus = env->hstatus; - prev_virt = get_field(hstatus, HSTATUS_SPV); + prev_virt = !!(get_field(hstatus, HSTATUS_SPV)); hstatus = set_field(hstatus, HSTATUS_SPV, 0); env->hstatus = hstatus; @@ -XXX,XX +XXX,XX @@ target_ulong helper_sret(CPURISCVState *env) } static void check_ret_from_m_mode(CPURISCVState *env, target_ulong retpc, - target_ulong prev_priv, + privilege_mode_t prev_priv, uintptr_t ra) { if (!(env->priv >= PRV_M)) { @@ -XXX,XX +XXX,XX @@ static void check_ret_from_m_mode(CPURISCVState *env, target_ulong retpc, } } static target_ulong ssdbltrp_mxret(CPURISCVState *env, target_ulong mstatus, - target_ulong prev_priv, - target_ulong prev_virt) + privilege_mode_t prev_priv, + bool prev_virt) { /* If returning to U, VS or VU, sstatus.sdt = 0 */ if (prev_priv == PRV_U || (prev_virt && @@ -XXX,XX +XXX,XX @@ target_ulong helper_mret(CPURISCVState *env) { target_ulong retpc = env->mepc & get_xepc_mask(env); uint64_t mstatus = env->mstatus; - target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); + privilege_mode_t prev_priv = get_field(mstatus, MSTATUS_MPP); uintptr_t ra = GETPC(); check_ret_from_m_mode(env, retpc, prev_priv, ra); - target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV) && - (prev_priv != PRV_M); + bool prev_virt = !!(get_field(env->mstatus, MSTATUS_MPV) && + (prev_priv != PRV_M)); mstatus = set_field(mstatus, MSTATUS_MIE, get_field(mstatus, MSTATUS_MPIE)); mstatus = set_field(mstatus, MSTATUS_MPIE, 1); @@ -XXX,XX +XXX,XX @@ target_ulong helper_mret(CPURISCVState *env) target_ulong helper_mnret(CPURISCVState *env) { target_ulong retpc = env->mnepc; - target_ulong prev_priv = get_field(env->mnstatus, MNSTATUS_MNPP); - target_ulong prev_virt; + privilege_mode_t prev_priv = get_field(env->mnstatus, MNSTATUS_MNPP); + bool prev_virt; uintptr_t ra = GETPC(); check_ret_from_m_mode(env, retpc, prev_priv, ra); - prev_virt = get_field(env->mnstatus, MNSTATUS_MNPV) && - (prev_priv != PRV_M); + prev_virt = !!(get_field(env->mnstatus, MNSTATUS_MNPV) && + (prev_priv != PRV_M)); env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, true); /* diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -XXX,XX +XXX,XX @@ static bool riscv_pmu_counter_enabled(RISCVCPU *cpu, uint32_t ctr_idx) * new priv and new virt values are passed in as arguments. */ static void riscv_pmu_icount_update_priv(CPURISCVState *env, - target_ulong newpriv, bool new_virt) + privilege_mode_t newpriv, + bool new_virt) { uint64_t *snapshot_prev, *snapshot_new; uint64_t current_icount; @@ -XXX,XX +XXX,XX @@ static void riscv_pmu_icount_update_priv(CPURISCVState *env, } static void riscv_pmu_cycle_update_priv(CPURISCVState *env, - target_ulong newpriv, bool new_virt) + privilege_mode_t newpriv, + bool new_virt) { uint64_t *snapshot_prev, *snapshot_new; uint64_t current_ticks; @@ -XXX,XX +XXX,XX @@ static void riscv_pmu_cycle_update_priv(CPURISCVState *env, counter_arr[env->priv] += delta; } -void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, target_ulong newpriv, +void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, + privilege_mode_t newpriv, bool new_virt) { riscv_pmu_cycle_update_priv(env, newpriv, new_virt); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -XXX,XX +XXX,XX @@ typedef struct DisasContext { RISCVExtStatus mstatus_fs; RISCVExtStatus mstatus_vs; uint32_t mem_idx; - uint32_t priv; + privilege_mode_t priv; /* * Remember the rounding mode encoded in the previous fp instruction, * which we have already installed into env->fp_status. Or -1 for -- 2.54.0
From: Anton Johansson <anjo@rev.ng> geilen takes the values 31 or 63, fix it to 8 bits. hgeie and hgeip are at most 64 bits in size, fix to 64. Update relevant function arguments and uses of hgeie and hgeip. Note, masking is widened to 64-bit as geilen is already verified to be smaller than the target long size, and an out-of-bounds shift would be UB anyway. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-14-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 10 +++++----- target/riscv/cpu.c | 4 ++-- target/riscv/cpu_helper.c | 4 ++-- target/riscv/csr.c | 4 ++-- target/riscv/machine.c | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { #ifndef CONFIG_USER_ONLY /* This contains QEMU specific information about the virt state. */ bool virt_enabled; - target_ulong geilen; + uint8_t geilen; uint64_t resetvec; uint64_t mhartid; @@ -XXX,XX +XXX,XX @@ struct CPUArchState { uint64_t htval; uint64_t htinst; uint64_t hgatp; - target_ulong hgeie; - target_ulong hgeip; + uint64_t hgeie; + uint64_t hgeip; uint64_t htimedelta; uint64_t hvien; @@ -XXX,XX +XXX,XX @@ int riscv_cpu_mirq_pending(CPURISCVState *env); int riscv_cpu_sirq_pending(CPURISCVState *env); int riscv_cpu_vsirq_pending(CPURISCVState *env); bool riscv_cpu_fp_enabled(CPURISCVState *env); -target_ulong riscv_cpu_get_geilen(CPURISCVState *env); -void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen); +uint8_t riscv_cpu_get_geilen(CPURISCVState *env); +void riscv_cpu_set_geilen(CPURISCVState *env, uint8_t geilen); bool riscv_cpu_vector_enabled(CPURISCVState *env); void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable); int riscv_env_mmu_index(CPURISCVState *env, bool ifetch); diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level) } /* Update HGEIP CSR */ - env->hgeip &= ~((target_ulong)1 << irq); + env->hgeip &= ~(1ULL << irq); if (level) { - env->hgeip |= (target_ulong)1 << irq; + env->hgeip |= 1ULL << irq; } /* Update mip.SGEIP bit */ diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -XXX,XX +XXX,XX @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env) } } -target_ulong riscv_cpu_get_geilen(CPURISCVState *env) +uint8_t riscv_cpu_get_geilen(CPURISCVState *env) { if (!riscv_has_ext(env, RVH)) { return 0; @@ -XXX,XX +XXX,XX @@ target_ulong riscv_cpu_get_geilen(CPURISCVState *env) return env->geilen; } -void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen) +void riscv_cpu_set_geilen(CPURISCVState *env, uint8_t geilen) { if (!riscv_has_ext(env, RVH)) { return; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException rmw_mip64(CPURISCVState *env, int csrno, if (csrno != CSR_HVIP) { gin = get_field(env->hstatus, HSTATUS_VGEIN); - old_mip |= (env->hgeip & ((target_ulong)1 << gin)) ? MIP_VSEIP : 0; + old_mip |= (env->hgeip & (1ULL << gin)) ? MIP_VSEIP : 0; old_mip |= env->vstime_irq ? MIP_VSTIP : 0; } @@ -XXX,XX +XXX,XX @@ static RISCVException write_hgeie(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { /* Only GEILEN:1 bits implemented and BIT0 is never implemented */ - val &= ((((target_ulong)1) << env->geilen) - 1) << 1; + val &= ((1ULL << env->geilen) - 1) << 1; env->hgeie = val; /* Update mip.SGEIP bit */ riscv_cpu_update_mip(env, MIP_SGEIP, diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_hyper = { VMSTATE_UINT64(env.htval, RISCVCPU), VMSTATE_UINT64(env.htinst, RISCVCPU), VMSTATE_UINT64(env.hgatp, RISCVCPU), - VMSTATE_UINTTL(env.hgeie, RISCVCPU), - VMSTATE_UINTTL(env.hgeip, RISCVCPU), + VMSTATE_UINT64(env.hgeie, RISCVCPU), + VMSTATE_UINT64(env.hgeip, RISCVCPU), VMSTATE_UINT64(env.hvien, RISCVCPU), VMSTATE_UINT64(env.hvip, RISCVCPU), VMSTATE_UINT64(env.htimedelta, RISCVCPU), -- 2.54.0
From: Anton Johansson <anjo@rev.ng> [m|s|vs]iselect are defined in version 20250508 of the privileged specification to be XLEN in size, however QEMU only ever uses at most 16 bits of these fields, so fix them to 16. Update relevant function arguments. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-15-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 8 ++++---- target/riscv/csr.c | 32 ++++++++++++++++---------------- target/riscv/machine.c | 6 +++--- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { uint8_t siprio[64]; /* AIA CSRs */ - target_ulong miselect; - target_ulong siselect; + uint16_t miselect; + uint16_t siselect; uint64_t mvien; uint64_t mvip; @@ -XXX,XX +XXX,XX @@ struct CPUArchState { uint64_t vsatp; /* AIA VS-mode CSRs */ - target_ulong vsiselect; + uint16_t vsiselect; uint64_t mtval2; uint64_t mtinst; @@ -XXX,XX +XXX,XX @@ static inline RISCVMXL cpu_address_xl(CPURISCVState *env) } #endif -static inline int riscv_cpu_xlen(CPURISCVState *env) +static inline uint16_t riscv_cpu_xlen(CPURISCVState *env) { return 16 << env->xl; } diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException rmw_xiselect(CPURISCVState *env, int csrno, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { - target_ulong *iselect; + uint16_t *iselect; int ret; ret = smstateen_acc_ok(env, 0, SMSTATEEN0_SVSLCT); @@ -XXX,XX +XXX,XX @@ static RISCVException rmw_xiselect(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static bool xiselect_aia_range(target_ulong isel) +static bool xiselect_aia_range(uint16_t isel) { return (ISELECT_IPRIO0 <= isel && isel <= ISELECT_IPRIO15) || (ISELECT_IMSIC_FIRST <= isel && isel <= ISELECT_IMSIC_LAST); } -static bool xiselect_cd_range(target_ulong isel) +static bool xiselect_cd_range(uint16_t isel) { return (ISELECT_CD_FIRST <= isel && isel <= ISELECT_CD_LAST); } -static bool xiselect_ctr_range(int csrno, target_ulong isel) +static bool xiselect_ctr_range(int csrno, uint16_t isel) { /* MIREG-MIREG6 for the range 0x200-0x2ff are not used by CTR. */ return CTR_ENTRIES_FIRST <= isel && isel <= CTR_ENTRIES_LAST && @@ -XXX,XX +XXX,XX @@ static bool xiselect_ctr_range(int csrno, target_ulong isel) } static int rmw_iprio(target_ulong xlen, - target_ulong iselect, uint8_t *iprio, + uint16_t iselect, uint8_t *iprio, target_ulong *val, target_ulong new_val, target_ulong wr_mask, int ext_irq_no) { @@ -XXX,XX +XXX,XX @@ static int rmw_iprio(target_ulong xlen, return 0; } -static int rmw_ctrsource(CPURISCVState *env, int isel, target_ulong *val, +static int rmw_ctrsource(CPURISCVState *env, uint16_t isel, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { /* @@ -XXX,XX +XXX,XX @@ static int rmw_ctrsource(CPURISCVState *env, int isel, target_ulong *val, return 0; } -static int rmw_ctrtarget(CPURISCVState *env, int isel, target_ulong *val, +static int rmw_ctrtarget(CPURISCVState *env, uint16_t isel, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { /* @@ -XXX,XX +XXX,XX @@ static int rmw_ctrtarget(CPURISCVState *env, int isel, target_ulong *val, return 0; } -static int rmw_ctrdata(CPURISCVState *env, int isel, target_ulong *val, +static int rmw_ctrdata(CPURISCVState *env, uint16_t isel, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { /* @@ -XXX,XX +XXX,XX @@ static int rmw_ctrdata(CPURISCVState *env, int isel, target_ulong *val, } static RISCVException rmw_xireg_aia(CPURISCVState *env, int csrno, - target_ulong isel, target_ulong *val, + uint16_t isel, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { bool virt = false, isel_reserved = false; @@ -XXX,XX +XXX,XX @@ done: } static int rmw_xireg_cd(CPURISCVState *env, int csrno, - target_ulong isel, target_ulong *val, + uint16_t isel, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { int ret = -EINVAL; - int ctr_index = isel - ISELECT_CD_FIRST; - int isel_hpm_start = ISELECT_CD_FIRST + 3; + uint16_t ctr_index = isel - ISELECT_CD_FIRST; + uint16_t isel_hpm_start = ISELECT_CD_FIRST + 3; if (!riscv_cpu_cfg(env)->ext_smcdeleg || !riscv_cpu_cfg(env)->ext_ssccfg) { ret = RISCV_EXCP_ILLEGAL_INST; @@ -XXX,XX +XXX,XX @@ done: } static int rmw_xireg_ctr(CPURISCVState *env, int csrno, - target_ulong isel, target_ulong *val, + uint16_t isel, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { if (!riscv_cpu_cfg(env)->ext_smctr && !riscv_cpu_cfg(env)->ext_ssctr) { @@ -XXX,XX +XXX,XX @@ static int rmw_xireg_ctr(CPURISCVState *env, int csrno, * extension using csrind should be implemented here. */ static int rmw_xireg_csrind(CPURISCVState *env, int csrno, - target_ulong isel, target_ulong *val, + uint16_t isel, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { bool virt = csrno == CSR_VSIREG ? true : false; @@ -XXX,XX +XXX,XX @@ static int rmw_xiregi(CPURISCVState *env, int csrno, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { int ret = -EINVAL; - target_ulong isel; + uint16_t isel; ret = smstateen_acc_ok(env, 0, SMSTATEEN0_SVSLCT); if (ret != RISCV_EXCP_NONE) { @@ -XXX,XX +XXX,XX @@ static RISCVException rmw_xireg(CPURISCVState *env, int csrno, target_ulong wr_mask) { int ret = -EINVAL; - target_ulong isel; + uint16_t isel; ret = smstateen_acc_ok(env, 0, SMSTATEEN0_SVSLCT); if (ret != RISCV_EXCP_NONE) { diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_hyper = { VMSTATE_UINT64(env.vscause, RISCVCPU), VMSTATE_UINT64(env.vstval, RISCVCPU), VMSTATE_UINT64(env.vsatp, RISCVCPU), - VMSTATE_UINTTL(env.vsiselect, RISCVCPU), + VMSTATE_UINT16(env.vsiselect, RISCVCPU), VMSTATE_UINT64(env.vsie, RISCVCPU), VMSTATE_UINT64(env.mtval2, RISCVCPU), @@ -XXX,XX +XXX,XX @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINT64(env.mepc, RISCVCPU), VMSTATE_UINT64(env.mcause, RISCVCPU), VMSTATE_UINT64(env.mtval, RISCVCPU), - VMSTATE_UINTTL(env.miselect, RISCVCPU), - VMSTATE_UINTTL(env.siselect, RISCVCPU), + VMSTATE_UINT16(env.miselect, RISCVCPU), + VMSTATE_UINT16(env.siselect, RISCVCPU), VMSTATE_UINT32(env.scounteren, RISCVCPU), VMSTATE_UINT32(env.mcounteren, RISCVCPU), VMSTATE_UINT32(env.scountinhibit, RISCVCPU), -- 2.54.0
From: Anton Johansson <anjo@rev.ng> In hw/ the relevant RISCVIMSICState fields eidelivery, eithreshold, eistate are uint32_t. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-16-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 42 ++++++++++++++++++++------------------- hw/intc/riscv_imsic.c | 34 +++++++++++++++---------------- target/riscv/cpu_helper.c | 12 ++++------- target/riscv/csr.c | 24 ++++++++++++---------- 4 files changed, 57 insertions(+), 55 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ FIELD(VTYPE, VMA, 7, 1) FIELD(VTYPE, ALTFMT, 8, 1) FIELD(VTYPE, RESERVED, 9, sizeof(uint64_t) * 8 - 10) +#ifndef CONFIG_USER_ONLY +/* machine specific AIA ireg read-modify-write callback */ +#define AIA_MAKE_IREG(__isel, __priv, __virt, __vgein, __xlen) \ + ((uint32_t)((((__xlen) & 0xff) << 24) | \ + (((__vgein) & 0x3f) << 20) | \ + (((__virt) & 0x1) << 18) | \ + (((__priv) & 0x3) << 16) | \ + (__isel & 0xffff))) +#define AIA_IREG_ISEL(__ireg) ((__ireg) & 0xffff) +#define AIA_IREG_PRIV(__ireg) (((__ireg) >> 16) & 0x3) +#define AIA_IREG_VIRT(__ireg) (((__ireg) >> 18) & 0x1) +#define AIA_IREG_VGEIN(__ireg) (((__ireg) >> 20) & 0x3f) +#define AIA_IREG_XLEN(__ireg) (((__ireg) >> 24) & 0xff) + +typedef int (*aia_ireg_rmw_fn)(void *arg, uint32_t reg, uint64_t *val, + uint64_t new_val, uint64_t write_mask); +#endif + typedef struct PMUCTRState { /* Current value of a counter */ uint64_t mhpmcounter_val; @@ -XXX,XX +XXX,XX @@ struct CPUArchState { void *rdtime_fn_arg; /* machine specific AIA ireg read-modify-write callback */ -#define AIA_MAKE_IREG(__isel, __priv, __virt, __vgein, __xlen) \ - ((((__xlen) & 0xff) << 24) | \ - (((__vgein) & 0x3f) << 20) | \ - (((__virt) & 0x1) << 18) | \ - (((__priv) & 0x3) << 16) | \ - (__isel & 0xffff)) -#define AIA_IREG_ISEL(__ireg) ((__ireg) & 0xffff) -#define AIA_IREG_PRIV(__ireg) (((__ireg) >> 16) & 0x3) -#define AIA_IREG_VIRT(__ireg) (((__ireg) >> 18) & 0x1) -#define AIA_IREG_VGEIN(__ireg) (((__ireg) >> 20) & 0x3f) -#define AIA_IREG_XLEN(__ireg) (((__ireg) >> 24) & 0xff) - int (*aia_ireg_rmw_fn[4])(void *arg, target_ulong reg, - target_ulong *val, target_ulong new_val, target_ulong write_mask); - void *aia_ireg_rmw_fn_arg[4]; + aia_ireg_rmw_fn aia_ireg_rmw_cb[4]; + void *aia_ireg_rmw_cb_arg[4]; /* True if in debugger mode. */ bool debugger; @@ -XXX,XX +XXX,XX @@ void riscv_cpu_interrupt(CPURISCVState *env); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *), void *arg); -void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, privilege_mode_t priv, - int (*rmw_fn)(void *arg, - target_ulong reg, - target_ulong *val, - target_ulong new_val, - target_ulong write_mask), +void riscv_cpu_set_aia_ireg_rmw_cb(CPURISCVState *env, privilege_mode_t priv, + aia_ireg_rmw_fn rmw_fn, void *rmw_fn_arg); RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit); diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/riscv_imsic.c +++ b/hw/intc/riscv_imsic.c @@ -XXX,XX +XXX,XX @@ static void riscv_imsic_update(RISCVIMSICState *imsic, uint32_t page) } static int riscv_imsic_eidelivery_rmw(RISCVIMSICState *imsic, uint32_t page, - target_ulong *val, - target_ulong new_val, - target_ulong wr_mask) + uint64_t *val, + uint64_t new_val, + uint64_t wr_mask) { - target_ulong old_val = imsic->eidelivery[page]; + uint32_t old_val = imsic->eidelivery[page]; if (val) { *val = old_val; @@ -XXX,XX +XXX,XX @@ static int riscv_imsic_eidelivery_rmw(RISCVIMSICState *imsic, uint32_t page, } static int riscv_imsic_eithreshold_rmw(RISCVIMSICState *imsic, uint32_t page, - target_ulong *val, - target_ulong new_val, - target_ulong wr_mask) + uint64_t *val, + uint64_t new_val, + uint64_t wr_mask) { - target_ulong old_val = imsic->eithreshold[page]; + uint32_t old_val = imsic->eithreshold[page]; if (val) { *val = old_val; @@ -XXX,XX +XXX,XX @@ static int riscv_imsic_eithreshold_rmw(RISCVIMSICState *imsic, uint32_t page, } static int riscv_imsic_topei_rmw(RISCVIMSICState *imsic, uint32_t page, - target_ulong *val, target_ulong new_val, - target_ulong wr_mask) + uint64_t *val, uint64_t new_val, + uint64_t wr_mask) { uint32_t base, topei = riscv_imsic_topei(imsic, page); @@ -XXX,XX +XXX,XX @@ static int riscv_imsic_topei_rmw(RISCVIMSICState *imsic, uint32_t page, static int riscv_imsic_eix_rmw(RISCVIMSICState *imsic, uint32_t xlen, uint32_t page, - uint32_t num, bool pend, target_ulong *val, - target_ulong new_val, target_ulong wr_mask) + uint32_t num, bool pend, uint64_t *val, + uint64_t new_val, uint64_t wr_mask) { uint32_t i, base, prev; - target_ulong mask; + uint64_t mask; uint32_t state = (pend) ? IMSIC_EISTATE_PENDING : IMSIC_EISTATE_ENABLED; if (xlen != 32) { @@ -XXX,XX +XXX,XX @@ static int riscv_imsic_eix_rmw(RISCVIMSICState *imsic, continue; } - mask = (target_ulong)1 << i; + mask = 1ull << i; if (wr_mask & mask) { if (new_val & mask) { prev = qatomic_fetch_or(&imsic->eistate[base + i], state); @@ -XXX,XX +XXX,XX @@ static int riscv_imsic_eix_rmw(RISCVIMSICState *imsic, return 0; } -static int riscv_imsic_rmw(void *arg, target_ulong reg, target_ulong *val, - target_ulong new_val, target_ulong wr_mask) +static int riscv_imsic_rmw(void *arg, uint32_t reg, uint64_t *val, + uint64_t new_val, uint64_t wr_mask) { RISCVIMSICState *imsic = arg; uint32_t isel, priv, virt, vgein, xlen, page; @@ -XXX,XX +XXX,XX @@ static void riscv_imsic_realize(DeviceState *dev, Error **errp) } if (!kvm_irqchip_in_kernel()) { - riscv_cpu_set_aia_ireg_rmw_fn(env, (imsic->mmode) ? PRV_M : PRV_S, + riscv_cpu_set_aia_ireg_rmw_cb(env, (imsic->mmode) ? PRV_M : PRV_S, riscv_imsic_rmw, imsic); } } diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -XXX,XX +XXX,XX @@ void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *), env->rdtime_fn_arg = arg; } -void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, privilege_mode_t priv, - int (*rmw_fn)(void *arg, - target_ulong reg, - target_ulong *val, - target_ulong new_val, - target_ulong write_mask), +void riscv_cpu_set_aia_ireg_rmw_cb(CPURISCVState *env, privilege_mode_t priv, + aia_ireg_rmw_fn rmw_fn, void *rmw_fn_arg) { if (priv <= PRV_M) { - env->aia_ireg_rmw_fn[priv] = rmw_fn; - env->aia_ireg_rmw_fn_arg[priv] = rmw_fn_arg; + env->aia_ireg_rmw_cb[priv] = rmw_fn; + env->aia_ireg_rmw_cb_arg[priv] = rmw_fn_arg; } } diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException rmw_xireg_aia(CPURISCVState *env, int csrno, uint8_t *iprio; privilege_mode_t priv; uint32_t vgein; + uint64_t wide_val; /* VS-mode CSR number passed in has already been translated */ switch (csrno) { @@ -XXX,XX +XXX,XX @@ static RISCVException rmw_xireg_aia(CPURISCVState *env, int csrno, } } else if (ISELECT_IMSIC_FIRST <= isel && isel <= ISELECT_IMSIC_LAST) { /* IMSIC registers only available when machine implements it. */ - if (env->aia_ireg_rmw_fn[priv]) { + if (env->aia_ireg_rmw_cb[priv]) { /* Selected guest interrupt file should not be zero */ if (virt && (!vgein || env->geilen < vgein)) { goto done; } /* Call machine specific IMSIC register emulation */ - ret = env->aia_ireg_rmw_fn[priv](env->aia_ireg_rmw_fn_arg[priv], + ret = env->aia_ireg_rmw_cb[priv](env->aia_ireg_rmw_cb_arg[priv], AIA_MAKE_IREG(isel, priv, virt, vgein, riscv_cpu_mxl_bits(env)), - val, new_val, wr_mask); + &wide_val, new_val, wr_mask); + *val = wide_val; } } else { isel_reserved = true; @@ -XXX,XX +XXX,XX @@ static RISCVException rmw_xtopei(CPURISCVState *env, int csrno, int ret = -EINVAL; privilege_mode_t priv; uint32_t vgein; + uint64_t wide_val; /* Translate CSR number for VS-mode */ csrno = aia_xlate_vs_csrno(env, csrno); @@ -XXX,XX +XXX,XX @@ static RISCVException rmw_xtopei(CPURISCVState *env, int csrno, }; /* IMSIC CSRs only available when machine implements IMSIC. */ - if (!env->aia_ireg_rmw_fn[priv]) { + if (!env->aia_ireg_rmw_cb[priv]) { goto done; } @@ -XXX,XX +XXX,XX @@ static RISCVException rmw_xtopei(CPURISCVState *env, int csrno, } /* Call machine specific IMSIC register emulation for TOPEI */ - ret = env->aia_ireg_rmw_fn[priv](env->aia_ireg_rmw_fn_arg[priv], + ret = env->aia_ireg_rmw_cb[priv](env->aia_ireg_rmw_cb_arg[priv], AIA_MAKE_IREG(ISELECT_IMSIC_TOPEI, priv, virt, vgein, riscv_cpu_mxl_bits(env)), - val, new_val, wr_mask); + &wide_val, new_val, wr_mask); + *val = wide_val; done: if (ret) { @@ -XXX,XX +XXX,XX @@ static RISCVException read_vstopi(CPURISCVState *env, int csrno, target_ulong *val) { int irq, ret; - target_ulong topei; + uint64_t topei = 0; uint64_t vseip, vsgein; uint32_t iid, iprio, hviid, hviprio, gein; uint32_t s, scount = 0, siid[VSTOPI_NUM_SRCS], siprio[VSTOPI_NUM_SRCS]; @@ -XXX,XX +XXX,XX @@ static RISCVException read_vstopi(CPURISCVState *env, int csrno, if (gein <= env->geilen && vseip) { siid[scount] = IRQ_S_EXT; siprio[scount] = IPRIO_MMAXIPRIO + 1; - if (env->aia_ireg_rmw_fn[PRV_S]) { + if (env->aia_ireg_rmw_cb[PRV_S]) { /* * Call machine specific IMSIC register emulation for * reading TOPEI. */ - ret = env->aia_ireg_rmw_fn[PRV_S]( - env->aia_ireg_rmw_fn_arg[PRV_S], + ret = env->aia_ireg_rmw_cb[PRV_S]( + env->aia_ireg_rmw_cb_arg[PRV_S], AIA_MAKE_IREG(ISELECT_IMSIC_TOPEI, PRV_S, true, gein, riscv_cpu_mxl_bits(env)), &topei, 0, 0); -- 2.54.0
From: Anton Johansson <anjo@rev.ng> Fix to 64 bits to hold all relevant values. Also update comment to reflect that irq_overflow_left stores the ns beyond INT64_MAX until the next overflow, this to account for the uint64_t/int64_t timer value difference between RISCV/QEMU. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-17-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ typedef struct PMUCTRState { uint64_t mhpmcounter_val; /* Snapshot value of a counter */ uint64_t mhpmcounter_prev; - /* Value beyond UINT32_MAX/UINT64_MAX before overflow interrupt trigger */ - target_ulong irq_overflow_left; + /* Value beyond INT64_MAX before overflow interrupt trigger */ + uint64_t irq_overflow_left; } PMUCTRState; typedef struct PMUFixedCtrState { -- 2.54.0
From: Anton Johansson <anjo@rev.ng> Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-18-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ typedef struct PMUCTRState { } PMUCTRState; typedef struct PMUFixedCtrState { - /* Track cycle and icount for each privilege mode */ - uint64_t counter[4]; - uint64_t counter_prev[4]; - /* Track cycle and icount for each privilege mode when V = 1*/ - uint64_t counter_virt[2]; - uint64_t counter_virt_prev[2]; + /* Track cycle and icount for each privilege mode */ + uint64_t counter[4]; + uint64_t counter_prev[4]; + /* Track cycle and icount for each privilege mode when V = 1*/ + uint64_t counter_virt[2]; + uint64_t counter_virt_prev[2]; } PMUFixedCtrState; struct CPUArchState { -- 2.54.0
From: Anton Johansson <anjo@rev.ng> Fix cause argument to 64 bit to match env->mcause. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-19-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 2 +- target/riscv/cpu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ extern const char * const riscv_int_regnamesh[]; extern const char * const riscv_fpr_regnames[]; extern const char * const riscv_rvv_regnames[]; -const char *riscv_cpu_get_trap_name(target_ulong cause, bool async); +const char *riscv_cpu_get_trap_name(uint64_t cause, bool async); int riscv_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, DumpState *s); int riscv_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static const char * const riscv_intr_names[] = { [IRQ_PMU_OVF] = "counter_overflow", }; -const char *riscv_cpu_get_trap_name(target_ulong cause, bool async) +const char *riscv_cpu_get_trap_name(uint64_t cause, bool async) { if (async) { if ((cause < ARRAY_SIZE(riscv_intr_names)) && riscv_intr_names[cause]) { -- 2.54.0
From: Anton Johansson <anjo@rev.ng> Widen to 64 bits in size to hold all relevant values. Note: src and dst arguments change from signed to unsigned but no functional change is incurred. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-20-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 5 +++-- target/riscv/cpu_helper.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit); void riscv_cpu_set_mode(CPURISCVState *env, privilege_mode_t newpriv, bool virt_en); -void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst, - enum CTRType type, privilege_mode_t prev_priv, bool prev_virt); +void riscv_ctr_add_entry(CPURISCVState *env, uint64_t src, uint64_t dst, + enum CTRType type, privilege_mode_t prev_priv, + bool prev_virt); void riscv_ctr_clear(CPURISCVState *env); void riscv_translate_init(void); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -XXX,XX +XXX,XX @@ static bool riscv_ctr_check_xte(CPURISCVState *env, * entry = isel - CTR_ENTRIES_FIRST; * idx = (sctrstatus.WRPTR - entry - 1) & (depth - 1); */ -void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst, - enum CTRType type, privilege_mode_t src_priv, bool src_virt) +void riscv_ctr_add_entry(CPURISCVState *env, uint64_t src, uint64_t dst, + enum CTRType type, privilege_mode_t src_priv, + bool src_virt) { bool tgt_virt = env->virt_enabled; uint64_t src_mask = riscv_ctr_priv_to_mask(src_priv, src_virt); -- 2.54.0
From: Anton Johansson <anjo@rev.ng> mcontext is at most 14 bits in size with the H extension, fix to 16 bits. trigger_cur indexes into tdata*[RV_MAX_TRIGGERS] which holds 2 elements, fix to 8 bits. This patch also adds a migration entry for mcontext which is used in tandem with other debug data that is already migrated. Note, the cpu/debug VMSTATE version is bumped, breaking migration from older versions. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-21-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 10 +++++----- target/riscv/machine.c | 13 +++++++------ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { target_ulong mseccfg; /* trigger module */ - target_ulong trigger_cur; - target_ulong tdata1[RV_MAX_TRIGGERS]; - target_ulong tdata2[RV_MAX_TRIGGERS]; - target_ulong tdata3[RV_MAX_TRIGGERS]; - target_ulong mcontext; + uint16_t mcontext; + uint8_t trigger_cur; + uint64_t tdata1[RV_MAX_TRIGGERS]; + uint64_t tdata2[RV_MAX_TRIGGERS]; + uint64_t tdata3[RV_MAX_TRIGGERS]; struct CPUBreakpoint *cpu_breakpoint[RV_MAX_TRIGGERS]; struct CPUWatchpoint *cpu_watchpoint[RV_MAX_TRIGGERS]; QEMUTimer *itrigger_timer[RV_MAX_TRIGGERS]; diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ static int debug_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_debug = { .name = "cpu/debug", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .needed = debug_needed, .post_load = debug_post_load, .fields = (const VMStateField[]) { - VMSTATE_UINTTL(env.trigger_cur, RISCVCPU), - VMSTATE_UINTTL_ARRAY(env.tdata1, RISCVCPU, RV_MAX_TRIGGERS), - VMSTATE_UINTTL_ARRAY(env.tdata2, RISCVCPU, RV_MAX_TRIGGERS), - VMSTATE_UINTTL_ARRAY(env.tdata3, RISCVCPU, RV_MAX_TRIGGERS), + VMSTATE_UINT16(env.mcontext, RISCVCPU), + VMSTATE_UINT8(env.trigger_cur, RISCVCPU), + VMSTATE_UINT64_ARRAY(env.tdata1, RISCVCPU, RV_MAX_TRIGGERS), + VMSTATE_UINT64_ARRAY(env.tdata2, RISCVCPU, RV_MAX_TRIGGERS), + VMSTATE_UINT64_ARRAY(env.tdata3, RISCVCPU, RV_MAX_TRIGGERS), VMSTATE_END_OF_LIST() } }; -- 2.54.0
From: Anton Johansson <anjo@rev.ng> mseccfg is defined in version 20250508 of the privileged specification to be 64 bits in size. Update relevant function arguments. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-22-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 2 +- target/riscv/pmp.h | 4 ++-- target/riscv/machine.c | 2 +- target/riscv/pmp.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { /* physical memory protection */ pmp_table_t pmp_state; - target_ulong mseccfg; + uint64_t mseccfg; /* trigger module */ uint16_t mcontext; diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -XXX,XX +XXX,XX @@ void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index, target_ulong val); target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index); -void mseccfg_csr_write(CPURISCVState *env, target_ulong val); -target_ulong mseccfg_csr_read(CPURISCVState *env); +void mseccfg_csr_write(CPURISCVState *env, uint64_t val); +uint64_t mseccfg_csr_read(CPURISCVState *env); void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, target_ulong val); diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_mseccfg = { .minimum_version_id = 1, .needed = mseccfg_needed, .fields = (const VMStateField[]) { - VMSTATE_UINTTL(env.mseccfg, RISCVCPU), + VMSTATE_UINT64(env.mseccfg, RISCVCPU), VMSTATE_END_OF_LIST() } }; diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -XXX,XX +XXX,XX @@ target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index) /* * Handle a write to a mseccfg CSR */ -void mseccfg_csr_write(CPURISCVState *env, target_ulong val) +void mseccfg_csr_write(CPURISCVState *env, uint64_t val) { int i; uint64_t mask = MSECCFG_MMWP | MSECCFG_MML; @@ -XXX,XX +XXX,XX @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val) /* * Handle a read from a mseccfg CSR */ -target_ulong mseccfg_csr_read(CPURISCVState *env) +uint64_t mseccfg_csr_read(CPURISCVState *env) { trace_mseccfg_csr_read(env->mhartid, env->mseccfg); return env->mseccfg; -- 2.54.0
From: Anton Johansson <anjo@rev.ng> All debug.h definitions except for RV_MAX_TRIGGERS are internal to target/riscv. Move RV_MAX_TRIGGERS to cpu.h and include debug.h from all translation units which relied on the cpu.h include. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20260520125406.28693-23-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 2 +- target/riscv/debug.h | 2 -- target/riscv/cpu.c | 3 +++ target/riscv/cpu_helper.c | 2 +- target/riscv/csr.c | 3 +++ target/riscv/debug.c | 1 + target/riscv/tcg/tcg-cpu.c | 1 + 7 files changed, 10 insertions(+), 4 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ extern RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[]; #if !defined(CONFIG_USER_ONLY) #include "pmp.h" -#include "debug.h" #endif #define RV_VLEN_MAX 1024 #define RV_MAX_MHPMEVENTS 32 #define RV_MAX_MHPMCOUNTERS 32 +#define RV_MAX_TRIGGERS 2 FIELD(VTYPE, VLMUL, 0, 3) FIELD(VTYPE, VSEW, 3, 3) diff --git a/target/riscv/debug.h b/target/riscv/debug.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/debug.h +++ b/target/riscv/debug.h @@ -XXX,XX +XXX,XX @@ #include "exec/breakpoint.h" #include "exec/target_long.h" -#define RV_MAX_TRIGGERS 2 - /* register index of tdata CSRs */ enum { TDATA1 = 0, diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ #include "kvm/kvm_riscv.h" #include "tcg/tcg-cpu.h" #include "tcg/tcg.h" +#if !defined(CONFIG_USER_ONLY) +#include "target/riscv/debug.h" +#endif /* RISC-V CPU definitions */ static const char riscv_single_letter_exts[] = "IEMAFDQCBPVH"; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -XXX,XX +XXX,XX @@ #include "semihosting/common-semi.h" #include "exec/icount.h" #include "cpu_bits.h" -#include "debug.h" +#include "target/riscv/debug.h" #include "pmp.h" #include "qemu/plugin.h" diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ #include "qapi/error.h" #include "tcg/insn-start-words.h" #include "internals.h" +#if !defined(CONFIG_USER_ONLY) +#include "target/riscv/debug.h" +#endif /* CSR function table public API */ void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) diff --git a/target/riscv/debug.c b/target/riscv/debug.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -XXX,XX +XXX,XX @@ #include "qemu/log.h" #include "qapi/error.h" #include "cpu.h" +#include "target/riscv/debug.h" #include "trace.h" #include "exec/helper-proto.h" #include "exec/watchpoint.h" diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ #include "hw/core/boards.h" #include "system/tcg.h" #include "exec/icount.h" +#include "target/riscv/debug.h" #endif /* Hash that stores user set extensions */ -- 2.54.0
From: Anton Johansson <anjo@rev.ng> Most of these definitions save riscv_csrr, riscv_csrrw, riscv_csr_read, riscv_csr_write are only used in target/. Move declarations to a separate headers which will soon be made internal to target/. csr.h is temporarily included from cpu.h to not break includes from outside target/, this include will be removed in the following commit. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-24-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 87 +--------------------------- target/riscv/csr.h | 103 ++++++++++++++++++++++++++++++++++ target/riscv/cpu.c | 1 + target/riscv/csr.c | 1 + target/riscv/gdbstub.c | 1 + target/riscv/kvm/kvm-cpu.c | 1 + target/riscv/mips_csr.c | 1 + target/riscv/monitor.c | 1 + target/riscv/op_helper.c | 1 + target/riscv/riscv-qmp-cmds.c | 1 + target/riscv/th_csr.c | 1 + 11 files changed, 113 insertions(+), 86 deletions(-) create mode 100644 target/riscv/csr.h diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ RISCVPmPmm riscv_pm_get_pmm(CPURISCVState *env); RISCVPmPmm riscv_pm_get_vm_ldst_pmm(CPURISCVState *env); uint32_t riscv_pm_get_pmlen(RISCVPmPmm pmm); -RISCVException riscv_csrr(CPURISCVState *env, int csrno, - target_ulong *ret_value); - -RISCVException riscv_csrrw(CPURISCVState *env, int csrno, - target_ulong *ret_value, target_ulong new_value, - target_ulong write_mask, uintptr_t ra); -RISCVException riscv_csrrw_debug(CPURISCVState *env, int csrno, - target_ulong *ret_value, - target_ulong new_value, - target_ulong write_mask); - -static inline void riscv_csr_write(CPURISCVState *env, int csrno, - target_ulong val) -{ - riscv_csrrw(env, csrno, NULL, val, MAKE_64BIT_MASK(0, TARGET_LONG_BITS), 0); -} - -static inline target_ulong riscv_csr_read(CPURISCVState *env, int csrno) -{ - target_ulong val = 0; - riscv_csrr(env, csrno, &val); - return val; -} - -typedef RISCVException (*riscv_csr_predicate_fn)(CPURISCVState *env, - int csrno); -typedef RISCVException (*riscv_csr_read_fn)(CPURISCVState *env, int csrno, - target_ulong *ret_value); -typedef RISCVException (*riscv_csr_write_fn)(CPURISCVState *env, int csrno, - target_ulong new_value, - uintptr_t ra); -typedef RISCVException (*riscv_csr_op_fn)(CPURISCVState *env, int csrno, - target_ulong *ret_value, - target_ulong new_value, - target_ulong write_mask); - -RISCVException riscv_csrr_i128(CPURISCVState *env, int csrno, - Int128 *ret_value); -RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, - Int128 *ret_value, Int128 new_value, - Int128 write_mask, uintptr_t ra); - -typedef RISCVException (*riscv_csr_read128_fn)(CPURISCVState *env, int csrno, - Int128 *ret_value); -typedef RISCVException (*riscv_csr_write128_fn)(CPURISCVState *env, int csrno, - Int128 new_value); - -typedef struct { - const char *name; - riscv_csr_predicate_fn predicate; - riscv_csr_read_fn read; - riscv_csr_write_fn write; - riscv_csr_op_fn op; - riscv_csr_read128_fn read128; - riscv_csr_write128_fn write128; - /* The default priv spec version should be PRIV_VERSION_1_10_0 (i.e 0) */ - uint32_t min_priv_ver; -} riscv_csr_operations; - -struct RISCVCSR { - int csrno; - bool (*insertion_test)(RISCVCPU *cpu); - riscv_csr_operations csr_ops; -}; - -/* CSR function table constants */ -enum { - CSR_TABLE_SIZE = 0x1000 -}; +#include "target/riscv/csr.h" /* * The event id are encoded based on the encoding specified in the @@ -XXX,XX +XXX,XX @@ void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp); void riscv_add_satp_mode_properties(Object *obj); bool riscv_cpu_accelerator_compatible(RISCVCPU *cpu); -/* CSR function table */ -extern riscv_csr_operations csr_ops[CSR_TABLE_SIZE]; -bool riscv_csr_is_fpu(int csrno); -bool riscv_csr_is_vpu(int csrno); - extern const bool valid_vm_1_10_32[], valid_vm_1_10_64[]; -void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops); -void riscv_set_csr_ops(int csrno, const riscv_csr_operations *ops); - void riscv_cpu_register_gdb_regs_for_features(CPUState *cs); -target_ulong riscv_new_csr_seed(target_ulong new_value, - target_ulong write_mask); - const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit); -/* In th_csr.c */ -extern const RISCVCSR th_csr_list[]; - -/* Implemented in mips_csr.c */ -extern const RISCVCSR mips_csr_list[]; - const char *priv_spec_to_str(int priv_version); #endif /* RISCV_CPU_H */ diff --git a/target/riscv/csr.h b/target/riscv/csr.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/target/riscv/csr.h @@ -XXX,XX +XXX,XX @@ +/* + * QEMU RISC-V CSRs + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RISCV_CSR_H +#define RISCV_CSR_H + +#include "exec/target_long.h" +#include "cpu_bits.h" + +target_ulong riscv_new_csr_seed(target_ulong new_value, + target_ulong write_mask); + +RISCVException riscv_csrr(CPURISCVState *env, int csrno, + target_ulong *ret_value); + +RISCVException riscv_csrrw(CPURISCVState *env, int csrno, + target_ulong *ret_value, target_ulong new_value, + target_ulong write_mask, uintptr_t ra); +RISCVException riscv_csrrw_debug(CPURISCVState *env, int csrno, + target_ulong *ret_value, + target_ulong new_value, + target_ulong write_mask); + +static inline void riscv_csr_write(CPURISCVState *env, int csrno, + target_ulong val) +{ + riscv_csrrw(env, csrno, NULL, val, MAKE_64BIT_MASK(0, TARGET_LONG_BITS), 0); +} + +static inline target_ulong riscv_csr_read(CPURISCVState *env, int csrno) +{ + target_ulong val = 0; + riscv_csrr(env, csrno, &val); + return val; +} + +typedef RISCVException (*riscv_csr_predicate_fn)(CPURISCVState *env, + int csrno); +typedef RISCVException (*riscv_csr_read_fn)(CPURISCVState *env, int csrno, + target_ulong *ret_value); +typedef RISCVException (*riscv_csr_write_fn)(CPURISCVState *env, int csrno, + target_ulong new_value, + uintptr_t ra); +typedef RISCVException (*riscv_csr_op_fn)(CPURISCVState *env, int csrno, + target_ulong *ret_value, + target_ulong new_value, + target_ulong write_mask); + +RISCVException riscv_csrr_i128(CPURISCVState *env, int csrno, + Int128 *ret_value); +RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, + Int128 *ret_value, Int128 new_value, + Int128 write_mask, uintptr_t ra); + +typedef RISCVException (*riscv_csr_read128_fn)(CPURISCVState *env, int csrno, + Int128 *ret_value); +typedef RISCVException (*riscv_csr_write128_fn)(CPURISCVState *env, int csrno, + Int128 new_value); + +typedef struct { + const char *name; + riscv_csr_predicate_fn predicate; + riscv_csr_read_fn read; + riscv_csr_write_fn write; + riscv_csr_op_fn op; + riscv_csr_read128_fn read128; + riscv_csr_write128_fn write128; + /* The default priv spec version should be PRIV_VERSION_1_10_0 (i.e 0) */ + uint32_t min_priv_ver; +} riscv_csr_operations; + +struct RISCVCSR { + int csrno; + bool (*insertion_test)(RISCVCPU *cpu); + riscv_csr_operations csr_ops; +}; + +/* CSR function table constants */ +enum { + CSR_TABLE_SIZE = 0x1000 +}; + +/* CSR function table */ +extern riscv_csr_operations csr_ops[CSR_TABLE_SIZE]; + +bool riscv_csr_is_fpu(int csrno); +bool riscv_csr_is_vpu(int csrno); + +void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops); +void riscv_set_csr_ops(int csrno, const riscv_csr_operations *ops); + +/* In th_csr.c */ +extern const RISCVCSR th_csr_list[]; + +/* Implemented in mips_csr.c */ +extern const RISCVCSR mips_csr_list[]; + +#endif /* RISCV_CSR_H */ diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ #include "qemu/log.h" #include "cpu.h" #include "cpu_vendorid.h" +#include "target/riscv/csr.h" #include "internals.h" #include "qapi/error.h" #include "qapi/visitor.h" diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ #include "qemu/log.h" #include "qemu/timer.h" #include "cpu.h" +#include "target/riscv/csr.h" #include "tcg/tcg-cpu.h" #include "pmu.h" #include "time_helper.h" diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -XXX,XX +XXX,XX @@ #include "gdbstub/helpers.h" #include "cpu.h" #include "internals.h" +#include "target/riscv/csr.h" struct TypeSize { const char *gdb_type; diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -XXX,XX +XXX,XX @@ #include "system/kvm.h" #include "system/kvm_int.h" #include "cpu.h" +#include "target/riscv/csr.h" #include "trace.h" #include "accel/accel-cpu-target.h" #include "hw/pci/pci.h" diff --git a/target/riscv/mips_csr.c b/target/riscv/mips_csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/mips_csr.c +++ b/target/riscv/mips_csr.c @@ -XXX,XX +XXX,XX @@ #include "qemu/osdep.h" #include "cpu.h" #include "cpu_vendorid.h" +#include "target/riscv/csr.h" /* Static MIPS CSR state storage */ static struct { diff --git a/target/riscv/monitor.c b/target/riscv/monitor.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/monitor.c +++ b/target/riscv/monitor.c @@ -XXX,XX +XXX,XX @@ #include "qemu/ctype.h" #include "qemu/qemu-print.h" #include "cpu.h" +#include "target/riscv/csr.h" #include "cpu_bits.h" #include "monitor/monitor.h" #include "monitor/hmp.h" diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -XXX,XX +XXX,XX @@ #include "qemu/osdep.h" #include "cpu.h" +#include "target/riscv/csr.h" #include "internals.h" #include "exec/cputlb.h" #include "accel/tcg/cpu-ldst.h" diff --git a/target/riscv/riscv-qmp-cmds.c b/target/riscv/riscv-qmp-cmds.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/riscv-qmp-cmds.c +++ b/target/riscv/riscv-qmp-cmds.c @@ -XXX,XX +XXX,XX @@ #include "system/tcg.h" #include "cpu-qom.h" #include "cpu.h" +#include "target/riscv/csr.h" static void riscv_cpu_add_definition(gpointer data, gpointer user_data) { diff --git a/target/riscv/th_csr.c b/target/riscv/th_csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/th_csr.c +++ b/target/riscv/th_csr.c @@ -XXX,XX +XXX,XX @@ #include "qemu/osdep.h" #include "cpu.h" #include "cpu_vendorid.h" +#include "target/riscv/csr.h" #define CSR_TH_SXSTATUS 0x5c0 -- 2.54.0
From: Anton Johansson <anjo@rev.ng> Convert riscv_csr_[read|write]() into target_ulong angnostic CSR access functions that can be safely used from outside of target/ without knowledge of the target register size. Replace the 4 existing CSR accesses in hw/ and linux-user/. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-25-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 7 ++++++- target/riscv/csr.h | 13 ------------- hw/riscv/riscv_hart.c | 7 +++---- linux-user/riscv/signal.c | 5 +++-- target/riscv/csr.c | 15 +++++++++++++++ 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ RISCVPmPmm riscv_pm_get_pmm(CPURISCVState *env); RISCVPmPmm riscv_pm_get_vm_ldst_pmm(CPURISCVState *env); uint32_t riscv_pm_get_pmlen(RISCVPmPmm pmm); -#include "target/riscv/csr.h" +/* + * Externally facing CSR access functions, wrappers around riscv_csr*(). + */ + +RISCVException riscv_csr_write_i64(CPURISCVState *env, int csrno, uint64_t val); +RISCVException riscv_csr_read_i64(CPURISCVState *env, int csrn, uint64_t *res); /* * The event id are encoded based on the encoding specified in the diff --git a/target/riscv/csr.h b/target/riscv/csr.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.h +++ b/target/riscv/csr.h @@ -XXX,XX +XXX,XX @@ RISCVException riscv_csrrw_debug(CPURISCVState *env, int csrno, target_ulong new_value, target_ulong write_mask); -static inline void riscv_csr_write(CPURISCVState *env, int csrno, - target_ulong val) -{ - riscv_csrrw(env, csrno, NULL, val, MAKE_64BIT_MASK(0, TARGET_LONG_BITS), 0); -} - -static inline target_ulong riscv_csr_read(CPURISCVState *env, int csrno) -{ - target_ulong val = 0; - riscv_csrr(env, csrno, &val); - return val; -} - typedef RISCVException (*riscv_csr_predicate_fn)(CPURISCVState *env, int csrno); typedef RISCVException (*riscv_csr_read_fn)(CPURISCVState *env, int csrno, diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -XXX,XX +XXX,XX @@ static void csr_call(char *cmd, uint64_t cpu_num, int csrno, uint64_t *val) RISCVCPU *cpu = RISCV_CPU(cpu_by_arch_id(cpu_num)); CPURISCVState *env = &cpu->env; - int ret = RISCV_EXCP_NONE; + RISCVException ret = RISCV_EXCP_NONE; if (strcmp(cmd, "get_csr") == 0) { - ret = riscv_csrr(env, csrno, (target_ulong *)val); + ret = riscv_csr_read_i64(env, csrno, val); } else if (strcmp(cmd, "set_csr") == 0) { - ret = riscv_csrrw(env, csrno, NULL, *(target_ulong *)val, - MAKE_64BIT_MASK(0, TARGET_LONG_BITS), 0); + ret = riscv_csr_write_i64(env, csrno, *val); } g_assert(ret == RISCV_EXCP_NONE); diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c index XXXXXXX..XXXXXXX 100644 --- a/linux-user/riscv/signal.c +++ b/linux-user/riscv/signal.c @@ -XXX,XX +XXX,XX @@ static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env) __put_user(env->fpr[i], &sc->fpr[i]); } - uint32_t fcsr = riscv_csr_read(env, CSR_FCSR); + uint64_t fcsr; + riscv_csr_read_i64(env, CSR_FCSR, &fcsr); __put_user(fcsr, &sc->fcsr); } @@ -XXX,XX +XXX,XX @@ static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc) uint32_t fcsr; __get_user(fcsr, &sc->fcsr); - riscv_csr_write(env, CSR_FCSR, fcsr); + riscv_csr_write_i64(env, CSR_FCSR, fcsr); } static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ RISCVException riscv_csrrw(CPURISCVState *env, int csrno, return riscv_csrrw_do64(env, csrno, ret_value, new_value, write_mask, ra); } +RISCVException riscv_csr_write_i64(CPURISCVState *env, int csrno, uint64_t val) +{ + return riscv_csrrw(env, csrno, NULL, val, + MAKE_64BIT_MASK(0, TARGET_LONG_BITS), 0); +} + +RISCVException riscv_csr_read_i64(CPURISCVState *env, int csrno, uint64_t *res) +{ + RISCVException ret; + target_ulong val = 0; + ret = riscv_csrr(env, csrno, &val); + *res = val; + return ret; +} + static RISCVException riscv_csrrw_do128(CPURISCVState *env, int csrno, Int128 *ret_value, Int128 new_value, -- 2.54.0
From: Anton Johansson <anjo@rev.ng> The pmp.h header is exposed through cpu.h. pmp_table_t is also used in CPUArchState. CSR declarations are only used in target/ and are moved to csr.h. In pmp.h, addr_reg is widened to 64 bits and the privilege mode parameter is fixed to 8 bits, similar to previous commits. Note, the cpu/pmp/entry and cpu/pmp VMSTATE versions are bumped, breaking migration from older versions. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-26-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.h | 12 ++++++++++++ target/riscv/pmp.h | 19 +++++-------------- target/riscv/machine.c | 10 +++++----- target/riscv/pmp.c | 10 ++++++---- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/target/riscv/csr.h b/target/riscv/csr.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.h +++ b/target/riscv/csr.h @@ -XXX,XX +XXX,XX @@ extern const RISCVCSR th_csr_list[]; /* Implemented in mips_csr.c */ extern const RISCVCSR mips_csr_list[]; +/* PMP CSRs, defined in pmp.c */ +void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index, + target_ulong val); +target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index); + +void mseccfg_csr_write(CPURISCVState *env, uint64_t val); +uint64_t mseccfg_csr_read(CPURISCVState *env); + +void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, + target_ulong val); +target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index); + #endif /* RISCV_CSR_H */ diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -XXX,XX +XXX,XX @@ #ifndef RISCV_PMP_H #define RISCV_PMP_H -#include "exec/target_long.h" #include "cpu.h" typedef enum { @@ -XXX,XX +XXX,XX @@ typedef enum { } mseccfg_field_t; typedef struct { - target_ulong addr_reg; + uint64_t addr_reg; uint8_t cfg_reg; } pmp_entry_t; @@ -XXX,XX +XXX,XX @@ typedef struct { uint32_t num_rules; } pmp_table_t; -void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index, - target_ulong val); -target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index); +typedef struct CPUArchState CPURISCVState; -void mseccfg_csr_write(CPURISCVState *env, uint64_t val); -uint64_t mseccfg_csr_read(CPURISCVState *env); - -void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, - target_ulong val); -target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index); bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr, - target_ulong size, pmp_priv_t privs, + int size, pmp_priv_t privs, pmp_priv_t *allowed_privs, - target_ulong mode); -target_ulong pmp_get_tlb_size(CPURISCVState *env, hwaddr addr); + privilege_mode_t mode); +uint64_t pmp_get_tlb_size(CPURISCVState *env, hwaddr addr); void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index); void pmp_update_rule_nums(CPURISCVState *env); uint32_t pmp_get_num_rules(CPURISCVState *env); diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ static int pmp_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_pmp_entry = { .name = "cpu/pmp/entry", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (const VMStateField[]) { - VMSTATE_UINTTL(addr_reg, pmp_entry_t), + VMSTATE_UINT64(addr_reg, pmp_entry_t), VMSTATE_UINT8(cfg_reg, pmp_entry_t), VMSTATE_END_OF_LIST() } @@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_pmp_entry = { static const VMStateDescription vmstate_pmp = { .name = "cpu/pmp", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .needed = pmp_needed, .post_load = pmp_post_load, .fields = (const VMStateField[]) { diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -XXX,XX +XXX,XX @@ #include "qemu/log.h" #include "qapi/error.h" #include "cpu.h" +#include "target/riscv/csr.h" #include "trace.h" #include "exec/cputlb.h" #include "exec/page-protection.h" @@ -XXX,XX +XXX,XX @@ static int pmp_is_in_range(CPURISCVState *env, int pmp_index, hwaddr addr) */ static bool pmp_hart_has_privs_default(CPURISCVState *env, pmp_priv_t privs, pmp_priv_t *allowed_privs, - target_ulong mode) + privilege_mode_t mode) { bool ret; @@ -XXX,XX +XXX,XX @@ static bool pmp_hart_has_privs_default(CPURISCVState *env, pmp_priv_t privs, * have no functional impact in QEMU emulation. */ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr, - target_ulong size, pmp_priv_t privs, - pmp_priv_t *allowed_privs, target_ulong mode) + int size, pmp_priv_t privs, + pmp_priv_t *allowed_privs, + privilege_mode_t mode) { int i = 0; int pmp_size = 0; @@ -XXX,XX +XXX,XX @@ uint64_t mseccfg_csr_read(CPURISCVState *env) * To avoid this we return a size of 1 (which means no caching) if the PMP * region only covers partial of the TLB page. */ -target_ulong pmp_get_tlb_size(CPURISCVState *env, hwaddr addr) +uint64_t pmp_get_tlb_size(CPURISCVState *env, hwaddr addr) { hwaddr pmp_sa; hwaddr pmp_ea; -- 2.54.0
From: Anton Johansson <anjo@rev.ng> The corresponding field CPUArchState::resetvec is uint64_t anyway, no need to use target_ulong. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-27-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 2 +- target/riscv/cpu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ uint8_t riscv_cpu_get_fflags(CPURISCVState *env); void riscv_cpu_set_fflags(CPURISCVState *env, uint8_t); #ifndef CONFIG_USER_ONLY -void cpu_set_exception_base(int vp_index, target_ulong address); +void cpu_set_exception_base(int vp_index, uint64_t address); #endif FIELD(TB_FLAGS, MEM_IDX, 0, 3) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ bool riscv_cpu_option_set(RISCVCPU *cpu, const char *optname) #ifndef CONFIG_USER_ONLY /* This is used in runtime only. */ -void cpu_set_exception_base(int vp_index, target_ulong address) +void cpu_set_exception_base(int vp_index, uint64_t address) { RISCVCPU *cpu; CPUState *cs = qemu_get_cpu(vp_index); -- 2.54.0
From: Anton Johansson <anjo@rev.ng> pmp.h is only needed and included for system mode, however relevant macros (MAX_RISCV_PMPS, OLD_MAX_RISCV_PMPS, MIN_RISCV_PMP_GRANULARITY) are required unconditionally by cpu.c, and so are defined in cpu.h. pmp.h then defines pmp_table_t depending on these macros and so requires cpu.h, and cpu.h in turn uses pmp_table_t resulting in circular inclusion. Move PMP macros to pmp.h and only expose PMP properties in system mode. Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260520125406.28693-28-anjo@rev.ng> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 4 ---- target/riscv/pmp.h | 4 +++- target/riscv/cpu.c | 6 ++++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ extern RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[]; #define MMU_USER_IDX 3 -#define MAX_RISCV_PMPS (64) -#define OLD_MAX_RISCV_PMPS (16) -#define MIN_RISCV_PMP_GRANULARITY 4 - #if !defined(CONFIG_USER_ONLY) #include "pmp.h" #endif diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -XXX,XX +XXX,XX @@ #ifndef RISCV_PMP_H #define RISCV_PMP_H -#include "cpu.h" +#define MAX_RISCV_PMPS (64) +#define OLD_MAX_RISCV_PMPS (16) +#define MIN_RISCV_PMP_GRANULARITY 4 typedef enum { PMP_READ = 1 << 0, diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_init(Object *obj) cpu->cfg.cbop_blocksize = 64; cpu->cfg.cboz_blocksize = 64; cpu->cfg.pmp_regions = 16; +#ifndef CONFIG_USER_ONLY cpu->cfg.pmp_granularity = MIN_RISCV_PMP_GRANULARITY; +#endif cpu->env.vext_ver = VEXT_VERSION_1_00_0; cpu->cfg.max_satp_mode = -1; @@ -XXX,XX +XXX,XX @@ static const PropertyInfo prop_mmu = { .set = prop_mmu_set, }; +#ifndef CONFIG_USER_ONLY static void prop_pmp_set(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -XXX,XX +XXX,XX @@ static const PropertyInfo prop_pmp_granularity = { .get = prop_pmp_granularity_get, .set = prop_pmp_granularity_set, }; +#endif /* !CONFIG_USER_ONLY */ static int priv_spec_from_str(const char *priv_spec_str) { @@ -XXX,XX +XXX,XX @@ static const Property riscv_cpu_properties[] = { {.name = "pmu-num", .info = &prop_pmu_num}, /* Deprecated */ {.name = "mmu", .info = &prop_mmu}, +#ifndef CONFIG_USER_ONLY {.name = "pmp", .info = &prop_pmp}, {.name = "num-pmp-regions", .info = &prop_num_pmp_regions}, {.name = "pmp-granularity", .info = &prop_pmp_granularity}, +#endif {.name = "priv_spec", .info = &prop_priv_spec}, {.name = "vext_spec", .info = &prop_vext_spec}, -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We're not doing anything special w.r.t PMA (Physical Memory Access) related faults, handling them like regular faults that will eventually turn to be regular page faults. Turns out we can't do that. Priv spec section "Virtual Address Translation Process" mentions: "If a store to the PTE at address a+va.vpn[i]×PTESIZE would violate a PMA or PMP check, raise an access-fault exception corresponding to the original access type." This means that we should handle PMA violations with access faults, like we're already doing with PMP. One clear code path where we should throw a PMA failure, exposed by [1], is the error return from address_space_ld* call. There's a separated issue with the error code being returned by them (it always return DECODE_ERROR even with 'rejected' reads) that we're going to work around it by assuming that we did a good job with the PTE address sanitization beforehand, and interpret that the error here is related to PMA. This is of course not ideal but fixing this QEMU API is out of scope for this work. All this said, we'll set the new pmp_pma_violation flag when we have either a PMP or a PMA fault, and everything else shall fall into place. [1] https://gitlab.com/qemu-project/qemu/-/work_items/3502 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3502 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Message-ID: <20260522172502.320529-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 3 ++- target/riscv/cpu_helper.c | 34 +++++++++++++++++++++++++--------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ enum { TRANSLATE_SUCCESS, TRANSLATE_FAIL, TRANSLATE_PMP_FAIL, - TRANSLATE_G_STAGE_FAIL + TRANSLATE_G_STAGE_FAIL, + TRANSLATE_PMA_FAIL, }; /* Extension context status */ diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -XXX,XX +XXX,XX @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, } if (res != MEMTX_OK) { - return TRANSLATE_FAIL; + /* + * The result of address_space_* APIs above does not take into + * consideration reject reads, putting all errors in the same + * cathegory (DECODE_ERROR), although there's a clear + * distinction between a rejected read versus other errors + * (see memory_region_dispatch_read() -> + * memory_region_access_valid()). This is something that + * we might have to deal with core QEMU logic some other + * day. + * + * For this particular error path, given that we made checks + * w.r.t legal PTE address before calling those APIs, we'll + * assume that anything != MEMTX_OK means a rejected read, + * i.e. a PMA error. + */ + return TRANSLATE_PMA_FAIL; } if (riscv_cpu_sxl(env) == MXL_RV32) { @@ -XXX,XX +XXX,XX @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, } static void raise_mmu_exception(CPURISCVState *env, target_ulong address, - MMUAccessType access_type, bool pmp_violation, + MMUAccessType access_type, + bool pmp_pma_violation, bool first_stage, bool two_stage, bool two_stage_indirect) { @@ -XXX,XX +XXX,XX @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, switch (access_type) { case MMU_INST_FETCH: - if (pmp_violation) { + if (pmp_pma_violation) { cs->exception_index = RISCV_EXCP_INST_ACCESS_FAULT; } else if (env->virt_enabled && !first_stage) { cs->exception_index = RISCV_EXCP_INST_GUEST_PAGE_FAULT; @@ -XXX,XX +XXX,XX @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, } break; case MMU_DATA_LOAD: - if (pmp_violation) { + if (pmp_pma_violation) { cs->exception_index = RISCV_EXCP_LOAD_ACCESS_FAULT; } else if (two_stage && !first_stage) { cs->exception_index = RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT; @@ -XXX,XX +XXX,XX @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, } break; case MMU_DATA_STORE: - if (pmp_violation) { + if (pmp_pma_violation) { cs->exception_index = RISCV_EXCP_STORE_AMO_ACCESS_FAULT; } else if (two_stage && !first_stage) { cs->exception_index = RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT; @@ -XXX,XX +XXX,XX @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, vaddr im_address; hwaddr pa = 0; int prot, prot2, prot_pmp; - bool pmp_violation = false; + bool pmp_pma_violation = false; bool first_stage_error = true; bool two_stage_lookup = mmuidx_2stage(mmu_idx); bool two_stage_indirect_error = false; @@ -XXX,XX +XXX,XX @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } } - if (ret == TRANSLATE_PMP_FAIL) { - pmp_violation = true; + if (ret == TRANSLATE_PMP_FAIL || ret == TRANSLATE_PMA_FAIL) { + pmp_pma_violation = true; } if (ret == TRANSLATE_SUCCESS) { @@ -XXX,XX +XXX,XX @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, cpu_check_watchpoint(cs, address, size, MEMTXATTRS_UNSPECIFIED, wp_access, retaddr); - raise_mmu_exception(env, address, access_type, pmp_violation, + raise_mmu_exception(env, address, access_type, pmp_pma_violation, first_stage_error, two_stage_lookup, two_stage_indirect_error); cpu_loop_exit_restore(cs, retaddr); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We did a recent change to disable svpbmt if satp_mode < sv39 that was discovered via a gitlab report. This time we don't have an opened bug but the problem is similar: RISC-V privileged ISA, chapter '"Svnapot" Extension for NAPOT Translation Contiguity, Version 1.0' states: "The Svnapot extension depends on the Sv39 extension." Do the same thing with svnapot, including the user warning in case we try to enable it without the required satp_mode: $ ./build/qemu-system-riscv64 -M virt,dumpdtb=fdt.dtb \ -cpu max,sv39=off,sv48=off,sv57=off,sv64=off,svnapot=on qemu-system-riscv64: warning: svnapot requires at least satp sv39, current satp mode: none Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260527213034.2094103-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/tcg/tcg-cpu.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) riscv_cpu_is_32bit(cpu))); } } + + if (cpu->cfg.ext_svnapot && cpu->cfg.max_satp_mode < VM_1_10_SV39) { + cpu->cfg.ext_svnapot = false; + if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_svnapot))) { + warn_report("svnapot requires at least satp sv39, " + "current satp mode: %s", + satp_mode_str(cpu->cfg.max_satp_mode, + riscv_cpu_is_32bit(cpu))); + } + } #endif /* * Disable isa extensions based on priv spec after we -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> inst_length() can return 0 if 'inst' happens to not match any known encoding (like [1]). Returning 0 is not desirable, even for unknown encodings, given that it will cause a loop in target_disas() later on. The most recent version of the RISC-V unpriv spec ditched the sophisticated instruction-length encoding. We're now supporting only 16-bit and 32-bit length instructions, where: "All the 32-bit instructions in the base ISA have their lowest two bits set to 11. The optional compressed 16-bit instruction-set extensions have their lowest two bits equal to 00, 01, or 10." So the code is now simpler, never returning 0, and in fact it's the same thing we're already doing in insn_len() from target/riscv/internals.h. Due to include shenarigans we can't use that function in disas/riscv.c, but I believe we can cut ourselves some slack this time and not lose sleep over a 1 line of duplicated logic. We're documenting it though! [1] https://gitlab.com/qemu-project/qemu/-/work_items/3479 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3479 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260527200355.2068879-2-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- disas/riscv.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index XXXXXXX..XXXXXXX 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -XXX,XX +XXX,XX @@ static bool check_constraints(rv_decode *dec, const rvc_constraint *c) return true; } -/* instruction length */ - +/* Same as insn_len() from target/riscv/internals.h */ static size_t inst_length(rv_inst inst) { - /* NOTE: supports maximum instruction size of 64-bits */ - - /* - * instruction length coding - * - * aa - 16 bit aa != 11 - * bbb11 - 32 bit bbb != 111 - * 011111 - 48 bit - * 0111111 - 64 bit - */ - - return (inst & 0b11) != 0b11 ? 2 - : (inst & 0b11100) != 0b11100 ? 4 - : (inst & 0b111111) == 0b011111 ? 6 - : (inst & 0b1111111) == 0b0111111 ? 8 - : 0; + return (inst & 3) == 3 ? 4 : 2; } /* format instruction */ -- 2.54.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> The data access endianness is constant during a translation block; rather than calling the mo_endian() method each time, initialize the DisasContext::mo_endianness field once in TranslatorOps::init_disas_context(). Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Message-ID: <20260527201348.29511-2-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/translate.c | 9 ++++++--- target/riscv/insn_trans/trans_rva.c.inc | 4 ++-- target/riscv/insn_trans/trans_rvd.c.inc | 4 ++-- target/riscv/insn_trans/trans_rvf.c.inc | 4 ++-- target/riscv/insn_trans/trans_rvi.c.inc | 8 ++++---- target/riscv/insn_trans/trans_rvzacas.c.inc | 4 ++-- target/riscv/insn_trans/trans_rvzalasr.c.inc | 4 ++-- target/riscv/insn_trans/trans_rvzce.c.inc | 4 ++-- target/riscv/insn_trans/trans_rvzfh.c.inc | 4 ++-- target/riscv/insn_trans/trans_rvzicfiss.c.inc | 4 ++-- target/riscv/insn_trans/trans_xmips.c.inc | 8 ++++---- target/riscv/insn_trans/trans_xthead.c.inc | 16 ++++++++-------- target/riscv/insn_trans/trans_zilsd.c.inc | 4 ++-- 13 files changed, 40 insertions(+), 37 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -XXX,XX +XXX,XX @@ typedef struct DisasContext { bool fcfi_lp_expected; /* zicfiss extension, if shadow stack was enabled during TB gen */ bool bcfi_enabled; + /* Data endianness from MSTATUS UBE/SBE/MBE */ + MemOp mo_endianness; } DisasContext; static inline bool has_ext(DisasContext *ctx, uint32_t ext) @@ -XXX,XX +XXX,XX @@ static inline MemOp mo_endian(DisasContext *ctx) #define get_address_xl(ctx) ((ctx)->address_xl) #endif -#define mxl_memop(ctx) ((get_xl(ctx) + 1) | mo_endian(ctx)) +#define mxl_memop(ctx) ((get_xl(ctx) + 1) | (ctx)->mo_endianness) /* The word size for this machine mode. */ static inline int __attribute__((unused)) get_xlen(DisasContext *ctx) @@ -XXX,XX +XXX,XX @@ static bool gen_amo(DisasContext *ctx, arg_atomic *a, TCGv src1, src2 = get_gpr(ctx, a->rs2, EXT_NONE); MemOp size = mop & MO_SIZE; - mop |= mo_endian(ctx); + mop |= ctx->mo_endianness; if (ctx->cfg_ptr->ext_zama16b && size >= MO_32) { mop |= MO_ATOM_WITHIN16; } else { @@ -XXX,XX +XXX,XX @@ static bool gen_cmpxchg(DisasContext *ctx, arg_atomic *a, MemOp mop) TCGv src1 = get_address(ctx, a->rs1, 0); TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); - mop |= mo_endian(ctx); + mop |= ctx->mo_endianness; decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); tcg_gen_atomic_cmpxchg_tl(dest, src1, dest, src2, ctx->mem_idx, mop); @@ -XXX,XX +XXX,XX @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->zero = tcg_constant_tl(0); ctx->virt_inst_excp = false; ctx->decoders = cpu->decoders; + ctx->mo_endianness = mo_endian(ctx); } static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) diff --git a/target/riscv/insn_trans/trans_rva.c.inc b/target/riscv/insn_trans/trans_rva.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rva.c.inc +++ b/target/riscv/insn_trans/trans_rva.c.inc @@ -XXX,XX +XXX,XX @@ static bool gen_lr(DisasContext *ctx, arg_atomic *a, MemOp mop) TCGv src1; mop |= MO_ALIGN; - mop |= mo_endian(ctx); + mop |= ctx->mo_endianness; decode_save_opc(ctx, 0); src1 = get_address(ctx, a->rs1, 0); @@ -XXX,XX +XXX,XX @@ static bool gen_sc(DisasContext *ctx, arg_atomic *a, MemOp mop) TCGLabel *l2 = gen_new_label(); mop |= MO_ALIGN; - mop |= mo_endian(ctx); + mop |= ctx->mo_endianness; decode_save_opc(ctx, 0); src1 = get_address(ctx, a->rs1, 0); diff --git a/target/riscv/insn_trans/trans_rvd.c.inc b/target/riscv/insn_trans/trans_rvd.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvd.c.inc +++ b/target/riscv/insn_trans/trans_rvd.c.inc @@ -XXX,XX +XXX,XX @@ static bool trans_fld(DisasContext *ctx, arg_fld *a) } else { memop |= MO_ATOM_IFALIGN; } - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; decode_save_opc(ctx, 0); addr = get_address(ctx, a->rs1, a->imm); @@ -XXX,XX +XXX,XX @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) } else { memop |= MO_ATOM_IFALIGN; } - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; decode_save_opc(ctx, 0); addr = get_address(ctx, a->rs1, a->imm); diff --git a/target/riscv/insn_trans/trans_rvf.c.inc b/target/riscv/insn_trans/trans_rvf.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvf.c.inc +++ b/target/riscv/insn_trans/trans_rvf.c.inc @@ -XXX,XX +XXX,XX @@ static bool trans_flw(DisasContext *ctx, arg_flw *a) REQUIRE_FPU; REQUIRE_EXT(ctx, RVF); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } @@ -XXX,XX +XXX,XX @@ static bool trans_fsw(DisasContext *ctx, arg_fsw *a) REQUIRE_FPU; REQUIRE_EXT(ctx, RVF); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -XXX,XX +XXX,XX @@ static bool gen_load_i128(DisasContext *ctx, arg_lb *a, MemOp memop) } } else { tcg_gen_qemu_ld_i128(t16, addrl, ctx->mem_idx, memop); - if (mo_endian(ctx) == MO_LE) { + if (ctx->mo_endianness == MO_LE) { tcg_gen_extr_i128_i64(tl, th, t16); } else { tcg_gen_extr_i128_i64(th, tl, t16); @@ -XXX,XX +XXX,XX @@ static bool gen_load(DisasContext *ctx, arg_lb *a, MemOp memop) { bool out; - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } @@ -XXX,XX +XXX,XX @@ static bool gen_store_i128(DisasContext *ctx, arg_sb *a, MemOp memop) tcg_gen_ext_tl_i64(tl, src2l); tcg_gen_ext_tl_i64(th, src2h); - if (mo_endian(ctx) == MO_LE) { + if (ctx->mo_endianness == MO_LE) { tcg_gen_concat_i64_i128(t16, tl, th); } else { tcg_gen_concat_i64_i128(t16, th, tl); @@ -XXX,XX +XXX,XX @@ static bool gen_store_i128(DisasContext *ctx, arg_sb *a, MemOp memop) static bool gen_store(DisasContext *ctx, arg_sb *a, MemOp memop) { - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } diff --git a/target/riscv/insn_trans/trans_rvzacas.c.inc b/target/riscv/insn_trans/trans_rvzacas.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvzacas.c.inc +++ b/target/riscv/insn_trans/trans_rvzacas.c.inc @@ -XXX,XX +XXX,XX @@ static bool gen_cmpxchg64(DisasContext *ctx, arg_atomic *a, MemOp mop) TCGv src1 = get_address(ctx, a->rs1, 0); TCGv_i64 src2 = get_gpr_pair(ctx, a->rs2); - mop |= mo_endian(ctx); + mop |= ctx->mo_endianness; decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); tcg_gen_atomic_cmpxchg_i64(dest, src1, dest, src2, ctx->mem_idx, mop); @@ -XXX,XX +XXX,XX @@ static bool trans_amocas_q(DisasContext *ctx, arg_amocas_q *a) TCGv_i64 desth = get_gpr(ctx, a->rd == 0 ? 0 : a->rd + 1, EXT_NONE); MemOp memop = MO_ALIGN | MO_UO; - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; tcg_gen_concat_i64_i128(src2, src2l, src2h); tcg_gen_concat_i64_i128(dest, destl, desth); decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); diff --git a/target/riscv/insn_trans/trans_rvzalasr.c.inc b/target/riscv/insn_trans/trans_rvzalasr.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvzalasr.c.inc +++ b/target/riscv/insn_trans/trans_rvzalasr.c.inc @@ -XXX,XX +XXX,XX @@ static bool gen_load_acquire(DisasContext *ctx, arg_lb_aqrl *a, MemOp memop) return false; } - memop |= MO_ALIGN | mo_endian(ctx); + memop |= MO_ALIGN | ctx->mo_endianness; memop |= (ctx->cfg_ptr->ext_zama16b) ? MO_ATOM_WITHIN16 : 0; tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, memop); @@ -XXX,XX +XXX,XX @@ static bool gen_store_release(DisasContext *ctx, arg_sb_aqrl *a, MemOp memop) return false; } - memop |= MO_ALIGN | mo_endian(ctx); + memop |= MO_ALIGN | ctx->mo_endianness; memop |= (ctx->cfg_ptr->ext_zama16b) ? MO_ATOM_WITHIN16 : 0; /* Add a memory barrier implied by RL (mandatory) and AQ (optional) */ diff --git a/target/riscv/insn_trans/trans_rvzce.c.inc b/target/riscv/insn_trans/trans_rvzce.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvzce.c.inc +++ b/target/riscv/insn_trans/trans_rvzce.c.inc @@ -XXX,XX +XXX,XX @@ static bool gen_pop(DisasContext *ctx, arg_cmpp *a, bool ret, bool ret_val) tcg_gen_addi_tl(addr, sp, stack_adj - reg_size); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; for (i = X_Sn + 11; i >= 0; i--) { if (reg_bitmap & (1 << i)) { TCGv dest = dest_gpr(ctx, i); @@ -XXX,XX +XXX,XX @@ static bool trans_cm_push(DisasContext *ctx, arg_cm_push *a) tcg_gen_subi_tl(addr, sp, reg_size); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; for (i = X_Sn + 11; i >= 0; i--) { if (reg_bitmap & (1 << i)) { TCGv val = get_gpr(ctx, i, EXT_NONE); diff --git a/target/riscv/insn_trans/trans_rvzfh.c.inc b/target/riscv/insn_trans/trans_rvzfh.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvzfh.c.inc +++ b/target/riscv/insn_trans/trans_rvzfh.c.inc @@ -XXX,XX +XXX,XX @@ static bool trans_flh(DisasContext *ctx, arg_flh *a) REQUIRE_FPU; REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; decode_save_opc(ctx, 0); t0 = get_gpr(ctx, a->rs1, EXT_NONE); if (a->imm) { @@ -XXX,XX +XXX,XX @@ static bool trans_fsh(DisasContext *ctx, arg_fsh *a) REQUIRE_FPU; REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; decode_save_opc(ctx, 0); t0 = get_gpr(ctx, a->rs1, EXT_NONE); if (a->imm) { diff --git a/target/riscv/insn_trans/trans_rvzicfiss.c.inc b/target/riscv/insn_trans/trans_rvzicfiss.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvzicfiss.c.inc +++ b/target/riscv/insn_trans/trans_rvzicfiss.c.inc @@ -XXX,XX +XXX,XX @@ static bool trans_ssamoswap_w(DisasContext *ctx, arg_amoswap_w *a) decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); src1 = get_address(ctx, a->rs1, 0); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; tcg_gen_atomic_xchg_tl(dest, src1, src2, SS_MMU_INDEX(ctx), memop); gen_set_gpr(ctx, a->rd, dest); return true; @@ -XXX,XX +XXX,XX @@ static bool trans_ssamoswap_d(DisasContext *ctx, arg_amoswap_w *a) decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); src1 = get_address(ctx, a->rs1, 0); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; tcg_gen_atomic_xchg_tl(dest, src1, src2, SS_MMU_INDEX(ctx), memop); gen_set_gpr(ctx, a->rd, dest); return true; diff --git a/target/riscv/insn_trans/trans_xmips.c.inc b/target/riscv/insn_trans/trans_xmips.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_xmips.c.inc +++ b/target/riscv/insn_trans/trans_xmips.c.inc @@ -XXX,XX +XXX,XX @@ static bool trans_ccmov(DisasContext *ctx, arg_ccmov *a) /* Load Doubleword Pair. */ static bool trans_ldp(DisasContext *ctx, arg_ldp *a) { - MemOp memop = MO_SQ | mo_endian(ctx); + MemOp memop = MO_SQ | ctx->mo_endianness; REQUIRE_XMIPSLSP(ctx); REQUIRE_64_OR_128BIT(ctx); @@ -XXX,XX +XXX,XX @@ static bool trans_ldp(DisasContext *ctx, arg_ldp *a) /* Load Word Pair. */ static bool trans_lwp(DisasContext *ctx, arg_lwp *a) { - MemOp memop = MO_SL | mo_endian(ctx); + MemOp memop = MO_SL | ctx->mo_endianness; REQUIRE_XMIPSLSP(ctx); @@ -XXX,XX +XXX,XX @@ static bool trans_lwp(DisasContext *ctx, arg_lwp *a) /* Store Doubleword Pair. */ static bool trans_sdp(DisasContext *ctx, arg_sdp *a) { - MemOp memop = MO_UQ | mo_endian(ctx); + MemOp memop = MO_UQ | ctx->mo_endianness; REQUIRE_XMIPSLSP(ctx); REQUIRE_64_OR_128BIT(ctx); @@ -XXX,XX +XXX,XX @@ static bool trans_sdp(DisasContext *ctx, arg_sdp *a) /* Store Word Pair. */ static bool trans_swp(DisasContext *ctx, arg_swp *a) { - MemOp memop = MO_SL | mo_endian(ctx); + MemOp memop = MO_SL | ctx->mo_endianness; REQUIRE_XMIPSLSP(ctx); diff --git a/target/riscv/insn_trans/trans_xthead.c.inc b/target/riscv/insn_trans/trans_xthead.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_xthead.c.inc +++ b/target/riscv/insn_trans/trans_xthead.c.inc @@ -XXX,XX +XXX,XX @@ static bool gen_fload_idx(DisasContext *ctx, arg_th_memidx *a, MemOp memop, TCGv_i64 rd = cpu_fpr[a->rd]; TCGv addr = get_th_address_indexed(ctx, a->rs1, a->rs2, a->imm2, zext_offs); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; tcg_gen_qemu_ld_i64(rd, addr, ctx->mem_idx, memop); if ((memop & MO_SIZE) == MO_32) { gen_nanbox_s(rd, rd); @@ -XXX,XX +XXX,XX @@ static bool gen_fstore_idx(DisasContext *ctx, arg_th_memidx *a, MemOp memop, TCGv_i64 rd = cpu_fpr[a->rd]; TCGv addr = get_th_address_indexed(ctx, a->rs1, a->rs2, a->imm2, zext_offs); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; tcg_gen_qemu_st_i64(rd, addr, ctx->mem_idx, memop); return true; @@ -XXX,XX +XXX,XX @@ static bool gen_load_inc(DisasContext *ctx, arg_th_meminc *a, MemOp memop, TCGv rd = dest_gpr(ctx, a->rd); TCGv rs1 = get_gpr(ctx, a->rs1, EXT_NONE); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; tcg_gen_qemu_ld_tl(rd, addr, ctx->mem_idx, memop); tcg_gen_addi_tl(rs1, rs1, imm); gen_set_gpr(ctx, a->rd, rd); @@ -XXX,XX +XXX,XX @@ static bool gen_store_inc(DisasContext *ctx, arg_th_meminc *a, MemOp memop, TCGv data = get_gpr(ctx, a->rd, EXT_NONE); TCGv rs1 = get_gpr(ctx, a->rs1, EXT_NONE); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, memop); tcg_gen_addi_tl(rs1, rs1, imm); gen_set_gpr(ctx, a->rs1, rs1); @@ -XXX,XX +XXX,XX @@ static bool gen_load_idx(DisasContext *ctx, arg_th_memidx *a, MemOp memop, TCGv rd = dest_gpr(ctx, a->rd); TCGv addr = get_th_address_indexed(ctx, a->rs1, a->rs2, a->imm2, zext_offs); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; tcg_gen_qemu_ld_tl(rd, addr, ctx->mem_idx, memop); gen_set_gpr(ctx, a->rd, rd); @@ -XXX,XX +XXX,XX @@ static bool gen_store_idx(DisasContext *ctx, arg_th_memidx *a, MemOp memop, TCGv data = get_gpr(ctx, a->rd, EXT_NONE); TCGv addr = get_th_address_indexed(ctx, a->rs1, a->rs2, a->imm2, zext_offs); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, memop); return true; @@ -XXX,XX +XXX,XX @@ static bool gen_loadpair_tl(DisasContext *ctx, arg_th_pair *a, MemOp memop, addr1 = get_address(ctx, a->rs, imm); addr2 = get_address(ctx, a->rs, memop_size(memop) + imm); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; tcg_gen_qemu_ld_tl(t1, addr1, ctx->mem_idx, memop); tcg_gen_qemu_ld_tl(t2, addr2, ctx->mem_idx, memop); gen_set_gpr(ctx, a->rd1, t1); @@ -XXX,XX +XXX,XX @@ static bool gen_storepair_tl(DisasContext *ctx, arg_th_pair *a, MemOp memop, addr1 = get_address(ctx, a->rs, imm); addr2 = get_address(ctx, a->rs, memop_size(memop) + imm); - memop |= mo_endian(ctx); + memop |= ctx->mo_endianness; tcg_gen_qemu_st_tl(data1, addr1, ctx->mem_idx, memop); tcg_gen_qemu_st_tl(data2, addr2, ctx->mem_idx, memop); return true; diff --git a/target/riscv/insn_trans/trans_zilsd.c.inc b/target/riscv/insn_trans/trans_zilsd.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_zilsd.c.inc +++ b/target/riscv/insn_trans/trans_zilsd.c.inc @@ -XXX,XX +XXX,XX @@ static bool gen_load_i64(DisasContext *ctx, arg_ld *a) TCGv addr = get_address(ctx, a->rs1, a->imm); TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(tmp, addr, ctx->mem_idx, MO_SQ | mo_endian(ctx)); + tcg_gen_qemu_ld_i64(tmp, addr, ctx->mem_idx, MO_SQ | ctx->mo_endianness); if (a->rd == 0) { return true; @@ -XXX,XX +XXX,XX @@ static bool gen_store_i64(DisasContext *ctx, arg_sd *a) } else { tcg_gen_concat_tl_i64(tmp, data_low, data_high); } - tcg_gen_qemu_st_i64(tmp, addr, ctx->mem_idx, MO_SQ | mo_endian(ctx)); + tcg_gen_qemu_st_i64(tmp, addr, ctx->mem_idx, MO_SQ | ctx->mo_endianness); return true; } -- 2.54.0
From: Djordje Todorovic <Djordje.Todorovic@htecgroup.com> Make data accesses honour the MSTATUS MBE/SBE/UBE endianness bits instead of being hardcoded to little-endian. Update mo_endian_env() to pick the bit corresponding to the current privilege level (MBE for M, SBE for S, UBE for U). Remove the now unused mo_endian() helper. Note, TB_FLAGS has no free bits, so the data endianness is carried in the extended RISC-V TB flags stored in cs_base. It uses EXT_TB_FLAGS.BIG_ENDIAN at bit 33, leaving bit 32 for EXT_TB_FLAGS.ALTFMT. This keys TBs correctly on the current data endianness. Instruction fetches remain MO_LE unconditionally; RISC-V instructions are always little-endian per the ISA specification. Update the disassembler comment to clarify that BFD_ENDIAN_LITTLE is correct. Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com> Co-developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Message-ID: <20260527201348.29511-3-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 1 + target/riscv/internals.h | 29 +++++++++++++++++++++-------- target/riscv/cpu.c | 7 ++----- target/riscv/tcg/tcg-cpu.c | 2 ++ target/riscv/translate.c | 15 ++------------- 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ FIELD(TB_FLAGS, PM_SIGNEXTEND, 31, 1) FIELD(EXT_TB_FLAGS, MISA_EXT, 0, 32) FIELD(EXT_TB_FLAGS, ALTFMT, 32, 1) +FIELD(EXT_TB_FLAGS, BIG_ENDIAN, 33, 1) #ifdef TARGET_RISCV32 #define riscv_cpu_mxl(env) ((void)(env), MXL_RV32) diff --git a/target/riscv/internals.h b/target/riscv/internals.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -XXX,XX +XXX,XX @@ static inline bool mmuidx_2stage(int mmu_idx) return mmu_idx & MMU_2STAGE_BIT; } +/* + * Return the endianness for the current privilege + * level, based on the MSTATUS MBE/SBE/UBE bits. + */ static inline MemOp mo_endian_env(CPURISCVState *env) { - /* - * A couple of bits in MSTATUS set the endianness: - * - MSTATUS_UBE (User-mode), - * - MSTATUS_SBE (Supervisor-mode), - * - MSTATUS_MBE (Machine-mode) - * but we don't implement that yet. - */ - return MO_LE; + bool be = false; +#if !defined(CONFIG_USER_ONLY) + switch (env->priv) { + case PRV_M: + be = env->mstatus & MSTATUS_MBE; + break; + case PRV_S: + be = env->mstatus & MSTATUS_SBE; + break; + case PRV_U: + be = env->mstatus & MSTATUS_UBE; + break; + default: + g_assert_not_reached(); + } +#endif + return be ? MO_BE : MO_LE; } /* share data between vector helpers and decode code */ diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_disas_set_info(const CPUState *s, disassemble_info *info) info->target_info = &cpu->cfg; /* - * A couple of bits in MSTATUS set the endianness: - * - MSTATUS_UBE (User-mode), - * - MSTATUS_SBE (Supervisor-mode), - * - MSTATUS_MBE (Machine-mode) - * but we don't implement that yet. + * RISC-V instructions are always little-endian, regardless of the + * data endianness configured via MSTATUS UBE/SBE/MBE bits. */ info->endian = BFD_ENDIAN_LITTLE; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static TCGTBCPUState riscv_get_tb_cpu_state(CPUState *cs) flags = FIELD_DP32(flags, TB_FLAGS, PM_SIGNEXTEND, pm_signext); ext_flags = FIELD_DP64(ext_flags, EXT_TB_FLAGS, MISA_EXT, env->misa_ext); + ext_flags = FIELD_DP64(ext_flags, EXT_TB_FLAGS, BIG_ENDIAN, + mo_endian_env(env) == MO_BE); return (TCGTBCPUState){ .pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc, diff --git a/target/riscv/translate.c b/target/riscv/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -XXX,XX +XXX,XX @@ static inline bool has_ext(DisasContext *ctx, uint32_t ext) return ctx->misa_ext & ext; } -static inline MemOp mo_endian(DisasContext *ctx) -{ - /* - * A couple of bits in MSTATUS set the endianness: - * - MSTATUS_UBE (User-mode), - * - MSTATUS_SBE (Supervisor-mode), - * - MSTATUS_MBE (Machine-mode) - * but we don't implement that yet. - */ - return MO_LE; -} - #ifdef TARGET_RISCV32 #define get_xl(ctx) MXL_RV32 #elif defined(CONFIG_USER_ONLY) @@ -XXX,XX +XXX,XX @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->zero = tcg_constant_tl(0); ctx->virt_inst_excp = false; ctx->decoders = cpu->decoders; - ctx->mo_endianness = mo_endian(ctx); + ctx->mo_endianness = FIELD_EX64(ext_tb_flags, EXT_TB_FLAGS, BIG_ENDIAN) + ? MO_BE : MO_LE; } static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) -- 2.54.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> get_physical_address() is quite complex already. In order to make the two next commits simplers, de-indent one if() ladder. No logical change intended. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Message-ID: <20260527201348.29511-5-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_helper.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -XXX,XX +XXX,XX @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, hwaddr l = sxlen_bytes, addr1; mr = address_space_translate(cs->as, pte_addr, &addr1, &l, false, MEMTXATTRS_UNSPECIFIED); - if (memory_region_is_ram(mr)) { - target_ulong *pte_pa = qemu_map_ram_ptr(mr->ram_block, addr1); - target_ulong old_pte; - if (riscv_cpu_sxl(env) == MXL_RV32) { - old_pte = qatomic_cmpxchg((uint32_t *)pte_pa, cpu_to_le32(pte), cpu_to_le32(updated_pte)); - old_pte = le32_to_cpu(old_pte); - } else { - old_pte = qatomic_cmpxchg(pte_pa, cpu_to_le64(pte), cpu_to_le64(updated_pte)); - old_pte = le64_to_cpu(old_pte); - } - if (old_pte != pte) { - goto restart; - } - pte = updated_pte; - } else { + if (!memory_region_is_ram(mr)) { /* * Misconfigured PTE in ROM (AD bits are not preset) or * PTE is in IO space and can't be updated atomically. */ return TRANSLATE_FAIL; } + + target_ulong *pte_pa = qemu_map_ram_ptr(mr->ram_block, addr1); + target_ulong old_pte; + + if (riscv_cpu_sxl(env) == MXL_RV32) { + old_pte = qatomic_cmpxchg((uint32_t *)pte_pa, cpu_to_le32(pte), + cpu_to_le32(updated_pte)); + old_pte = le32_to_cpu(old_pte); + } else { + old_pte = qatomic_cmpxchg(pte_pa, cpu_to_le64(pte), + cpu_to_le64(updated_pte)); + old_pte = le64_to_cpu(old_pte); + } + if (old_pte != pte) { + goto restart; + } + pte = updated_pte; } /* For superpage mappings, make a fake leaf PTE for the TLB's benefit. */ -- 2.54.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Use uint32_t for RV32, uint64_t otherwise. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Message-ID: <20260527201348.29511-6-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_helper.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -XXX,XX +XXX,XX @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, return TRANSLATE_FAIL; } - target_ulong *pte_pa = qemu_map_ram_ptr(mr->ram_block, addr1); - target_ulong old_pte; + void *pte_pa = qemu_map_ram_ptr(mr->ram_block, addr1); + uint64_t old_pte; if (riscv_cpu_sxl(env) == MXL_RV32) { - old_pte = qatomic_cmpxchg((uint32_t *)pte_pa, cpu_to_le32(pte), - cpu_to_le32(updated_pte)); + uint32_t cmp = cpu_to_le32(pte); + uint32_t val = cpu_to_le32(updated_pte); + old_pte = qatomic_cmpxchg((uint32_t *)pte_pa, cmp, val); old_pte = le32_to_cpu(old_pte); } else { - old_pte = qatomic_cmpxchg(pte_pa, cpu_to_le64(pte), - cpu_to_le64(updated_pte)); + uint64_t cmp = cpu_to_le64(pte); + uint64_t val = cpu_to_le64(updated_pte); + old_pte = qatomic_cmpxchg((uint64_t *)pte_pa, cmp, val); old_pte = le64_to_cpu(old_pte); } if (old_pte != pte) { -- 2.54.0
From: Djordje Todorovic <Djordje.Todorovic@htecgroup.com> The page table walker reads PTEs using address_space_ldl/ldq which use compile-time native endianness (always LE for RISC-V). However, when a big-endian kernel writes PTEs via normal store instructions, they are stored in big-endian byte order. The walker then misinterprets the PTE values, causing page faults and a hang when the kernel enables the MMU. The RISC-V privileged specification states that implicit data memory accesses to supervisor-level memory management data structures follow the hart's endianness setting (MSTATUS SBE/MBE bits). Fix both PTE reads and atomic A/D bit updates to use the explicit _le or _be memory access variants based on the hart's runtime endianness. Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Message-ID: <20260527201348.29511-7-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_helper.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -XXX,XX +XXX,XX @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, target_ulong pte; hwaddr pte_addr; const hwaddr base_root = base; + const bool be = mo_endian_env(env) == MO_BE; int i; restart: @@ -XXX,XX +XXX,XX @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, } if (riscv_cpu_mxl(env) == MXL_RV32) { - pte = address_space_ldl_le(cs->as, pte_addr, attrs, &res); + pte = be ? address_space_ldl_be(cs->as, pte_addr, attrs, &res) + : address_space_ldl_le(cs->as, pte_addr, attrs, &res); } else { - pte = address_space_ldq_le(cs->as, pte_addr, attrs, &res); + pte = be ? address_space_ldq_be(cs->as, pte_addr, attrs, &res) + : address_space_ldq_le(cs->as, pte_addr, attrs, &res); } if (res != MEMTX_OK) { @@ -XXX,XX +XXX,XX @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, uint64_t old_pte; if (riscv_cpu_sxl(env) == MXL_RV32) { - uint32_t cmp = cpu_to_le32(pte); - uint32_t val = cpu_to_le32(updated_pte); + uint32_t cmp = be ? cpu_to_be32(pte) : cpu_to_le32(pte); + uint32_t val = be ? cpu_to_be32(updated_pte) : cpu_to_le32(updated_pte); old_pte = qatomic_cmpxchg((uint32_t *)pte_pa, cmp, val); - old_pte = le32_to_cpu(old_pte); + old_pte = be ? be32_to_cpu(old_pte) : le32_to_cpu(old_pte); } else { - uint64_t cmp = cpu_to_le64(pte); - uint64_t val = cpu_to_le64(updated_pte); + uint64_t cmp = be ? cpu_to_be64(pte) : cpu_to_le64(pte); + uint64_t val = be ? cpu_to_be64(updated_pte) : cpu_to_le64(updated_pte); old_pte = qatomic_cmpxchg((uint64_t *)pte_pa, cmp, val); - old_pte = le64_to_cpu(old_pte); + old_pte = be ? be64_to_cpu(old_pte) : le64_to_cpu(old_pte); } if (old_pte != pte) { goto restart; -- 2.54.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> In order to make the following commits easier to review, do not pre-initialize the reset_vec[] array, fill each word one by one. Set the start and FDT load addresses using the load/ store APIs. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Message-ID: <20260527201348.29511-8-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/boot.c | 55 ++++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -XXX,XX +XXX,XX @@ */ #include "qemu/osdep.h" +#include "qemu/bswap.h" #include "qemu/datadir.h" #include "qemu/units.h" #include "qemu/error-report.h" @@ -XXX,XX +XXX,XX @@ void riscv_rom_copy_firmware_info(MachineState *machine, &address_space_memory); } +#define CODE_WORDS 6 +#define DATA_WORDS 4 + void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts, hwaddr start_addr, hwaddr rom_base, hwaddr rom_size, @@ -XXX,XX +XXX,XX @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts uint64_t fdt_load_addr) { int i; - uint32_t start_addr_hi32 = 0x00000000; - uint32_t fdt_load_addr_hi32 = 0x00000000; - - if (!riscv_is_32bit(harts)) { - start_addr_hi32 = start_addr >> 32; - fdt_load_addr_hi32 = fdt_load_addr >> 32; - } - /* reset vector */ - uint32_t reset_vec[10] = { - 0x00000297, /* 1: auipc t0, %pcrel_hi(fw_dyn) */ - 0x02828613, /* addi a2, t0, %pcrel_lo(1b) */ - 0xf1402573, /* csrr a0, mhartid */ - 0, - 0, - 0x00028067, /* jr t0 */ - start_addr, /* start: .dword */ - start_addr_hi32, - fdt_load_addr, /* fdt_laddr: .dword */ - fdt_load_addr_hi32, - /* fw_dyn: */ - }; - if (riscv_is_32bit(harts)) { - reset_vec[3] = 0x0202a583; /* lw a1, 32(t0) */ - reset_vec[4] = 0x0182a283; /* lw t0, 24(t0) */ + const bool rv32 = riscv_is_32bit(harts); + uint32_t reset_vec[CODE_WORDS + DATA_WORDS]; + + /* .text */ + reset_vec[0] = 0x00000297; /* 1: auipc t0, %pcrel_hi(fw_dyn) */ + reset_vec[1] = 0x02828613; /* addi a2, t0, %pcrel_lo(1b) */ + if (harts->harts[0].cfg.ext_zicsr) { + reset_vec[2] = 0xf1402573; /* csrr a0, mhartid */ } else { - reset_vec[3] = 0x0202b583; /* ld a1, 32(t0) */ - reset_vec[4] = 0x0182b283; /* ld t0, 24(t0) */ - } - - if (!harts->harts[0].cfg.ext_zicsr) { /* * The Zicsr extension has been disabled, so let's ensure we don't * run the CSR instruction. Let's fill the address with a non * compressed nop. */ - reset_vec[2] = 0x00000013; /* addi x0, x0, 0 */ + reset_vec[2] = 0x00000013; /* addi x0, x0, 0 */ + } + if (rv32) { + reset_vec[3] = 0x0202a583; /* lw a1, 32(t0) */ + reset_vec[4] = 0x0182a283; /* lw t0, 24(t0) */ + } else { + reset_vec[3] = 0x0202b583; /* ld a1, 32(t0) */ + reset_vec[4] = 0x0182b283; /* ld t0, 24(t0) */ } + reset_vec[5] = 0x00028067; /* jr t0 */ + + /* .data */ + stq_p(&reset_vec[6], start_addr); /* start: .dword */ + stq_p(&reset_vec[8], fdt_load_addr); /* fdt_laddr: .dword */ /* copy in the reset vector in little_endian byte order */ for (i = 0; i < ARRAY_SIZE(reset_vec); i++) { -- 2.54.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Rather than adapting the array endianness when it it filled, directly initialize the CODE words with the correct endianness. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Message-ID: <20260527201348.29511-9-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/boot.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -XXX,XX +XXX,XX @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts uint64_t kernel_entry, uint64_t fdt_load_addr) { - int i; const bool rv32 = riscv_is_32bit(harts); uint32_t reset_vec[CODE_WORDS + DATA_WORDS]; - /* .text */ - reset_vec[0] = 0x00000297; /* 1: auipc t0, %pcrel_hi(fw_dyn) */ - reset_vec[1] = 0x02828613; /* addi a2, t0, %pcrel_lo(1b) */ + /* .text (RISC-V instructions are always little-endian) */ + reset_vec[0] = const_le32(0x00000297); /* 1: auipc t0, %pcrel_hi(fw_dyn) */ + reset_vec[1] = const_le32(0x02828613); /* addi a2, t0, %pcrel_lo(1b) */ + reset_vec[2] = const_le32(0xf1402573); /* csrr a0, mhartid */ if (harts->harts[0].cfg.ext_zicsr) { - reset_vec[2] = 0xf1402573; /* csrr a0, mhartid */ + reset_vec[2] = const_le32(0xf1402573); /* csrr a0, mhartid */ } else { /* * The Zicsr extension has been disabled, so let's ensure we don't * run the CSR instruction. Let's fill the address with a non * compressed nop. */ - reset_vec[2] = 0x00000013; /* addi x0, x0, 0 */ + reset_vec[2] = const_le32(0x00000013); /* addi x0, x0, 0 */ } if (rv32) { - reset_vec[3] = 0x0202a583; /* lw a1, 32(t0) */ - reset_vec[4] = 0x0182a283; /* lw t0, 24(t0) */ + reset_vec[3] = const_le32(0x0202a583); /* lw a1, 32(t0) */ + reset_vec[4] = const_le32(0x0182a283); /* lw t0, 24(t0) */ } else { - reset_vec[3] = 0x0202b583; /* ld a1, 32(t0) */ - reset_vec[4] = 0x0182b283; /* ld t0, 24(t0) */ + reset_vec[3] = const_le32(0x0202b583); /* ld a1, 32(t0) */ + reset_vec[4] = const_le32(0x0182b283); /* ld t0, 24(t0) */ } - reset_vec[5] = 0x00028067; /* jr t0 */ + reset_vec[5] = const_le32(0x00028067); /* jr t0 */ /* .data */ - stq_p(&reset_vec[6], start_addr); /* start: .dword */ - stq_p(&reset_vec[8], fdt_load_addr); /* fdt_laddr: .dword */ + stq_le_p(&reset_vec[6], start_addr); /* start: .dword */ + stq_le_p(&reset_vec[8], fdt_load_addr); /* fdt_laddr: .dword */ - /* copy in the reset vector in little_endian byte order */ - for (i = 0; i < ARRAY_SIZE(reset_vec); i++) { - reset_vec[i] = cpu_to_le32(reset_vec[i]); - } rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), rom_base, &address_space_memory); riscv_rom_copy_firmware_info(machine, harts, -- 2.54.0
From: Djordje Todorovic <Djordje.Todorovic@htecgroup.com> Add a big_endian field to RISCVCPUConfig and wire it into the CPU reset path. When cfg.big_endian is set, riscv_cpu_reset_hold() writes 1 into the MSTATUS MBE/SBE/UBE fields using set_field(); otherwise it writes 0. This makes the reset value deterministic on both cold and warm reset. This models fixed-endian harts, not mixed-endian implementations where the guest can toggle MBE/SBE/UBE at runtime. The MBE/SBE/UBE bits are not included in the writable mask of any mstatus/mstatush/sstatus CSR write path (unchanged by this series), so the value chosen at reset is effectively hardwired per section 3.1.6.5 of the RISC-V Privileged Specification. The user-facing property and documentation are added in a later patch, once the full endianness support is in place. Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Message-ID: <20260527201348.29511-10-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_cfg_fields.h.inc | 1 + target/riscv/cpu.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -XXX,XX +XXX,XX @@ BOOL_FIELD(ext_xmipscmov) BOOL_FIELD(ext_xmipslsp) BOOL_FIELD(ext_xlrbr) +BOOL_FIELD(big_endian) BOOL_FIELD(mmu) BOOL_FIELD(pmp) BOOL_FIELD(debug) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type) env->mstatus = set_field(env->mstatus, MSTATUS_MDT, 1); } } + /* + * Model fixed-endian harts: MBE/SBE/UBE are initialized from the + * CPU configuration and are intentionally not writable via status CSRs. + */ + env->mstatus = set_field(env->mstatus, MSTATUS_MBE, cpu->cfg.big_endian); + env->mstatus = set_field(env->mstatus, MSTATUS_SBE, cpu->cfg.big_endian); + env->mstatus = set_field(env->mstatus, MSTATUS_UBE, cpu->cfg.big_endian); env->mcause = 0; env->miclaim = MIP_SGEIP; env->pc = env->resetvec; -- 2.54.0
From: Djordje Todorovic <Djordje.Todorovic@htecgroup.com> Check the hart endianness property and use it throughout the boot code: - ELF loading: pass ELFDATA2MSB or ELFDATA2LSB based on endianness - Firmware dynamic info - Reset vector: instructions (entries 0-5) remain always little-endian, data words (entries 6-9) use target data endianness. Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com> Co-developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Message-ID: <20260527201348.29511-11-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/boot.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -XXX,XX +XXX,XX @@ void riscv_rom_copy_firmware_info(MachineState *machine, struct fw_dynamic_info64 dinfo64; void *dinfo_ptr = NULL; size_t dinfo_len; + const bool rv32 = riscv_is_32bit(harts); + const bool be = harts->harts[0].cfg.big_endian; - if (riscv_is_32bit(harts)) { - dinfo32.magic = cpu_to_le32(FW_DYNAMIC_INFO_MAGIC_VALUE); - dinfo32.version = cpu_to_le32(FW_DYNAMIC_INFO_VERSION); - dinfo32.next_mode = cpu_to_le32(FW_DYNAMIC_INFO_NEXT_MODE_S); - dinfo32.next_addr = cpu_to_le32(kernel_entry); + if (rv32) { + dinfo32.magic = be ? cpu_to_be32(FW_DYNAMIC_INFO_MAGIC_VALUE) + : cpu_to_le32(FW_DYNAMIC_INFO_MAGIC_VALUE); + dinfo32.version = be ? cpu_to_be32(FW_DYNAMIC_INFO_VERSION) + : cpu_to_le32(FW_DYNAMIC_INFO_VERSION); + dinfo32.next_mode = be ? cpu_to_be32(FW_DYNAMIC_INFO_NEXT_MODE_S) + : cpu_to_le32(FW_DYNAMIC_INFO_NEXT_MODE_S); + dinfo32.next_addr = be ? cpu_to_be32(kernel_entry) + : cpu_to_le32(kernel_entry); dinfo32.options = 0; dinfo32.boot_hart = 0; dinfo_ptr = &dinfo32; dinfo_len = sizeof(dinfo32); } else { - dinfo64.magic = cpu_to_le64(FW_DYNAMIC_INFO_MAGIC_VALUE); - dinfo64.version = cpu_to_le64(FW_DYNAMIC_INFO_VERSION); - dinfo64.next_mode = cpu_to_le64(FW_DYNAMIC_INFO_NEXT_MODE_S); - dinfo64.next_addr = cpu_to_le64(kernel_entry); + dinfo64.magic = be ? cpu_to_be64(FW_DYNAMIC_INFO_MAGIC_VALUE) + : cpu_to_le64(FW_DYNAMIC_INFO_MAGIC_VALUE); + dinfo64.version = be ? cpu_to_be64(FW_DYNAMIC_INFO_VERSION) + : cpu_to_le64(FW_DYNAMIC_INFO_VERSION); + dinfo64.next_mode = be ? cpu_to_be64(FW_DYNAMIC_INFO_NEXT_MODE_S) + : cpu_to_le64(FW_DYNAMIC_INFO_NEXT_MODE_S); + dinfo64.next_addr = be ? cpu_to_be64(kernel_entry) + : cpu_to_le64(kernel_entry); dinfo64.options = 0; dinfo64.boot_hart = 0; dinfo_ptr = &dinfo64; @@ -XXX,XX +XXX,XX @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts uint64_t fdt_load_addr) { const bool rv32 = riscv_is_32bit(harts); + const bool big_endian = harts->harts[0].cfg.big_endian; uint32_t reset_vec[CODE_WORDS + DATA_WORDS]; /* .text (RISC-V instructions are always little-endian) */ @@ -XXX,XX +XXX,XX @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts } reset_vec[5] = const_le32(0x00028067); /* jr t0 */ - /* .data */ - stq_le_p(&reset_vec[6], start_addr); /* start: .dword */ - stq_le_p(&reset_vec[8], fdt_load_addr); /* fdt_laddr: .dword */ + /* .data (must match the firmware's data endianness) */ + if (big_endian) { + stq_be_p(&reset_vec[6], start_addr); /* start: .dword */ + stq_be_p(&reset_vec[8], fdt_load_addr); /* fdt_laddr: .dword */ + } else { + stq_le_p(&reset_vec[6], start_addr); + stq_le_p(&reset_vec[8], fdt_load_addr); + } rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), rom_base, &address_space_memory); -- 2.54.0
From: Djordje Todorovic <Djordje.Todorovic@htecgroup.com> Now that the full big-endian data path is in place (runtime MSTATUS bits, boot code, and page-table walks), expose the "big-endian" property to users via DEFINE_PROP_BOOL and document it in docs/system/target-riscv.rst. Document that the property models fixed-endian hardware: it selects harts whose MBE/SBE/UBE fields are fixed to 1, and it does not model a mixed-endian implementation where software can toggle those bits at runtime. The property can be enabled from the command line, e.g.: -cpu <cpu>,big-endian=on Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Message-ID: <20260527201348.29511-12-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- docs/system/target-riscv.rst | 29 +++++++++++++++++++++++++++++ target/riscv/cpu.c | 1 + 2 files changed, 30 insertions(+) diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/target-riscv.rst +++ b/docs/system/target-riscv.rst @@ -XXX,XX +XXX,XX @@ the images they need. * ``-bios <file>`` Tells QEMU to load the specified file as the firmware. + +RISC-V CPU endianness +--------------------- + +The RISC-V ISA specifies that instruction fetches are always little-endian, +while data accesses can be either little-endian or big-endian under control +of the MSTATUS ``MBE``/``SBE``/``UBE`` bits (see section 3.1.6.5, "Memory +Endianness", in the RISC-V Privileged Specification). + +QEMU implements the full data-endianness behaviour described by those bits. +In addition, the RISC-V CPU object exposes a ``big-endian`` boolean property +which models a big-endian-only hardware implementation, where the +``MBE``/``SBE``/``UBE`` bits are hardwired to 1. When the property is set, +the CPU is reset with all three bits initialised to 1, so the guest starts +executing in big-endian data mode from the reset vector. The property is a +static, per-CPU hardware configuration option and is not meant to be toggled +at runtime. + +The property does not model a mixed-endian implementation where software can +toggle ``MBE``/``SBE``/``UBE`` at runtime. QEMU's RISC-V CPUs treat these +fields as fixed by the CPU configuration: they are reset to 0 by default and +to 1 when ``big-endian`` is enabled. + +The property can be enabled from the command line, for example:: + + -cpu <cpu>,big-endian=on + +No upstream CPU model currently defaults to big-endian; the property is +provided so that big-endian-only RISC-V CPU variants can be modelled. diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = { static const Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true), + DEFINE_PROP_BOOL("big-endian", RISCVCPU, cfg.big_endian, false), {.name = "pmu-mask", .info = &prop_pmu_mask}, {.name = "pmu-num", .info = &prop_pmu_num}, /* Deprecated */ -- 2.54.0
From: Djordje Todorovic <Djordje.Todorovic@htecgroup.com> Add functional test for the RISC-V 'big-endian' CPU property. Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Message-ID: <20260527201348.29511-13-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- tests/functional/riscv64/meson.build | 1 + tests/functional/riscv64/test_endianness.py | 57 +++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 tests/functional/riscv64/test_endianness.py diff --git a/tests/functional/riscv64/meson.build b/tests/functional/riscv64/meson.build index XXXXXXX..XXXXXXX 100644 --- a/tests/functional/riscv64/meson.build +++ b/tests/functional/riscv64/meson.build @@ -XXX,XX +XXX,XX @@ tests_riscv64_system_quick = [ ] tests_riscv64_system_thorough = [ + 'endianness', 'boston', 'sifive_u', 'tuxrun', diff --git a/tests/functional/riscv64/test_endianness.py b/tests/functional/riscv64/test_endianness.py new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/functional/riscv64/test_endianness.py @@ -XXX,XX +XXX,XX @@ +#!/usr/bin/env python3 +# +# Functional tests for RISC-V big-endian support +# +# Copyright (c) 2026 MIPS +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern + + +class RiscvBigEndian(QemuSystemTest): + """ + Tests for RISC-V runtime big-endian data support. + + Uses a bare-metal RV64 ELF that detects data endianness at runtime + by storing a 32-bit word and reading back byte 0. Prints "ENDIAN: BE" + or "ENDIAN: LE" to the NS16550A UART on the virt machine. + """ + + timeout = 10 + + ASSET_BE_TEST = Asset( + 'https://github.com/MIPS/linux-test-downloads/raw/main/' + 'riscvbe-baremetal/be-test-bare-metal.elf', + '9ad51b675e101de65908fadbac064ed1d0564c17463715d09dd734db86ea0f58') + + def _run_bare_metal(self, big_endian=False): + self.set_machine('virt') + kernel = self.ASSET_BE_TEST.fetch() + self.vm.add_args('-bios', 'none') + self.vm.add_args('-kernel', kernel) + if big_endian: + self.vm.add_args('-cpu', 'rv64,big-endian=on') + self.vm.set_console() + self.vm.launch() + expected = 'ENDIAN: BE' if big_endian else 'ENDIAN: LE' + wait_for_console_pattern(self, expected) + + def test_bare_metal_littleendian(self): + """ + Boot bare-metal ELF on virt with default little-endian CPU. + Expects "ENDIAN: LE" on UART. + """ + self._run_bare_metal(big_endian=False) + + def test_bare_metal_bigendian(self): + """ + Boot bare-metal ELF on virt with big-endian=on CPU property. + Expects "ENDIAN: BE" on UART. + """ + self._run_bare_metal(big_endian=True) + + +if __name__ == '__main__': + QemuSystemTest.main() -- 2.54.0
From: Jim Shu <jim.shu@sifive.com> Add the missing implied rule from G to imafd_zicsr_zifencei. We can also remove the auto-enables in riscv_cpu_validate_g() as IMAFD, Zicsr, Zifencei extensions can be enabled by the implied rule. Signed-off-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260528054213.678458-2-frank.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 14 +++++++++++++- target/riscv/tcg/tcg-cpu.c | 21 ++++----------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static RISCVCPUImpliedExtsRule RVV_IMPLIED = { }, }; +static RISCVCPUImpliedExtsRule RVG_IMPLIED = { + .is_misa = true, + .ext = RVG, + .implied_misa_exts = RVI | RVM | RVA | RVF | RVD, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zicsr), + CPU_CFG_OFFSET(ext_zifencei), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + static RISCVCPUImpliedExtsRule ZCB_IMPLIED = { .ext = CPU_CFG_OFFSET(ext_zcb), .implied_multi_exts = { @@ -XXX,XX +XXX,XX @@ static RISCVCPUImpliedExtsRule ZVFBFA_IMPLIED = { RISCVCPUImpliedExtsRule *riscv_misa_ext_implied_rules[] = { &RVA_IMPLIED, &RVD_IMPLIED, &RVF_IMPLIED, - &RVM_IMPLIED, &RVV_IMPLIED, NULL + &RVM_IMPLIED, &RVV_IMPLIED, &RVG_IMPLIED, NULL }; RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = { diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_validate_g(RISCVCPU *cpu) continue; } - if (!cpu_misa_ext_is_user_set(bit)) { - riscv_cpu_write_misa_bit(cpu, bit, true); - continue; - } - if (send_warn) { warn_report(warn_msg, riscv_get_misa_ext_name(bit)); } } - if (!cpu->cfg.ext_zicsr) { - if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zicsr))) { - cpu->cfg.ext_zicsr = true; - } else if (send_warn) { - warn_report(warn_msg, "zicsr"); - } + if (!cpu->cfg.ext_zicsr && send_warn) { + warn_report(warn_msg, "zicsr"); } - if (!cpu->cfg.ext_zifencei) { - if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zifencei))) { - cpu->cfg.ext_zifencei = true; - } else if (send_warn) { - warn_report(warn_msg, "zifencei"); - } + if (!cpu->cfg.ext_zifencei && send_warn) { + warn_report(warn_msg, "zifencei"); } } -- 2.54.0
From: Frank Chang <frank.chang@sifive.com> Add the missing implied rule for standard B extension. Standard B extension implies Zba, Zbb, Zbs extensions. We can also remove the auto-enables in riscv_cpu_validate_b() as Zba, Zbb, Zbs extensions can be enabled by the implied rule. RISC-V B spec: https://github.com/riscv/riscv-b Reviewed-by: Jerry Zhang Jian <jerry.zhangjian@sifive.com> Reviewed-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Signed-off-by: Frank Chang <frank.chang@sifive.com> Signed-off-by: Frank Chang <frank.chang@sifive.com> Message-ID: <20260528054213.678458-3-frank.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 14 +++++++++++++- target/riscv/tcg/tcg-cpu.c | 18 +++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static RISCVCPUImpliedExtsRule RVG_IMPLIED = { }, }; +static RISCVCPUImpliedExtsRule RVB_IMPLIED = { + .is_misa = true, + .ext = RVB, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zba), CPU_CFG_OFFSET(ext_zbb), + CPU_CFG_OFFSET(ext_zbs), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + static RISCVCPUImpliedExtsRule ZCB_IMPLIED = { .ext = CPU_CFG_OFFSET(ext_zcb), .implied_multi_exts = { @@ -XXX,XX +XXX,XX @@ static RISCVCPUImpliedExtsRule ZVFBFA_IMPLIED = { RISCVCPUImpliedExtsRule *riscv_misa_ext_implied_rules[] = { &RVA_IMPLIED, &RVD_IMPLIED, &RVF_IMPLIED, - &RVM_IMPLIED, &RVV_IMPLIED, &RVG_IMPLIED, NULL + &RVM_IMPLIED, &RVV_IMPLIED, &RVG_IMPLIED, + &RVB_IMPLIED, NULL }; RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = { diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_validate_b(RISCVCPU *cpu) const char *warn_msg = "RVB mandates disabled extension %s"; if (!cpu->cfg.ext_zba) { - if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zba))) { - cpu->cfg.ext_zba = true; - } else { - warn_report(warn_msg, "zba"); - } + warn_report(warn_msg, "zba"); } if (!cpu->cfg.ext_zbb) { - if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zbb))) { - cpu->cfg.ext_zbb = true; - } else { - warn_report(warn_msg, "zbb"); - } + warn_report(warn_msg, "zbb"); } if (!cpu->cfg.ext_zbs) { - if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zbs))) { - cpu->cfg.ext_zbs = true; - } else { - warn_report(warn_msg, "zbs"); - } + warn_report(warn_msg, "zbs"); } } -- 2.54.0
From: Anton Blanchard <antonb@tenstorrent.com> The privilege level and ELP are implicit state (like virt), so print them out. Signed-off-by: Anton Blanchard <antonb@tenstorrent.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260529053519.1224019-1-antonb@tenstorrent.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_dump_csr(CPURISCVState *env, int csrno, FILE *f) } } +#if !defined(CONFIG_USER_ONLY) +static const char *riscv_priv_str(uint32_t priv) +{ + switch (priv) { + case PRV_M: + return "M"; + case PRV_S: + return "S"; + case PRV_U: + return "U"; + } + + return "?"; +} +#endif + static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) { RISCVCPU *cpu = RISCV_CPU(cs); @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) uint8_t *p; #if !defined(CONFIG_USER_ONLY) + qemu_fprintf(f, " %s %s\n", "priv = ", riscv_priv_str(env->priv)); + if (riscv_has_ext(env, RVH)) { qemu_fprintf(f, " %s %d\n", "V = ", env->virt_enabled); } + + if (cpu->cfg.ext_zicfilp) { + qemu_fprintf(f, " %s %d\n", "elp = ", env->elp); + } #endif qemu_fprintf(f, " %s %" PRIx64 "\n", "pc ", env->pc); #ifndef CONFIG_USER_ONLY -- 2.54.0
From: Anton Blanchard <antonb@tenstorrent.com> Align all the CSR values. mcountinhibit is the longest CSR name at 13 characters, so use that width for PC, implicit state, and CSR names. Also remove the redundant '=' from the implicit state lines.t Signed-off-by: Anton Blanchard <antonb@tenstorrent.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260529053519.1224019-2-antonb@tenstorrent.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_dump_csr(CPURISCVState *env, int csrno, FILE *f) * to do the filtering of the registers that are present. */ if (res == RISCV_EXCP_NONE) { - qemu_fprintf(f, " %-8s " TARGET_FMT_lx "\n", + qemu_fprintf(f, " %-13s " TARGET_FMT_lx "\n", csr_ops[csrno].name, val); } } @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) uint8_t *p; #if !defined(CONFIG_USER_ONLY) - qemu_fprintf(f, " %s %s\n", "priv = ", riscv_priv_str(env->priv)); + qemu_fprintf(f, " %-13s %s\n", "priv", riscv_priv_str(env->priv)); if (riscv_has_ext(env, RVH)) { - qemu_fprintf(f, " %s %d\n", "V = ", env->virt_enabled); + qemu_fprintf(f, " %-13s %d\n", "V", env->virt_enabled); } if (cpu->cfg.ext_zicfilp) { - qemu_fprintf(f, " %s %d\n", "elp = ", env->elp); + qemu_fprintf(f, " %-13s %d\n", "elp", env->elp); } #endif - qemu_fprintf(f, " %s %" PRIx64 "\n", "pc ", env->pc); + qemu_fprintf(f, " %-13s %" PRIx64 "\n", "pc", env->pc); #ifndef CONFIG_USER_ONLY for (i = 0; i < ARRAY_SIZE(csr_ops); i++) { int csrno = i; -- 2.54.0
From: Abhigyan Kumar <314abh@gmail.com> Citing the RISC-V specification: "The vector fixed-point rounding-mode register holds a two-bit read-write rounding-mode field in the least-significant bits (vxrm[1:0]). The upper bits, vxrm[XLEN-1:2], should be written as zeros." QEMU wrote full value into env->vxrm causing read of upper bits too. Used existing macros for bit-masking. Previous had a hard-coded value. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3470 Signed-off-by: Abhigyan Kumar <314abh@gmail.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260530102100.78150-1-314abh@gmail.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -XXX,XX +XXX,XX @@ static RISCVException write_vxrm(CPURISCVState *env, int csrno, #if !defined(CONFIG_USER_ONLY) env->mstatus |= MSTATUS_VS; #endif - env->vxrm = val; + env->vxrm = val & (VCSR_VXRM >> VCSR_VXRM_SHIFT); return RISCV_EXCP_NONE; } -- 2.54.0
From: Frank Chang <frank.chang@sifive.com> Smrnmi CPU fields introduced by commit: #5db557f should be reset when the CPU resets, so move these fields above the CPU reset line. Fixes: 5db557f82bff ("target/riscv: Add Smrnmi CSRs") Signed-off-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260528060007.717307-1-frank.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ struct CPUArchState { uint64_t hstateen[SMSTATEEN_MAX_COUNT]; uint64_t sstateen[SMSTATEEN_MAX_COUNT]; uint64_t henvcfg; + + /* RNMI */ + uint64_t mnscratch; + uint64_t mnepc; + uint64_t mncause; /* mncause without bit XLEN-1 set to 1 */ + uint64_t mnstatus; + uint64_t rnmip; + uint64_t rnmi_irqvec; + uint64_t rnmi_excpvec; #endif /* Fields from here on are preserved across CPU reset. */ @@ -XXX,XX +XXX,XX @@ struct CPUArchState { uint64_t kvm_timer_state; uint64_t kvm_timer_frequency; #endif /* CONFIG_KVM */ - - /* RNMI */ - uint64_t mnscratch; - uint64_t mnepc; - uint64_t mncause; /* mncause without bit XLEN-1 set to 1 */ - uint64_t mnstatus; - uint64_t rnmip; - uint64_t rnmi_irqvec; - uint64_t rnmi_excpvec; }; /* -- 2.54.0
From: Xuemei Liu <liu.xuemei1@zte.com.cn> Supplement RISC-V cpu topology arguments, including support socket cluster and threads per core. Signed-off-by: Xuemei Liu <liu.xuemei1@zte.com.cn> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260529181848378wiq8pXCmbwAZR5_-wZFJd@zte.com.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/numa.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/riscv/numa.c b/hw/riscv/numa.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/numa.c +++ b/hw/riscv/numa.c @@ -XXX,XX +XXX,XX @@ const CPUArchIdList *riscv_numa_possible_cpu_arch_ids(MachineState *ms) for (n = 0; n < ms->possible_cpus->len; n++) { ms->possible_cpus->cpus[n].type = ms->cpu_type; ms->possible_cpus->cpus[n].arch_id = n; + ms->possible_cpus->cpus[n].props.has_socket_id = true; + ms->possible_cpus->cpus[n].props.socket_id = + n / (ms->smp.clusters * ms->smp.cores * ms->smp.threads); + ms->possible_cpus->cpus[n].props.has_cluster_id = true; + ms->possible_cpus->cpus[n].props.cluster_id = + (n / (ms->smp.cores * ms->smp.threads)) % ms->smp.clusters; ms->possible_cpus->cpus[n].props.has_core_id = true; - ms->possible_cpus->cpus[n].props.core_id = n; + ms->possible_cpus->cpus[n].props.core_id = + (n / ms->smp.threads) % ms->smp.cores; + ms->possible_cpus->cpus[n].props.has_thread_id = true; + ms->possible_cpus->cpus[n].props.thread_id = + n % ms->smp.threads; } return ms->possible_cpus; -- 2.54.0
From: Jay Chang <jay.chang@sifive.com> In Bare mode the IOMMU does not perform DDT look-ups, therefore caching DDT entries is unnecessary. Signed-off-by: Jay Chang <jay.chang@sifive.com> Reviewed-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Message-ID: <20260518072239.16293-2-jay.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -XXX,XX +XXX,XX @@ static RISCVIOMMUContext *riscv_iommu_ctx(RISCVIOMMUState *s, int fault = riscv_iommu_ctx_fetch(s, ctx); if (!fault) { - if (g_hash_table_size(ctx_cache) >= LIMIT_CACHE_CTX) { + if (mode != RISCV_IOMMU_DDTP_MODE_BARE) { + if (g_hash_table_size(ctx_cache) >= LIMIT_CACHE_CTX) { + g_hash_table_unref(ctx_cache); + ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash, + riscv_iommu_ctx_equal, + g_free, NULL); + g_hash_table_ref(ctx_cache); + g_hash_table_unref(qatomic_xchg(&s->ctx_cache, ctx_cache)); + } + + g_hash_table_add(ctx_cache, ctx); + *ref = ctx_cache; + } else { g_hash_table_unref(ctx_cache); - ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash, - riscv_iommu_ctx_equal, - g_free, NULL); - g_hash_table_ref(ctx_cache); - g_hash_table_unref(qatomic_xchg(&s->ctx_cache, ctx_cache)); + /* Remember ctx so it can be freed */ + *ref = ctx; } - g_hash_table_add(ctx_cache, ctx); - *ref = ctx_cache; return ctx; } -- 2.54.0
From: Jay Chang <jay.chang@sifive.com> Align SPEC: Bare mode contexts are not cached, so they require direct memory deallocation via g_free instead of hash table cleanup. Signed-off-by: Jay Chang <jay.chang@sifive.com> Reviewed-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Message-ID: <20260518072239.16293-3-jay.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -XXX,XX +XXX,XX @@ static RISCVIOMMUContext *riscv_iommu_ctx(RISCVIOMMUState *s, static void riscv_iommu_ctx_put(RISCVIOMMUState *s, void *ref) { - if (ref) { + unsigned mode = get_field(s->ddtp, RISCV_IOMMU_DDTP_MODE); + + if (!ref) { + return; + } + + /* ref is pointing to ctx in Bare mode. Bare mode ctx is not cached */ + if (mode == RISCV_IOMMU_DDTP_MODE_BARE) { + g_free(ref); + } else { g_hash_table_unref((GHashTable *)ref); } } -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Based on the DT documentation of 'iommu-map': https://www.kernel.org/doc/Documentation/devicetree/bindings/pci/pci-iommu.txt - iommu-map: Maps a Requester ID to an IOMMU and associated IOMMU specifier data. The property is an arbitrary number of tuples of (rid-base,iommu,iommu-base,length). Reviewed-by: Alistair Francis <alistair.francis@wdc.com> ---------- We're adding a no-op entry (length = 0) in iommu-map: qemu_fdt_setprop_cells(ms->fdt, name, "iommu-map", 0, iommu_sys_phandle, 0, 0, 0, iommu_sys_phandle, 0, 0xffff); This is easily seen in the generated DT: iommu-map = <0x00 0x8000 0x00 0x00 0x00 0x8000 0x00 0xffff>; The tuple (0 0 0x8000 0) does nothing since it has length = 0. The information we want to advertise is in the second tuple only. Thus remove the empty tuple. While we're at it, seems like we've mistaken the API and we're using 0xffff as 'last address', but in fact it is length. This means that we're telling the DT we're mapping 0x0 -> 0xfffe, which wasn't our intention. Therefore change size to '0x10000' to reflect the address mapping we want (0x0 -> 0xffff). Found while reviewing the RISC-V Server Platform DT generation, which happens to copy a lot of code from the 'virt' board, and this nit is also present there. Fixes: 2c12de1460 ("hw/riscv/virt: Add IOMMU as platform device if the option is set") Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260608210642.464131-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -XXX,XX +XXX,XX @@ static void create_fdt_pcie(RISCVVirtState *s, if (virt_is_iommu_sys_enabled(s)) { qemu_fdt_setprop_cells(ms->fdt, name, "iommu-map", - 0, iommu_sys_phandle, 0, 0, 0, - iommu_sys_phandle, 0, 0xffff); + 0, iommu_sys_phandle, 0, 0x10000); } create_pcie_irq_map(s, ms->fdt, name, irq_pcie_phandle); -- 2.54.0
From: Max Chou <max.chou@sifive.com> According to the RISC-V privileged spec 3.1.6, any instruction that modifies FP extension state (FP CSRs including fflags, or f registers) must set mstatus.FS to Dirty. Raising fflags bits is modifying fcsr (an FP CSR). Scalar FP instructions that write integer registers (FP comparisons and FP-to-integer conversions) never call mark_fs_dirty at translation time to set mstatus.FS to dirty. However, they can raise FP exception flags via softfloat functions, which modifies fflags without any mechanism to dirty mstatus.FS. The affected helpers: - Comparisons: fle/fleq/flt/fltq/feq — raise NV on NaN operands - FP-to-integer: fcvt.[w|wu|l|lu]/fcvtmod.w.d — raise NX on inexact or NV on out-of-range Fix this issue by 1. Save float_exception_flags before the softfloat operation 2. Perform the operation 3. If any new exception bits are set, set fs to dirty Signed-off-by: Max Chou <max.chou@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260611105037.157773-2-max.chou@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 1 + target/riscv/fpu_helper.c | 157 +++++++++++++++++++++++++++++++------- 2 files changed, 130 insertions(+), 28 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ G_NORETURN void riscv_raise_exception(CPURISCVState *env, uint8_t riscv_cpu_get_fflags(CPURISCVState *env); void riscv_cpu_set_fflags(CPURISCVState *env, uint8_t); +void riscv_cpu_check_fflags(CPURISCVState *env, FloatExceptionFlags); #ifndef CONFIG_USER_ONLY void cpu_set_exception_base(int vp_index, uint64_t address); diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -XXX,XX +XXX,XX @@ void riscv_cpu_set_fflags(CPURISCVState *env, uint8_t hard) set_float_exception_flags(soft, &env->fp_status); } +#ifndef CONFIG_USER_ONLY +void riscv_cpu_check_fflags(CPURISCVState *env, + FloatExceptionFlags pre_fflag) +{ + if (get_float_exception_flags(&env->fp_status) & ~pre_fflag) { + env->mstatus |= MSTATUS_FS; + if (env->virt_enabled) { + env->mstatus_hs |= MSTATUS_FS; + } + } +} +#else +void riscv_cpu_check_fflags(CPURISCVState *env, + FloatExceptionFlags pre_fflag) {} +#endif + void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm) { FloatRoundMode softrm; @@ -XXX,XX +XXX,XX @@ target_ulong helper_fle_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float32 frs1 = check_nanbox_s(env, rs1); float32 frs2 = check_nanbox_s(env, rs2); - return float32_le(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float32_le(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fleq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float32 frs1 = check_nanbox_s(env, rs1); float32 frs2 = check_nanbox_s(env, rs2); - return float32_le_quiet(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float32_le_quiet(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_flt_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float32 frs1 = check_nanbox_s(env, rs1); float32 frs2 = check_nanbox_s(env, rs2); - return float32_lt(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float32_lt(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fltq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float32 frs1 = check_nanbox_s(env, rs1); float32 frs2 = check_nanbox_s(env, rs2); - return float32_lt_quiet(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float32_lt_quiet(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_feq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float32 frs1 = check_nanbox_s(env, rs1); float32 frs2 = check_nanbox_s(env, rs2); - return float32_eq_quiet(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float32_eq_quiet(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fcvt_w_s(CPURISCVState *env, uint64_t rs1) { float32 frs1 = check_nanbox_s(env, rs1); - return float32_to_int32(frs1, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float32_to_int32(frs1, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fcvt_wu_s(CPURISCVState *env, uint64_t rs1) { float32 frs1 = check_nanbox_s(env, rs1); - return (int32_t)float32_to_uint32(frs1, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = (int32_t)float32_to_uint32(frs1, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fcvt_l_s(CPURISCVState *env, uint64_t rs1) { float32 frs1 = check_nanbox_s(env, rs1); - return float32_to_int64(frs1, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float32_to_int64(frs1, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fcvt_lu_s(CPURISCVState *env, uint64_t rs1) { float32 frs1 = check_nanbox_s(env, rs1); - return float32_to_uint64(frs1, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float32_to_uint64(frs1, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } uint64_t helper_fcvt_s_w(CPURISCVState *env, target_ulong rs1) @@ -XXX,XX +XXX,XX @@ uint64_t helper_fsqrt_d(CPURISCVState *env, uint64_t frs1) target_ulong helper_fle_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { - return float64_le(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float64_le(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fleq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { - return float64_le_quiet(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float64_le_quiet(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_flt_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { - return float64_lt(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float64_lt(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fltq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { - return float64_lt_quiet(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float64_lt_quiet(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_feq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { - return float64_eq_quiet(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float64_eq_quiet(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fcvt_w_d(CPURISCVState *env, uint64_t frs1) { - return float64_to_int32(frs1, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float64_to_int32(frs1, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } uint64_t helper_fcvtmod_w_d(CPURISCVState *env, uint64_t value) { - return float64_to_int32_modulo(value, float_round_to_zero, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + uint64_t ret = float64_to_int32_modulo(value, float_round_to_zero, + &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fcvt_wu_d(CPURISCVState *env, uint64_t frs1) { - return (int32_t)float64_to_uint32(frs1, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = (int32_t)float64_to_uint32(frs1, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fcvt_l_d(CPURISCVState *env, uint64_t frs1) { - return float64_to_int64(frs1, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float64_to_int64(frs1, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fcvt_lu_d(CPURISCVState *env, uint64_t frs1) { - return float64_to_uint64(frs1, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float64_to_uint64(frs1, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } uint64_t helper_fcvt_d_w(CPURISCVState *env, target_ulong rs1) @@ -XXX,XX +XXX,XX @@ target_ulong helper_fle_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float16 frs1 = check_nanbox_h(env, rs1); float16 frs2 = check_nanbox_h(env, rs2); - return float16_le(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float16_le(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fleq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float16 frs1 = check_nanbox_h(env, rs1); float16 frs2 = check_nanbox_h(env, rs2); - return float16_le_quiet(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float16_le_quiet(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_flt_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float16 frs1 = check_nanbox_h(env, rs1); float16 frs2 = check_nanbox_h(env, rs2); - return float16_lt(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float16_lt(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fltq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float16 frs1 = check_nanbox_h(env, rs1); float16 frs2 = check_nanbox_h(env, rs2); - return float16_lt_quiet(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float16_lt_quiet(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_feq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float16 frs1 = check_nanbox_h(env, rs1); float16 frs2 = check_nanbox_h(env, rs2); - return float16_eq_quiet(frs1, frs2, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float16_eq_quiet(frs1, frs2, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fclass_h(CPURISCVState *env, uint64_t rs1) @@ -XXX,XX +XXX,XX @@ uint64_t helper_froundnx_h(CPURISCVState *env, uint64_t rs1) target_ulong helper_fcvt_w_h(CPURISCVState *env, uint64_t rs1) { float16 frs1 = check_nanbox_h(env, rs1); - return float16_to_int32(frs1, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float16_to_int32(frs1, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fcvt_wu_h(CPURISCVState *env, uint64_t rs1) { float16 frs1 = check_nanbox_h(env, rs1); - return (int32_t)float16_to_uint32(frs1, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = (int32_t)float16_to_uint32(frs1, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fcvt_l_h(CPURISCVState *env, uint64_t rs1) { float16 frs1 = check_nanbox_h(env, rs1); - return float16_to_int64(frs1, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float16_to_int64(frs1, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } target_ulong helper_fcvt_lu_h(CPURISCVState *env, uint64_t rs1) { float16 frs1 = check_nanbox_h(env, rs1); - return float16_to_uint64(frs1, &env->fp_status); + FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status); + target_ulong ret = float16_to_uint64(frs1, &env->fp_status); + riscv_cpu_check_fflags(env, pre_fflag); + return ret; } uint64_t helper_fcvt_h_w(CPURISCVState *env, target_ulong rs1) -- 2.54.0
From: Max Chou <max.chou@sifive.com> According to the RISC-V privileged spec 3.1.6, any instruction that modifies FP extension state (FP CSRs including fflags, or f registers) must set mstatus.FS to Dirty. Raising fflags bits is modifying fcsr (an FP CSR). When a vector FP instruction raises a floating-point exception, it modifies fflags (an FP CSR), but current implementation was not marking mstatus.FS dirty in this case. Fix the issue by snapshot fflags before the element loop and OR MSTATUS_FS into env->mstatus if any new exception bits are set afterwards. Signed-off-by: Max Chou <max.chou@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260611105037.157773-3-max.chou@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/vector_helper.c | 241 +++++++++++++++++++---------------- 1 file changed, 128 insertions(+), 113 deletions(-) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -XXX,XX +XXX,XX @@ static void do_##NAME(void *vd, void *vs1, void *vs2, int i, \ *((TD *)vd + HD(i)) = OP(s2, s1, &env->fp_status); \ } -#define GEN_VEXT_VV_ENV(NAME, ESZ) \ -void HELPER(NAME)(void *vd, void *v0, void *vs1, \ - void *vs2, CPURISCVState *env, \ - uint32_t desc) \ -{ \ - uint32_t vm = vext_vm(desc); \ - uint32_t vl = env->vl; \ - uint32_t total_elems = \ - vext_get_total_elems(env, desc, ESZ); \ - uint32_t vta = vext_vta(desc); \ - uint32_t vma = vext_vma(desc); \ - uint32_t i; \ - \ - VSTART_CHECK_EARLY_EXIT(env, vl); \ - \ - for (i = env->vstart; i < vl; i++) { \ - if (!vm && !vext_elem_mask(v0, i)) { \ - /* set masked-off elements to 1s */ \ - vext_set_elems_1s(vd, vma, i * ESZ, \ - (i + 1) * ESZ); \ - continue; \ - } \ - do_##NAME(vd, vs1, vs2, i, env); \ - } \ - env->vstart = 0; \ - /* set tail elements to 1s */ \ - vext_set_elems_1s(vd, vta, vl * ESZ, \ - total_elems * ESZ); \ +#define GEN_VEXT_VV_ENV(NAME, ESZ) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t total_elems = \ + vext_get_total_elems(env, desc, ESZ); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ + uint32_t i; \ + FloatExceptionFlags pre_fflag = \ + get_float_exception_flags(&env->fp_status); \ + \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ + for (i = env->vstart; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * ESZ, \ + (i + 1) * ESZ); \ + continue; \ + } \ + do_##NAME(vd, vs1, vs2, i, env); \ + } \ + env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * ESZ, \ + total_elems * ESZ); \ + riscv_cpu_check_fflags(env, pre_fflag); \ } RVVCALL(OPFVV2, vfadd_vv_h_bf16, OP_UUU_H, H2, H2, H2, bfloat16_add) @@ -XXX,XX +XXX,XX @@ static void do_##NAME(void *vd, uint64_t s1, void *vs2, int i, \ *((TD *)vd + HD(i)) = OP(s2, (TX1)(T1)s1, &env->fp_status);\ } -#define GEN_VEXT_VF(NAME, ESZ) \ -void HELPER(NAME)(void *vd, void *v0, uint64_t s1, \ - void *vs2, CPURISCVState *env, \ - uint32_t desc) \ -{ \ - uint32_t vm = vext_vm(desc); \ - uint32_t vl = env->vl; \ - uint32_t total_elems = \ - vext_get_total_elems(env, desc, ESZ); \ - uint32_t vta = vext_vta(desc); \ - uint32_t vma = vext_vma(desc); \ - uint32_t i; \ - \ - VSTART_CHECK_EARLY_EXIT(env, vl); \ - \ - for (i = env->vstart; i < vl; i++) { \ - if (!vm && !vext_elem_mask(v0, i)) { \ - /* set masked-off elements to 1s */ \ - vext_set_elems_1s(vd, vma, i * ESZ, \ - (i + 1) * ESZ); \ - continue; \ - } \ - do_##NAME(vd, s1, vs2, i, env); \ - } \ - env->vstart = 0; \ - /* set tail elements to 1s */ \ - vext_set_elems_1s(vd, vta, vl * ESZ, \ - total_elems * ESZ); \ +#define GEN_VEXT_VF(NAME, ESZ) \ +void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t total_elems = \ + vext_get_total_elems(env, desc, ESZ); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ + uint32_t i; \ + FloatExceptionFlags pre_fflag = \ + get_float_exception_flags(&env->fp_status); \ + \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ + for (i = env->vstart; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * ESZ, \ + (i + 1) * ESZ); \ + continue; \ + } \ + do_##NAME(vd, s1, vs2, i, env); \ + } \ + env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * ESZ, \ + total_elems * ESZ); \ + riscv_cpu_check_fflags(env, pre_fflag); \ } RVVCALL(OPFVF2, vfadd_vf_h_bf16, OP_UUU_H, H2, H2, bfloat16_add) @@ -XXX,XX +XXX,XX @@ static void do_##NAME(void *vd, void *vs2, int i, \ *((TD *)vd + HD(i)) = OP(s2, &env->fp_status); \ } -#define GEN_VEXT_V_ENV(NAME, ESZ) \ -void HELPER(NAME)(void *vd, void *v0, void *vs2, \ - CPURISCVState *env, uint32_t desc) \ -{ \ - uint32_t vm = vext_vm(desc); \ - uint32_t vl = env->vl; \ - uint32_t total_elems = \ - vext_get_total_elems(env, desc, ESZ); \ - uint32_t vta = vext_vta(desc); \ - uint32_t vma = vext_vma(desc); \ - uint32_t i; \ - \ - VSTART_CHECK_EARLY_EXIT(env, vl); \ - \ - if (vl == 0) { \ - return; \ - } \ - for (i = env->vstart; i < vl; i++) { \ - if (!vm && !vext_elem_mask(v0, i)) { \ - /* set masked-off elements to 1s */ \ - vext_set_elems_1s(vd, vma, i * ESZ, \ - (i + 1) * ESZ); \ - continue; \ - } \ - do_##NAME(vd, vs2, i, env); \ - } \ - env->vstart = 0; \ - vext_set_elems_1s(vd, vta, vl * ESZ, \ - total_elems * ESZ); \ +#define GEN_VEXT_V_ENV(NAME, ESZ) \ +void HELPER(NAME)(void *vd, void *v0, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t total_elems = \ + vext_get_total_elems(env, desc, ESZ); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ + uint32_t i; \ + FloatExceptionFlags pre_fflag = \ + get_float_exception_flags(&env->fp_status); \ + \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ + if (vl == 0) { \ + return; \ + } \ + for (i = env->vstart; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * ESZ, \ + (i + 1) * ESZ); \ + continue; \ + } \ + do_##NAME(vd, vs2, i, env); \ + } \ + env->vstart = 0; \ + vext_set_elems_1s(vd, vta, vl * ESZ, \ + total_elems * ESZ); \ + riscv_cpu_check_fflags(env, pre_fflag); \ } RVVCALL(OPFVV1, vfsqrt_v_h, OP_UU_H, H2, H2, float16_sqrt) @@ -XXX,XX +XXX,XX @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint32_t vta_all_1s = vext_vta_all_1s(desc); \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ + FloatExceptionFlags pre_fflag = \ + get_float_exception_flags(&env->fp_status); \ \ VSTART_CHECK_EARLY_EXIT(env, vl); \ \ @@ -XXX,XX +XXX,XX @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ vext_set_elem_mask(vd, i, 1); \ } \ } \ + riscv_cpu_check_fflags(env, pre_fflag); \ } GEN_VEXT_CMP_VV_ENV(vmfeq_vv_h_bf16, uint16_t, H2, bfloat16_eq_quiet) @@ -XXX,XX +XXX,XX @@ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ uint32_t vta_all_1s = vext_vta_all_1s(desc); \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ + FloatExceptionFlags pre_fflag = \ + get_float_exception_flags(&env->fp_status); \ \ VSTART_CHECK_EARLY_EXIT(env, vl); \ \ @@ -XXX,XX +XXX,XX @@ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ vext_set_elem_mask(vd, i, 1); \ } \ } \ + riscv_cpu_check_fflags(env, pre_fflag); \ } GEN_VEXT_CMP_VF(vmfeq_vf_h_bf16, uint16_t, H2, bfloat16_eq_quiet) @@ -XXX,XX +XXX,XX @@ GEN_VEXT_RED(vwredsumu_vs_h, uint32_t, uint16_t, H4, H2, DO_ADD) GEN_VEXT_RED(vwredsumu_vs_w, uint64_t, uint32_t, H8, H4, DO_ADD) /* Vector Single-Width Floating-Point Reduction Instructions */ -#define GEN_VEXT_FRED(NAME, TD, TS2, HD, HS2, OP) \ -void HELPER(NAME)(void *vd, void *v0, void *vs1, \ - void *vs2, CPURISCVState *env, \ - uint32_t desc) \ -{ \ - uint32_t vm = vext_vm(desc); \ - uint32_t vl = env->vl; \ - uint32_t esz = sizeof(TD); \ - uint32_t vlenb = simd_maxsz(desc); \ - uint32_t vta = vext_vta(desc); \ - uint32_t i; \ - TD s1 = *((TD *)vs1 + HD(0)); \ - \ - VSTART_CHECK_EARLY_EXIT(env, vl); \ - \ - for (i = env->vstart; i < vl; i++) { \ - TS2 s2 = *((TS2 *)vs2 + HS2(i)); \ - if (!vm && !vext_elem_mask(v0, i)) { \ - continue; \ - } \ - s1 = OP(s1, (TD)s2, &env->fp_status); \ - } \ - if (vl > 0) { \ - *((TD *)vd + HD(0)) = s1; \ - } \ - env->vstart = 0; \ - /* set tail elements to 1s */ \ - vext_set_elems_1s(vd, vta, esz, vlenb); \ +#define GEN_VEXT_FRED(NAME, TD, TS2, HD, HS2, OP) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t esz = sizeof(TD); \ + uint32_t vlenb = simd_maxsz(desc); \ + uint32_t vta = vext_vta(desc); \ + uint32_t i; \ + FloatExceptionFlags pre_fflag = \ + get_float_exception_flags(&env->fp_status); \ + TD s1 = *((TD *)vs1 + HD(0)); \ + \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ + for (i = env->vstart; i < vl; i++) { \ + TS2 s2 = *((TS2 *)vs2 + HS2(i)); \ + if (!vm && !vext_elem_mask(v0, i)) { \ + continue; \ + } \ + s1 = OP(s1, (TD)s2, &env->fp_status); \ + } \ + if (vl > 0) { \ + *((TD *)vd + HD(0)) = s1; \ + } \ + env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, esz, vlenb); \ + riscv_cpu_check_fflags(env, pre_fflag); \ } /* Unordered sum */ -- 2.54.0
From: imaginos <imaginos32@gmail.com> The translator has supported mnret since commit 3157a553ec6b9a ("target/riscv: Add Smrnmi mnret instruction"), but the disassembler still renders it as illegal. Add it unguarded, since the encoding does not overlap any other extension. Signed-off-by: imaginos <imaginos32@gmail.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260614141315.17320-1-imaginos32@gmail.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- disas/riscv.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/disas/riscv.c b/disas/riscv.c index XXXXXXX..XXXXXXX 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -XXX,XX +XXX,XX @@ typedef enum { rv_op_cbo_clean = 957, rv_op_cbo_flush = 958, rv_op_cbo_zero = 959, + rv_op_mnret = 960, } rv_op; /* register names */ @@ -XXX,XX +XXX,XX @@ const rv_opcode_data rvi_opcode_data[] = { { "cbo.clean", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, { "cbo.flush", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, { "cbo.zero", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "mnret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, }; /* CSR names */ @@ -XXX,XX +XXX,XX @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 64: op = rv_op_mret; break; } break; + case 1792: + switch ((inst >> 15) & 0b1111111111) { + case 64: op = rv_op_mnret; break; + } + break; case 1952: switch ((inst >> 15) & 0b1111111111) { case 576: op = rv_op_dret; break; -- 2.54.0
From: Chao Liu <chao.liu@zevorn.cn> The C908 processor is based on the RV64GCB[V] instruction set, compatible to RVA22 Profile and implements the XIE (XuanTie Instruction Extension) technology. Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com> Suggested-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Tested-by: Peng Jiang <3160104094@zju.edu.cn> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Message-ID: <a3e232ace12afd93adb60aed198cac3284daa56c.1781246408.git.chao.liu@processmission.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu-qom.h | 2 + target/riscv/cpu.c | 51 ++++++ target/riscv/th_csr.c | 376 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 428 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -XXX,XX +XXX,XX @@ #define TYPE_RISCV_CPU_SIFIVE_U34 RISCV_CPU_TYPE_NAME("sifive-u34") #define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54") #define TYPE_RISCV_CPU_THEAD_C906 RISCV_CPU_TYPE_NAME("thead-c906") +#define TYPE_RISCV_CPU_THEAD_C908 RISCV_CPU_TYPE_NAME("thead-c908") +#define TYPE_RISCV_CPU_THEAD_C908V RISCV_CPU_TYPE_NAME("thead-c908v") #define TYPE_RISCV_CPU_VEYRON_V1 RISCV_CPU_TYPE_NAME("veyron-v1") #define TYPE_RISCV_CPU_TT_ASCALON RISCV_CPU_TYPE_NAME("tt-ascalon") #define TYPE_RISCV_CPU_XIANGSHAN_NANHU RISCV_CPU_TYPE_NAME("xiangshan-nanhu") diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static const TypeInfo riscv_cpu_type_infos[] = { #endif ), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_THEAD_C908, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU, + .priv_spec = PRIV_VERSION_1_12_0, + + /* ISA extensions */ + .cfg.ext_xtheadba = true, + .cfg.ext_xtheadbb = true, + .cfg.ext_xtheadbs = true, + .cfg.ext_xtheadcmo = true, + .cfg.ext_xtheadcondmov = true, + .cfg.ext_xtheadfmv = true, + .cfg.ext_xtheadfmemidx = true, + .cfg.ext_xtheadmac = true, + .cfg.ext_xtheadmemidx = true, + .cfg.ext_xtheadmempair = true, + .cfg.ext_xtheadsync = true, + .cfg.ext_smepmp = true, + .cfg.ext_sscofpmf = true, + .cfg.ext_sstc = true, + .cfg.ext_svpbmt = true, + .cfg.ext_svinval = true, + .cfg.ext_svnapot = true, + .cfg.ext_zba = true, + .cfg.ext_zbb = true, + .cfg.ext_zbc = true, + .cfg.ext_zbs = true, + .cfg.ext_zkt = true, + .cfg.ext_zbkc = true, + .cfg.ext_zicsr = true, + .cfg.ext_zifencei = true, + .cfg.ext_zihintpause = true, + .cfg.ext_zicbom = true, + .cfg.ext_zicboz = true, + + .cfg.pmp = true, + .cfg.mmu = true, + .cfg.max_satp_mode = VM_1_10_SV48, + + .cfg.marchid = 0x8d143000, + .cfg.mvendorid = THEAD_VENDOR_ID, +#ifndef CONFIG_USER_ONLY + .custom_csrs = th_csr_list, +#endif + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_THEAD_C908V, TYPE_RISCV_CPU_THEAD_C908, + .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU | RVV, + .vext_spec = VEXT_VERSION_1_00_0, + ), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_TT_ASCALON, TYPE_RISCV_VENDOR_CPU, .misa_mxl_max = MXL_RV64, .misa_ext = RVG | RVC | RVS | RVU | RVH | RVV, diff --git a/target/riscv/th_csr.c b/target/riscv/th_csr.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/th_csr.c +++ b/target/riscv/th_csr.c @@ -XXX,XX +XXX,XX @@ * T-Head-specific CSRs. * * Copyright (c) 2024 VRULL GmbH + * Copyright (c) 2025 Chao Liu <chao.liu.zevorn@gmail.com> + * + * For more information, see XuanTie-C908-UserManual_xrvm_20240530.pdf * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -XXX,XX +XXX,XX @@ #include "cpu_vendorid.h" #include "target/riscv/csr.h" -#define CSR_TH_SXSTATUS 0x5c0 +/* Extended M-mode control registers of T-Head */ +#define CSR_TH_MXSTATUS 0x7c0 +#define CSR_TH_MHCR 0x7c1 +#define CSR_TH_MCOR 0x7c2 +#define CSR_TH_MCCR2 0x7c3 +#define CSR_TH_MHINT 0x7c5 +#define CSR_TH_MRVBR 0x7c7 +#define CSR_TH_MCOUNTERWEN 0x7c9 +#define CSR_TH_MCOUNTERINTEN 0x7ca +#define CSR_TH_MCOUNTEROF 0x7cb +#define CSR_TH_MCINS 0x7d2 +#define CSR_TH_MCINDEX 0x7d3 +#define CSR_TH_MCDATA0 0x7d4 +#define CSR_TH_MCDATA1 0x7d5 +#define CSR_TH_MSMPR 0x7f3 +#define CSR_TH_CPUID 0xfc0 +#define CSR_TH_MAPBADDR 0xfc1 + +/* TH_MXSTATUS bits */ +#define TH_MXSTATUS_UCME BIT(16) +#define TH_MXSTATUS_MAEE BIT(21) +#define TH_MXSTATUS_THEADISAEE BIT(22) + +/* Extended S-mode control registers of T-Head */ +#define CSR_TH_SXSTATUS 0x5c0 +#define CSR_TH_SHCR 0x5c1 +#define CSR_TH_SCER2 0x5c2 +#define CSR_TH_SCER 0x5c3 +#define CSR_TH_SCOUNTERINTEN 0x5c4 +#define CSR_TH_SCOUNTEROF 0x5c5 +#define CSR_TH_SCYCLE 0x5e0 +#define CSR_TH_SHPMCOUNTER3 0x5e3 +#define CSR_TH_SHPMCOUNTER4 0x5e4 +#define CSR_TH_SHPMCOUNTER5 0x5e5 +#define CSR_TH_SHPMCOUNTER6 0x5e6 +#define CSR_TH_SHPMCOUNTER7 0x5e7 +#define CSR_TH_SHPMCOUNTER8 0x5e8 +#define CSR_TH_SHPMCOUNTER9 0x5e9 +#define CSR_TH_SHPMCOUNTER10 0x5ea +#define CSR_TH_SHPMCOUNTER11 0x5eb +#define CSR_TH_SHPMCOUNTER12 0x5ec +#define CSR_TH_SHPMCOUNTER13 0x5ed +#define CSR_TH_SHPMCOUNTER14 0x5ee +#define CSR_TH_SHPMCOUNTER15 0x5ef +#define CSR_TH_SHPMCOUNTER16 0x5f0 +#define CSR_TH_SHPMCOUNTER17 0x5f1 +#define CSR_TH_SHPMCOUNTER18 0x5f2 +#define CSR_TH_SHPMCOUNTER19 0x5f3 +#define CSR_TH_SHPMCOUNTER20 0x5f4 +#define CSR_TH_SHPMCOUNTER21 0x5f5 +#define CSR_TH_SHPMCOUNTER22 0x5f6 +#define CSR_TH_SHPMCOUNTER23 0x5f7 +#define CSR_TH_SHPMCOUNTER24 0x5f8 +#define CSR_TH_SHPMCOUNTER25 0x5f9 +#define CSR_TH_SHPMCOUNTER26 0x5fa +#define CSR_TH_SHPMCOUNTER27 0x5fb +#define CSR_TH_SHPMCOUNTER28 0x5fc +#define CSR_TH_SHPMCOUNTER29 0x5fd +#define CSR_TH_SHPMCOUNTER30 0x5fe +#define CSR_TH_SHPMCOUNTER31 0x5ff +#define CSR_TH_SMIR 0x9c0 +#define CSR_TH_SMLO0 0x9c1 +#define CSR_TH_SMEH 0x9c2 +#define CSR_TH_SMCIR 0x9c3 + +/* Extended U-mode control registers of T-Head */ +#define CSR_TH_FXCR 0x800 /* TH_SXSTATUS bits */ #define TH_SXSTATUS_UCME BIT(16) #define TH_SXSTATUS_MAEE BIT(21) #define TH_SXSTATUS_THEADISAEE BIT(22) +static RISCVException mmode(CPURISCVState *env, int csrno) +{ + return RISCV_EXCP_NONE; +} + static RISCVException smode(CPURISCVState *env, int csrno) { if (riscv_has_ext(env, RVS)) { @@ -XXX,XX +XXX,XX @@ static RISCVException smode(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } +static RISCVException any(CPURISCVState *env, int csrno) +{ + return RISCV_EXCP_NONE; +} + static bool test_thead_mvendorid(RISCVCPU *cpu) { return cpu->cfg.mvendorid == THEAD_VENDOR_ID; } +static RISCVException read_th_mxstatus(CPURISCVState *env, int csrno, + target_ulong *val) +{ + /* We don't set MAEE here, because QEMU does not implement MAEE. */ + *val = TH_MXSTATUS_UCME | TH_MXSTATUS_THEADISAEE; + return RISCV_EXCP_NONE; +} + +static RISCVException read_unimp_th_csr(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = 0; + return RISCV_EXCP_NONE; +} + static RISCVException read_th_sxstatus(CPURISCVState *env, int csrno, target_ulong *val) { @@ -XXX,XX +XXX,XX @@ static RISCVException read_th_sxstatus(CPURISCVState *env, int csrno, } const RISCVCSR th_csr_list[] = { + { + .csrno = CSR_TH_MXSTATUS, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mxstatus", mmode, read_th_mxstatus } + }, + { + .csrno = CSR_TH_MHCR, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mhcr", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_MCOR, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mcor", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_MCCR2, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mccr2", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_MHINT, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mhint", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_MRVBR, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mrvbr", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_MCOUNTERWEN, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mcounterwen", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_MCOUNTERINTEN, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mcounterinten", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_MCOUNTEROF, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mcounterof", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_MCINS, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mcins", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_MCINDEX, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mcindex", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_MCDATA0, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mcdata0", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_MCDATA1, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mcdata1", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_MSMPR, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.msmpr", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_CPUID, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.cpuid", mmode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_MAPBADDR, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.mapbaddr", mmode, read_unimp_th_csr } + }, { .csrno = CSR_TH_SXSTATUS, .insertion_test = test_thead_mvendorid, .csr_ops = { "th.sxstatus", smode, read_th_sxstatus } }, + { + .csrno = CSR_TH_SHCR, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shcr", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SCER2, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.scer2", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SCER, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.scer", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SCOUNTERINTEN, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.scounterinten", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SCOUNTEROF, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.scounterof", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SCYCLE, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.scycle", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER3, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter3", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER4, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter4", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER5, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter5", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER6, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter6", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER7, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter7", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER8, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter8", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER9, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter9", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER10, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter10", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER11, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter11", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER12, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter12", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER13, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter13", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER14, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter14", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER15, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter15", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER16, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter16", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER17, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter17", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER18, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter18", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER19, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter19", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER20, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter20", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER21, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter21", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER22, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter22", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER23, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter23", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER24, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter24", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER25, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter25", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER26, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter26", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER27, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter27", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER28, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter28", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER29, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter29", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER30, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter30", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SHPMCOUNTER31, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.shpmcounter31", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SMIR, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.smir", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SMLO0, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.smlo0", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SMEH, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.smeh", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_SMCIR, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.smcir", smode, read_unimp_th_csr } + }, + { + .csrno = CSR_TH_FXCR, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.fxcr", any, read_unimp_th_csr } + }, { } }; -- 2.54.0
From: Chao Liu <chao.liu.zevorn@gmail.com> K230 Board compatible with Kendryte K230 SDK. Preliminarily supports the C908 small core, which can run U-Boot and Linux kernels compiled by the K230 SDK. The K230 boot flow provides its device tree from firmware or software. QEMU does not generate a K230 DTB; users can pass one with -dtb for direct Linux boot, or rely on firmware/kernel built-in DTB for other payloads. Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com> Tested-by: Peng Jiang <3160104094@zju.edu.cn> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Message-ID: <a161697a249b896e44e2748435f6c0caec12c9f4.1781246408.git.chao.liu@processmission.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- MAINTAINERS | 7 + include/hw/riscv/k230.h | 145 ++++++++++++ hw/riscv/k230.c | 508 ++++++++++++++++++++++++++++++++++++++++ hw/riscv/Kconfig | 10 + hw/riscv/meson.build | 2 +- 5 files changed, 671 insertions(+), 1 deletion(-) create mode 100644 include/hw/riscv/k230.h create mode 100644 hw/riscv/k230.c diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ F: docs/system/riscv/xiangshan-kunminghu.rst F: hw/riscv/xiangshan_kmh.c F: include/hw/riscv/xiangshan_kmh.h +K230 Machines +M: Chao Liu <chao.liu.zevorn@gmail.com> +L: qemu-riscv@nongnu.org +S: Maintained +F: hw/riscv/k230.c +F: include/hw/riscv/k230.h + RX Machines ----------- rx-gdbsim diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/riscv/k230.h @@ -XXX,XX +XXX,XX @@ +/* + * QEMU RISC-V Virt Board Compatible with kendryte K230 SDK + * + * Copyright (c) 2025 Chao Liu <chao.liu.zevorn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Provides a board compatible with the kendryte K230 SDK + * + * K230 Technical Reference Manual V0.3.1 (2024-11-18): + * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf + * + * For more information, see <https://www.kendryte.com/en/proDetail/230> + */ +#ifndef HW_K230_H +#define HW_K230_H + +#include "hw/core/boards.h" +#include "hw/riscv/riscv_hart.h" + +#define C908_CPU_HARTID (0) + +#define TYPE_RISCV_K230_SOC "riscv.k230.soc" +#define RISCV_K230_SOC(obj) \ + OBJECT_CHECK(K230SoCState, (obj), TYPE_RISCV_K230_SOC) + +typedef struct K230SoCState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + RISCVHartArrayState c908_cpu; /* Small core */ + + MemoryRegion sram; + MemoryRegion bootrom; + + DeviceState *c908_plic; +} K230SoCState; + +#define TYPE_RISCV_K230_MACHINE MACHINE_TYPE_NAME("k230") +#define RISCV_K230_MACHINE(obj) \ + OBJECT_CHECK(K230MachineState, (obj), TYPE_RISCV_K230_MACHINE) + +typedef struct K230MachineState { + /*< private >*/ + MachineState parent_obj; + + /*< public >*/ + K230SoCState soc; + Notifier machine_done; +} K230MachineState; + +enum { + K230_DEV_DDRC, + K230_DEV_KPU_L2_CACHE, + K230_DEV_SRAM, + K230_DEV_KPU_CFG, + K230_DEV_FFT, + K230_DEV_AI_2D_ENGINE, + K230_DEV_GSDMA, + K230_DEV_DMA, + K230_DEV_DECOMP_GZIP, + K230_DEV_NON_AI_2D, + K230_DEV_ISP, + K230_DEV_DEWARP, + K230_DEV_RX_CSI, + K230_DEV_H264, + K230_DEV_2P5D, + K230_DEV_VO, + K230_DEV_VO_CFG, + K230_DEV_3D_ENGINE, + K230_DEV_PMU, + K230_DEV_RTC, + K230_DEV_CMU, + K230_DEV_RMU, + K230_DEV_BOOT, + K230_DEV_PWR, + K230_DEV_MAILBOX, + K230_DEV_IOMUX, + K230_DEV_TIMER, + K230_DEV_WDT0, + K230_DEV_WDT1, + K230_DEV_TS, + K230_DEV_HDI, + K230_DEV_STC, + K230_DEV_BOOTROM, + K230_DEV_SECURITY, + K230_DEV_UART0, + K230_DEV_UART1, + K230_DEV_UART2, + K230_DEV_UART3, + K230_DEV_UART4, + K230_DEV_I2C0, + K230_DEV_I2C1, + K230_DEV_I2C2, + K230_DEV_I2C3, + K230_DEV_I2C4, + K230_DEV_PWM, + K230_DEV_GPIO0, + K230_DEV_GPIO1, + K230_DEV_ADC, + K230_DEV_CODEC, + K230_DEV_I2S, + K230_DEV_USB0, + K230_DEV_USB1, + K230_DEV_SD0, + K230_DEV_SD1, + K230_DEV_QSPI0, + K230_DEV_QSPI1, + K230_DEV_SPI, + K230_DEV_HI_SYS_CFG, + K230_DEV_DDRC_CFG, + K230_DEV_FLASH, + K230_DEV_PLIC, + K230_DEV_CLINT, +}; + +enum { + /* + * K230 TRM v0.3.1 section 2.4 lists peripheral interrupt bits; SDK + * DTBs expose the corresponding PLIC IDs as bit + 16. + */ + K230_UART0_IRQ = 16, + K230_UART1_IRQ = 17, + K230_UART2_IRQ = 18, + K230_UART3_IRQ = 19, + K230_UART4_IRQ = 20, +}; + +#define K230_UART_COUNT 5 + +/* + * Integrates with the interrupt controller (PLIC), + * which can process 208 interrupt external sources + */ +#define K230_PLIC_NUM_SOURCES 208 +#define K230_PLIC_NUM_PRIORITIES 7 +#define K230_PLIC_PRIORITY_BASE 0x00 +#define K230_PLIC_PENDING_BASE 0x1000 +#define K230_PLIC_ENABLE_BASE 0x2000 +#define K230_PLIC_ENABLE_STRIDE 0x80 +#define K230_PLIC_CONTEXT_BASE 0x200000 +#define K230_PLIC_CONTEXT_STRIDE 0x1000 + +#endif diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/riscv/k230.c @@ -XXX,XX +XXX,XX @@ +/* + * QEMU RISC-V Virt Board Compatible with Kendryte K230 SDK + * + * Copyright (c) 2025 Chao Liu <chao.liu.zevorn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Provides a board compatible with the Kendryte K230 SDK + * + * K230 Technical Reference Manual V0.3.1 (2024-11-18): + * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf + * + * For more information, see <https://www.kendryte.com/en/proDetail/230> + */ + +#include "qemu/osdep.h" +#include "cpu-qom.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "system/device_tree.h" +#include "system/system.h" +#include "system/memory.h" +#include "target/riscv/cpu.h" +#include "hw/core/loader.h" +#include "hw/core/sysbus.h" +#include "hw/riscv/k230.h" +#include "hw/riscv/boot.h" +#include "hw/riscv/machines-qom.h" +#include "hw/intc/riscv_aclint.h" +#include "hw/intc/sifive_plic.h" +#include "hw/char/serial-mm.h" +#include "hw/misc/unimp.h" + +/* Align K230_SDK k230_canmv_defconfig */ +#define K230_DIRECT_OPENSBI_ADDR 0x8000000 +#define K230_DIRECT_KERNEL_ADDR 0x8200000 +#define K230_DIRECT_DTB_ADDR 0xa000000 + +static const MemMapEntry memmap[] = { + [K230_DEV_DDRC] = { 0x00000000, 0x80000000 }, + [K230_DEV_KPU_L2_CACHE] = { 0x80000000, 0x00200000 }, + [K230_DEV_SRAM] = { 0x80200000, 0x00200000 }, + [K230_DEV_KPU_CFG] = { 0x80400000, 0x00000800 }, + [K230_DEV_FFT] = { 0x80400800, 0x00000400 }, + [K230_DEV_AI_2D_ENGINE] = { 0x80400C00, 0x00000800 }, + [K230_DEV_GSDMA] = { 0x80800000, 0x00004000 }, + [K230_DEV_DMA] = { 0x80804000, 0x00004000 }, + [K230_DEV_DECOMP_GZIP] = { 0x80808000, 0x00004000 }, + [K230_DEV_NON_AI_2D] = { 0x8080C000, 0x00004000 }, + [K230_DEV_ISP] = { 0x90000000, 0x00008000 }, + [K230_DEV_DEWARP] = { 0x90008000, 0x00001000 }, + [K230_DEV_RX_CSI] = { 0x90009000, 0x00002000 }, + [K230_DEV_H264] = { 0x90400000, 0x00010000 }, + [K230_DEV_2P5D] = { 0x90800000, 0x00040000 }, + [K230_DEV_VO] = { 0x90840000, 0x00010000 }, + [K230_DEV_VO_CFG] = { 0x90850000, 0x00001000 }, + [K230_DEV_3D_ENGINE] = { 0x90A00000, 0x00000800 }, + [K230_DEV_PMU] = { 0x91000000, 0x00000C00 }, + [K230_DEV_RTC] = { 0x91000C00, 0x00000400 }, + [K230_DEV_CMU] = { 0x91100000, 0x00001000 }, + [K230_DEV_RMU] = { 0x91101000, 0x00001000 }, + [K230_DEV_BOOT] = { 0x91102000, 0x00001000 }, + [K230_DEV_PWR] = { 0x91103000, 0x00001000 }, + [K230_DEV_MAILBOX] = { 0x91104000, 0x00001000 }, + [K230_DEV_IOMUX] = { 0x91105000, 0x00000800 }, + [K230_DEV_TIMER] = { 0x91105800, 0x00000800 }, + [K230_DEV_WDT0] = { 0x91106000, 0x00000800 }, + [K230_DEV_WDT1] = { 0x91106800, 0x00000800 }, + [K230_DEV_TS] = { 0x91107000, 0x00000800 }, + [K230_DEV_HDI] = { 0x91107800, 0x00000800 }, + [K230_DEV_STC] = { 0x91108000, 0x00000800 }, + [K230_DEV_BOOTROM] = { 0x91200000, 0x00010000 }, + [K230_DEV_SECURITY] = { 0x91210000, 0x00008000 }, + [K230_DEV_UART0] = { 0x91400000, 0x00001000 }, + [K230_DEV_UART1] = { 0x91401000, 0x00001000 }, + [K230_DEV_UART2] = { 0x91402000, 0x00001000 }, + [K230_DEV_UART3] = { 0x91403000, 0x00001000 }, + [K230_DEV_UART4] = { 0x91404000, 0x00001000 }, + [K230_DEV_I2C0] = { 0x91405000, 0x00001000 }, + [K230_DEV_I2C1] = { 0x91406000, 0x00001000 }, + [K230_DEV_I2C2] = { 0x91407000, 0x00001000 }, + [K230_DEV_I2C3] = { 0x91408000, 0x00001000 }, + [K230_DEV_I2C4] = { 0x91409000, 0x00001000 }, + [K230_DEV_PWM] = { 0x9140A000, 0x00001000 }, + [K230_DEV_GPIO0] = { 0x9140B000, 0x00001000 }, + [K230_DEV_GPIO1] = { 0x9140C000, 0x00001000 }, + [K230_DEV_ADC] = { 0x9140D000, 0x00001000 }, + [K230_DEV_CODEC] = { 0x9140E000, 0x00001000 }, + [K230_DEV_I2S] = { 0x9140F000, 0x00001000 }, + [K230_DEV_USB0] = { 0x91500000, 0x00010000 }, + [K230_DEV_USB1] = { 0x91540000, 0x00010000 }, + [K230_DEV_SD0] = { 0x91580000, 0x00001000 }, + [K230_DEV_SD1] = { 0x91581000, 0x00001000 }, + [K230_DEV_QSPI0] = { 0x91582000, 0x00001000 }, + [K230_DEV_QSPI1] = { 0x91583000, 0x00001000 }, + [K230_DEV_SPI] = { 0x91584000, 0x00001000 }, + [K230_DEV_HI_SYS_CFG] = { 0x91585000, 0x00000400 }, + [K230_DEV_DDRC_CFG] = { 0x98000000, 0x02000000 }, + [K230_DEV_FLASH] = { 0xC0000000, 0x08000000 }, + [K230_DEV_PLIC] = { 0xF00000000, 0x00400000 }, + [K230_DEV_CLINT] = { 0xF04000000, 0x00400000 }, +}; + +static void k230_soc_init(Object *obj) +{ + K230SoCState *s = RISCV_K230_SOC(obj); + RISCVHartArrayState *cpu0 = &s->c908_cpu; + + object_initialize_child(obj, "c908-cpu", cpu0, TYPE_RISCV_HART_ARRAY); + qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0); + qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C908); + qdev_prop_set_uint64(DEVICE(cpu0), "resetvec", + memmap[K230_DEV_BOOTROM].base); +} + +static DeviceState *k230_create_plic(int base_hartid, int hartid_count) +{ + g_autofree char *plic_hart_config = NULL; + + /* Per-socket PLIC hart topology configuration string */ + plic_hart_config = riscv_plic_hart_config_string(hartid_count); + + /* Per-socket PLIC */ + return sifive_plic_create(memmap[K230_DEV_PLIC].base, + plic_hart_config, hartid_count, base_hartid, + K230_PLIC_NUM_SOURCES, + K230_PLIC_NUM_PRIORITIES, + K230_PLIC_PRIORITY_BASE, K230_PLIC_PENDING_BASE, + K230_PLIC_ENABLE_BASE, K230_PLIC_ENABLE_STRIDE, + K230_PLIC_CONTEXT_BASE, + K230_PLIC_CONTEXT_STRIDE, + memmap[K230_DEV_PLIC].size); +} + +static void k230_create_uart(MemoryRegion *sys_mem, DeviceState *plic, + int index) +{ + int uart_dev = K230_DEV_UART0 + index; + g_autofree char *name = g_strdup_printf("uart%d", index); + + /* Cover the non-16550 part of the SDK's 0x1000 UART window. */ + create_unimplemented_device(name, memmap[uart_dev].base, + memmap[uart_dev].size); + + serial_mm_init(sys_mem, memmap[uart_dev].base, 2, + qdev_get_gpio_in(plic, K230_UART0_IRQ + index), + 399193, serial_hd(index), DEVICE_LITTLE_ENDIAN); +} + +static void k230_soc_realize(DeviceState *dev, Error **errp) +{ + K230SoCState *s = RISCV_K230_SOC(dev); + MemoryRegion *sys_mem = get_system_memory(); + int c908_cpus; + + sysbus_realize(SYS_BUS_DEVICE(&s->c908_cpu), &error_fatal); + + c908_cpus = s->c908_cpu.num_harts; + + /* SRAM */ + memory_region_init_ram(&s->sram, OBJECT(dev), "sram", + memmap[K230_DEV_SRAM].size, &error_fatal); + memory_region_add_subregion(sys_mem, memmap[K230_DEV_SRAM].base, + &s->sram); + + /* BootROM */ + memory_region_init_rom(&s->bootrom, OBJECT(dev), "bootrom", + memmap[K230_DEV_BOOTROM].size, &error_fatal); + memory_region_add_subregion(sys_mem, memmap[K230_DEV_BOOTROM].base, + &s->bootrom); + + /* PLIC */ + s->c908_plic = k230_create_plic(C908_CPU_HARTID, c908_cpus); + + /* CLINT */ + riscv_aclint_swi_create(memmap[K230_DEV_CLINT].base, + C908_CPU_HARTID, c908_cpus, false); + riscv_aclint_mtimer_create(memmap[K230_DEV_CLINT].base + 0x4000, + RISCV_ACLINT_DEFAULT_MTIMER_SIZE, + C908_CPU_HARTID, c908_cpus, + RISCV_ACLINT_DEFAULT_MTIMECMP, + RISCV_ACLINT_DEFAULT_MTIME, + RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true); + + /* UART */ + for (int i = 0; i < K230_UART_COUNT; i++) { + k230_create_uart(sys_mem, DEVICE(s->c908_plic), i); + } + + /* unimplemented devices */ + create_unimplemented_device("kpu.l2-cache", + memmap[K230_DEV_KPU_L2_CACHE].base, + memmap[K230_DEV_KPU_L2_CACHE].size); + + create_unimplemented_device("kpu_cfg", memmap[K230_DEV_KPU_CFG].base, + memmap[K230_DEV_KPU_CFG].size); + + create_unimplemented_device("fft", memmap[K230_DEV_FFT].base, + memmap[K230_DEV_FFT].size); + + create_unimplemented_device("2d-engine.ai", + memmap[K230_DEV_AI_2D_ENGINE].base, + memmap[K230_DEV_AI_2D_ENGINE].size); + + create_unimplemented_device("gsdma", memmap[K230_DEV_GSDMA].base, + memmap[K230_DEV_GSDMA].size); + + create_unimplemented_device("dma", memmap[K230_DEV_DMA].base, + memmap[K230_DEV_DMA].size); + + create_unimplemented_device("decomp-gzip", + memmap[K230_DEV_DECOMP_GZIP].base, + memmap[K230_DEV_DECOMP_GZIP].size); + + create_unimplemented_device("2d-engine.non-ai", + memmap[K230_DEV_NON_AI_2D].base, + memmap[K230_DEV_NON_AI_2D].size); + + create_unimplemented_device("isp", memmap[K230_DEV_ISP].base, + memmap[K230_DEV_ISP].size); + + create_unimplemented_device("dewarp", memmap[K230_DEV_DEWARP].base, + memmap[K230_DEV_DEWARP].size); + + create_unimplemented_device("rx-csi", memmap[K230_DEV_RX_CSI].base, + memmap[K230_DEV_RX_CSI].size); + + create_unimplemented_device("vpu", memmap[K230_DEV_H264].base, + memmap[K230_DEV_H264].size); + + create_unimplemented_device("gpu", memmap[K230_DEV_2P5D].base, + memmap[K230_DEV_2P5D].size); + + create_unimplemented_device("vo", memmap[K230_DEV_VO].base, + memmap[K230_DEV_VO].size); + + create_unimplemented_device("vo_cfg", memmap[K230_DEV_VO_CFG].base, + memmap[K230_DEV_VO_CFG].size); + + create_unimplemented_device("3d-engine", memmap[K230_DEV_3D_ENGINE].base, + memmap[K230_DEV_3D_ENGINE].size); + + create_unimplemented_device("pmu", memmap[K230_DEV_PMU].base, + memmap[K230_DEV_PMU].size); + + create_unimplemented_device("rtc", memmap[K230_DEV_RTC].base, + memmap[K230_DEV_RTC].size); + + create_unimplemented_device("cmu", memmap[K230_DEV_CMU].base, + memmap[K230_DEV_CMU].size); + + create_unimplemented_device("rmu", memmap[K230_DEV_RMU].base, + memmap[K230_DEV_RMU].size); + + create_unimplemented_device("boot", memmap[K230_DEV_BOOT].base, + memmap[K230_DEV_BOOT].size); + + create_unimplemented_device("pwr", memmap[K230_DEV_PWR].base, + memmap[K230_DEV_PWR].size); + + create_unimplemented_device("ipcm", memmap[K230_DEV_MAILBOX].base, + memmap[K230_DEV_MAILBOX].size); + + create_unimplemented_device("iomux", memmap[K230_DEV_IOMUX].base, + memmap[K230_DEV_IOMUX].size); + + create_unimplemented_device("timer", memmap[K230_DEV_TIMER].base, + memmap[K230_DEV_TIMER].size); + + create_unimplemented_device("wdt0", memmap[K230_DEV_WDT0].base, + memmap[K230_DEV_WDT0].size); + + create_unimplemented_device("wdt1", memmap[K230_DEV_WDT1].base, + memmap[K230_DEV_WDT1].size); + + create_unimplemented_device("ts", memmap[K230_DEV_TS].base, + memmap[K230_DEV_TS].size); + + create_unimplemented_device("hdi", memmap[K230_DEV_HDI].base, + memmap[K230_DEV_HDI].size); + + create_unimplemented_device("stc", memmap[K230_DEV_STC].base, + memmap[K230_DEV_STC].size); + + create_unimplemented_device("security", memmap[K230_DEV_SECURITY].base, + memmap[K230_DEV_SECURITY].size); + + create_unimplemented_device("i2c0", memmap[K230_DEV_I2C0].base, + memmap[K230_DEV_I2C0].size); + + create_unimplemented_device("i2c1", memmap[K230_DEV_I2C1].base, + memmap[K230_DEV_I2C1].size); + + create_unimplemented_device("i2c2", memmap[K230_DEV_I2C2].base, + memmap[K230_DEV_I2C2].size); + + create_unimplemented_device("i2c3", memmap[K230_DEV_I2C3].base, + memmap[K230_DEV_I2C3].size); + + create_unimplemented_device("i2c4", memmap[K230_DEV_I2C4].base, + memmap[K230_DEV_I2C4].size); + + create_unimplemented_device("pwm", memmap[K230_DEV_PWM].base, + memmap[K230_DEV_PWM].size); + + create_unimplemented_device("gpio0", memmap[K230_DEV_GPIO0].base, + memmap[K230_DEV_GPIO0].size); + + create_unimplemented_device("gpio1", memmap[K230_DEV_GPIO1].base, + memmap[K230_DEV_GPIO1].size); + + create_unimplemented_device("adc", memmap[K230_DEV_ADC].base, + memmap[K230_DEV_ADC].size); + + create_unimplemented_device("codec", memmap[K230_DEV_CODEC].base, + memmap[K230_DEV_CODEC].size); + + create_unimplemented_device("i2s", memmap[K230_DEV_I2S].base, + memmap[K230_DEV_I2S].size); + + create_unimplemented_device("usb0", memmap[K230_DEV_USB0].base, + memmap[K230_DEV_USB0].size); + + create_unimplemented_device("usb1", memmap[K230_DEV_USB1].base, + memmap[K230_DEV_USB1].size); + + create_unimplemented_device("sd0", memmap[K230_DEV_SD0].base, + memmap[K230_DEV_SD0].size); + + create_unimplemented_device("sd1", memmap[K230_DEV_SD1].base, + memmap[K230_DEV_SD1].size); + + create_unimplemented_device("qspi0", memmap[K230_DEV_QSPI0].base, + memmap[K230_DEV_QSPI0].size); + + create_unimplemented_device("qspi1", memmap[K230_DEV_QSPI1].base, + memmap[K230_DEV_QSPI1].size); + + create_unimplemented_device("spi", memmap[K230_DEV_SPI].base, + memmap[K230_DEV_SPI].size); + + create_unimplemented_device("hi_sys_cfg", memmap[K230_DEV_HI_SYS_CFG].base, + memmap[K230_DEV_HI_SYS_CFG].size); + + create_unimplemented_device("ddrc_cfg", memmap[K230_DEV_DDRC_CFG].base, + memmap[K230_DEV_DDRC_CFG].size); + + create_unimplemented_device("flash", memmap[K230_DEV_FLASH].base, + memmap[K230_DEV_FLASH].size); +} + +static void k230_soc_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = k230_soc_realize; +} + +static const TypeInfo k230_soc_type_info = { + .name = TYPE_RISCV_K230_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(K230SoCState), + .instance_init = k230_soc_init, + .class_init = k230_soc_class_init, +}; + +static void k230_soc_register_types(void) +{ + type_register_static(&k230_soc_type_info); +} + +type_init(k230_soc_register_types) + +static void k230_direct_boot(K230MachineState *s, MachineState *machine) +{ + const char *firmware_name = riscv_default_firmware_name(&s->soc.c908_cpu); + RISCVBootInfo boot_info = {0}; + hwaddr start_addr = K230_DIRECT_OPENSBI_ADDR; + hwaddr firmware_end_addr = 0; + hwaddr kernel_entry = 0; + int fdt_size = 0; + + if (machine->firmware && !strcmp(machine->firmware, "none")) { + error_report("K230 direct boot requires OpenSBI firmware; omit " + "-bios none or pass OpenSBI with -bios"); + exit(EXIT_FAILURE); + } + + if (!machine->dtb) { + error_report("K230 direct boot requires -dtb"); + exit(EXIT_FAILURE); + } + + machine->fdt = load_device_tree(machine->dtb, &fdt_size); + if (!machine->fdt) { + error_report("load_device_tree() failed"); + exit(EXIT_FAILURE); + } + + qemu_fdt_add_path(machine->fdt, "/chosen"); + + riscv_boot_info_init(&boot_info, &s->soc.c908_cpu); + riscv_load_kernel(machine, &boot_info, K230_DIRECT_KERNEL_ADDR, true, NULL); + kernel_entry = boot_info.image_low_addr; + + riscv_load_fdt(K230_DIRECT_DTB_ADDR, machine->fdt); + + firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, + &start_addr, NULL); + if (firmware_end_addr > K230_DIRECT_KERNEL_ADDR) { + error_report("K230 firmware overlaps kernel address 0x%x", + K230_DIRECT_KERNEL_ADDR); + exit(EXIT_FAILURE); + } + + riscv_setup_rom_reset_vec(machine, &s->soc.c908_cpu, start_addr, + memmap[K230_DEV_BOOTROM].base, + memmap[K230_DEV_BOOTROM].size, kernel_entry, + K230_DIRECT_DTB_ADDR); +} + +static void k230_firmware_boot(K230MachineState *s, MachineState *machine) +{ + const char *firmware_name = riscv_default_firmware_name(&s->soc.c908_cpu); + hwaddr start_addr = memmap[K230_DEV_DDRC].base; + + if (machine->dtb || (machine->kernel_cmdline && *machine->kernel_cmdline)) { + error_report("K230 firmware boot does not support -dtb or -append"); + exit(EXIT_FAILURE); + } + + riscv_find_and_load_firmware(machine, firmware_name, &start_addr, NULL); + + riscv_setup_rom_reset_vec(machine, &s->soc.c908_cpu, start_addr, + memmap[K230_DEV_BOOTROM].base, + memmap[K230_DEV_BOOTROM].size, 0, 0); +} + +static void k230_machine_done(Notifier *notifier, void *data) +{ + K230MachineState *s = container_of(notifier, K230MachineState, + machine_done); + MachineState *machine = MACHINE(s); + + if (machine->kernel_filename) { + k230_direct_boot(s, machine); + } else { + k230_firmware_boot(s, machine); + } +} + +static void k230_machine_init(MachineState *machine) +{ + MachineClass *mc = MACHINE_GET_CLASS(machine); + K230MachineState *s = RISCV_K230_MACHINE(machine); + MemoryRegion *sys_mem = get_system_memory(); + + if (machine->ram_size < mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } + + /* Initialize SoC */ + object_initialize_child(OBJECT(machine), "soc", &s->soc, + TYPE_RISCV_K230_SOC); + qdev_realize(DEVICE(&s->soc), NULL, &error_fatal); + + /* Data Memory */ + memory_region_add_subregion(sys_mem, memmap[K230_DEV_DDRC].base, + machine->ram); + + s->machine_done.notify = k230_machine_done; + qemu_add_machine_init_done_notifier(&s->machine_done); +} + +static void k230_machine_instance_init(Object *obj) +{ +} + +static void k230_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "RISC-V Board compatible with Kendryte K230 SDK"; + mc->init = k230_machine_init; + mc->default_cpus = 1; + mc->default_ram_id = "riscv.K230.ram"; /* DDR */ + mc->default_ram_size = memmap[K230_DEV_DDRC].size; +} + +static const TypeInfo k230_machine_typeinfo = { + .name = TYPE_RISCV_K230_MACHINE, + .parent = TYPE_MACHINE, + .class_init = k230_machine_class_init, + .instance_init = k230_machine_instance_init, + .instance_size = sizeof(K230MachineState), + .interfaces = riscv64_machine_interfaces, +}; + +static void k230_machine_init_register_types(void) +{ + type_register_static(&k230_machine_typeinfo); +} + +type_init(k230_machine_init_register_types) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -XXX,XX +XXX,XX @@ config MIPS_BOSTON_AIA select RISCV_MIPS_CMGCR select RISCV_MIPS_CPC select RISCV_MIPS_CPS + +config K230 + bool + default y + depends on RISCV64 + select RISCV_ACLINT + select RISCV_APLIC + select RISCV_IMSIC + select SERIAL_MM + select UNIMP diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -XXX,XX +XXX,XX @@ 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_MIPS_CPS', if_true: files('cps.c')) riscv_ss.add(when: 'CONFIG_MIPS_BOSTON_AIA', if_true: files('boston-aia.c')) +riscv_ss.add(when: 'CONFIG_K230', if_true: files('k230.c')) hw_arch += {'riscv': riscv_ss} -- 2.54.0
From: Chao Liu <chao.liu@zevorn.cn> Add programmable Watchdog Timer (WDT) peripheral for K230 machine. Signed-off-by: Mig Yang <temashking@foxmail.com> Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Message-ID: <805a04d9467556ee6a5f4742c9eb4bbb6fc7898c.1781246408.git.chao.liu@processmission.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- MAINTAINERS | 2 + include/hw/riscv/k230.h | 4 + include/hw/watchdog/k230_wdt.h | 121 ++++++++++++++ hw/riscv/k230.c | 18 ++ hw/watchdog/k230_wdt.c | 296 +++++++++++++++++++++++++++++++++ hw/riscv/Kconfig | 1 + hw/watchdog/Kconfig | 4 + hw/watchdog/meson.build | 1 + hw/watchdog/trace-events | 9 + 9 files changed, 456 insertions(+) create mode 100644 include/hw/watchdog/k230_wdt.h create mode 100644 hw/watchdog/k230_wdt.c diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ M: Chao Liu <chao.liu.zevorn@gmail.com> L: qemu-riscv@nongnu.org S: Maintained F: hw/riscv/k230.c +F: hw/watchdog/k230_wdt.c F: include/hw/riscv/k230.h +F: include/hw/watchdog/k230_wdt.h RX Machines ----------- diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/k230.h +++ b/include/hw/riscv/k230.h @@ -XXX,XX +XXX,XX @@ #include "hw/core/boards.h" #include "hw/riscv/riscv_hart.h" +#include "hw/watchdog/k230_wdt.h" #define C908_CPU_HARTID (0) @@ -XXX,XX +XXX,XX @@ typedef struct K230SoCState { /*< public >*/ RISCVHartArrayState c908_cpu; /* Small core */ + K230WdtState wdt[2]; MemoryRegion sram; MemoryRegion bootrom; @@ -XXX,XX +XXX,XX @@ enum { K230_UART2_IRQ = 18, K230_UART3_IRQ = 19, K230_UART4_IRQ = 20, + K230_WDT0_IRQ = 107, + K230_WDT1_IRQ = 108, }; #define K230_UART_COUNT 5 diff --git a/include/hw/watchdog/k230_wdt.h b/include/hw/watchdog/k230_wdt.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/watchdog/k230_wdt.h @@ -XXX,XX +XXX,XX @@ +/* + * K230 Watchdog Timer + * + * K230 Technical Reference Manual V0.3.1 (2024-11-18): + * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf + * + * Copyright (c) 2025 Mig Yang <temashking@foxmail.com> + * Copyright (c) 2025 Chao Liu <chao.liu.zevorn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef K230_WDT_H +#define K230_WDT_H + +#include "qemu/bitops.h" +#include "hw/core/sysbus.h" +#include "hw/core/irq.h" +#include "hw/core/ptimer.h" +#include "qom/object.h" + +#define TYPE_K230_WDT "riscv.k230.wdt" +OBJECT_DECLARE_SIMPLE_TYPE(K230WdtState, K230_WDT) + +#define K230_WDT_DEFAULT_FREQ (32768) + +/* K230 Watchdog Register Map */ +enum K230WdtRegisters { + K230_WDT_CR = 0x00, /* Control Register */ + K230_WDT_TORR = 0x04, /* Timeout Range Register */ + K230_WDT_CCVR = 0x08, /* Current Counter Value Register */ + K230_WDT_CRR = 0x0c, /* Counter Restart Register */ + K230_WDT_STAT = 0x10, /* Interrupt Status Register */ + K230_WDT_EOI = 0x14, /* Interrupt Clear Register */ + K230_WDT_PROT_LEVEL = 0x1c, /* Protection Level Register */ + K230_WDT_COMP_PARAM_5 = 0xe4, /* Component Parameters Register 5 */ + K230_WDT_COMP_PARAM_4 = 0xe8, /* Component Parameters Register 4 */ + K230_WDT_COMP_PARAM_3 = 0xec, /* Component Parameters Register 3 */ + K230_WDT_COMP_PARAM_2 = 0xf0, /* Component Parameters Register 2 */ + K230_WDT_COMP_PARAM_1 = 0xf4, /* Component Parameters Register 1 */ + K230_WDT_COMP_VERSION = 0xf8, /* Component Version Register */ + K230_WDT_COMP_TYPE = 0xfc, /* Component Type Register */ +}; + +#define K230_WDT_MMIO_SIZE 0x100 + +/* Control Register (WDT_CR) definitions */ +#define K230_WDT_CR_RPL_MASK 0x7 /* Reset Pulse Length */ +#define K230_WDT_CR_RPL_SHIFT 2 +#define K230_WDT_CR_RMOD BIT(1) /* Response Mode */ +#define K230_WDT_CR_WDT_EN BIT(0) /* Watchdog Enable */ + +/* Reset Pulse Length values */ +#define K230_WDT_RPL_2_CYCLES 0x0 +#define K230_WDT_RPL_4_CYCLES 0x1 +#define K230_WDT_RPL_8_CYCLES 0x2 +#define K230_WDT_RPL_16_CYCLES 0x3 +#define K230_WDT_RPL_32_CYCLES 0x4 +#define K230_WDT_RPL_64_CYCLES 0x5 +#define K230_WDT_RPL_128_CYCLES 0x6 +#define K230_WDT_RPL_256_CYCLES 0x7 + +/* Timeout Range Register (WDT_TORR) definitions */ +#define K230_WDT_TORR_TOP_MASK 0xf /* Timeout Period */ + +/* Interrupt Status Register (WDT_STAT) definitions */ +#define K230_WDT_STAT_INT BIT(0) /* Interrupt Status */ + +/* Counter Restart Register (WDT_CRR) magic value */ +#define K230_WDT_CRR_RESTART 0x76 /* Restart command */ + +/* Component Parameters Register 1 (WDT_COMP_PARAM_1) definitions */ +#define K230_WDT_CNT_WIDTH_MASK 0x1f000000 /* Counter Width */ +#define K230_WDT_CNT_WIDTH_SHIFT 24 +#define K230_WDT_DFLT_TOP_INIT_MASK 0xf00000 /* Default Initial Timeout */ +#define K230_WDT_DFLT_TOP_INIT_SHIFT 20 +#define K230_WDT_DFLT_TOP_MASK 0xf0000 /* Default Timeout */ +#define K230_WDT_DFLT_TOP_SHIFT 16 +#define K230_WDT_DFLT_RPL_MASK 0x7 /* Default Reset Pulse Length */ +#define K230_WDT_DFLT_RPL_SHIFT 10 +#define K230_WDT_APB_DATA_WIDTH_MASK 0x3 /* APB Data Width */ +#define K230_WDT_APB_DATA_WIDTH_SHIFT 8 +#define K230_WDT_USE_FIX_TOP BIT(6) /* Use Fixed Timeout Values */ +#define K230_WDT_HC_TOP BIT(5) /* Hard-coded Timeout */ +#define K230_WDT_HC_RPL BIT(4) /* Hard-coded Reset Pulse Length */ +#define K230_WDT_HC_RMOD BIT(3) /* Hard-coded Response Mode */ +#define K230_WDT_DUAL_TOP BIT(2) /* Dual Timeout Period */ +#define K230_WDT_DFLT_RMOD BIT(1) /* Default Response Mode */ +#define K230_WDT_ALWAYS_EN BIT(0) /* Always Enabled */ + +/* Component Type Register value */ +#define K230_WDT_COMP_TYPE_VAL 0x44570120 + +/* Component Version Register value */ +#define K230_WDT_COMP_VERSION_VAL 0x3131302a /* "110*" */ + +struct K230WdtState { + /* <private> */ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; + qemu_irq irq; + + struct ptimer_state *timer; + + /* Register state */ + uint32_t cr; /* Control Register */ + uint32_t torr; /* Timeout Range Register */ + uint32_t ccvr; /* Current Counter Value Register */ + uint32_t stat; /* Interrupt Status Register */ + uint32_t prot_level; /* Protection Level Register */ + + /* Internal state */ + bool interrupt_pending; + bool enabled; + uint32_t timeout_value; + uint32_t current_count; +}; + +#endif /* K230_WDT_H */ diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/k230.c +++ b/hw/riscv/k230.c @@ -XXX,XX +XXX,XX @@ static void k230_soc_init(Object *obj) RISCVHartArrayState *cpu0 = &s->c908_cpu; object_initialize_child(obj, "c908-cpu", cpu0, TYPE_RISCV_HART_ARRAY); + object_initialize_child(obj, "k230-wdt0", &s->wdt[0], TYPE_K230_WDT); + object_initialize_child(obj, "k230-wdt1", &s->wdt[1], TYPE_K230_WDT); + qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0); qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C908); qdev_prop_set_uint64(DEVICE(cpu0), "resetvec", @@ -XXX,XX +XXX,XX @@ static void k230_soc_realize(DeviceState *dev, Error **errp) k230_create_uart(sys_mem, DEVICE(s->c908_plic), i); } + /* Watchdog */ + for (int i = 0; i < 2; i++) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { + return; + } + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[0]), 0, memmap[K230_DEV_WDT0].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[0]), 0, + qdev_get_gpio_in(DEVICE(s->c908_plic), K230_WDT0_IRQ)); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[1]), 0, memmap[K230_DEV_WDT1].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[1]), 0, + qdev_get_gpio_in(DEVICE(s->c908_plic), K230_WDT1_IRQ)); + /* unimplemented devices */ create_unimplemented_device("kpu.l2-cache", memmap[K230_DEV_KPU_L2_CACHE].base, diff --git a/hw/watchdog/k230_wdt.c b/hw/watchdog/k230_wdt.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/watchdog/k230_wdt.c @@ -XXX,XX +XXX,XX @@ +/* + * K230 Watchdog Compatible with kendryte K230 SDK + * + * Copyright (c) 2025 Mig Yang <temashking@foxmail.com> + * Copyright (c) 2025 Chao Liu <chao.liu.zevorn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Provides a board compatible with the kendryte K230 SDK + * + * K230 Technical Reference Manual V0.3.1 (2024-11-18): + * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf + * + * For more information, see <https://www.kendryte.com/en/proDetail/230> + */ +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/module.h" +#include "system/watchdog.h" +#include "migration/vmstate.h" +#include "hw/core/qdev-properties.h" +#include "hw/watchdog/k230_wdt.h" +#include "trace.h" + +static void k230_wdt_timeout(void *opaque) +{ + K230WdtState *s = K230_WDT(opaque); + + trace_k230_wdt_timeout(); + + /* Set interrupt status if in interrupt mode */ + if (s->cr & K230_WDT_CR_RMOD) { + s->stat |= K230_WDT_STAT_INT; + s->interrupt_pending = true; + qemu_set_irq(s->irq, 1); + trace_k230_wdt_interrupt(); + } else { + /* Direct reset mode */ + trace_k230_wdt_reset(); + watchdog_perform_action(); + } + + /* Restart counter */ + s->current_count = s->timeout_value; + ptimer_set_count(s->timer, s->current_count); + ptimer_run(s->timer, 1); +} + +static void k230_wdt_reset(DeviceState *dev) +{ + K230WdtState *s = K230_WDT(dev); + + trace_k230_wdt_reset_device(); + + ptimer_transaction_begin(s->timer); + ptimer_stop(s->timer); + ptimer_transaction_commit(s->timer); + + /* Reset registers to default values */ + s->cr = 0; + s->torr = 0; + s->ccvr = 0xFFFFFFFF; + s->stat = 0; + s->prot_level = 0x2; + + s->interrupt_pending = false; + s->enabled = false; + s->timeout_value = 0; + s->current_count = 0xFFFFFFFF; +} + +static uint64_t k230_wdt_read(void *opaque, hwaddr addr, unsigned int size) +{ + K230WdtState *s = K230_WDT(opaque); + uint32_t value = 0; + + switch (addr) { + case K230_WDT_CR: + value = s->cr; + break; + case K230_WDT_TORR: + value = s->torr; + break; + case K230_WDT_CCVR: + if (s->enabled) { + value = ptimer_get_count(s->timer); + } else { + value = s->current_count; + } + break; + case K230_WDT_STAT: + value = s->stat; + break; + case K230_WDT_PROT_LEVEL: + value = s->prot_level; + break; + case K230_WDT_COMP_PARAM_5: + value = 0; /* Upper limit of Timeout Period parameters */ + break; + case K230_WDT_COMP_PARAM_4: + value = 0; /* Upper limit of Initial Timeout Period parameters */ + break; + case K230_WDT_COMP_PARAM_3: + value = 0; /* Derived from WDT_TOP_RST parameter */ + break; + case K230_WDT_COMP_PARAM_2: + value = 0xFFFFFFFF; /* Derived from WDT_RST_CNT parameter */ + break; + case K230_WDT_COMP_PARAM_1: + /* Component parameters */ + value = (32 << K230_WDT_CNT_WIDTH_SHIFT) | /* 32-bit counter */ + (0 << K230_WDT_DFLT_TOP_INIT_SHIFT) | + (0 << K230_WDT_DFLT_TOP_SHIFT) | + (K230_WDT_RPL_16_CYCLES << K230_WDT_DFLT_RPL_SHIFT) | + (2 << K230_WDT_APB_DATA_WIDTH_SHIFT) | /* 32-bit APB */ + K230_WDT_USE_FIX_TOP; /* Use fixed timeout values */ + break; + case K230_WDT_COMP_VERSION: + value = K230_WDT_COMP_VERSION_VAL; + break; + case K230_WDT_COMP_TYPE: + value = K230_WDT_COMP_TYPE_VAL; + break; + default: + /* Other registers return 0 */ + break; + } + + trace_k230_wdt_read(addr, value); + return value; +} + +static void k230_wdt_update_timer(K230WdtState *s) +{ + ptimer_transaction_begin(s->timer); + + if (s->enabled && s->timeout_value > 0) { + ptimer_set_count(s->timer, s->current_count); + ptimer_run(s->timer, 1); + } else { + ptimer_stop(s->timer); + } + + ptimer_transaction_commit(s->timer); +} + +static uint32_t k230_wdt_calculate_timeout(uint32_t top_value) +{ + /* Calculate timeout based on TOP value */ + /* For fixed timeout mode: 2^(16 + top_value) */ + if (top_value <= 15) { + return 1 << (16 + top_value); + } + return 1 << 31; /* Maximum value for 32-bit counter */ +} + +static void k230_wdt_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + K230WdtState *s = K230_WDT(opaque); + + trace_k230_wdt_write(addr, value); + + switch (addr) { + case K230_WDT_CR: + s->cr = value & (K230_WDT_CR_RPL_MASK << K230_WDT_CR_RPL_SHIFT | + K230_WDT_CR_RMOD | K230_WDT_CR_WDT_EN); + + /* Update enabled state */ + s->enabled = (s->cr & K230_WDT_CR_WDT_EN) != 0; + + /* Update timer */ + k230_wdt_update_timer(s); + break; + + case K230_WDT_TORR: + s->torr = value & K230_WDT_TORR_TOP_MASK; + + /* Calculate new timeout value */ + s->timeout_value = k230_wdt_calculate_timeout(s->torr); + s->current_count = s->timeout_value; + + /* Update timer if enabled */ + if (s->enabled) { + k230_wdt_update_timer(s); + } + break; + + case K230_WDT_CRR: + /* Restart counter with magic value 0x76 */ + if ((value & 0xFF) == K230_WDT_CRR_RESTART) { + trace_k230_wdt_restart(); + s->current_count = s->timeout_value; + + /* Clear interrupt if pending */ + if (s->interrupt_pending) { + s->stat &= ~K230_WDT_STAT_INT; + s->interrupt_pending = false; + qemu_set_irq(s->irq, 0); + } + + /* Update timer */ + k230_wdt_update_timer(s); + } + break; + + case K230_WDT_EOI: + /* Clear interrupt */ + s->stat &= ~K230_WDT_STAT_INT; + s->interrupt_pending = false; + qemu_set_irq(s->irq, 0); + break; + + case K230_WDT_PROT_LEVEL: + s->prot_level = value & 0x7; + break; + + default: + /* Read-only registers, ignore writes */ + break; + } +} + +static const MemoryRegionOps k230_wdt_ops = { + .read = k230_wdt_read, + .write = k230_wdt_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static const VMStateDescription vmstate_k230_wdt = { + .name = "k230.wdt", + .fields = (const VMStateField[]) { + VMSTATE_PTIMER(timer, K230WdtState), + VMSTATE_UINT32(cr, K230WdtState), + VMSTATE_UINT32(torr, K230WdtState), + VMSTATE_UINT32(ccvr, K230WdtState), + VMSTATE_UINT32(stat, K230WdtState), + VMSTATE_UINT32(prot_level, K230WdtState), + VMSTATE_BOOL(interrupt_pending, K230WdtState), + VMSTATE_BOOL(enabled, K230WdtState), + VMSTATE_UINT32(timeout_value, K230WdtState), + VMSTATE_UINT32(current_count, K230WdtState), + VMSTATE_END_OF_LIST() + } +}; + +static void k230_wdt_realize(DeviceState *dev, Error **errp) +{ + K230WdtState *s = K230_WDT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->mmio, OBJECT(dev), + &k230_wdt_ops, s, + TYPE_K230_WDT, + K230_WDT_MMIO_SIZE); + sysbus_init_mmio(sbd, &s->mmio); + sysbus_init_irq(sbd, &s->irq); + + s->timer = ptimer_init(k230_wdt_timeout, s, + PTIMER_POLICY_NO_IMMEDIATE_TRIGGER | + PTIMER_POLICY_NO_IMMEDIATE_RELOAD | + PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + + ptimer_transaction_begin(s->timer); + ptimer_set_freq(s->timer, K230_WDT_DEFAULT_FREQ); + ptimer_transaction_commit(s->timer); +} + +static void k230_wdt_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = k230_wdt_realize; + device_class_set_legacy_reset(dc, k230_wdt_reset); + dc->vmsd = &vmstate_k230_wdt; + dc->desc = "K230 watchdog timer"; + set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); +} + +static const TypeInfo k230_wdt_info = { + .name = TYPE_K230_WDT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(K230WdtState), + .class_init = k230_wdt_class_init, +}; + +static void k230_wdt_register_type(void) +{ + type_register_static(&k230_wdt_info); +} +type_init(k230_wdt_register_type) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -XXX,XX +XXX,XX @@ config K230 select RISCV_IMSIC select SERIAL_MM select UNIMP + select K230_WDT diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/watchdog/Kconfig +++ b/hw/watchdog/Kconfig @@ -XXX,XX +XXX,XX @@ config WDT_DIAG288 config WDT_IMX2 bool +config K230_WDT + bool + select PTIMER + config WDT_SBSA bool diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/watchdog/meson.build +++ b/hw/watchdog/meson.build @@ -XXX,XX +XXX,XX @@ system_ss.add(when: 'CONFIG_WDT_IB700', if_true: files('wdt_ib700.c')) system_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c')) system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c')) system_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) +system_ss.add(when: 'CONFIG_K230_WDT', if_true: files('k230_wdt.c')) system_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_watchdog.c')) diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/watchdog/trace-events +++ b/hw/watchdog/trace-events @@ -XXX,XX +XXX,XX @@ spapr_watchdog_expired(uint64_t num, unsigned action) "num=%" PRIu64 " action=%u # watchdog.c watchdog_perform_action(unsigned int action) "action=%u" watchdog_set_action(unsigned int action) "action=%u" + +# k230_wdt.c +k230_wdt_read(uint64_t addr, uint32_t data) "K230 WDT read: [0x%" PRIx64 "] -> 0x%" PRIx32 +k230_wdt_write(uint64_t addr, uint64_t data) "K230 WDT write: [0x%" PRIx64 "] <- 0x%" PRIx64 +k230_wdt_timeout(void) "K230 WDT timeout" +k230_wdt_interrupt(void) "K230 WDT interrupt" +k230_wdt_reset(void) "K230 WDT system reset" +k230_wdt_restart(void) "K230 WDT restart" +k230_wdt_reset_device(void) "K230 WDT device reset" -- 2.54.0
From: Chao Liu <chao.liu@zevorn.cn> Testing the Basic Functions of K230 WDT: 1. Reset Function 2. Timeout Check 3. Interrupt Function Signed-off-by: Mig Yang <temashking@foxmail.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Acked-by: Fabiano Rosas <farosas@suse.de> Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Message-ID: <791beb1d8db07e4d1011cbeb4a8ac3add5b24f09.1781246408.git.chao.liu@processmission.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- MAINTAINERS | 1 + tests/qtest/k230-wdt-test.c | 189 ++++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 3 +- 3 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/k230-wdt-test.c diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ F: hw/riscv/k230.c F: hw/watchdog/k230_wdt.c F: include/hw/riscv/k230.h F: include/hw/watchdog/k230_wdt.h +F: tests/qtest/k230-wdt-test.c RX Machines ----------- diff --git a/tests/qtest/k230-wdt-test.c b/tests/qtest/k230-wdt-test.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/qtest/k230-wdt-test.c @@ -XXX,XX +XXX,XX @@ +/* + * QTest testcase for K230 Watchdog + * + * Copyright (c) 2025 Mig Yang <temashking@foxmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Provides a board compatible with the kendryte K230 SDK + * + * K230 Technical Reference Manual V0.3.1 (2024-11-18): + * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf + * + * For more information, see <https://www.kendryte.com/en/proDetail/230> + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "qemu/bitops.h" +#include "libqtest.h" +#include "hw/watchdog/k230_wdt.h" + +/* K230 WDT0 base address */ +#define K230_WDT0_BASE 0x91106000 +#define K230_WDT1_BASE 0x91106800 + +/* Test WDT0 by default */ +#define WDT_BASE K230_WDT0_BASE + +static void test_register_read_write(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + /* Test Control Register (CR) read/write */ + qtest_writel(qts, WDT_BASE + K230_WDT_CR, 0xFFFFFFFF); + g_assert_cmphex(qtest_readl(qts, WDT_BASE + K230_WDT_CR), ==, + (K230_WDT_CR_RPL_MASK << K230_WDT_CR_RPL_SHIFT) | + K230_WDT_CR_RMOD | K230_WDT_CR_WDT_EN); + + /* Test Timeout Range Register (TORR) read/write */ + qtest_writel(qts, WDT_BASE + K230_WDT_TORR, 0xFFFFFFFF); + g_assert_cmphex(qtest_readl(qts, WDT_BASE + K230_WDT_TORR), ==, + K230_WDT_TORR_TOP_MASK); + + /* Test Protection Level Register read/write */ + qtest_writel(qts, WDT_BASE + K230_WDT_PROT_LEVEL, 0xFFFFFFFF); + g_assert_cmphex(qtest_readl(qts, WDT_BASE + K230_WDT_PROT_LEVEL), ==, 0x7); + + qtest_quit(qts); +} + +static void test_counter_restart(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + /* Enable watchdog and set timeout */ + qtest_writel(qts, WDT_BASE + K230_WDT_CR, K230_WDT_CR_WDT_EN); + qtest_writel(qts, WDT_BASE + K230_WDT_TORR, 0x5); /* TOP = 5 */ + + /* Read current counter value */ + uint32_t initial_count = qtest_readl(qts, WDT_BASE + K230_WDT_CCVR); + g_assert_cmpuint(initial_count, >, 0); + + /* Restart counter with magic value */ + qtest_writel(qts, WDT_BASE + K230_WDT_CRR, K230_WDT_CRR_RESTART); + + /* Wait for time */ + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 2); + + /* Counter should be reset to timeout value */ + uint32_t new_count = qtest_readl(qts, WDT_BASE + K230_WDT_CCVR); + g_assert_cmpuint(new_count, >, 0); + g_assert_cmpuint(new_count, !=, initial_count); + + qtest_quit(qts); +} + +static void test_interrupt_mode(void) +{ + QTestState *qts = qtest_init("-machine k230 --trace k230_*,file=k230.log"); + + /* Set interrupt mode and enable watchdog */ + qtest_writel(qts, WDT_BASE + K230_WDT_CR, + K230_WDT_CR_RMOD | K230_WDT_CR_WDT_EN); + qtest_writel(qts, WDT_BASE + K230_WDT_TORR, 0x1); /* Short timeout */ + + /* Wait for timeout to trigger interrupt */ + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 10); + + /* Check interrupt status */ + uint32_t stat = qtest_readl(qts, WDT_BASE + K230_WDT_STAT); + g_assert_cmphex(stat & K230_WDT_STAT_INT, ==, K230_WDT_STAT_INT); + + /* Clear interrupt */ + qtest_writel(qts, WDT_BASE + K230_WDT_EOI, 0x1); + stat = qtest_readl(qts, WDT_BASE + K230_WDT_STAT); + g_assert_cmphex(stat & K230_WDT_STAT_INT, ==, 0); + + qtest_quit(qts); +} + +static void test_reset_mode(void) +{ + QTestState *qts = qtest_init("-machine k230 -no-reboot"); + + /* Set reset mode and enable watchdog */ + qtest_writel(qts, WDT_BASE + K230_WDT_CR, K230_WDT_CR_WDT_EN); + qtest_writel(qts, WDT_BASE + K230_WDT_TORR, 0x1); /* Short timeout */ + + /* Wait for timeout to trigger reset */ + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 2); + + /* In reset mode, the system should reset */ + /* This test verifies that reset mode is properly configured */ + + qtest_quit(qts); +} + +static void test_timeout_calculation(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + /* Test different timeout values */ + for (uint32_t top = 0; top <= 15; top++) { + qtest_writel(qts, WDT_BASE + K230_WDT_TORR, top); + qtest_writel(qts, WDT_BASE + K230_WDT_CR, K230_WDT_CR_WDT_EN); + + /* Read current counter value */ + uint32_t count = qtest_readl(qts, WDT_BASE + K230_WDT_CCVR); + g_assert_cmpuint(count, >, 0); + + /* Disable watchdog for next iteration */ + qtest_writel(qts, WDT_BASE + K230_WDT_CR, 0); + } + + qtest_quit(qts); +} + +static void test_wdt1_registers(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + /* Test WDT1 registers (second watchdog) */ + qtest_writel(qts, K230_WDT1_BASE + K230_WDT_CR, 0xFFFFFFFF); + g_assert_cmphex(qtest_readl(qts, K230_WDT1_BASE + K230_WDT_CR), ==, + (K230_WDT_CR_RPL_MASK << K230_WDT_CR_RPL_SHIFT) | + K230_WDT_CR_RMOD | K230_WDT_CR_WDT_EN); + + qtest_writel(qts, K230_WDT1_BASE + K230_WDT_TORR, 0xFFFFFFFF); + g_assert_cmphex(qtest_readl(qts, K230_WDT1_BASE + K230_WDT_TORR), ==, + K230_WDT_TORR_TOP_MASK); + + qtest_quit(qts); +} + +static void test_enable_disable(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + /* Initially disabled */ + uint32_t cr = qtest_readl(qts, WDT_BASE + K230_WDT_CR); + g_assert_cmphex(cr & K230_WDT_CR_WDT_EN, ==, 0); + + /* Enable watchdog */ + qtest_writel(qts, WDT_BASE + K230_WDT_CR, K230_WDT_CR_WDT_EN); + cr = qtest_readl(qts, WDT_BASE + K230_WDT_CR); + g_assert_cmphex(cr & K230_WDT_CR_WDT_EN, ==, K230_WDT_CR_WDT_EN); + + /* Disable watchdog */ + qtest_writel(qts, WDT_BASE + K230_WDT_CR, 0); + cr = qtest_readl(qts, WDT_BASE + K230_WDT_CR); + g_assert_cmphex(cr & K230_WDT_CR_WDT_EN, ==, 0); + + qtest_quit(qts); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/k230-wdt/register_read_write", test_register_read_write); + qtest_add_func("/k230-wdt/counter_restart", test_counter_restart); + qtest_add_func("/k230-wdt/interrupt_mode", test_interrupt_mode); + qtest_add_func("/k230-wdt/reset_mode", test_reset_mode); + qtest_add_func("/k230-wdt/timeout_calculation", test_timeout_calculation); + qtest_add_func("/k230-wdt/wdt1_registers", test_wdt1_registers); + qtest_add_func("/k230-wdt/enable_disable", test_enable_disable); + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -XXX,XX +XXX,XX @@ qtests_riscv64 = ['riscv-csr-test'] + \ (unpack_edk2_blobs ? ['bios-tables-test'] : []) + \ (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and config_all_devices.has_key('CONFIG_RISCV_IOMMU') ? - ['iommu-riscv-test'] : []) + ['iommu-riscv-test'] : []) + \ + (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : []) qos_test_ss = ss.source_set() qos_test_ss.add( -- 2.54.0
From: Chao Liu <chao.liu.zevorn@gmail.com> Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <81d2e2fa42ecabf638f841321cf36cee8f10af01.1781246408.git.chao.liu@processmission.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- MAINTAINERS | 1 + docs/system/riscv/k230.rst | 113 +++++++++++++++++++++++++++++++++++ docs/system/target-riscv.rst | 1 + 3 files changed, 115 insertions(+) create mode 100644 docs/system/riscv/k230.rst diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ K230 Machines M: Chao Liu <chao.liu.zevorn@gmail.com> L: qemu-riscv@nongnu.org S: Maintained +F: docs/system/riscv/k230.rst F: hw/riscv/k230.c F: hw/watchdog/k230_wdt.c F: include/hw/riscv/k230.h diff --git a/docs/system/riscv/k230.rst b/docs/system/riscv/k230.rst new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/docs/system/riscv/k230.rst @@ -XXX,XX +XXX,XX @@ +Kendryte K230 virt reference platform (``k230``) +========================================================================== +The ``k230`` machine is compatible with the Kendryte K230 SDK. + +The K230 is a chip from the AIoT SoC series made by Kendryte ® — a part of +Canaan Inc. It uses a brand-new multi-heterogeneous unit accelerated computing +structure. + +This chip has 2 RISC-V computing cores and a new-generation KPU (Knowledge +Process Unit) smart computing unit. + +For more information, see <https://www.kendryte.com/en/proDetail/230> + +Supported devices +----------------- +The ``k230`` machine supports the following devices: + +* 1 c908 cores (little core) +* Core Local Interruptor (CLINT) +* Platform-Level Interrupt Controller (PLIC) +* 2 K230 Watchdog Timer +* 5 UART + +Boot options +------------ +The ``k230`` machine supports K230 SDK boot through M-mode U-Boot, which then +starts OpenSBI/Linux with ``bootm``. It also supports direct Linux boot. + +K230 SDK Linux kernels use T-HEAD C9xx private MAEE page table attributes. QEMU +does not implement MAEE in the generic RISC-V MMU, so such kernels need to be +built with standard RISC-V PTE bits before they can boot under QEMU. + +Running +------- + +Direct Linux boot +~~~~~~~~~~~~~~~~~ + +This flow lets QEMU load OpenSBI, Linux, initrd, and DTB directly, without +running SDK U-Boot. The Linux Image must be rebuilt with standard RISC-V PTE +bits before running under QEMU. + +.. code-block:: bash + + $ SDK=k230_sdk/output/k230_canmv_defconfig + $ qemu-system-riscv64 -machine k230 \ + -kernel "$SDK/images/little-core/Image" \ + -dtb "/tmp/user-k230-qemu.dtb" \ + -initrd "$SDK/images/little-core/rootfs.cpio.gz" \ + -append "console=ttyS0,115200 earlycon=sbi cma=0" \ + -nographic + +Direct boot uses the SDK little-core RAM layout for OpenSBI at +``0x08000000``, Linux at ``0x08200000``, and the DTB at ``0x0a000000``. The +initrd is placed by QEMU's generic RISC-V boot helper, and QEMU writes the +initrd range and kernel command line into ``/chosen``. The DTB passed with +``-dtb`` should be derived from ``$SDK/images/little-core/k230.dtb`` and must +describe that initrd location as usable memory and disable any devices that are +not emulated by this machine. + +U-Boot boot +~~~~~~~~~~~ + +This flow starts SDK U-Boot in M-mode with ``-bios``. Until the SDK storage +path is modeled, place OpenSBI, Linux, initrd, and DTB in RAM with loader +devices and run ``bootm`` manually. The Linux Image must be rebuilt with +standard RISC-V PTE bits before running under QEMU. + +.. code-block:: bash + + $ SDK=k230_sdk/output/k230_canmv_defconfig + $ IMAGE=$SDK/images/little-core/Image + $ INITRD=$SDK/images/little-core/rootfs.cpio.gz + $ DTB=$SDK/images/little-core/k230.dtb + $ FWJUMP_UIMAGE=/tmp/k230-fw-jump.uImage + $ INITRD_END=$(printf "0x%x" $((0x0a100000 + $(stat -c %s "$INITRD")))) + $ "$SDK/little/buildroot-ext/host/bin/mkimage" \ + -A riscv -O linux -T kernel -C none \ + -a 0x8000000 -e 0x8000000 -n opensbi \ + -d "$SDK/images/little-core/fw_jump.bin" "$FWJUMP_UIMAGE" + $ qemu-system-riscv64 -machine k230 \ + -bios "$SDK/little/uboot/u-boot" \ + -device loader,file="$FWJUMP_UIMAGE",addr=0xc100000,force-raw=on \ + -device loader,file="$IMAGE",addr=0x8200000,force-raw=on \ + -device loader,file="$INITRD",addr=0xa100000,force-raw=on \ + -device loader,file="$DTB",addr=0xa000000,force-raw=on \ + -nographic + +The loader addresses mirror the SDK ``k230_canmv_defconfig`` output. Read the +U-Boot addresses from the generated environment, and read the Linux RAM base +from the generated ``hw/k230.dts.txt``. This replaces the SDK storage and +decompression steps. + +Press Enter to stop autoboot. At the U-Boot prompt, run these commands: + +.. code-block:: bash + + K230# setenv bootargs console=ttyS0,115200 earlycon=sbi cma=0 + K230# fdt addr 0xa000000 + K230# fdt resize 8192 + K230# fdt set /chosen linux,initrd-start <0x0 0xa100000> + K230# fdt set /chosen linux,initrd-end <0x0 ${INITRD_END}> + K230# fdt set /soc/sdhci0@91580000 status disabled + K230# fdt set /soc/sdhci1@91581000 status disabled + K230# bootm 0xc100000 - 0xa000000 + +Use ``setenv`` so ``bootm`` writes the kernel command line into +``/chosen/bootargs``. The ``fdt`` commands select the loaded DTB, add space for +edits, describe the initrd range in ``/chosen``, and disable SDHCI nodes because +this machine does not emulate those controllers yet. Replace ``${INITRD_END}`` +with the host-calculated value above when typing the command. ``cma=0`` avoids +the SDK kernel reserving too much of the little-core memory window for initramfs +boot. diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/target-riscv.rst +++ b/docs/system/target-riscv.rst @@ -XXX,XX +XXX,XX @@ undocumented; you can get a complete list by running .. toctree:: :maxdepth: 1 + riscv/k230 riscv/microblaze-v-generic riscv/microchip-icicle-kit riscv/mips -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We're assigning a 'cpu_phandle' phandle to the cpu-intc phandle field. Make it more in line with the other boards by assigning both a cpu_phandle and a intc phandle. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615203734.954428-2-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/sifive_u.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, qemu_fdt_setprop_string(fdt, nodename, "status", "okay"); qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu); qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu"); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", cpu_phandle); + qemu_fdt_add_subnode(fdt, intc); - qemu_fdt_setprop_cell(fdt, intc, "phandle", cpu_phandle); + qemu_fdt_setprop_cell(fdt, intc, "phandle", phandle++); qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc"); qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0); qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> There's too much duplication between RISC-V boards and one of the most common culprits is the FDT functions. Add a new file for board FDT helpers. Start by creating a helper that initializes the FDT and init it with the common board boilerplate. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615203734.954428-3-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/fdt-common.h | 14 +++++++++++++ hw/riscv/fdt-common.c | 37 +++++++++++++++++++++++++++++++++++ hw/riscv/sifive_u.c | 20 +++---------------- hw/riscv/spike.c | 19 +++--------------- hw/riscv/virt.c | 19 +++--------------- hw/riscv/meson.build | 1 + 6 files changed, 61 insertions(+), 49 deletions(-) create mode 100644 include/hw/riscv/fdt-common.h create mode 100644 hw/riscv/fdt-common.c diff --git a/include/hw/riscv/fdt-common.h b/include/hw/riscv/fdt-common.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/riscv/fdt-common.h @@ -XXX,XX +XXX,XX @@ +/* + * RISC-V board helpers for FDT generation. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RISCV_VIRT_FDT_H +#define RISCV_VIRT_FDT_H + +void *create_board_device_tree(const char *model, const char *compatible, + int *fdt_size); +#endif diff --git a/hw/riscv/fdt-common.c b/hw/riscv/fdt-common.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/riscv/fdt-common.c @@ -XXX,XX +XXX,XX @@ +/* + * RISC-V board helpers for FDT generation. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "qemu/error-report.h" +#include "system/device_tree.h" +#include "hw/riscv/fdt-common.h" + +void *create_board_device_tree(const char *model, const char *compatible, + int *fdt_size) +{ + void *fdt = create_device_tree(fdt_size); + + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + qemu_fdt_setprop_string(fdt, "/", "model", model); + qemu_fdt_setprop_string(fdt, "/", "compatible", compatible); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); + + qemu_fdt_add_subnode(fdt, "/soc"); + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus"); + qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); + + return fdt; +} diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -XXX,XX +XXX,XX @@ #include "hw/riscv/sifive_u.h" #include "hw/riscv/boot.h" #include "hw/riscv/machines-qom.h" +#include "hw/riscv/fdt-common.h" #include "hw/char/sifive_uart.h" #include "hw/intc/riscv_aclint.h" #include "hw/intc/sifive_plic.h" @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, "sifive,plic-1.0.0", "riscv,plic0" }; - fdt = ms->fdt = create_device_tree(&s->fdt_size); - if (!fdt) { - error_report("create_device_tree() failed"); - exit(1); - } - - qemu_fdt_setprop_string(fdt, "/", "model", "SiFive HiFive Unleashed A00"); - qemu_fdt_setprop_string(fdt, "/", "compatible", - "sifive,hifive-unleashed-a00"); - qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); - qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); - - qemu_fdt_add_subnode(fdt, "/soc"); - qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); - qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus"); - qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); - qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); + fdt = ms->fdt = create_board_device_tree("SiFive HiFive Unleashed A00", + "sifive,hifive-unleashed-a00", &s->fdt_size); hfclk_phandle = phandle++; nodename = g_strdup_printf("/hfclk"); diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -XXX,XX +XXX,XX @@ #include "hw/riscv/riscv_hart.h" #include "hw/riscv/spike.h" #include "hw/riscv/boot.h" +#include "hw/riscv/fdt-common.h" #include "hw/riscv/numa.h" #include "hw/riscv/machines-qom.h" #include "hw/char/riscv_htif.h" @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, "sifive,clint0", "riscv,clint0" }; - fdt = ms->fdt = create_device_tree(&fdt_size); - if (!fdt) { - error_report("create_device_tree() failed"); - exit(1); - } - - qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu"); - qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev"); - qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); - qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); + fdt = ms->fdt = create_board_device_tree("ucbbar,spike-bare,qemu", + "ucbbar,spike-bare-dev", &fdt_size); qemu_fdt_add_subnode(fdt, "/htif"); qemu_fdt_setprop_string(fdt, "/htif", "compatible", "ucb,htif0"); @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, 0x0, memmap[SPIKE_HTIF].base, 0x0, memmap[SPIKE_HTIF].size); } - qemu_fdt_add_subnode(fdt, "/soc"); - qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); - qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus"); - qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); - qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); - qemu_fdt_add_subnode(fdt, "/cpus"); qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -XXX,XX +XXX,XX @@ #include "hw/riscv/riscv-iommu-bits.h" #include "hw/riscv/virt.h" #include "hw/riscv/boot.h" +#include "hw/riscv/fdt-common.h" #include "hw/riscv/machines-qom.h" #include "hw/riscv/numa.h" #include "kvm/kvm_riscv.h" @@ -XXX,XX +XXX,XX @@ static void create_fdt(RISCVVirtState *s) uint8_t rng_seed[32]; g_autofree char *name = NULL; - ms->fdt = create_device_tree(&s->fdt_size); - if (!ms->fdt) { - error_report("create_device_tree() failed"); - exit(1); - } - - qemu_fdt_setprop_string(ms->fdt, "/", "model", "riscv-virtio,qemu"); - qemu_fdt_setprop_string(ms->fdt, "/", "compatible", "riscv-virtio"); - qemu_fdt_setprop_cell(ms->fdt, "/", "#size-cells", 0x2); - qemu_fdt_setprop_cell(ms->fdt, "/", "#address-cells", 0x2); - - qemu_fdt_add_subnode(ms->fdt, "/soc"); - qemu_fdt_setprop(ms->fdt, "/soc", "ranges", NULL, 0); - qemu_fdt_setprop_string(ms->fdt, "/soc", "compatible", "simple-bus"); - qemu_fdt_setprop_cell(ms->fdt, "/soc", "#size-cells", 0x2); - qemu_fdt_setprop_cell(ms->fdt, "/soc", "#address-cells", 0x2); + ms->fdt = create_board_device_tree("riscv-virtio,qemu", "riscv-virtio", + &s->fdt_size); /* * The "/soc/pci@..." node is needed for PCIE hotplugs diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -XXX,XX +XXX,XX @@ riscv_ss = ss.source_set() riscv_ss.add(files('boot.c')) +riscv_ss.add(files('fdt-common.c')) riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: files('numa.c')) riscv_ss.add(files('riscv_hart.c')) riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c')) -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> There's FDT logic gated around 'numa_enabled()' in virt.c and spike.c. We want to move the FDT code to a common helper without having to call hw/riscv/numa.c functions from it, but at the same time being aware of the FDT changes if numa is enabled. To do that the boards will inform the FDT helpers if we have numa_enabled in the env or not. And for the boards to be able to do that we need the static 'numa_enabled' function to be public. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-ID: <20260615203734.954428-4-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/numa.h | 8 ++++++++ hw/riscv/numa.c | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/include/hw/riscv/numa.h b/include/hw/riscv/numa.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/numa.h +++ b/include/hw/riscv/numa.h @@ -XXX,XX +XXX,XX @@ #include "hw/core/sysbus.h" #include "system/numa.h" +/** + * riscv_numa_enabled: + * @ms: pointer to machine state + * + * Returns: true if NUMA is enabled in the machine state. + */ +bool riscv_numa_enabled(const MachineState *ms); + /** * riscv_socket_count: * @ms: pointer to machine state diff --git a/hw/riscv/numa.c b/hw/riscv/numa.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/numa.c +++ b/hw/riscv/numa.c @@ -XXX,XX +XXX,XX @@ #include "hw/riscv/numa.h" #include "system/device_tree.h" -static bool numa_enabled(const MachineState *ms) +bool riscv_numa_enabled(const MachineState *ms) { return (ms->numa_state && ms->numa_state->num_nodes) ? true : false; } int riscv_socket_count(const MachineState *ms) { - return (numa_enabled(ms)) ? ms->numa_state->num_nodes : 1; + return (riscv_numa_enabled(ms)) ? ms->numa_state->num_nodes : 1; } int riscv_socket_first_hartid(const MachineState *ms, int socket_id) { int i, first_hartid = ms->smp.cpus; - if (!numa_enabled(ms)) { + if (!riscv_numa_enabled(ms)) { return (!socket_id) ? 0 : -1; } @@ -XXX,XX +XXX,XX @@ int riscv_socket_last_hartid(const MachineState *ms, int socket_id) { int i, last_hartid = -1; - if (!numa_enabled(ms)) { + if (!riscv_numa_enabled(ms)) { return (!socket_id) ? ms->smp.cpus - 1 : -1; } @@ -XXX,XX +XXX,XX @@ int riscv_socket_hart_count(const MachineState *ms, int socket_id) { int first_hartid, last_hartid; - if (!numa_enabled(ms)) { + if (!riscv_numa_enabled(ms)) { return (!socket_id) ? ms->smp.cpus : -1; } @@ -XXX,XX +XXX,XX @@ bool riscv_socket_check_hartids(const MachineState *ms, int socket_id) { int i, first_hartid, last_hartid; - if (!numa_enabled(ms)) { + if (!riscv_numa_enabled(ms)) { return (!socket_id) ? true : false; } @@ -XXX,XX +XXX,XX @@ uint64_t riscv_socket_mem_offset(const MachineState *ms, int socket_id) int i; uint64_t mem_offset = 0; - if (!numa_enabled(ms)) { + if (!riscv_numa_enabled(ms)) { return 0; } @@ -XXX,XX +XXX,XX @@ uint64_t riscv_socket_mem_offset(const MachineState *ms, int socket_id) uint64_t riscv_socket_mem_size(const MachineState *ms, int socket_id) { - if (!numa_enabled(ms)) { + if (!riscv_numa_enabled(ms)) { return (!socket_id) ? ms->ram_size : 0; } @@ -XXX,XX +XXX,XX @@ uint64_t riscv_socket_mem_size(const MachineState *ms, int socket_id) void riscv_socket_fdt_write_id(const MachineState *ms, const char *node_name, int socket_id) { - if (numa_enabled(ms)) { + if (riscv_numa_enabled(ms)) { qemu_fdt_setprop_cell(ms->fdt, node_name, "numa-node-id", socket_id); } } @@ -XXX,XX +XXX,XX @@ void riscv_socket_fdt_write_distance_matrix(const MachineState *ms) g_autofree uint32_t *dist_matrix = NULL; uint32_t dist_matrix_size; - if (numa_enabled(ms) && ms->numa_state->have_numa_distance) { + if (riscv_numa_enabled(ms) && ms->numa_state->have_numa_distance) { dist_matrix_size = riscv_socket_count(ms) * riscv_socket_count(ms); dist_matrix_size *= (3 * sizeof(uint32_t)); dist_matrix = g_malloc0(dist_matrix_size); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> This helper encapsulates the creation of /memory@addr FDT subnodes. Boards are responsible for calculating the adequate addr, size and inform if we have numa enabled. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615203734.954428-5-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/fdt-common.h | 2 ++ hw/riscv/fdt-common.c | 15 +++++++++++++++ hw/riscv/sifive_u.c | 11 ++--------- hw/riscv/spike.c | 18 +++++++----------- hw/riscv/virt.c | 23 ++++++----------------- 5 files changed, 32 insertions(+), 37 deletions(-) diff --git a/include/hw/riscv/fdt-common.h b/include/hw/riscv/fdt-common.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/fdt-common.h +++ b/include/hw/riscv/fdt-common.h @@ -XXX,XX +XXX,XX @@ void *create_board_device_tree(const char *model, const char *compatible, int *fdt_size); +void create_fdt_socket_memory(void *fdt, hwaddr addr, uint64_t size, + int socket_id, bool numa_enabled); #endif diff --git a/hw/riscv/fdt-common.c b/hw/riscv/fdt-common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/fdt-common.c +++ b/hw/riscv/fdt-common.c @@ -XXX,XX +XXX,XX @@ #include "qemu/error-report.h" #include "system/device_tree.h" +#include "hw/core/boards.h" #include "hw/riscv/fdt-common.h" void *create_board_device_tree(const char *model, const char *compatible, @@ -XXX,XX +XXX,XX @@ void *create_board_device_tree(const char *model, const char *compatible, return fdt; } + +void create_fdt_socket_memory(void *fdt, hwaddr addr, uint64_t size, + int socket_id, bool numa_enabled) +{ + g_autofree char *mem_name = g_strdup_printf("/memory@%"HWADDR_PRIx, addr); + + qemu_fdt_add_subnode(fdt, mem_name); + qemu_fdt_setprop_sized_cells(fdt, mem_name, "reg", 2, addr, 2, size); + qemu_fdt_setprop_string(fdt, mem_name, "device_type", "memory"); + + if (numa_enabled) { + qemu_fdt_setprop_cell(fdt, mem_name, "numa-node-id", socket_id); + } +} diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, bool is_32_bit) { MachineState *ms = MACHINE(s); - uint64_t mem_size = ms->ram_size; void *fdt; int cpu; uint32_t *cells; @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, qemu_fdt_setprop_cell(fdt, nodename, "#clock-cells", 0x0); g_free(nodename); - nodename = g_strdup_printf("/memory@%lx", - (long)memmap[SIFIVE_U_DEV_DRAM].base); - qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_cells(fdt, nodename, "reg", - memmap[SIFIVE_U_DEV_DRAM].base >> 32, memmap[SIFIVE_U_DEV_DRAM].base, - mem_size >> 32, mem_size); - qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); - g_free(nodename); + create_fdt_socket_memory(fdt, memmap[SIFIVE_U_DEV_DRAM].base, + ms->ram_size, 0, false); qemu_fdt_add_subnode(fdt, "/cpus"); qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, { void *fdt; int fdt_size; - uint64_t addr, size; unsigned long clint_addr; int cpu, socket; MachineState *ms = MACHINE(s); uint32_t *clint_cells; uint32_t cpu_phandle, intc_phandle, phandle = 1; - char *mem_name, *clint_name, *clust_name; + char *clint_name, *clust_name; char *core_name, *cpu_name, *intc_name; static const char * const clint_compat[2] = { "sifive,clint0", "riscv,clint0" @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, qemu_fdt_add_subnode(fdt, "/cpus/cpu-map"); for (socket = (riscv_socket_count(ms) - 1); socket >= 0; socket--) { + hwaddr memaddr = memmap[SPIKE_DRAM].base + + riscv_socket_mem_offset(ms, socket); + uint64_t memsize = riscv_socket_mem_size(ms, socket); + clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket); qemu_fdt_add_subnode(fdt, clust_name); @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, g_free(cpu_name); } - addr = memmap[SPIKE_DRAM].base + riscv_socket_mem_offset(ms, socket); - size = riscv_socket_mem_size(ms, socket); - mem_name = g_strdup_printf("/memory@%lx", (long)addr); - qemu_fdt_add_subnode(fdt, mem_name); - qemu_fdt_setprop_cells(fdt, mem_name, "reg", - addr >> 32, addr, size >> 32, size); - qemu_fdt_setprop_string(fdt, mem_name, "device_type", "memory"); - riscv_socket_fdt_write_id(ms, mem_name, socket); - g_free(mem_name); + create_fdt_socket_memory(fdt, memaddr, memsize, socket, + riscv_numa_enabled(ms)); clint_addr = memmap[SPIKE_CLINT].base + (memmap[SPIKE_CLINT].size * socket); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -XXX,XX +XXX,XX @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, } } -static void create_fdt_socket_memory(RISCVVirtState *s, int socket) -{ - g_autofree char *mem_name = NULL; - hwaddr addr; - uint64_t size; - MachineState *ms = MACHINE(s); - - addr = s->memmap[VIRT_DRAM].base + riscv_socket_mem_offset(ms, socket); - size = riscv_socket_mem_size(ms, socket); - mem_name = g_strdup_printf("/memory@%"HWADDR_PRIx, addr); - qemu_fdt_add_subnode(ms->fdt, mem_name); - qemu_fdt_setprop_sized_cells(ms->fdt, mem_name, "reg", 2, addr, 2, size); - qemu_fdt_setprop_string(ms->fdt, mem_name, "device_type", "memory"); - riscv_socket_fdt_write_id(ms, mem_name, socket); -} - static void create_fdt_socket_clint(RISCVVirtState *s, int socket, uint32_t *intc_phandles) @@ -XXX,XX +XXX,XX @@ static void create_fdt_sockets(RISCVVirtState *s, phandle_pos = ms->smp.cpus; for (socket = (socket_count - 1); socket >= 0; socket--) { g_autofree char *clust_name = NULL; + hwaddr memaddr = s->memmap[VIRT_DRAM].base + + riscv_socket_mem_offset(ms, socket); + uint64_t memsize = riscv_socket_mem_size(ms, socket); + phandle_pos -= s->soc[socket].num_harts; clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket); @@ -XXX,XX +XXX,XX @@ static void create_fdt_sockets(RISCVVirtState *s, create_fdt_socket_cpus(s, socket, clust_name, phandle, &intc_phandles[phandle_pos]); - create_fdt_socket_memory(s, socket); + create_fdt_socket_memory(ms->fdt, memaddr, memsize, + socket, riscv_numa_enabled(ms)); if (virt_aclint_allowed() && s->have_aclint) { create_fdt_socket_aclint(s, socket, -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Store the intc phandles in an 'intc_phandles' array, like the 'virt' board does, instead of re-creating the interrupt-controller FDT string and using qemu_fdt_get_phandle() to fetch it. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615203734.954428-6-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/sifive_u.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, static const char * const plic_compat[2] = { "sifive,plic-1.0.0", "riscv,plic0" }; + g_autofree uint32_t *intc_phandles = g_new0(uint32_t, ms->smp.cpus); fdt = ms->fdt = create_board_device_tree("SiFive HiFive Unleashed A00", "sifive,hifive-unleashed-a00", &s->fdt_size); @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu"); qemu_fdt_setprop_cell(fdt, nodename, "phandle", cpu_phandle); + intc_phandles[cpu] = phandle++; + qemu_fdt_add_subnode(fdt, intc); - qemu_fdt_setprop_cell(fdt, intc, "phandle", phandle++); + qemu_fdt_setprop_cell(fdt, intc, "phandle", intc_phandles[cpu]); qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc"); qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0); qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1); @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, cells = g_new0(uint32_t, ms->smp.cpus * 4); for (cpu = 0; cpu < ms->smp.cpus; cpu++) { - nodename = - g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); - uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); - cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]); cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT); - cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]); cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); - g_free(nodename); } nodename = g_strdup_printf("/soc/clint@%lx", (long)memmap[SIFIVE_U_DEV_CLINT].base); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> The clint FDT generation uses a cells array (clint_cells) that are populated in the middle of the loop that creates the CPU socket FDT. This is completely fine but it differs from the other boards that creates the clint cells array right before creating the clint FDT. 'virt' and 'sifive_u' store the intc phandles in a intc_phandles array during FDT CPU socket creation, and this array is used to create the clint FDT cells. Standardize the clint FDT creation for spike doing the same here, allowing us to move everything to a common helper later. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615203734.954428-7-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/spike.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, int cpu, socket; MachineState *ms = MACHINE(s); uint32_t *clint_cells; - uint32_t cpu_phandle, intc_phandle, phandle = 1; + uint32_t cpu_phandle, phandle = 1; char *clint_name, *clust_name; char *core_name, *cpu_name, *intc_name; static const char * const clint_compat[2] = { @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, qemu_fdt_add_subnode(fdt, "/cpus/cpu-map"); for (socket = (riscv_socket_count(ms) - 1); socket >= 0; socket--) { + g_autofree uint32_t *intc_phandles = g_new0(uint32_t, + s->soc[socket].num_harts); hwaddr memaddr = memmap[SPIKE_DRAM].base + riscv_socket_mem_offset(ms, socket); uint64_t memsize = riscv_socket_mem_size(ms, socket); @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket); qemu_fdt_add_subnode(fdt, clust_name); - clint_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4); - for (cpu = s->soc[socket].num_harts - 1; cpu >= 0; cpu--) { cpu_phandle = phandle++; @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, riscv_socket_fdt_write_id(ms, cpu_name, socket); qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle); + intc_phandles[cpu] = phandle++; + intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name); qemu_fdt_add_subnode(fdt, intc_name); - intc_phandle = phandle++; - qemu_fdt_setprop_cell(fdt, intc_name, "phandle", intc_phandle); + qemu_fdt_setprop_cell(fdt, intc_name, "phandle", + intc_phandles[cpu]); qemu_fdt_setprop_string(fdt, intc_name, "compatible", "riscv,cpu-intc"); qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0); qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1); - clint_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle); - clint_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT); - clint_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle); - clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); - core_name = g_strdup_printf("%s/core%d", clust_name, cpu); qemu_fdt_add_subnode(fdt, core_name); qemu_fdt_setprop_cell(fdt, core_name, "cpu", cpu_phandle); @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, create_fdt_socket_memory(fdt, memaddr, memsize, socket, riscv_numa_enabled(ms)); + clint_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4); + + for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) { + clint_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]); + clint_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT); + clint_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]); + clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); + } + clint_addr = memmap[SPIKE_CLINT].base + (memmap[SPIKE_CLINT].size * socket); clint_name = g_strdup_printf("/soc/clint@%lx", clint_addr); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Move all clint FDT generation to fdt-common.c reducing code repetition. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615203734.954428-8-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/fdt-common.h | 5 ++++ hw/riscv/fdt-common.c | 33 +++++++++++++++++++++++++ hw/riscv/sifive_u.c | 25 +++---------------- hw/riscv/spike.c | 31 ++++------------------- hw/riscv/virt.c | 46 ++++++----------------------------- 5 files changed, 54 insertions(+), 86 deletions(-) diff --git a/include/hw/riscv/fdt-common.h b/include/hw/riscv/fdt-common.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/fdt-common.h +++ b/include/hw/riscv/fdt-common.h @@ -XXX,XX +XXX,XX @@ void *create_board_device_tree(const char *model, const char *compatible, int *fdt_size); void create_fdt_socket_memory(void *fdt, hwaddr addr, uint64_t size, int socket_id, bool numa_enabled); +void create_fdt_clint(void *fdt, hwaddr addr, uint64_t size, + uint32_t *intc_phandles, int num_harts); +void create_fdt_socket_clint(void *fdt, hwaddr addr, uint64_t size, + int socket_id, uint32_t *intc_phandles, + int num_harts, bool numa_enabled); #endif diff --git a/hw/riscv/fdt-common.c b/hw/riscv/fdt-common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/fdt-common.c +++ b/hw/riscv/fdt-common.c @@ -XXX,XX +XXX,XX @@ #include "system/device_tree.h" #include "hw/core/boards.h" #include "hw/riscv/fdt-common.h" +#include "target/riscv/cpu_bits.h" void *create_board_device_tree(const char *model, const char *compatible, int *fdt_size) @@ -XXX,XX +XXX,XX @@ void create_fdt_socket_memory(void *fdt, hwaddr addr, uint64_t size, qemu_fdt_setprop_cell(fdt, mem_name, "numa-node-id", socket_id); } } + +void create_fdt_socket_clint(void *fdt, hwaddr addr, uint64_t size, + int socket_id, uint32_t *intc_phandles, + int num_harts, bool numa_enabled) +{ + g_autofree uint32_t *clint_cells = g_new0(uint32_t, num_harts * 4); + g_autofree char *clint_name = NULL; + static const char * const clint_compat[2] = { + "sifive,clint0", "riscv,clint0" + }; + + for (int cpu = 0; cpu < num_harts; cpu++) { + clint_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]); + clint_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT); + clint_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]); + clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); + } + + clint_name = g_strdup_printf("/soc/clint@%"HWADDR_PRIx, addr); + qemu_fdt_add_subnode(fdt, clint_name); + qemu_fdt_setprop_string_array(fdt, clint_name, "compatible", + (char **)&clint_compat, + ARRAY_SIZE(clint_compat)); + qemu_fdt_setprop_sized_cells(fdt, clint_name, "reg", + 2, addr, 2, size); + qemu_fdt_setprop(fdt, clint_name, "interrupts-extended", + clint_cells, num_harts * sizeof(uint32_t) * 4); + + if (numa_enabled) { + qemu_fdt_setprop_cell(fdt, clint_name, "numa-node-id", socket_id); + } +} diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, uint32_t plic_phandle, prci_phandle, gpio_phandle, phandle = 1; uint32_t hfclk_phandle, rtcclk_phandle, phy_phandle; static const char * const ethclk_names[2] = { "pclk", "hclk" }; - static const char * const clint_compat[2] = { - "sifive,clint0", "riscv,clint0" - }; static const char * const plic_compat[2] = { "sifive,plic-1.0.0", "riscv,plic0" }; @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, g_free(nodename); } - cells = g_new0(uint32_t, ms->smp.cpus * 4); - for (cpu = 0; cpu < ms->smp.cpus; cpu++) { - cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]); - cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT); - cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]); - cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); - } - nodename = g_strdup_printf("/soc/clint@%lx", - (long)memmap[SIFIVE_U_DEV_CLINT].base); - qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_string_array(fdt, nodename, "compatible", - (char **)&clint_compat, ARRAY_SIZE(clint_compat)); - qemu_fdt_setprop_cells(fdt, nodename, "reg", - 0x0, memmap[SIFIVE_U_DEV_CLINT].base, - 0x0, memmap[SIFIVE_U_DEV_CLINT].size); - qemu_fdt_setprop(fdt, nodename, "interrupts-extended", - cells, ms->smp.cpus * sizeof(uint32_t) * 4); - g_free(cells); - g_free(nodename); + create_fdt_socket_clint(fdt, memmap[SIFIVE_U_DEV_CLINT].base, + memmap[SIFIVE_U_DEV_CLINT].size, 0, + intc_phandles, ms->smp.cpus, false); nodename = g_strdup_printf("/soc/otp@%lx", (long)memmap[SIFIVE_U_DEV_OTP].base); diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, unsigned long clint_addr; int cpu, socket; MachineState *ms = MACHINE(s); - uint32_t *clint_cells; uint32_t cpu_phandle, phandle = 1; - char *clint_name, *clust_name; + char *clust_name; char *core_name, *cpu_name, *intc_name; - static const char * const clint_compat[2] = { - "sifive,clint0", "riscv,clint0" - }; + bool numa_enabled = riscv_numa_enabled(ms); fdt = ms->fdt = create_board_device_tree("ucbbar,spike-bare,qemu", "ucbbar,spike-bare-dev", &fdt_size); @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, create_fdt_socket_memory(fdt, memaddr, memsize, socket, riscv_numa_enabled(ms)); - clint_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4); - - for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) { - clint_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]); - clint_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT); - clint_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]); - clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); - } - clint_addr = memmap[SPIKE_CLINT].base + (memmap[SPIKE_CLINT].size * socket); - clint_name = g_strdup_printf("/soc/clint@%lx", clint_addr); - qemu_fdt_add_subnode(fdt, clint_name); - qemu_fdt_setprop_string_array(fdt, clint_name, "compatible", - (char **)&clint_compat, ARRAY_SIZE(clint_compat)); - qemu_fdt_setprop_cells(fdt, clint_name, "reg", - 0x0, clint_addr, 0x0, memmap[SPIKE_CLINT].size); - qemu_fdt_setprop(fdt, clint_name, "interrupts-extended", - clint_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4); - riscv_socket_fdt_write_id(ms, clint_name, socket); - - g_free(clint_name); - g_free(clint_cells); + create_fdt_socket_clint(fdt, clint_addr, memmap[SPIKE_CLINT].size, + socket, intc_phandles, + s->soc[socket].num_harts, numa_enabled); g_free(clust_name); } diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -XXX,XX +XXX,XX @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, } } -static void create_fdt_socket_clint(RISCVVirtState *s, - int socket, - uint32_t *intc_phandles) -{ - int cpu; - g_autofree char *clint_name = NULL; - g_autofree uint32_t *clint_cells = NULL; - hwaddr clint_addr; - MachineState *ms = MACHINE(s); - static const char * const clint_compat[2] = { - "sifive,clint0", "riscv,clint0" - }; - - clint_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4); - - for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) { - clint_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]); - clint_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT); - clint_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]); - clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); - } - - clint_addr = s->memmap[VIRT_CLINT].base + - s->memmap[VIRT_CLINT].size * socket; - clint_name = g_strdup_printf("/soc/clint@%"HWADDR_PRIx, clint_addr); - qemu_fdt_add_subnode(ms->fdt, clint_name); - qemu_fdt_setprop_string_array(ms->fdt, clint_name, "compatible", - (char **)&clint_compat, - ARRAY_SIZE(clint_compat)); - qemu_fdt_setprop_sized_cells(ms->fdt, clint_name, "reg", - 2, clint_addr, 2, s->memmap[VIRT_CLINT].size); - qemu_fdt_setprop(ms->fdt, clint_name, "interrupts-extended", - clint_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4); - riscv_socket_fdt_write_id(ms, clint_name, socket); -} - static void create_fdt_socket_aclint(RISCVVirtState *s, int socket, uint32_t *intc_phandles) @@ -XXX,XX +XXX,XX @@ static void create_fdt_sockets(RISCVVirtState *s, uint32_t xplic_phandles[MAX_NODES]; g_autofree uint32_t *intc_phandles = NULL; int socket_count = riscv_socket_count(ms); + bool numa_enabled = riscv_numa_enabled(ms); qemu_fdt_add_subnode(ms->fdt, "/cpus"); qemu_fdt_setprop_cell(ms->fdt, "/cpus", "timebase-frequency", @@ -XXX,XX +XXX,XX @@ static void create_fdt_sockets(RISCVVirtState *s, create_fdt_socket_aclint(s, socket, &intc_phandles[phandle_pos]); } else if (tcg_enabled()) { - create_fdt_socket_clint(s, socket, - &intc_phandles[phandle_pos]); + hwaddr clintaddr = s->memmap[VIRT_CLINT].base + + s->memmap[VIRT_CLINT].size * socket; + + create_fdt_socket_clint(ms->fdt, clintaddr, + s->memmap[VIRT_CLINT].size, + socket, &intc_phandles[phandle_pos], + s->soc[socket].num_harts, numa_enabled); } } -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We want to consolidate the CPU socket FDT creation into a single helper. 'virt' and spike has the same code but sifive_u does not have cpu-map, cluster and core subnodes. These subnodes are present in other boards even in single socket configs without NUMA. This is a strong indicator that their presence doesn't hurt a NUMA-less board like sifive_u. Add these DTs to make the FDT standardization straightforward. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615203734.954428-9-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/sifive_u.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, "sifive,plic-1.0.0", "riscv,plic0" }; g_autofree uint32_t *intc_phandles = g_new0(uint32_t, ms->smp.cpus); + g_autofree char *clust_name = NULL; fdt = ms->fdt = create_board_device_tree("SiFive HiFive Unleashed A00", "sifive,hifive-unleashed-a00", &s->fdt_size); @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, CLINT_TIMEBASE_FREQ); qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); + qemu_fdt_add_subnode(fdt, "/cpus/cpu-map"); + + clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", 0); + qemu_fdt_add_subnode(fdt, clust_name); for (cpu = ms->smp.cpus - 1; cpu >= 0; cpu--) { int cpu_phandle = phandle++; nodename = g_strdup_printf("/cpus/cpu@%d", cpu); char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); + g_autofree char *core_name = NULL; + qemu_fdt_add_subnode(fdt, nodename); /* cpu 0 is the management hart that does not have mmu */ if (cpu != 0) { @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc"); qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0); qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1); + + core_name = g_strdup_printf("%s/core%d", clust_name, cpu); + qemu_fdt_add_subnode(fdt, core_name); + qemu_fdt_setprop_cell(fdt, core_name, "cpu", cpu_phandle); + g_free(intc); g_free(nodename); } -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Consolidate the '/cpus' FDT root node creation into a single place. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615203734.954428-10-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/fdt-common.h | 1 + hw/riscv/fdt-common.c | 10 ++++++++++ hw/riscv/sifive_u.c | 7 +------ hw/riscv/spike.c | 7 +------ hw/riscv/virt.c | 11 +++-------- 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/include/hw/riscv/fdt-common.h b/include/hw/riscv/fdt-common.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/fdt-common.h +++ b/include/hw/riscv/fdt-common.h @@ -XXX,XX +XXX,XX @@ void create_fdt_clint(void *fdt, hwaddr addr, uint64_t size, void create_fdt_socket_clint(void *fdt, hwaddr addr, uint64_t size, int socket_id, uint32_t *intc_phandles, int num_harts, bool numa_enabled); +void fdt_create_cpu_socket_subnode(void *fdt, uint64_t timebase_frequency); #endif diff --git a/hw/riscv/fdt-common.c b/hw/riscv/fdt-common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/fdt-common.c +++ b/hw/riscv/fdt-common.c @@ -XXX,XX +XXX,XX @@ void create_fdt_socket_clint(void *fdt, hwaddr addr, uint64_t size, qemu_fdt_setprop_cell(fdt, clint_name, "numa-node-id", socket_id); } } + +void fdt_create_cpu_socket_subnode(void *fdt, uint64_t timebase_frequency) +{ + qemu_fdt_add_subnode(fdt, "/cpus"); + qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", + timebase_frequency); + qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); + qemu_fdt_add_subnode(fdt, "/cpus/cpu-map"); +} diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, create_fdt_socket_memory(fdt, memmap[SIFIVE_U_DEV_DRAM].base, ms->ram_size, 0, false); - qemu_fdt_add_subnode(fdt, "/cpus"); - qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", - CLINT_TIMEBASE_FREQ); - qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); - qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); - qemu_fdt_add_subnode(fdt, "/cpus/cpu-map"); + fdt_create_cpu_socket_subnode(fdt, CLINT_TIMEBASE_FREQ); clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", 0); qemu_fdt_add_subnode(fdt, clust_name); diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, 0x0, memmap[SPIKE_HTIF].base, 0x0, memmap[SPIKE_HTIF].size); } - qemu_fdt_add_subnode(fdt, "/cpus"); - qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", - RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ); - qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); - qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); - qemu_fdt_add_subnode(fdt, "/cpus/cpu-map"); + fdt_create_cpu_socket_subnode(fdt, RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ); for (socket = (riscv_socket_count(ms) - 1); socket >= 0; socket--) { g_autofree uint32_t *intc_phandles = g_new0(uint32_t, diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -XXX,XX +XXX,XX @@ static void create_fdt_sockets(RISCVVirtState *s, int socket_count = riscv_socket_count(ms); bool numa_enabled = riscv_numa_enabled(ms); - qemu_fdt_add_subnode(ms->fdt, "/cpus"); - qemu_fdt_setprop_cell(ms->fdt, "/cpus", "timebase-frequency", - kvm_enabled() ? - kvm_riscv_get_timebase_frequency(&s->soc->harts[0]) : - RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ); - qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); - qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1); - qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map"); + fdt_create_cpu_socket_subnode(ms->fdt, + kvm_enabled() ? kvm_riscv_get_timebase_frequency(&s->soc->harts[0]) : + RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ); intc_phandles = g_new0(uint32_t, ms->smp.cpus); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Consolidate the creation of CPUs socket FDT in a helper that can be shared across all boards. The code was basically moved from the function with the same name from 'virt.c', with additional bits to create the cluster subnode beforehand. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615203734.954428-11-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/fdt-common.h | 7 +++ hw/riscv/fdt-common.c | 74 +++++++++++++++++++++++++++++++ hw/riscv/virt.c | 83 +++-------------------------------- 3 files changed, 87 insertions(+), 77 deletions(-) diff --git a/include/hw/riscv/fdt-common.h b/include/hw/riscv/fdt-common.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/fdt-common.h +++ b/include/hw/riscv/fdt-common.h @@ -XXX,XX +XXX,XX @@ #ifndef RISCV_VIRT_FDT_H #define RISCV_VIRT_FDT_H +#include "target/riscv/cpu.h" + void *create_board_device_tree(const char *model, const char *compatible, int *fdt_size); void create_fdt_socket_memory(void *fdt, hwaddr addr, uint64_t size, @@ -XXX,XX +XXX,XX @@ void create_fdt_socket_clint(void *fdt, hwaddr addr, uint64_t size, int socket_id, uint32_t *intc_phandles, int num_harts, bool numa_enabled); void fdt_create_cpu_socket_subnode(void *fdt, uint64_t timebase_frequency); +void create_fdt_socket_cpus(void *fdt, RISCVCPU *socket_harts, + int socket_id, int num_harts_socket, + int socket_hartid_base, uint32_t *phandle, + uint32_t *intc_phandles, bool numa_enabled, + bool is_32_bit); #endif diff --git a/hw/riscv/fdt-common.c b/hw/riscv/fdt-common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/fdt-common.c +++ b/hw/riscv/fdt-common.c @@ -XXX,XX +XXX,XX @@ void fdt_create_cpu_socket_subnode(void *fdt, uint64_t timebase_frequency) qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); qemu_fdt_add_subnode(fdt, "/cpus/cpu-map"); } + +void create_fdt_socket_cpus(void *fdt, RISCVCPU *socket_harts, + int socket_id, int num_harts_socket, + int socket_hartid_base, uint32_t *phandle, + uint32_t *intc_phandles, bool numa_enabled, + bool is_32_bit) +{ + g_autofree char *clust_name = NULL; + uint32_t cpu_phandle; + + clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket_id); + qemu_fdt_add_subnode(fdt, clust_name); + + for (int cpu = num_harts_socket - 1; cpu >= 0; cpu--) { + RISCVCPU *cpu_ptr = &socket_harts[cpu]; + int8_t satp_mode_max = cpu_ptr->cfg.max_satp_mode; + g_autofree char *cpu_name = NULL; + g_autofree char *core_name = NULL; + g_autofree char *intc_name = NULL; + + cpu_phandle = (*phandle)++; + + cpu_name = g_strdup_printf("/cpus/cpu@%d", socket_hartid_base + cpu); + qemu_fdt_add_subnode(fdt, cpu_name); + + if (satp_mode_max != -1) { + g_autofree char *sv_name = NULL; + sv_name = g_strdup_printf("riscv,%s", + satp_mode_str(satp_mode_max, is_32_bit)); + qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", sv_name); + } + riscv_isa_write_fdt(cpu_ptr, fdt, cpu_name); + + if (cpu_ptr->cfg.ext_zicbom) { + qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cbom-block-size", + cpu_ptr->cfg.cbom_blocksize); + } + + if (cpu_ptr->cfg.ext_zicboz) { + qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cboz-block-size", + cpu_ptr->cfg.cboz_blocksize); + } + + if (cpu_ptr->cfg.ext_zicbop) { + qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cbop-block-size", + cpu_ptr->cfg.cbop_blocksize); + } + + qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv"); + qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay"); + qemu_fdt_setprop_cell(fdt, cpu_name, "reg", + socket_hartid_base + cpu); + qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); + if (numa_enabled) { + qemu_fdt_setprop_cell(fdt, cpu_name, "numa-node-id", socket_id); + } + qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle); + + intc_phandles[cpu] = (*phandle)++; + + intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name); + qemu_fdt_add_subnode(fdt, intc_name); + qemu_fdt_setprop_cell(fdt, intc_name, "phandle", + intc_phandles[cpu]); + qemu_fdt_setprop_string(fdt, intc_name, "compatible", + "riscv,cpu-intc"); + qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1); + + core_name = g_strdup_printf("%s/core%d", clust_name, cpu); + qemu_fdt_add_subnode(fdt, core_name); + qemu_fdt_setprop_cell(fdt, core_name, "cpu", cpu_phandle); + } +} diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -XXX,XX +XXX,XX @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, 0x1800, 0, 0, 0x7); } -static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, - char *clust_name, uint32_t *phandle, - uint32_t *intc_phandles) -{ - int cpu; - uint32_t cpu_phandle; - MachineState *ms = MACHINE(s); - bool is_32_bit = riscv_is_32bit(&s->soc[0]); - - for (cpu = s->soc[socket].num_harts - 1; cpu >= 0; cpu--) { - RISCVCPU *cpu_ptr = &s->soc[socket].harts[cpu]; - int8_t satp_mode_max = cpu_ptr->cfg.max_satp_mode; - g_autofree char *cpu_name = NULL; - g_autofree char *core_name = NULL; - g_autofree char *intc_name = NULL; - g_autofree char *sv_name = NULL; - - cpu_phandle = (*phandle)++; - - cpu_name = g_strdup_printf("/cpus/cpu@%d", - s->soc[socket].hartid_base + cpu); - qemu_fdt_add_subnode(ms->fdt, cpu_name); - - if (satp_mode_max != -1) { - sv_name = g_strdup_printf("riscv,%s", - satp_mode_str(satp_mode_max, is_32_bit)); - qemu_fdt_setprop_string(ms->fdt, cpu_name, "mmu-type", sv_name); - } - - riscv_isa_write_fdt(cpu_ptr, ms->fdt, cpu_name); - - if (cpu_ptr->cfg.ext_zicbom) { - qemu_fdt_setprop_cell(ms->fdt, cpu_name, "riscv,cbom-block-size", - cpu_ptr->cfg.cbom_blocksize); - } - - if (cpu_ptr->cfg.ext_zicboz) { - qemu_fdt_setprop_cell(ms->fdt, cpu_name, "riscv,cboz-block-size", - cpu_ptr->cfg.cboz_blocksize); - } - - if (cpu_ptr->cfg.ext_zicbop) { - qemu_fdt_setprop_cell(ms->fdt, cpu_name, "riscv,cbop-block-size", - cpu_ptr->cfg.cbop_blocksize); - } - - qemu_fdt_setprop_string(ms->fdt, cpu_name, "compatible", "riscv"); - qemu_fdt_setprop_string(ms->fdt, cpu_name, "status", "okay"); - qemu_fdt_setprop_cell(ms->fdt, cpu_name, "reg", - s->soc[socket].hartid_base + cpu); - qemu_fdt_setprop_string(ms->fdt, cpu_name, "device_type", "cpu"); - riscv_socket_fdt_write_id(ms, cpu_name, socket); - qemu_fdt_setprop_cell(ms->fdt, cpu_name, "phandle", cpu_phandle); - - intc_phandles[cpu] = (*phandle)++; - - intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name); - qemu_fdt_add_subnode(ms->fdt, intc_name); - qemu_fdt_setprop_cell(ms->fdt, intc_name, "phandle", - intc_phandles[cpu]); - qemu_fdt_setprop_string(ms->fdt, intc_name, "compatible", - "riscv,cpu-intc"); - qemu_fdt_setprop(ms->fdt, intc_name, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(ms->fdt, intc_name, "#interrupt-cells", 1); - - core_name = g_strdup_printf("%s/core%d", clust_name, cpu); - qemu_fdt_add_subnode(ms->fdt, core_name); - qemu_fdt_setprop_cell(ms->fdt, core_name, "cpu", cpu_phandle); - } -} - static void create_fdt_socket_aclint(RISCVVirtState *s, int socket, uint32_t *intc_phandles) @@ -XXX,XX +XXX,XX @@ static void create_fdt_sockets(RISCVVirtState *s, g_autofree uint32_t *intc_phandles = NULL; int socket_count = riscv_socket_count(ms); bool numa_enabled = riscv_numa_enabled(ms); + bool is_32_bit = riscv_is_32bit(&s->soc[0]); fdt_create_cpu_socket_subnode(ms->fdt, kvm_enabled() ? kvm_riscv_get_timebase_frequency(&s->soc->harts[0]) : @@ -XXX,XX +XXX,XX @@ static void create_fdt_sockets(RISCVVirtState *s, phandle_pos = ms->smp.cpus; for (socket = (socket_count - 1); socket >= 0; socket--) { - g_autofree char *clust_name = NULL; hwaddr memaddr = s->memmap[VIRT_DRAM].base + riscv_socket_mem_offset(ms, socket); uint64_t memsize = riscv_socket_mem_size(ms, socket); phandle_pos -= s->soc[socket].num_harts; - clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket); - qemu_fdt_add_subnode(ms->fdt, clust_name); - - create_fdt_socket_cpus(s, socket, clust_name, phandle, - &intc_phandles[phandle_pos]); + create_fdt_socket_cpus(ms->fdt, (&s->soc[socket])->harts, socket, + s->soc[socket].num_harts, + s->soc[socket].hartid_base, + phandle, &intc_phandles[phandle_pos], + numa_enabled, is_32_bit); create_fdt_socket_memory(ms->fdt, memaddr, memsize, socket, riscv_numa_enabled(ms)); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Use the new FDT helper to create FDTs for the CPU sockets. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615203734.954428-12-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/spike.c | 54 +++++++----------------------------------------- 1 file changed, 7 insertions(+), 47 deletions(-) diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, void *fdt; int fdt_size; unsigned long clint_addr; - int cpu, socket; + int socket; MachineState *ms = MACHINE(s); - uint32_t cpu_phandle, phandle = 1; - char *clust_name; - char *core_name, *cpu_name, *intc_name; + uint32_t phandle = 1; bool numa_enabled = riscv_numa_enabled(ms); fdt = ms->fdt = create_board_device_tree("ucbbar,spike-bare,qemu", @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, riscv_socket_mem_offset(ms, socket); uint64_t memsize = riscv_socket_mem_size(ms, socket); - clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket); - qemu_fdt_add_subnode(fdt, clust_name); - - for (cpu = s->soc[socket].num_harts - 1; cpu >= 0; cpu--) { - cpu_phandle = phandle++; - - cpu_name = g_strdup_printf("/cpus/cpu@%d", - s->soc[socket].hartid_base + cpu); - qemu_fdt_add_subnode(fdt, cpu_name); - if (is_32_bit) { - qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv32"); - } else { - qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv48"); - } - riscv_isa_write_fdt(&s->soc[socket].harts[cpu], fdt, cpu_name); - qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv"); - qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay"); - qemu_fdt_setprop_cell(fdt, cpu_name, "reg", - s->soc[socket].hartid_base + cpu); - qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); - riscv_socket_fdt_write_id(ms, cpu_name, socket); - qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle); - - intc_phandles[cpu] = phandle++; - - intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name); - qemu_fdt_add_subnode(fdt, intc_name); - qemu_fdt_setprop_cell(fdt, intc_name, "phandle", - intc_phandles[cpu]); - qemu_fdt_setprop_string(fdt, intc_name, "compatible", - "riscv,cpu-intc"); - qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1); - - core_name = g_strdup_printf("%s/core%d", clust_name, cpu); - qemu_fdt_add_subnode(fdt, core_name); - qemu_fdt_setprop_cell(fdt, core_name, "cpu", cpu_phandle); - - g_free(core_name); - g_free(intc_name); - g_free(cpu_name); - } + create_fdt_socket_cpus(fdt, (&s->soc[socket])->harts, socket, + s->soc[socket].num_harts, + s->soc[socket].hartid_base, + &phandle, intc_phandles, numa_enabled, + is_32_bit); create_fdt_socket_memory(fdt, memaddr, memsize, socket, riscv_numa_enabled(ms)); @@ -XXX,XX +XXX,XX @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, create_fdt_socket_clint(fdt, clint_addr, memmap[SPIKE_CLINT].size, socket, intc_phandles, s->soc[socket].num_harts, numa_enabled); - g_free(clust_name); } riscv_socket_fdt_write_distance_matrix(ms); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> The sifive_u board does not share the same CPU socket FDT bits from the other boards. In particular the riscv,isa creation is done using either CPU0 from soc.e_cpus.harts, and for all other CPUs soc.u_cups.harts is used. It would be too cumbersome to add all these details in the common code so we're going to add a special sifive_u only helper that shares the common bits with the common helper used by the other boards. create_fdt_socket_cpu_internal() contains the common bits shared between the sifive_u board and the rest. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615203734.954428-13-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/fdt-common.c | 108 +++++++++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 43 deletions(-) diff --git a/hw/riscv/fdt-common.c b/hw/riscv/fdt-common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/fdt-common.c +++ b/hw/riscv/fdt-common.c @@ -XXX,XX +XXX,XX @@ void fdt_create_cpu_socket_subnode(void *fdt, uint64_t timebase_frequency) qemu_fdt_add_subnode(fdt, "/cpus/cpu-map"); } -void create_fdt_socket_cpus(void *fdt, RISCVCPU *socket_harts, - int socket_id, int num_harts_socket, - int socket_hartid_base, uint32_t *phandle, - uint32_t *intc_phandles, bool numa_enabled, - bool is_32_bit) +static void +create_fdt_socket_cpu_internal(void *fdt, char *clust_name, RISCVCPU *cpu_ptr, + int cpu, int socket_id, int socket_hartid_base, + uint32_t *phandle, uint32_t *intc_phandles, + bool numa_enabled, bool is_32_bit) { - g_autofree char *clust_name = NULL; - uint32_t cpu_phandle; - - clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket_id); - qemu_fdt_add_subnode(fdt, clust_name); - - for (int cpu = num_harts_socket - 1; cpu >= 0; cpu--) { - RISCVCPU *cpu_ptr = &socket_harts[cpu]; + g_autofree char *cpu_name = NULL; + g_autofree char *core_name = NULL; + g_autofree char *intc_name = NULL; + uint32_t cpu_phandle = (*phandle)++; + bool is_sifive_u = cpu_ptr == NULL; + + cpu_name = g_strdup_printf("/cpus/cpu@%d", socket_hartid_base + cpu); + + /* + * The sifive_u board has an exclusive satp and riscv,isa + * schema that can't be shared with other boards, so part + * of the CPU FDT creation (i.e. the /cpus/cpu@N subnode) + * is still being done by the board. + */ + if (!is_sifive_u) { int8_t satp_mode_max = cpu_ptr->cfg.max_satp_mode; - g_autofree char *cpu_name = NULL; - g_autofree char *core_name = NULL; - g_autofree char *intc_name = NULL; - - cpu_phandle = (*phandle)++; - cpu_name = g_strdup_printf("/cpus/cpu@%d", socket_hartid_base + cpu); qemu_fdt_add_subnode(fdt, cpu_name); if (satp_mode_max != -1) { @@ -XXX,XX +XXX,XX @@ void create_fdt_socket_cpus(void *fdt, RISCVCPU *socket_harts, qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cbop-block-size", cpu_ptr->cfg.cbop_blocksize); } + } - qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv"); - qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay"); - qemu_fdt_setprop_cell(fdt, cpu_name, "reg", - socket_hartid_base + cpu); - qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); - if (numa_enabled) { - qemu_fdt_setprop_cell(fdt, cpu_name, "numa-node-id", socket_id); - } - qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle); - - intc_phandles[cpu] = (*phandle)++; - - intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name); - qemu_fdt_add_subnode(fdt, intc_name); - qemu_fdt_setprop_cell(fdt, intc_name, "phandle", - intc_phandles[cpu]); - qemu_fdt_setprop_string(fdt, intc_name, "compatible", - "riscv,cpu-intc"); - qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1); - - core_name = g_strdup_printf("%s/core%d", clust_name, cpu); - qemu_fdt_add_subnode(fdt, core_name); - qemu_fdt_setprop_cell(fdt, core_name, "cpu", cpu_phandle); + qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv"); + qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay"); + qemu_fdt_setprop_cell(fdt, cpu_name, "reg", + socket_hartid_base + cpu); + qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); + if (numa_enabled) { + qemu_fdt_setprop_cell(fdt, cpu_name, "numa-node-id", socket_id); + } + qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle); + + intc_phandles[cpu] = (*phandle)++; + + intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name); + qemu_fdt_add_subnode(fdt, intc_name); + qemu_fdt_setprop_cell(fdt, intc_name, "phandle", + intc_phandles[cpu]); + qemu_fdt_setprop_string(fdt, intc_name, "compatible", + "riscv,cpu-intc"); + qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1); + + core_name = g_strdup_printf("%s/core%d", clust_name, cpu); + qemu_fdt_add_subnode(fdt, core_name); + qemu_fdt_setprop_cell(fdt, core_name, "cpu", cpu_phandle); +} + +void create_fdt_socket_cpus(void *fdt, RISCVCPU *socket_harts, + int socket_id, int num_harts_socket, + int socket_hartid_base, uint32_t *phandle, + uint32_t *intc_phandles, bool numa_enabled, + bool is_32_bit) +{ + g_autofree char *clust_name = NULL; + + clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket_id); + qemu_fdt_add_subnode(fdt, clust_name); + + for (int cpu = num_harts_socket - 1; cpu >= 0; cpu--) { + RISCVCPU *cpu_ptr = &socket_harts[cpu]; + + create_fdt_socket_cpu_internal(fdt, clust_name, cpu_ptr, cpu, + socket_id, socket_hartid_base, + phandle, intc_phandles, numa_enabled, + is_32_bit); } } -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> This sifive_u only helper shares DT code with other boards. The idea is to reduce code repetition while keeping sifive_u characteristics in place. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615203734.954428-14-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/fdt-common.h | 4 ++++ hw/riscv/fdt-common.c | 11 +++++++++++ hw/riscv/sifive_u.c | 24 +++--------------------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/include/hw/riscv/fdt-common.h b/include/hw/riscv/fdt-common.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/fdt-common.h +++ b/include/hw/riscv/fdt-common.h @@ -XXX,XX +XXX,XX @@ void create_fdt_socket_cpus(void *fdt, RISCVCPU *socket_harts, int socket_hartid_base, uint32_t *phandle, uint32_t *intc_phandles, bool numa_enabled, bool is_32_bit); +void create_fdt_socket_cpu_sifive(void *fdt, char *clust_name, + int cpu_id, int socket_id, + int socket_hartid_base, uint32_t *phandle, + uint32_t *intc_phandles); #endif diff --git a/hw/riscv/fdt-common.c b/hw/riscv/fdt-common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/fdt-common.c +++ b/hw/riscv/fdt-common.c @@ -XXX,XX +XXX,XX @@ void create_fdt_socket_cpus(void *fdt, RISCVCPU *socket_harts, is_32_bit); } } + +void +create_fdt_socket_cpu_sifive(void *fdt, char *clust_name, + int cpu_id, int socket_id, + int socket_hartid_base, uint32_t *phandle, + uint32_t *intc_phandles) +{ + create_fdt_socket_cpu_internal(fdt, clust_name, NULL, cpu_id, + socket_id, socket_hartid_base, + phandle, intc_phandles, false, false); +} diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, qemu_fdt_add_subnode(fdt, clust_name); for (cpu = ms->smp.cpus - 1; cpu >= 0; cpu--) { - int cpu_phandle = phandle++; nodename = g_strdup_printf("/cpus/cpu@%d", cpu); - char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); - g_autofree char *core_name = NULL; - qemu_fdt_add_subnode(fdt, nodename); + /* cpu 0 is the management hart that does not have mmu */ if (cpu != 0) { if (is_32_bit) { @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, } else { riscv_isa_write_fdt(&s->soc.e_cpus.harts[0], fdt, nodename); } - qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv"); - qemu_fdt_setprop_string(fdt, nodename, "status", "okay"); - qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu); - qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu"); - qemu_fdt_setprop_cell(fdt, nodename, "phandle", cpu_phandle); - - intc_phandles[cpu] = phandle++; - - qemu_fdt_add_subnode(fdt, intc); - qemu_fdt_setprop_cell(fdt, intc, "phandle", intc_phandles[cpu]); - qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc"); - qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1); - core_name = g_strdup_printf("%s/core%d", clust_name, cpu); - qemu_fdt_add_subnode(fdt, core_name); - qemu_fdt_setprop_cell(fdt, core_name, "cpu", cpu_phandle); + create_fdt_socket_cpu_sifive(fdt, clust_name, cpu, 0, 0, + &phandle, intc_phandles); - g_free(intc); g_free(nodename); } -- 2.54.0