: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 30e8a06b64aa58a3990ba39cb5d09531e7d265e0: Merge tag 'net-pull-request' of https://github.com/jasowang/qemu into staging (2026-06-29 17:41:42 +0200) are available in the Git repository at: https://github.com/alistair23/qemu.git tags/pull-riscv-to-apply-20260701 for you to fetch changes up to 64ce9ac18757d79f3b5b337f7bcbdd0dabef3ce1: hw/riscv/riscv-iommu.c: always fault with SADE=0 and A=0 (2026-07-01 20:14:20 +1000) ---------------------------------------------------------------- RISC-V PR for 11.1 * Fix IMSIC CSR write and add tests * Parametrise debug trigger number * Add 'svbare' satp-mode * Fix RINTC PLIC context ID for KVM * Avoid abort when reading vtype before env->xl is set * Skip reset for KVM irqchip * Skip FP/Vector sync on KVM_PUT_RUNTIME_STATE * More FDT cleanups (PLIC) * Make FCTL.BE in IOMMU read only 0 * Check DC.TC reserved bits in IOMMU * Apply UXL WARL handling to vsstatus * Set cmd_ill IOFENCE.C if rsvp bits are set in IOMMU * Set RISCV_IOMMU_FQ_HDR_PV appropriately * Fix MSI MRIF IOMMU interrupt-pending offset * Report QEMU CPU archid as 42 * Check PMP before updating PTE * Add the Tenstorrent Atlantis machine ---------------------------------------------------------------- Alistair Francis (1): hw/riscv/atlantis: Ensure OpenSBI has a non-zero next_addr Charlie Jenkins (1): target/riscv: Report QEMU CPU archid as 42 Chris Rauer (1): hw/i2c: Add DesignWare I2C Controller Daniel Henrique Barboza (16): target/riscv/cpu: add CPU unrealize callback target/riscv: dynamic alloc of debug trigger arrays target/riscv: add 'num-triggers' debug property target/riscv/cpu.c: add 'svbare' satp-mode hw/riscv/sifive_u.c: remove unneeded qemu_fdt_get_phandle() call hw/riscv/sifive_u.c: use intc_phandle in plic creation hw/riscv/sifive_u: add #address-cells in PLIC FDT hw/riscv/virt.c: change 'plic' nodename to 'interrupt-controller' hw/riscv: add create_fdt_plic() helper hw/riscv/riscv-iommu: rename regs_rw to regs hw/riscv/riscv-iommu.c: make FCTL.BE read only 0 hw/riscv/riscv-iommu: check DC.TC reserved bits hw/riscv/riscv-iommu: set cmd_ill IOFENCE.C rsvp bits are set hw/riscv/riscv-iommu.c: set RISCV_IOMMU_FQ_HDR_PV appropriately hw/riscv/riscv-iommu.c: fix MSI MRIF interrupt-pending offset hw/riscv/riscv-iommu.c: always fault with SADE=0 and A=0 Inochi Amaoto (1): target/riscv: Check PMP before updating PTE Joel Stanley (9): target/riscv: Avoid NULL deref in IMSIC CSR write tests/functional/riscv64: Use newer kernel for tuxrun boot tests/functional/riscv64: Add virt machine AIA boot test hw/riscv/virt: Move AIA initialisation to helper file hw/riscv/aia: Provide number of irq sources hw/riscv/aia: Configure stride for the M-mode IMSIC hw/riscv: Add Tenstorrent Atlantis machine hw/riscv/atlantis: Integrate i2c controllers hw/riscv/atlantis: Add some i2c peripherals Meng Zhuo (1): target/riscv/kvm: skip FP/Vector sync on KVM_PUT_RUNTIME_STATE Nicholas Piggin (4): hw/riscv/boot: Describe discontiguous memory in boot_info hw/riscv/boot: Account for discontiguous memory when loading firmware target/riscv: tt-ascalon: Enable Zkr extension tests/functional/riscv64: Add tt-atlantis tests Qingwei Hu (3): hw/riscv/virt-acpi-build: Fix RINTC PLIC context ID for KVM hw/intc: riscv_aplic: Skip reset for KVM irqchip hw/intc: riscv_imsic: Skip reset for KVM irqchip SeungJu Cheon (1): target/riscv: Apply UXL WARL handling to vsstatus ZhengXiang Qin (1): target/riscv: avoid abort when reading vtype before env->xl is set MAINTAINERS | 21 + docs/system/riscv/tt_atlantis.rst | 41 ++ docs/system/target-riscv.rst | 1 + hw/riscv/aia.h | 26 + hw/riscv/riscv-iommu-bits.h | 4 + hw/riscv/riscv-iommu.h | 26 +- include/hw/i2c/designware_i2c.h | 56 ++ include/hw/riscv/boot.h | 12 +- include/hw/riscv/fdt-common.h | 5 + include/hw/riscv/sifive_u.h | 2 + include/hw/riscv/tt_atlantis.h | 64 +++ include/hw/riscv/virt.h | 2 +- target/riscv/cpu.h | 39 +- target/riscv/debug.h | 1 + hw/i2c/designware_i2c.c | 745 +++++++++++++++++++++++++++ hw/intc/riscv_aplic.c | 4 + hw/intc/riscv_imsic.c | 4 + hw/riscv/aia.c | 94 ++++ hw/riscv/boot.c | 34 +- hw/riscv/fdt-common.c | 30 ++ hw/riscv/k230.c | 8 +- hw/riscv/microchip_pfsoc.c | 8 +- hw/riscv/opentitan.c | 6 +- hw/riscv/riscv-iommu-hpm.c | 4 +- hw/riscv/riscv-iommu.c | 73 ++- hw/riscv/shakti_c.c | 6 +- hw/riscv/sifive_u.c | 48 +- hw/riscv/spike.c | 6 +- hw/riscv/tt_atlantis.c | 617 ++++++++++++++++++++++ hw/riscv/virt-acpi-build.c | 29 +- hw/riscv/virt.c | 148 ++---- hw/riscv/xiangshan_kmh.c | 6 +- target/riscv/cpu.c | 43 +- target/riscv/cpu_helper.c | 8 + target/riscv/csr.c | 48 +- target/riscv/debug.c | 46 +- target/riscv/kvm/kvm-cpu.c | 19 +- target/riscv/machine.c | 16 +- hw/i2c/Kconfig | 5 + hw/i2c/meson.build | 1 + hw/riscv/Kconfig | 13 + hw/riscv/meson.build | 2 + tests/functional/riscv64/meson.build | 1 + tests/functional/riscv64/test_opensbi.py | 4 + tests/functional/riscv64/test_tt_atlantis.py | 57 ++ tests/functional/riscv64/test_tuxrun.py | 11 +- 46 files changed, 2196 insertions(+), 248 deletions(-) create mode 100644 docs/system/riscv/tt_atlantis.rst create mode 100644 hw/riscv/aia.h create mode 100644 include/hw/i2c/designware_i2c.h create mode 100644 include/hw/riscv/tt_atlantis.h create mode 100644 hw/i2c/designware_i2c.c create mode 100644 hw/riscv/aia.c create mode 100644 hw/riscv/tt_atlantis.c create mode 100755 tests/functional/riscv64/test_tt_atlantis.py
From: Joel Stanley <joel@jms.id.au> rmw_xireg_aia() and rmw_xtopei() were changed to store the IMSIC callback's value in a local before passing it back to the caller. For write only CSR accesses that pointer is NULL, causing a crash when guest programs write to xireg or xtopei, such as when the guest sets up the IMSIC. This only happens when setting aia=aplic-imsic so none of the existing boot tests caught it. Fix it by guarding the pointer dereference as done for other CSRs. Fixes: 63469ad75dcc ("target/riscv: Fix arguments to board IMSIC emulation callbacks") Signed-off-by: Joel Stanley <joel@jms.id.au> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260617054034.1020724-2-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 8 ++++++-- 1 file changed, 6 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 rmw_xireg_aia(CPURISCVState *env, int csrno, AIA_MAKE_IREG(isel, priv, virt, vgein, riscv_cpu_mxl_bits(env)), &wide_val, new_val, wr_mask); - *val = wide_val; + if (val) { + *val = wide_val; + } } } else { isel_reserved = true; @@ -XXX,XX +XXX,XX @@ static RISCVException rmw_xtopei(CPURISCVState *env, int csrno, AIA_MAKE_IREG(ISELECT_IMSIC_TOPEI, priv, virt, vgein, riscv_cpu_mxl_bits(env)), &wide_val, new_val, wr_mask); - *val = wide_val; + if (val) { + *val = wide_val; + } done: if (ret) { -- 2.54.0
From: Joel Stanley <joel@jms.id.au> The published tuxrun kernel is 6.4 which predates the introduction of AIA support in Linux. Update it to a 6.11 kernel, the newest build on the tuxrun site. Boot times on a laptop are slightly longer but still reasonable: | v6.4.16 | v6.11.9 ---------------------|---------|-------- test_riscv64 | 4.82s | 5.56s ---------------------|---------|-------- test_riscv64_maxcpu | 4.94s | 5.30s Signed-off-by: Joel Stanley <joel@jms.id.au> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260617054034.1020724-3-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- tests/functional/riscv64/test_tuxrun.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/riscv64/test_tuxrun.py b/tests/functional/riscv64/test_tuxrun.py index XXXXXXX..XXXXXXX 100755 --- a/tests/functional/riscv64/test_tuxrun.py +++ b/tests/functional/riscv64/test_tuxrun.py @@ -XXX,XX +XXX,XX @@ class TuxRunRiscV64Test(TuxRunBaselineTest): ASSET_RISCV64_KERNEL = Asset( - 'https://storage.tuxboot.com/buildroot/20241119/riscv64/Image', - '2bd8132a3bf21570290042324fff48c987f42f2a00c08de979f43f0662ebadba') + 'https://storage.tuxboot.com/kernels/6.11.9/riscv64/Image', + '174f8bb87f08961e54fa3fcd954a8e31f4645f6d6af4dd43983d5e9841490fb0') ASSET_RISCV64_ROOTFS = Asset( 'https://storage.tuxboot.com/buildroot/20241119/riscv64/rootfs.ext4.zst', 'aa4736a9872651dfc0d95e709465eedf1134fd19d42b8cb305bfd776f9801004') -- 2.54.0
From: Joel Stanley <joel@jms.id.au> Add coverage of the riscv64 virt machine's Advanced Interrupt Architecture models. With this the APLIC and IMSIC models are used by Linux, catching regressions. This test requires a kernel >= 6.10, as AIA drivers were added to Linux in 6.10. Boot time is ~5s on a laptop. Signed-off-by: Joel Stanley <joel@jms.id.au> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260617054034.1020724-4-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- tests/functional/riscv64/test_tuxrun.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/functional/riscv64/test_tuxrun.py b/tests/functional/riscv64/test_tuxrun.py index XXXXXXX..XXXXXXX 100755 --- a/tests/functional/riscv64/test_tuxrun.py +++ b/tests/functional/riscv64/test_tuxrun.py @@ -XXX,XX +XXX,XX @@ def test_riscv64_maxcpu(self): self.common_tuxrun(kernel_asset=self.ASSET_RISCV64_KERNEL, rootfs_asset=self.ASSET_RISCV64_ROOTFS) + def test_riscv64_aia(self): + self.set_machine('virt') + self.vm.set_machine('virt,aia=aplic-imsic') + self.cpu='max' + self.common_tuxrun(kernel_asset=self.ASSET_RISCV64_KERNEL, + rootfs_asset=self.ASSET_RISCV64_ROOTFS) + def test_riscv64_rv32(self): self.set_machine('virt') self.cpu='rv32' -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Next patch is going to dynamically allocate debug trigger arrays during realize() time. We need a way of freeing them during unrealize(), which doesn't exist at this moment. There's a lot going on in that patch already so we're adding the callback infrastructure beforehand. Suggested-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260617131710.1855353-2-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 2 ++ target/riscv/cpu.c | 9 +++++++++ 2 files changed, 11 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 RISCVCPUDef { /** * RISCVCPUClass: * @parent_realize: The parent class' realize handler. + * @parent_unrealize: The parent class' unrealize handler. * @parent_phases: The parent class' reset phase handlers. * * A RISCV CPU model. @@ -XXX,XX +XXX,XX @@ struct RISCVCPUClass { CPUClass parent_class; DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; ResettablePhases parent_phases; RISCVCPUDef *def; }; 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_realize(DeviceState *dev, Error **errp) mcc->parent_realize(dev, errp); } +static void riscv_cpu_unrealize(DeviceState *dev) +{ + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); + + mcc->parent_unrealize(dev); +} + bool riscv_cpu_accelerator_compatible(RISCVCPU *cpu) { if (tcg_enabled()) { @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_common_class_init(ObjectClass *c, const void *data) device_class_set_parent_realize(dc, riscv_cpu_realize, &mcc->parent_realize); + device_class_set_parent_unrealize(dc, riscv_cpu_unrealize, + &mcc->parent_unrealize); resettable_class_set_parent_phases(rc, NULL, riscv_cpu_reset_hold, NULL, &mcc->parent_phases); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> The debug trigger facility consists of a set of arrays: tdata1-3, cpu_breakpoint, cpu_watchpoint and itrigger_timer. All of them are static allocated with RV_MAX_TRIGGERS (2). This means that all RISC-V cpus will have 2 triggers per hart. The RISC-V Server Ref demands at least 11 triggers per hart, and several CPUs in the wild works with 4+ triggers. We need more flexibility, ergo we need to parametrize the amount of triggers and make it configurable. Before doing that we need to handle a situation faced in a previous attempt [1]. We were unable to set the tdataN array length in vmstate_debug, meaning that we would always migrate RV_MAX_TRIGGERS regardless of the actual amount of triggers in play. To fix that we need to change the tdata arrays from static to dynamic, allowing us to use VMSTATE_VARRAY_UINT32(). This also means that, in contrast with [1], we have the opportunity to turn all trigger arrays into dynamic allocation and reduce the amount of stuff being carried by CPURISCVState, or in other words, we can carry just what we're using instead of a static max value. All the forementioned trigger facility arrays are now dynamic. They are allocated and freed during realize/unrealize, and their size is expressed by env->num_triggers. All relevant code is changed to use env->num_triggers instead of the RV_MAX_TRIGGERS to loop through each array. This will make it easier for the next patch to parametrize env->num_triggers. [1] https://lore.kernel.org/qemu-devel/94c772b0-5231-40e4-9cab-6ac39b9e4d45@oss.qualcomm.com/ Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260617131710.1855353-3-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 18 +++++++++++------ target/riscv/debug.h | 1 + target/riscv/cpu.c | 6 ++++++ target/riscv/csr.c | 2 +- target/riscv/debug.c | 46 ++++++++++++++++++++++++++++++++++-------- target/riscv/machine.c | 16 ++++++++++----- 6 files changed, 69 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 @@ struct CPUArchState { /* trigger module */ 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]; + /* + * num_triggers is the length of tdata1, tdata2, tdata3, + * cpu_breakpoint, cpu_watchpoint and itrigger_timer + * arrays. + */ + uint32_t num_triggers; + uint64_t *tdata1; + uint64_t *tdata2; + uint64_t *tdata3; + struct CPUBreakpoint **cpu_breakpoint; + struct CPUWatchpoint **cpu_watchpoint; + QEMUTimer **itrigger_timer; int64_t last_icount; bool itrigger_enabled; 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 @@ bool riscv_cpu_debug_check_breakpoint(CPUState *cs); bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp); void riscv_trigger_realize(CPURISCVState *env); +void riscv_trigger_unrealize(CPURISCVState *env); void riscv_trigger_reset_hold(CPURISCVState *env); bool riscv_itrigger_enabled(CPURISCVState *env); 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_realize(DeviceState *dev, Error **errp) static void riscv_cpu_unrealize(DeviceState *dev) { RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); +#ifndef CONFIG_USER_ONLY + RISCVCPU *cpu = RISCV_CPU(dev); + if (cpu->cfg.debug) { + riscv_trigger_unrealize(&cpu->env); + } +#endif mcc->parent_unrealize(dev); } 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_tdata(CPURISCVState *env, int csrno, target_ulong *val) { /* return 0 in tdata1 to end the trigger enumeration */ - if (env->trigger_cur >= RV_MAX_TRIGGERS && csrno == CSR_TDATA1) { + if (env->trigger_cur >= env->num_triggers && csrno == CSR_TDATA1) { *val = 0; return RISCV_EXCP_NONE; } 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 @@ target_ulong tselect_csr_read(CPURISCVState *env) void tselect_csr_write(CPURISCVState *env, target_ulong val) { - if (val < RV_MAX_TRIGGERS) { + if (val < env->num_triggers) { env->trigger_cur = val; } } @@ -XXX,XX +XXX,XX @@ static bool check_itrigger_priv(CPURISCVState *env, int index) bool riscv_itrigger_enabled(CPURISCVState *env) { int count; - for (int i = 0; i < RV_MAX_TRIGGERS; i++) { + for (int i = 0; i < env->num_triggers; i++) { if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) { continue; } @@ -XXX,XX +XXX,XX @@ bool riscv_itrigger_enabled(CPURISCVState *env) void helper_itrigger_match(CPURISCVState *env) { int count; - for (int i = 0; i < RV_MAX_TRIGGERS; i++) { + for (int i = 0; i < env->num_triggers; i++) { if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) { continue; } @@ -XXX,XX +XXX,XX @@ static void riscv_itrigger_update_count(CPURISCVState *env) int64_t last_icount = env->last_icount, current_icount; current_icount = env->last_icount = icount_get_raw(); - for (int i = 0; i < RV_MAX_TRIGGERS; i++) { + for (int i = 0; i < env->num_triggers; i++) { if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) { continue; } @@ -XXX,XX +XXX,XX @@ bool riscv_cpu_debug_check_breakpoint(CPUState *cs) int i; QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - for (i = 0; i < RV_MAX_TRIGGERS; i++) { + for (i = 0; i < env->num_triggers; i++) { trigger_type = get_trigger_type(env, i); if (!trigger_common_match(env, trigger_type, i)) { @@ -XXX,XX +XXX,XX @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) int flags; int i; - for (i = 0; i < RV_MAX_TRIGGERS; i++) { + for (i = 0; i < env->num_triggers; i++) { trigger_type = get_trigger_type(env, i); if (!trigger_common_match(env, trigger_type, i)) { @@ -XXX,XX +XXX,XX @@ void riscv_trigger_realize(CPURISCVState *env) { int i; - for (i = 0; i < RV_MAX_TRIGGERS; i++) { + /* + * Alloc env->tdata1/2/3, cpu_breakpoint, cpu_watchpoint and + * itrigger_timer dynamically. This is overkill now + * given that they could be static arrays with RV_MAX_TRIGGERS + * but we'll parametrize the trigger number later, i.e. the + * array length won't be static. + */ + env->num_triggers = RV_MAX_TRIGGERS; + env->tdata1 = g_new0(uint64_t, env->num_triggers); + env->tdata2 = g_new0(uint64_t, env->num_triggers); + env->tdata3 = g_new0(uint64_t, env->num_triggers); + env->cpu_breakpoint = g_new0(struct CPUBreakpoint *, env->num_triggers); + env->cpu_watchpoint = g_new0(struct CPUWatchpoint *, env->num_triggers); + env->itrigger_timer = g_new0(QEMUTimer *, env->num_triggers); + + for (i = 0; i < env->num_triggers; i++) { env->itrigger_timer[i] = timer_new_ns(QEMU_CLOCK_VIRTUAL, riscv_itrigger_timer_cb, env); } } +void riscv_trigger_unrealize(CPURISCVState *env) +{ + g_free(env->tdata1); + g_free(env->tdata2); + g_free(env->tdata3); + + g_free(env->cpu_breakpoint); + g_free(env->cpu_watchpoint); + + for (int i = 0; i < env->num_triggers; i++) { + timer_del(env->itrigger_timer[i]); + } + g_free(env->itrigger_timer); +} + void riscv_trigger_reset_hold(CPURISCVState *env) { target_ulong tdata1 = build_tdata1(env, TRIGGER_TYPE_AD_MATCH, 0, 0); int i; /* init to type 2 triggers */ - for (i = 0; i < RV_MAX_TRIGGERS; i++) { + for (i = 0; i < env->num_triggers; i++) { /* * type = TRIGGER_TYPE_AD_MATCH * dmode = 0 (both debug and M-mode can write tdata) 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 = 3, - .minimum_version_id = 3, + .version_id = 4, + .minimum_version_id = 4, .needed = debug_needed, .post_load = debug_post_load, .fields = (const VMStateField[]) { 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_VARRAY_UINT32(env.tdata1, RISCVCPU, + env.num_triggers, 0, + vmstate_info_uint64, uint64_t), + VMSTATE_VARRAY_UINT32(env.tdata2, RISCVCPU, + env.num_triggers, 0, + vmstate_info_uint64, uint64_t), + VMSTATE_VARRAY_UINT32(env.tdata3, RISCVCPU, + env.num_triggers, 0, + vmstate_info_uint64, uint64_t), VMSTATE_END_OF_LIST() } }; -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> All CPUs have the same amount of triggers: 2 triggers per hart, set via RV_MAX_TRIGGERS. This is not enough anymore: we'll have at least one future CPU that will demand more triggers per hart when implementing the RISC-V Server Ref Platform, requiring at least 11 triggers per hart. Parametrize the trigger amount using a new 'num_triggers' property. The default amount is kept at 2 for backwards compatibility. The new maximum is bumped to a generous 1024 triggers per hart, which hopefully will be enough for the foreseeable future. The property can be set in two ways: - a '.num_triggers' CPU definition flag, allowing CPUs to set a custom amount inside the CPU def in DEFINE_RISCV_CPU(); - a new 'num-triggers' user property. The user property has a higher priority than an existing '.num_triggers' CPU def setting. Assuming a hypothetical case where a CPU 'X' is defined with '.num_triggers = 8': - -cpu X,num-triggers=30 => num_triggers set to 30 - -cpu X (...) => num_triggers set to 8 For a CPU that doesn't set '.num_triggers': - -cpu rv64,num-triggers=30 => num_triggers set to 30 - -cpu rv64 (...) => num_triggers set to 2 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260617131710.1855353-4-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 19 ++++++++++++++++++- target/riscv/cpu.c | 11 +++++++++++ target/riscv/debug.c | 16 ++++++++-------- 3 files changed, 37 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 @@ extern RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[]; #define RV_VLEN_MAX 1024 #define RV_MAX_MHPMEVENTS 32 #define RV_MAX_MHPMCOUNTERS 32 -#define RV_MAX_TRIGGERS 2 + +/* + * The Debug 1.0 spec allows a humongous amount of triggers. Section + * "Enumeration" says: "The above algorithm reads back tselect so that + * implementations which have 2^n triggers only need to implement n + * bits of tselect.". tselect can have up to XLEN bits, so the max + * theoretical RV_MAX_TRIGGERS value is 2^XLEN. + * + * Allowing 2^XLEN triggers per hart is silly so we'll set a max to a + * modest 1024 triggers, which is way more than what we see current + * hardware use (most chips uses 2-4 triggers per hart, RISC-V Server + * Ref requires at least 11). With a 1024 max per hart we'll be set + * for a long time ... hopefully. + */ +#define RV_MAX_TRIGGERS 1024 +#define RV_DEFAULT_NUM_TRIGGERS 2 FIELD(VTYPE, VLMUL, 0, 3) FIELD(VTYPE, VSEW, 3, 3) @@ -XXX,XX +XXX,XX @@ typedef struct RISCVCPUDef { RISCVCPUConfig cfg; bool bare; const RISCVCSR *custom_csrs; + /* This is just a setter for env->num_triggers. */ + uint32_t num_triggers; } RISCVCPUDef; /** 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) IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX); qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_nmi, "riscv.cpu.rnmi", RNMI_MAX); + + if (mcc->def->num_triggers) { + env->num_triggers = mcc->def->num_triggers; + } + #endif /* CONFIG_USER_ONLY */ cpu->user_options = g_hash_table_new(g_str_hash, g_str_equal); @@ -XXX,XX +XXX,XX @@ static const Property riscv_cpu_properties[] = { DEFAULT_RNMI_IRQVEC), DEFINE_PROP_UINT64("rnmi-exception-vector", RISCVCPU, env.rnmi_excpvec, DEFAULT_RNMI_EXCPVEC), + DEFINE_PROP_UINT32("num-triggers", RISCVCPU, env.num_triggers, + RV_DEFAULT_NUM_TRIGGERS), #endif DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false), @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_class_base_init(ObjectClass *c, const void *data) !valid_vm_1_10_32[mcc->def->cfg.max_satp_mode]) { mcc->def->cfg.max_satp_mode = VM_1_10_SV32; } + + if (def->num_triggers) { + mcc->def->num_triggers = def->num_triggers; + } #endif } if (def->priv_spec != RISCV_PROFILE_ATTR_UNUSED) { 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/osdep.h" #include "qemu/log.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "cpu.h" #include "target/riscv/debug.h" #include "trace.h" @@ -XXX,XX +XXX,XX @@ void riscv_trigger_realize(CPURISCVState *env) { int i; - /* - * Alloc env->tdata1/2/3, cpu_breakpoint, cpu_watchpoint and - * itrigger_timer dynamically. This is overkill now - * given that they could be static arrays with RV_MAX_TRIGGERS - * but we'll parametrize the trigger number later, i.e. the - * array length won't be static. - */ - env->num_triggers = RV_MAX_TRIGGERS; + if (env->num_triggers > RV_MAX_TRIGGERS) { + error_report( + "Invalid configuration: 'num-triggers' must be less than %u", + RV_MAX_TRIGGERS); + exit(1); + } + env->tdata1 = g_new0(uint64_t, env->num_triggers); env->tdata2 = g_new0(uint64_t, env->num_triggers); env->tdata3 = g_new0(uint64_t, env->num_triggers); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Seems like we forgot to add a flag to set satp-mode=bare. What we're doing instead is to set the CPU default satp-mode to 'off' to set it to bare ... assuming we know the default satp-mode. Otherwise one needs to do -cpu X,sv39=off,sv48=off,sv57=off,sv64=off to get a bare-mode CPU. Add a flag to make this process easier. The name choice is based on the fact that I didn't find many references to 'mbare' in RISC-V docs, but 'svbare' is a flag name that toolchain uses to set satp-mode=bare. Might as well use the same name to help with cross project compat. After this patch: $ ./build/qemu-system-riscv64 -M virt,dumpdtb=fdt.dtb -cpu rv64 $ dtc -I dtb -O dts fdt.dtb | grep mmu-type mmu-type = "riscv,sv57"; $ ./build/qemu-system-riscv64 -M virt,dumpdtb=fdt.dtb -cpu rv64,svbare=on $ dtc -I dtb -O dts fdt.dtb | grep mmu-type mmu-type = "riscv,none"; Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260611162223.2361941-2-daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 5 ++++- 1 file changed, 4 insertions(+), 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 @@ int riscv_cpu_max_xlen(RISCVCPUClass *mcc) #ifndef CONFIG_USER_ONLY static uint8_t satp_mode_from_str(const char *satp_mode_str) { - if (!strncmp(satp_mode_str, "mbare", 5)) { + if (!strncmp(satp_mode_str, "svbare", 6)) { return VM_1_10_MBARE; } @@ -XXX,XX +XXX,XX @@ void riscv_add_satp_mode_properties(Object *obj) { RISCVCPU *cpu = RISCV_CPU(obj); + object_property_add(obj, "svbare", "bool", cpu_riscv_get_satp, + cpu_riscv_set_satp, NULL, &cpu->satp_modes); + if (cpu->env.misa_mxl == MXL_RV32) { object_property_add(obj, "sv32", "bool", cpu_riscv_get_satp, cpu_riscv_set_satp, NULL, &cpu->satp_modes); -- 2.54.0
From: Qingwei Hu <qingwei.hu@bytedance.com> Each RISC-V MADT RINTC entry contains an External Interrupt Controller ID field. On the virt machine without AIA, this field identifies the S-mode PLIC context associated with the hart. TCG virt has both M-mode and S-mode PLIC contexts, so the S-mode context ID is odd and 2 * local_cpu_id + 1 is correct. KVM virt exposes only S-mode PLIC contexts, and those contexts are numbered contiguously from 0. Reporting the TCG context ID for KVM makes the guest enable a different PLIC context from the one used by QEMU. With ACPI enabled, this can leave PCI INTx interrupts pending in QEMU while the guest-programmed PLIC context remains disabled. A virtio-blk root disk can then stall during boot because its first interrupt is never delivered. Use local_cpu_id for KVM and keep the existing odd S-mode context ID for TCG. Fixes: d641da6ed43 ("hw/riscv/virt-acpi-build.c: Add PLIC in MADT") Signed-off-by: Qingwei Hu <qingwei.hu@bytedance.com> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Reviewed-by: Sunil V L <sunilvl@oss.qualcomm.com> Message-ID: <20260604120017.398890-1-qingwei.hu@bytedance.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt-acpi-build.c | 2 ++ 1 file changed, 2 insertions(+) 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 riscv_acpi_madt_add_rintc(uint32_t uid, build_append_int_noprefix(entry, ACPI_BUILD_INTC_ID( arch_ids->cpus[uid].props.node_id, + kvm_enabled() ? + local_cpu_id : 2 * local_cpu_id + 1), 4); } else { -- 2.54.0
From: ZhengXiang Qin <qinzhengxiang@foxmail.com> TCG plugins may read registers from the vcpu_init_cb() callback. For vtype, this reaches read_vtype() before env->xl has been initialized. In that case read_vtype() currently hits g_assert_not_reached() because env->xl is zero. Fall back to the CPU's maximum XLEN only for this early-init case. Fixes: 638181a180bd ("core/cpu-common: initialise plugin state before thread creation") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3545 Signed-off-by: ZhengXiang Qin <qinzhengxiang@foxmail.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <tencent_FB929239B227F05F30D62745E38BA01D4307@qq.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 11 ++++++++++- 1 file changed, 10 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 read_vtype(CPURISCVState *env, int csrno, target_ulong *val) { uint64_t vill; - switch (env->xl) { + int xl = env->xl; + /* + * TCG plugins can read registers before env->xl is initialized. + * Fall back to the CPU's maximum XLEN in that early-init case. + */ + if (xl == 0) { + xl = riscv_cpu_mxl(env); + } + + switch (xl) { case MXL_RV32: vill = (uint32_t)env->vill << 31; break; -- 2.54.0
From: Qingwei Hu <qingwei.hu@bytedance.com> The emulated APLIC state arrays are only allocated when QEMU handles the interrupt controller state itself. With KVM AIA/APLIC-IMSIC, the interrupt controller state is owned by the KVM in-kernel irqchip, so these emulated state arrays are not allocated. The APLIC reset handler still clears those arrays unconditionally. This makes qemu_system_reset(), which runs during machine creation, dereference NULL pointers with -machine virt,aia=aplic-imsic and KVM. Skip the emulated APLIC reset path when the interrupt controller is handled by KVM. The emulated path is unchanged for TCG and for configurations that use QEMU emulation. Fixes: 99bfcd329a ("hw/intc: riscv_aplic: Add reset API to APLIC") Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Signed-off-by: Qingwei Hu <qingwei.hu@bytedance.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615131925.3019370-2-qingwei.hu@bytedance.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/intc/riscv_aplic.c | 4 ++++ 1 file changed, 4 insertions(+) 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_reset_enter(Object *obj, ResetType type) RISCVAPLICState *aplic = RISCV_APLIC(obj); int i; + if (!riscv_use_emulated_aplic(aplic->msimode)) { + return; + } + aplic->domaincfg = 0; memset(aplic->sourcecfg, 0, sizeof(uint32_t) * aplic->num_irqs); memset(aplic->target, 0, sizeof(uint32_t) * aplic->num_irqs); -- 2.54.0
From: Qingwei Hu <qingwei.hu@bytedance.com> The emulated IMSIC state arrays are only allocated when QEMU handles the interrupt controller state itself. With KVM AIA/APLIC-IMSIC, the interrupt controller state is owned by the KVM in-kernel irqchip, so these emulated state arrays are not allocated. The IMSIC reset handler still clears those arrays unconditionally. This makes qemu_system_reset(), which runs during machine creation, dereference NULL pointers with -machine virt,aia=aplic-imsic and KVM. Skip the emulated IMSIC reset path when the interrupt controller is handled by KVM. The emulated path is unchanged for TCG and for configurations that use QEMU emulation. Fixes: 766391483b ("hw/intc: riscv_imsic: Add reset API to IMSIC") Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Signed-off-by: Qingwei Hu <qingwei.hu@bytedance.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260615131925.3019370-3-qingwei.hu@bytedance.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/intc/riscv_imsic.c | 4 ++++ 1 file changed, 4 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 void riscv_imsic_reset_enter(Object *obj, ResetType type) RISCVIMSICState *imsic = RISCV_IMSIC(obj); int i; + if (kvm_irqchip_in_kernel()) { + return; + } + memset(imsic->eidelivery, 0, sizeof(uint32_t) * imsic->num_pages); memset(imsic->eithreshold, 0, sizeof(uint32_t) * imsic->num_pages); -- 2.54.0
From: Meng Zhuo <mengzhuo@iscas.ac.cn> During KVM exit processing (KVM_PUT_RUNTIME_STATE), QEMU never modifies FP or Vector registers — only core GPRs/PC and CSRs are potentially changed. Re-syncing 32 FP and 32 Vector registers on every KVM exit wastes 36-68 individual KVM_SET_ONE_REG ioctls per vCPU exit. Follow the s390x pattern: early return on RUNTIME after syncing core registers and CSRs. KVM_PUT_FULL_STATE and KVM_PUT_RESET_STATE continue to sync everything for correctness during migration and initialization. Signed-off-by: Meng Zhuo <mengzhuo@iscas.ac.cn> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Message-ID: <20260518102118.2768383-1-mengzhuo@iscas.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/kvm/kvm-cpu.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 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 @@ int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) return ret; } + /* + * For RUNTIME_STATE, KVM already has the correct FP and Vector state + * from the preceding KVM_RUN exit. QEMU never modifies these registers + * during exit handling, so re-syncing is unnecessary. This saves ~68 + * KVM_SET_ONE_REG ioctls per vCPU exit. See also s390x which uses + * the same pattern. + */ + if (KVM_PUT_RUNTIME_STATE == level) { + return ret; + } + ret = kvm_riscv_put_regs_fp(cs); if (ret) { return ret; @@ -XXX,XX +XXX,XX @@ int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) if (KVM_PUT_RESET_STATE == level) { RISCVCPU *cpu = RISCV_CPU(cs); - if (cs->cpu_index == 0) { - ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_RUNNABLE); - } else { - ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_STOPPED); - } + int state = cs->cpu_index == 0 ? KVM_MP_STATE_RUNNABLE + : KVM_MP_STATE_STOPPED; + ret = kvm_riscv_sync_mpstate_to_kvm(cpu, state); if (ret) { return ret; } -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> create_fdt() has the following code sequence: plic_phandle = phandle++; (...) qemu_fdt_setprop_cell(fdt, nodename, "phandle", plic_phandle); plic_phandle = qemu_fdt_get_phandle(fdt, nodename); What this is doing is assigning a value to plic_phandle, then use that as phandle of "nodename", and then fetching the phandle of "nodename" again in the same variable that already had the right phandle val. Here's how it looks like in gdb: Thread 1 "qemu-system-ris" hit Breakpoint 1, create_fdt (s=0xaaaaac162a30, memmap=0xaaaaab8f70c8 <sifive_u_memmap>, is_32_bit=false) at ../hw/riscv/sifive_u.c:199 199 plic_phandle = phandle++; (gdb) n 200 cells = g_new0(uint32_t, ms->smp.cpus * 4 - 2); (gdb) p plic_phandle $3 = 12 (gdb) c Continuing. Thread 1 "qemu-system-ris" hit Breakpoint 2, create_fdt (s=0xaaaaac162a30, memmap=0xaaaaab8f70c8 <sifive_u_memmap>, is_32_bit=false) at ../hw/riscv/sifive_u.c:232 232 plic_phandle = qemu_fdt_get_phandle(fdt, nodename); (gdb) n 233 g_free(cells); (gdb) p plic_phandle $4 = 12 (gdb) Remove the extra qemu_fdt_get_phandle() call. Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260616235939.1358663-2-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/sifive_u.c | 1 - 1 file changed, 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_cell(fdt, nodename, "riscv,ndev", SIFIVE_U_PLIC_NUM_SOURCES - 1); qemu_fdt_setprop_cell(fdt, nodename, "phandle", plic_phandle); - plic_phandle = qemu_fdt_get_phandle(fdt, nodename); g_free(cells); g_free(nodename); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> The info about intc_phandles for each CPU is already stored in the intc_phandles array. We don't need to fetch them again using qemu_fdt_get_phandle(). Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260616235939.1358663-3-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/sifive_u.c | 10 +++------- 1 file changed, 3 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, plic_phandle = phandle++; cells = g_new0(uint32_t, ms->smp.cpus * 4 - 2); 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); /* cpu 0 is the management hart that does not have S-mode */ if (cpu == 0) { - cells[0] = cpu_to_be32(intc_phandle); + cells[0] = cpu_to_be32(intc_phandles[cpu]); cells[1] = cpu_to_be32(IRQ_M_EXT); } else { - cells[cpu * 4 - 2] = cpu_to_be32(intc_phandle); + cells[cpu * 4 - 2] = cpu_to_be32(intc_phandles[cpu]); cells[cpu * 4 - 1] = cpu_to_be32(IRQ_M_EXT); - 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_S_EXT); } - g_free(nodename); } nodename = g_strdup_printf("/soc/interrupt-controller@%lx", (long)memmap[SIFIVE_U_DEV_PLIC].base); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> By Linux FDT docs in [1] the "address-cells" property is mandatory. Set it to zero. While we're at it let's also put this new value and the interrupt-cells value in macros, like the 'virt' board is doing. [1] https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/sifive%2Cplic-1.0.0.txt Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260616235939.1358663-4-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/sifive_u.h | 2 ++ hw/riscv/sifive_u.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -XXX,XX +XXX,XX @@ enum { #define SIFIVE_U_MANAGEMENT_CPU_COUNT 1 #define SIFIVE_U_COMPUTE_CPU_COUNT 4 +#define SIFIVE_U_PLIC_ADDR_CELLS 0 +#define SIFIVE_U_PLIC_INT_CELLS 1 #define SIFIVE_U_PLIC_NUM_SOURCES 54 #define SIFIVE_U_PLIC_NUM_PRIORITIES 7 #define SIFIVE_U_PLIC_PRIORITY_BASE 0x00 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, nodename = g_strdup_printf("/soc/interrupt-controller@%lx", (long)memmap[SIFIVE_U_DEV_PLIC].base); qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", + SIFIVE_U_PLIC_INT_CELLS); + qemu_fdt_setprop_cell(fdt, nodename, "#address-cells", + SIFIVE_U_PLIC_ADDR_CELLS); qemu_fdt_setprop_string_array(fdt, nodename, "compatible", (char **)&plic_compat, ARRAY_SIZE(plic_compat)); qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We're still using "/soc/plic@..." as FDT nodename in virt.c. This is not the right nodename per the DT docs: https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/sifive%2Cplic-1.0.0.txt The nodename must be 'interrupt-controller@...' since the node inherits the 'interrupt-controller' type. In fact, ever since at least the 2020 Linux kernel commit c825a081c169cc7f ("dt-bindings: riscv: convert plic bindings to json-schema") the correct nodename has been 'interrupt-controller' for the sifive PLIC controller. There's no deprecation needed for bug fixes so we're just fixing the name. This was the policy we dud when fixing the aplic [1] and the imsic [2] nodenames to 'interrupt-controller@...' as well. The sifive_u PLIC FDT already uses the correct nodename for PLIC, so it is safe to assume that available SW is already aware of the correct nodename and this change won't affect well-behaved SW. [1] commit 29390fd ("hw/riscv/virt.c: rename aplic nodename to 'interrupt-controller'") [2] commit e8ad581 ("hw/riscv/virt.c: change imsic nodename to 'interrupt-controller'") Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260616235939.1358663-5-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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_plic(RISCVVirtState *s, plic_phandles[socket] = (*phandle)++; plic_addr = s->memmap[VIRT_PLIC].base + (s->memmap[VIRT_PLIC].size * socket); - plic_name = g_strdup_printf("/soc/plic@%lx", plic_addr); + plic_name = g_strdup_printf("/soc/interrupt-controller@%lx", plic_addr); qemu_fdt_add_subnode(ms->fdt, plic_name); qemu_fdt_setprop_cell(ms->fdt, plic_name, "#interrupt-cells", FDT_PLIC_INT_CELLS); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Consolidate the common plic FDT code between 'virt' and sifive_u boards 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: <20260616235939.1358663-6-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 | 30 +++++++++++++++++++++ hw/riscv/sifive_u.c | 34 +++++++----------------- hw/riscv/virt.c | 49 ++++++++++++----------------------- 4 files changed, 61 insertions(+), 57 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_cpu_sifive(void *fdt, char *clust_name, int cpu_id, int socket_id, int socket_hartid_base, uint32_t *phandle, uint32_t *intc_phandles); +void create_fdt_plic(void *fdt, hwaddr addr, uint64_t size, + uint32_t plic_phandle, uint32_t int_cells, + uint32_t addr_cells, uint32_t *plic_cells, + uint32_t cells_size, uint32_t ndev_sources, + bool numa_enabled, int socket); #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 @@ create_fdt_socket_cpu_sifive(void *fdt, char *clust_name, socket_id, socket_hartid_base, phandle, intc_phandles, false, false); } + +void create_fdt_plic(void *fdt, hwaddr addr, uint64_t size, + uint32_t plic_phandle, uint32_t int_cells, + uint32_t addr_cells, uint32_t *plic_cells, + uint32_t cells_size, uint32_t ndev_sources, + bool numa_enabled, int socket_id) +{ + g_autofree char *nodename = NULL; + static const char * const plic_compat[2] = { + "sifive,plic-1.0.0", "riscv,plic0" + }; + + nodename = g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIx, addr); + + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", int_cells); + qemu_fdt_setprop_cell(fdt, nodename, "#address-cells", addr_cells); + qemu_fdt_setprop_string_array(fdt, nodename, "compatible", + (char **)&plic_compat, ARRAY_SIZE(plic_compat)); + qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop(fdt, nodename, "interrupts-extended", + plic_cells, cells_size); + qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", + 2, addr, 2, size); + qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", ndev_sources); + if (numa_enabled) { + qemu_fdt_setprop_cell(fdt, nodename, "numa-node-id", socket_id); + } + qemu_fdt_setprop_cell(fdt, nodename, "phandle", plic_phandle); +} 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, MachineState *ms = MACHINE(s); void *fdt; int cpu; - uint32_t *cells; + uint32_t *cells, cells_length; char *nodename; 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 plic_compat[2] = { - "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; @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, g_free(nodename); plic_phandle = phandle++; - cells = g_new0(uint32_t, ms->smp.cpus * 4 - 2); + cells_length = ms->smp.cpus * 4 - 2; + cells = g_new0(uint32_t, cells_length); for (cpu = 0; cpu < ms->smp.cpus; cpu++) { /* cpu 0 is the management hart that does not have S-mode */ if (cpu == 0) { @@ -XXX,XX +XXX,XX @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, cells[cpu * 4 + 1] = cpu_to_be32(IRQ_S_EXT); } } - nodename = g_strdup_printf("/soc/interrupt-controller@%lx", - (long)memmap[SIFIVE_U_DEV_PLIC].base); - qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", - SIFIVE_U_PLIC_INT_CELLS); - qemu_fdt_setprop_cell(fdt, nodename, "#address-cells", - SIFIVE_U_PLIC_ADDR_CELLS); - qemu_fdt_setprop_string_array(fdt, nodename, "compatible", - (char **)&plic_compat, ARRAY_SIZE(plic_compat)); - qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); - qemu_fdt_setprop(fdt, nodename, "interrupts-extended", - cells, (ms->smp.cpus * 4 - 2) * sizeof(uint32_t)); - qemu_fdt_setprop_cells(fdt, nodename, "reg", - 0x0, memmap[SIFIVE_U_DEV_PLIC].base, - 0x0, memmap[SIFIVE_U_DEV_PLIC].size); - qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", - SIFIVE_U_PLIC_NUM_SOURCES - 1); - qemu_fdt_setprop_cell(fdt, nodename, "phandle", plic_phandle); + + create_fdt_plic(fdt, memmap[SIFIVE_U_DEV_PLIC].base, + memmap[SIFIVE_U_DEV_PLIC].size, + plic_phandle, SIFIVE_U_PLIC_INT_CELLS, + SIFIVE_U_PLIC_ADDR_CELLS, cells, + cells_length * sizeof(uint32_t), + SIFIVE_U_PLIC_NUM_SOURCES - 1, false, 0); g_free(cells); - g_free(nodename); gpio_phandle = phandle++; nodename = g_strdup_printf("/soc/gpio@%lx", 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_plic(RISCVVirtState *s, int cpu; g_autofree char *plic_name = NULL; g_autofree uint32_t *plic_cells; - unsigned long plic_addr; MachineState *ms = MACHINE(s); - static const char * const plic_compat[2] = { - "sifive,plic-1.0.0", "riscv,plic0" - }; + unsigned long plic_addr = s->memmap[VIRT_PLIC].base + + (s->memmap[VIRT_PLIC].size * socket); + bool numa_enabled = riscv_numa_enabled(MACHINE(s)); + uint32_t cells_length; - plic_phandles[socket] = (*phandle)++; - plic_addr = s->memmap[VIRT_PLIC].base + - (s->memmap[VIRT_PLIC].size * socket); plic_name = g_strdup_printf("/soc/interrupt-controller@%lx", plic_addr); - qemu_fdt_add_subnode(ms->fdt, plic_name); - qemu_fdt_setprop_cell(ms->fdt, plic_name, - "#interrupt-cells", FDT_PLIC_INT_CELLS); - qemu_fdt_setprop_cell(ms->fdt, plic_name, - "#address-cells", FDT_PLIC_ADDR_CELLS); - qemu_fdt_setprop_string_array(ms->fdt, plic_name, "compatible", - (char **)&plic_compat, - ARRAY_SIZE(plic_compat)); - qemu_fdt_setprop(ms->fdt, plic_name, "interrupt-controller", NULL, 0); if (kvm_enabled()) { - plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2); + cells_length = s->soc[socket].num_harts * 2; + plic_cells = g_new0(uint32_t, cells_length); for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) { plic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); plic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_EXT); } - - qemu_fdt_setprop(ms->fdt, plic_name, "interrupts-extended", - plic_cells, - s->soc[socket].num_harts * sizeof(uint32_t) * 2); } else { - plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4); + cells_length = s->soc[socket].num_harts * 4; + plic_cells = g_new0(uint32_t, cells_length); for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) { plic_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]); @@ -XXX,XX +XXX,XX @@ static void create_fdt_socket_plic(RISCVVirtState *s, plic_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]); plic_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT); } - - qemu_fdt_setprop(ms->fdt, plic_name, "interrupts-extended", - plic_cells, - s->soc[socket].num_harts * sizeof(uint32_t) * 4); } - qemu_fdt_setprop_sized_cells(ms->fdt, plic_name, "reg", - 2, plic_addr, 2, s->memmap[VIRT_PLIC].size); - qemu_fdt_setprop_cell(ms->fdt, plic_name, "riscv,ndev", - VIRT_IRQCHIP_NUM_SOURCES - 1); - riscv_socket_fdt_write_id(ms, plic_name, socket); - qemu_fdt_setprop_cell(ms->fdt, plic_name, "phandle", - plic_phandles[socket]); + plic_phandles[socket] = (*phandle)++; + + create_fdt_plic(ms->fdt, plic_addr, s->memmap[VIRT_PLIC].size, + plic_phandles[socket], FDT_PLIC_INT_CELLS, + FDT_PLIC_ADDR_CELLS, plic_cells, + cells_length * sizeof(uint32_t), + VIRT_IRQCHIP_NUM_SOURCES - 1, + numa_enabled, socket); if (!socket) { platform_bus_add_all_fdt_nodes(ms->fdt, plic_name, -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> The existing nomenclature can be misleading: regs_rw can cosplay as 'read and write' mask, in particular because we have regs_ro which is a read only mask. regs_rw is the current reg value, and all bits that aren't on the regs_ro mask is considered r/w bits. Rename regs_rw to 'regs' to be on par with the nomenclature other devices uses (e.g. cadence_gem). Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260625210833.3294437-2-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu.h | 26 +++++++++++++++----------- hw/riscv/riscv-iommu-hpm.c | 4 ++-- hw/riscv/riscv-iommu.c | 36 ++++++++++++++++++------------------ 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu.h +++ b/hw/riscv/riscv-iommu.h @@ -XXX,XX +XXX,XX @@ struct RISCVIOMMUState { /* MMIO Hardware Interface */ MemoryRegion regs_mr; - uint8_t *regs_rw; /* register state (user write) */ + uint8_t *regs; /* current register state */ uint8_t *regs_wc; /* write-1-to-clear mask */ - uint8_t *regs_ro; /* read-only mask */ + /* + * read-only mask. NOTE: bits not present in this RO + * mask are assumed to be read and write. + */ + uint8_t *regs_ro; QLIST_ENTRY(RISCVIOMMUState) iommus; QLIST_HEAD(, RISCVIOMMUSpace) spaces; @@ -XXX,XX +XXX,XX @@ struct RISCVIOMMUContext { static inline uint32_t riscv_iommu_reg_mod32(RISCVIOMMUState *s, unsigned idx, uint32_t set, uint32_t clr) { - uint32_t val = ldl_le_p(s->regs_rw + idx); - stl_le_p(s->regs_rw + idx, (val & ~clr) | set); + uint32_t val = ldl_le_p(s->regs + idx); + stl_le_p(s->regs + idx, (val & ~clr) | set); return val; } static inline void riscv_iommu_reg_set32(RISCVIOMMUState *s, unsigned idx, uint32_t set) { - stl_le_p(s->regs_rw + idx, set); + stl_le_p(s->regs + idx, set); } static inline uint32_t riscv_iommu_reg_get32(RISCVIOMMUState *s, unsigned idx) { - return ldl_le_p(s->regs_rw + idx); + return ldl_le_p(s->regs + idx); } static inline uint64_t riscv_iommu_reg_mod64(RISCVIOMMUState *s, unsigned idx, uint64_t set, uint64_t clr) { - uint64_t val = ldq_le_p(s->regs_rw + idx); - stq_le_p(s->regs_rw + idx, (val & ~clr) | set); + uint64_t val = ldq_le_p(s->regs + idx); + stq_le_p(s->regs + idx, (val & ~clr) | set); return val; } static inline void riscv_iommu_reg_set64(RISCVIOMMUState *s, unsigned idx, uint64_t set) { - stq_le_p(s->regs_rw + idx, set); + stq_le_p(s->regs + idx, set); } static inline uint64_t riscv_iommu_reg_get64(RISCVIOMMUState *s, - unsigned idx) + unsigned idx) { - return ldq_le_p(s->regs_rw + idx); + return ldq_le_p(s->regs + idx); } #endif 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 @@ static void hpm_incr_ctr(RISCVIOMMUState *s, uint32_t ctr_idx) const uint32_t off = ctr_idx << 3; uint64_t cntr_val; - cntr_val = ldq_le_p(&s->regs_rw[RISCV_IOMMU_REG_IOHPMCTR_BASE + off]); - stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_IOHPMCTR_BASE + off], cntr_val + 1); + cntr_val = ldq_le_p(&s->regs[RISCV_IOMMU_REG_IOHPMCTR_BASE + off]); + stq_le_p(&s->regs[RISCV_IOMMU_REG_IOHPMCTR_BASE + off], cntr_val + 1); trace_riscv_iommu_hpm_incr_ctr(cntr_val); 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) s->cq_mask = (2ULL << get_field(base, RISCV_IOMMU_CQB_LOG2SZ)) - 1; s->cq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_CQB_PPN)); stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~s->cq_mask); - stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQH], 0); - stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQT], 0); + stl_le_p(&s->regs[RISCV_IOMMU_REG_CQH], 0); + stl_le_p(&s->regs[RISCV_IOMMU_REG_CQT], 0); ctrl_set = RISCV_IOMMU_CQCSR_CQON; ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQMF | RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CMD_TO | @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_process_fq_control(RISCVIOMMUState *s) s->fq_mask = (2ULL << get_field(base, RISCV_IOMMU_FQB_LOG2SZ)) - 1; s->fq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_FQB_PPN)); stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~s->fq_mask); - stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQH], 0); - stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQT], 0); + stl_le_p(&s->regs[RISCV_IOMMU_REG_FQH], 0); + stl_le_p(&s->regs[RISCV_IOMMU_REG_FQT], 0); ctrl_set = RISCV_IOMMU_FQCSR_FQON; ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQMF | RISCV_IOMMU_FQCSR_FQOF; @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_process_pq_control(RISCVIOMMUState *s) s->pq_mask = (2ULL << get_field(base, RISCV_IOMMU_PQB_LOG2SZ)) - 1; s->pq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_PQB_PPN)); stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~s->pq_mask); - stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQH], 0); - stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQT], 0); + stl_le_p(&s->regs[RISCV_IOMMU_REG_PQH], 0); + stl_le_p(&s->regs[RISCV_IOMMU_REG_PQT], 0); ctrl_set = RISCV_IOMMU_PQCSR_PQON; ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQMF | RISCV_IOMMU_PQCSR_PQOF; @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_write_reg_val(RISCVIOMMUState *s, { uint64_t ro = ldn_le_p(&s->regs_ro[reg_addr], size); uint64_t wc = ldn_le_p(&s->regs_wc[reg_addr], size); - uint64_t rw = ldn_le_p(&s->regs_rw[reg_addr], size); + uint64_t curr_val = ldn_le_p(&s->regs[reg_addr], size); - stn_le_p(dest, size, ((rw & ro) | (data & ~ro)) & ~(data & wc)); + stn_le_p(dest, size, ((curr_val & ro) | (data & ~ro)) & ~(data & wc)); } static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr, @@ -XXX,XX +XXX,XX @@ static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr, * is set IOMMU behavior of additional writes to the register * is UNSPECIFIED. */ - riscv_iommu_write_reg_val(s, &s->regs_rw[addr], addr, size, data); + riscv_iommu_write_reg_val(s, &s->regs[addr], addr, size, data); /* Busy flag update, MSB 4-byte register. */ if (busy) { - uint32_t rw = ldl_le_p(&s->regs_rw[regb]); - stl_le_p(&s->regs_rw[regb], rw | busy); + uint32_t rw = ldl_le_p(&s->regs[regb]); + stl_le_p(&s->regs[regb], rw | busy); } /* Process HPM writes and update any internal state if needed. */ @@ -XXX,XX +XXX,XX @@ static MemTxResult riscv_iommu_mmio_read(void *opaque, hwaddr addr, * it's not dependent over the timer callback and is computed * from cycle overflow. */ - val = ldq_le_p(&s->regs_rw[addr]); + val = ldq_le_p(&s->regs[addr]); val |= (riscv_iommu_hpmcycle_read(s) & RISCV_IOMMU_IOHPMCYCLES_OVF) ? RISCV_IOMMU_IOCOUNTOVF_CY : 0; ptr = (uint8_t *)&val + (addr & 3); } else { - ptr = &s->regs_rw[addr]; + ptr = &s->regs[addr]; } val = ldn_le_p(ptr, size); @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_instance_init(Object *obj) s->cap |= RISCV_IOMMU_CAP_PD8; /* register storage */ - s->regs_rw = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); + s->regs = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); s->regs_ro = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); s->regs_wc = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_instance_finalize(Object *obj) { RISCVIOMMUState *s = RISCV_IOMMU(obj); - g_free(s->regs_rw); + g_free(s->regs); g_free(s->regs_ro); g_free(s->regs_wc); @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp) "riscv-iommu-regs", RISCV_IOMMU_REG_SIZE); /* Set power-on register state */ - stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_CAP], s->cap); - stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_FCTL], 0); + stq_le_p(&s->regs[RISCV_IOMMU_REG_CAP], s->cap); + stq_le_p(&s->regs[RISCV_IOMMU_REG_FCTL], 0); stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FCTL], ~(RISCV_IOMMU_FCTL_BE | RISCV_IOMMU_FCTL_WSI)); stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_DDTP], @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp) RISCV_IOMMU_PQCSR_BUSY); stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_IPSR], ~0); stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_ICVEC], 0); - stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_DDTP], s->ddtp); + stq_le_p(&s->regs[RISCV_IOMMU_REG_DDTP], s->ddtp); /* If debug registers enabled. */ if (s->cap & RISCV_IOMMU_CAP_DBG) { stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_IOVA], 0); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We do not support FCTL.BE equal to 1 hence do not allow this bit to be set by software. While we're at it: the riscv-iommu spec allows FCTL.GXL to be set freely and we do not have hardcoded restrictions on it, so make it writable. Fixes: 0c54acb824 ("hw/riscv: add RISC-V IOMMU base emulation") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3576 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260625210833.3294437-3-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu.c | 4 +++- 1 file changed, 3 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 void riscv_iommu_realize(DeviceState *dev, Error **errp) /* Set power-on register state */ stq_le_p(&s->regs[RISCV_IOMMU_REG_CAP], s->cap); + stq_le_p(&s->regs[RISCV_IOMMU_REG_FCTL], 0); stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FCTL], - ~(RISCV_IOMMU_FCTL_BE | RISCV_IOMMU_FCTL_WSI)); + ~(RISCV_IOMMU_FCTL_GXL | RISCV_IOMMU_FCTL_WSI)); + stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_DDTP], ~(RISCV_IOMMU_DDTP_PPN | RISCV_IOMMU_DDTP_MODE)); stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQB], -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We are not checking for reserved TC bits being set during device context validation. Fixes: 0c54acb8243d ("hw/riscv: add RISC-V IOMMU base emulation") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3548 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260627194640.4130073-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu-bits.h | 3 +++ hw/riscv/riscv-iommu.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/hw/riscv/riscv-iommu-bits.h b/hw/riscv/riscv-iommu-bits.h index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu-bits.h +++ b/hw/riscv/riscv-iommu-bits.h @@ -XXX,XX +XXX,XX @@ struct riscv_iommu_dc { #define RISCV_IOMMU_DC_IOHGATP_GSCID GENMASK_ULL(59, 44) #define RISCV_IOMMU_DC_IOHGATP_MODE RISCV_IOMMU_ATP_MODE_FIELD +#define RISCV_IOMMU_DC_TC_RESERVED (GENMASK_ULL(23, 12) \ + | GENMASK_ULL(63, 32)) + enum riscv_iommu_dc_iohgatp_modes { RISCV_IOMMU_DC_IOHGATP_MODE_BARE = 0, RISCV_IOMMU_DC_IOHGATP_MODE_SV32X4 = 8, 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, uint32_t fsc_mode, msi_mode; uint64_t gatp; + if (ctx->tc & RISCV_IOMMU_DC_TC_RESERVED) { + return false; + } + if (!(s->cap & RISCV_IOMMU_CAP_ATS) && (ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS || ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI || -- 2.54.0
From: SeungJu Cheon <suunj1331@gmail.com> write_mstatus() already handles the reserved UXL value by writing a legal value instead. Apply the same handling when writing vsstatus so that reserved UXL values follow the same WARL behavior. Factor the common logic into a local helper riscv_write_uxl() and use it for both mstatus and vsstatus. Suggested-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Fixes: f310df58bd2 ("target/riscv: Enable uxl field write") Signed-off-by: SeungJu Cheon <suunj1331@gmail.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260625081521.595683-1-suunj1331@gmail.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 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 target_ulong legalize_mpp(CPURISCVState *env, target_ulong old_mpp, return val; } +static uint64_t riscv_write_uxl(CPURISCVState *env, uint64_t val, + uint64_t field) +{ + RISCVMXL xl = riscv_cpu_mxl(env); + uint64_t uxl = get_field(val, field); + + if (uxl == MXL_RV128) { + uxl = xl == MXL_RV128 ? MXL_RV64 : xl; + val = set_field(val, field, uxl); + } + + return val; +} + static RISCVException write_mstatus(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { @@ -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); - } + val = riscv_write_uxl(env, val, MSTATUS64_UXL); } } @@ -XXX,XX +XXX,XX @@ static RISCVException write_vsstatus(CPURISCVState *env, int csrno, uint64_t mask = (target_ulong)-1; if ((val & VSSTATUS64_UXL) == 0) { mask &= ~VSSTATUS64_UXL; + } else { + val = riscv_write_uxl(env, val, VSSTATUS64_UXL); } if ((env->henvcfg & HENVCFG_DTE)) { if ((val & SSTATUS_SDT) != 0) { -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We're not setting RISCV_IOMMU_CQCSR_CMD_ILL if a reserved bit happens to be set in an IOFENCE.C command. Fixes: 0c54acb8243d ("hw/riscv: add RISC-V IOMMU base emulation") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3575 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260626170533.3562484-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu-bits.h | 1 + hw/riscv/riscv-iommu.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/hw/riscv/riscv-iommu-bits.h b/hw/riscv/riscv-iommu-bits.h index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu-bits.h +++ b/hw/riscv/riscv-iommu-bits.h @@ -XXX,XX +XXX,XX @@ struct riscv_iommu_command { #define RISCV_IOMMU_CMD_IOFENCE_OPCODE 2 #define RISCV_IOMMU_CMD_IOFENCE_FUNC_C 0 #define RISCV_IOMMU_CMD_IOFENCE_AV BIT_ULL(10) +#define RISCV_IOMMU_CMD_IOFENCE_RESERVED GENMASK_ULL(31, 14) #define RISCV_IOMMU_CMD_IOFENCE_DATA GENMASK_ULL(63, 32) #define RISCV_IOMMU_CMD_IODIR_OPCODE 3 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_tail(RISCVIOMMUState *s) switch (cmd_opcode) { case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOFENCE_FUNC_C, RISCV_IOMMU_CMD_IOFENCE_OPCODE): + if (cmd.dword0 & RISCV_IOMMU_CMD_IOFENCE_RESERVED) { + goto cmd_ill; + } + res = riscv_iommu_iofence(s, cmd.dword0 & RISCV_IOMMU_CMD_IOFENCE_AV, cmd.dword1 << 2, get_field(cmd.dword0, RISCV_IOMMU_CMD_IOFENCE_DATA)); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We're hardcoding 'true' to FQ_HDR_PV in riscv_iommu_report_fault(). Use the 'pv' bool the caller provides that indicates if the process_id is valid. Fixes: 0c54acb8243d ("hw/riscv: add RISC-V IOMMU base emulation") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3556 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260626173341.3661446-1-daniel.barboza@oss.qualcomm.com> 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 void riscv_iommu_report_fault(RISCVIOMMUState *s, ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_CAUSE, cause); ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_TTYPE, fault_type); ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_DID, ctx->devid); - ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PV, true); + ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PV, pv); if (pv) { ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PID, ctx->process_id); -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> We're doing a wrong shift when calculating the offset for the interrupt-pending bits, off by one right shift order. This went undercover for awhile because the calculation works for interrupt entities 1 to 63. The math goes wrong when using interrupt entities 64 or greater. Instead of fixing the issue and running we're also adding some notes on where this calc comes from. Fixes: 0c54acb8243d ("hw/riscv: add RISC-V IOMMU base emulation") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3561 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260626220529.3800372-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu.c | 21 ++++++++++++++++++++- 1 file changed, 20 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 MemTxResult riscv_iommu_msi_write(RISCVIOMMUState *s, /* MRIF pending bit address */ addr = get_field(pte[0], RISCV_IOMMU_MSI_PTE_MRIF_ADDR) << 9; - addr = addr | ((data & 0x7c0) >> 3); + /* + * AIA spec section "Format of a memory-resident interrupt file": + * address offset 0x000 contains interrupt-pending bits for + * identities 1-63, offfset 0x010 for identities 64-127, and + * so it goes up to 0x1F0 for identities 1984-2047. + * + * Hence each batch of identities advances offset by 16 (0x010) + * for every interrupt-pending bits. This means that doing + * (data & 0x7c0) will filter out the first 6 bits, then + * a >> 2 will turn the result in the 0x10 steps we need. + * + * E.g: + * + * - (1-63 & 0x7c0) = 0, 0 >> 2 = 0, offset 0x000 + * - (64-127 & 0x7c0) = 64, 64 >> 2 = 16, offset 0x010 + * - (128-191 & 0x7c0) = 128, 128 >> 2 = 32, offset 0x020 + * + * and so on. + */ + addr = addr | ((data & 0x7c0) >> 2); trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid), PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid), -- 2.54.0
From: Charlie Jenkins <thecharlesjenkins@gmail.com> When a non-vendor CPU is used, report the archid as 42 which has been allocated for QEMU in the riscv isa manual [1]. This can help software check if it is running in QEMU. [1] https://github.com/riscv/riscv-isa-manual/blob/main/marchid.md Signed-off-by: Charlie Jenkins <thecharlesjenkins@gmail.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-ID: <20260625-marchid-v2-1-3821c351028b@gmail.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 11 +++++++++++ 1 file changed, 11 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 const char riscv_single_letter_exts[] = "IEMAFDQCBPVH"; const uint32_t misa_bits[] = {RVI, RVE, RVM, RVA, RVF, RVD, RVV, RVC, RVS, RVU, RVH, RVG, RVB, 0}; +#define RISCV_CPU_MVENDORID 0 +#define RISCV_CPU_MIMPID 0 +/* + * marchid allocated for qemu: + * https://github.com/riscv/riscv-isa-manual/blob/main/marchid.md + */ +#define RISCV_CPU_MARCHID 42 /* * From vector_helper.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_init(Object *obj) mcc->def->profile->enabled = true; } + cpu->cfg.mvendorid = RISCV_CPU_MVENDORID; + cpu->cfg.marchid = RISCV_CPU_MARCHID; + cpu->cfg.mimpid = RISCV_CPU_MIMPID; + env->misa_ext_mask = env->misa_ext = mcc->def->misa_ext; riscv_cpu_cfg_merge(&cpu->cfg, &mcc->def->cfg); -- 2.54.0
From: Inochi Amaoto <inochiama@gmail.com> According to the RISC-V spec, the PTE update is a supervisor write operations, and it should also follow the CPU PMP configuration like the PTE read. Cc: qemu-stable@nongnu.org Signed-off-by: Inochi Amaoto <inochiama@gmail.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260622113402.563196-1-inochiama@gmail.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_helper.c | 8 ++++++++ 1 file changed, 8 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, /* Page table updates need to be atomic with MTTCG enabled */ if (updated_pte != pte && !is_debug) { + int pmp_prot, pmp_ret; + if (!adue) { return TRANSLATE_FAIL; } + pmp_ret = get_physical_address_pmp(env, &pmp_prot, pte_addr, + sxlen_bytes, MMU_DATA_STORE, PRV_S); + if (pmp_ret != TRANSLATE_SUCCESS) { + return TRANSLATE_PMP_FAIL; + } + /* * - if accessed or dirty bits need updating, and the PTE is * in RAM, then we do so atomically with a compare and swap. -- 2.54.0
From: Nicholas Piggin <npiggin@gmail.com> Machines that have discontiguous memory may need to adjust where firmware and images are loaded at boot. Provide an interface for machines to describe a discontiguous low/high RAM scheme for this purpose. Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20260630024952.1520546-2-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/boot.h | 7 +++++++ hw/riscv/boot.c | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/boot.h +++ b/include/hw/riscv/boot.h @@ -XXX,XX +XXX,XX @@ #define RISCV64_BIOS_BIN "opensbi-riscv64-generic-fw_dynamic.bin" typedef struct RISCVBootInfo { + /* First contiguous RAM region. If size is zero then assume entire RAM */ + hwaddr ram_low_start; + hwaddr ram_low_size; + ssize_t kernel_size; hwaddr image_low_addr; hwaddr image_high_addr; @@ -XXX,XX +XXX,XX @@ bool riscv_is_32bit(RISCVHartArrayState *harts); char *riscv_plic_hart_config_string(int hart_count); void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts); +void riscv_boot_info_init_discontig_mem(RISCVBootInfo *info, + RISCVHartArrayState *harts, + hwaddr low_start, hwaddr low_size); vaddr riscv_calc_kernel_start_addr(RISCVBootInfo *info, hwaddr firmware_end_addr); hwaddr riscv_find_and_load_firmware(MachineState *machine, 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 @@ char *riscv_plic_hart_config_string(int hart_count) void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts) { + info->ram_low_start = 0; + info->ram_low_size = 0; info->kernel_size = 0; info->initrd_size = 0; info->is_32bit = riscv_is_32bit(harts); } +/* + * This can be used instead of riscv_boot_info_init() if the machine has + * discontiguous physical memory. The low memory range specified will be + * used to place firmware images. + */ +void riscv_boot_info_init_discontig_mem(RISCVBootInfo *info, + RISCVHartArrayState *harts, + hwaddr low_start, hwaddr low_size) +{ + riscv_boot_info_init(info, harts); + info->ram_low_start = low_start; + info->ram_low_size = low_size; +} + vaddr riscv_calc_kernel_start_addr(RISCVBootInfo *info, hwaddr firmware_end_addr) { if (info->is_32bit) { -- 2.54.0
From: Nicholas Piggin <npiggin@gmail.com> This loads firmware into the first (low) memory range, accounting for machines having discontiguous memory regions. Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20260630024952.1520546-3-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/boot.h | 5 ++++- hw/riscv/boot.c | 18 ++++++++++++------ hw/riscv/k230.c | 8 ++++++-- hw/riscv/microchip_pfsoc.c | 8 ++++++-- hw/riscv/opentitan.c | 6 ++++-- hw/riscv/shakti_c.c | 6 +++++- hw/riscv/sifive_u.c | 6 ++++-- hw/riscv/spike.c | 6 ++++-- hw/riscv/virt.c | 7 ++++--- hw/riscv/xiangshan_kmh.c | 6 +++++- 10 files changed, 54 insertions(+), 22 deletions(-) diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/boot.h +++ b/include/hw/riscv/boot.h @@ -XXX,XX +XXX,XX @@ void riscv_boot_info_init_discontig_mem(RISCVBootInfo *info, vaddr riscv_calc_kernel_start_addr(RISCVBootInfo *info, hwaddr firmware_end_addr); hwaddr riscv_find_and_load_firmware(MachineState *machine, + RISCVBootInfo *info, const char *default_machine_firmware, hwaddr *firmware_load_addr, symbol_fn_t sym_cb); const char *riscv_default_firmware_name(RISCVHartArrayState *harts); char *riscv_find_firmware(const char *firmware_filename, const char *default_machine_firmware); -hwaddr riscv_load_firmware(const char *firmware_filename, +hwaddr riscv_load_firmware(MachineState *machine, + const RISCVBootInfo *info, + const char *firmware_filename, hwaddr *firmware_load_addr, symbol_fn_t sym_cb); void riscv_load_kernel(MachineState *machine, 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 @@ char *riscv_find_firmware(const char *firmware_filename, } hwaddr riscv_find_and_load_firmware(MachineState *machine, + RISCVBootInfo *info, const char *default_machine_firmware, hwaddr *firmware_load_addr, symbol_fn_t sym_cb) @@ -XXX,XX +XXX,XX @@ hwaddr riscv_find_and_load_firmware(MachineState *machine, if (firmware_filename) { /* If not "none" load the firmware */ - firmware_end_addr = riscv_load_firmware(firmware_filename, + firmware_end_addr = riscv_load_firmware(machine, info, + firmware_filename, firmware_load_addr, sym_cb); g_free(firmware_filename); } @@ -XXX,XX +XXX,XX @@ hwaddr riscv_find_and_load_firmware(MachineState *machine, return firmware_end_addr; } -hwaddr riscv_load_firmware(const char *firmware_filename, +hwaddr riscv_load_firmware(MachineState *machine, + const RISCVBootInfo *info, + const char *firmware_filename, hwaddr *firmware_load_addr, symbol_fn_t sym_cb) { + uint64_t mem_size = info->ram_low_size ?: machine->ram_size; uint64_t firmware_entry, firmware_end; ssize_t firmware_size; @@ -XXX,XX +XXX,XX @@ hwaddr riscv_load_firmware(const char *firmware_filename, firmware_size = load_image_targphys_as(firmware_filename, *firmware_load_addr, - current_machine->ram_size, NULL, + mem_size, NULL, NULL); if (firmware_size > 0) { @@ -XXX,XX +XXX,XX @@ hwaddr riscv_load_firmware(const char *firmware_filename, static void riscv_load_initrd(MachineState *machine, RISCVBootInfo *info) { const char *filename = machine->initrd_filename; - uint64_t mem_size = machine->ram_size; + uint64_t mem_size = info->ram_low_size ?: machine->ram_size; void *fdt = machine->fdt; hwaddr start, end; ssize_t size; @@ -XXX,XX +XXX,XX @@ void riscv_load_kernel(MachineState *machine, bool load_initrd, symbol_fn_t sym_cb) { + uint64_t mem_size = info->ram_low_size ?: machine->ram_size; const char *kernel_filename = machine->kernel_filename; ssize_t kernel_size; void *fdt = machine->fdt; @@ -XXX,XX +XXX,XX @@ void riscv_load_kernel(MachineState *machine, } kernel_size = load_image_targphys_as(kernel_filename, kernel_start_addr, - current_machine->ram_size, NULL, NULL); + mem_size, NULL, NULL); if (kernel_size > 0) { info->kernel_size = kernel_size; info->image_low_addr = kernel_start_addr; @@ -XXX,XX +XXX,XX @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, dtb_start = QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB); if (dtb_start_limit && (dtb_start < dtb_start_limit)) { - error_report("No enough memory to place DTB after kernel/initrd"); + error_report("Not enough memory to place DTB after kernel/initrd"); exit(1); } 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_direct_boot(K230MachineState *s, MachineState *machine) riscv_load_fdt(K230_DIRECT_DTB_ADDR, machine->fdt); - firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, + firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info, + firmware_name, &start_addr, NULL); if (firmware_end_addr > K230_DIRECT_KERNEL_ADDR) { error_report("K230 firmware overlaps kernel address 0x%x", @@ -XXX,XX +XXX,XX @@ 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; + RISCVBootInfo boot_info = {0}; 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_boot_info_init(&boot_info, &s->soc.c908_cpu); + riscv_find_and_load_firmware(machine, &boot_info, firmware_name, + &start_addr, NULL); riscv_setup_rom_reset_vec(machine, &s->soc.c908_cpu, start_addr, memmap[K230_DEV_BOOTROM].base, diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -XXX,XX +XXX,XX @@ static void microchip_icicle_kit_machine_init(MachineState *machine) firmware_load_addr = RESET_VECTOR; } + riscv_boot_info_init_discontig_mem(&boot_info, &s->soc.u_cpus, + memmap[MICROCHIP_PFSOC_DRAM_LO].base, + mem_low_size); + /* Load the firmware if necessary */ firmware_end_addr = firmware_load_addr; if (firmware_name) { char *filename = riscv_find_firmware(firmware_name, NULL); if (filename) { - firmware_end_addr = riscv_load_firmware(filename, + firmware_end_addr = riscv_load_firmware(machine, &boot_info, + filename, &firmware_load_addr, NULL); g_free(filename); } } - riscv_boot_info_init(&boot_info, &s->soc.u_cpus); if (machine->kernel_filename) { kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -XXX,XX +XXX,XX @@ static void opentitan_machine_init(MachineState *machine) memory_region_add_subregion(sys_mem, memmap[IBEX_DEV_RAM].base, machine->ram); + riscv_boot_info_init(&boot_info, &s->soc.cpus); + if (machine->firmware) { hwaddr firmware_load_addr = memmap[IBEX_DEV_RAM].base; - riscv_load_firmware(machine->firmware, &firmware_load_addr, NULL); + riscv_load_firmware(machine, &boot_info, machine->firmware, + &firmware_load_addr, NULL); } - riscv_boot_info_init(&boot_info, &s->soc.cpus); if (machine->kernel_filename) { riscv_load_kernel(machine, &boot_info, memmap[IBEX_DEV_RAM].base, 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_state_init(MachineState *mstate) { ShaktiCMachineState *sms = RISCV_SHAKTI_MACHINE(mstate); MemoryRegion *system_memory = get_system_memory(); + RISCVBootInfo boot_info; hwaddr firmware_load_addr = shakti_c_memmap[SHAKTI_C_RAM].base; /* Initialize SoC */ @@ -XXX,XX +XXX,XX @@ static void shakti_c_machine_state_init(MachineState *mstate) shakti_c_memmap[SHAKTI_C_RAM].base, mstate->ram); + riscv_boot_info_init(&boot_info, &sms->soc.cpus); + if (mstate->firmware) { - riscv_load_firmware(mstate->firmware, &firmware_load_addr, NULL); + riscv_load_firmware(mstate, &boot_info, mstate->firmware, + &firmware_load_addr, NULL); } /* ROM reset vector */ 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 sifive_u_machine_init(MachineState *machine) break; } + riscv_boot_info_init(&boot_info, &s->soc.u_cpus); + firmware_name = riscv_default_firmware_name(&s->soc.u_cpus); - firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, + firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info, + firmware_name, &start_addr, NULL); - riscv_boot_info_init(&boot_info, &s->soc.u_cpus); if (machine->kernel_filename) { kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); 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_board_init(MachineState *machine) } } + riscv_boot_info_init(&boot_info, &s->soc[0]); + /* Load firmware */ if (firmware_name) { - firmware_end_addr = riscv_load_firmware(firmware_name, + firmware_end_addr = riscv_load_firmware(machine, &boot_info, + firmware_name, &firmware_load_addr, htif_symbol_callback); g_free(firmware_name); @@ -XXX,XX +XXX,XX @@ static void spike_board_init(MachineState *machine) create_fdt(s, memmap, riscv_is_32bit(&s->soc[0]), htif_custom_base); /* Load kernel */ - riscv_boot_info_init(&boot_info, &s->soc[0]); if (machine->kernel_filename) { kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); 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 virt_machine_done(Notifier *notifier, void *data) } } - firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, + riscv_boot_info_init(&boot_info, &s->soc[0]); + + firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info, + firmware_name, &start_addr, NULL); pflash_blk0 = pflash_cfi01_get_blk(s->flash[0]); @@ -XXX,XX +XXX,XX @@ static void virt_machine_done(Notifier *notifier, void *data) } } - riscv_boot_info_init(&boot_info, &s->soc[0]); - if (machine->kernel_filename && !kernel_entry) { kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); diff --git a/hw/riscv/xiangshan_kmh.c b/hw/riscv/xiangshan_kmh.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/xiangshan_kmh.c +++ b/hw/riscv/xiangshan_kmh.c @@ -XXX,XX +XXX,XX @@ static void xiangshan_kmh_machine_init(MachineState *machine) const MemMapEntry *memmap = xiangshan_kmh_memmap; MemoryRegion *system_memory = get_system_memory(); hwaddr start_addr = memmap[XIANGSHAN_KMH_DRAM].base; + RISCVBootInfo boot_info; /* Initialize SoC */ object_initialize_child(OBJECT(machine), "soc", &s->soc, @@ -XXX,XX +XXX,XX @@ static void xiangshan_kmh_machine_init(MachineState *machine) memmap[XIANGSHAN_KMH_DRAM].base, machine->ram); + riscv_boot_info_init(&boot_info, &s->soc.cpus); + /* ROM reset vector */ riscv_setup_rom_reset_vec(machine, &s->soc.cpus, start_addr, memmap[XIANGSHAN_KMH_ROM].base, memmap[XIANGSHAN_KMH_ROM].size, 0, 0); if (machine->firmware) { - riscv_load_firmware(machine->firmware, &start_addr, NULL); + riscv_load_firmware(machine, &boot_info, machine->firmware, + &start_addr, NULL); } /* Note: dtb has been integrated into firmware(OpenSBI) when compiling */ -- 2.54.0
From: Joel Stanley <joel@jms.id.au> The AIA init will be used by any server class riscv machine. Separate it out in order to share code with such systems. The virt machine keeps machine specific #defines such as VIRT_IRQCHIP_NUM_MSIS, VIRT_IRQCHIP_NUM_PRIO_BITS. Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20260630024952.1520546-4-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/aia.h | 26 +++++++++++ include/hw/riscv/virt.h | 1 - hw/riscv/aia.c | 89 ++++++++++++++++++++++++++++++++++++++ hw/riscv/virt-acpi-build.c | 2 + hw/riscv/virt.c | 87 +++++-------------------------------- hw/riscv/meson.build | 1 + 6 files changed, 129 insertions(+), 77 deletions(-) create mode 100644 hw/riscv/aia.h create mode 100644 hw/riscv/aia.c diff --git a/hw/riscv/aia.h b/hw/riscv/aia.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/riscv/aia.h @@ -XXX,XX +XXX,XX @@ +/* + * QEMU RISC-V Advanced Interrupt Architecture (AIA) + * + * Copyright (C) 2019 Western Digital Corporation or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_RISCV_AIA_H +#define HW_RISCV_AIA_H + +#include "exec/hwaddr.h" + +#define VIRT_IRQCHIP_NUM_SOURCES 96 + +uint32_t imsic_num_bits(uint32_t count); + +DeviceState *riscv_create_aia(bool msimode, int aia_guests, + const MemMapEntry *aplic_m, + const MemMapEntry *aplic_s, + const MemMapEntry *imsic_m, + const MemMapEntry *imsic_s, + int socket, int base_hartid, int hart_count, + uint32_t num_msis, uint32_t num_prio_bits); + +#endif diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -XXX,XX +XXX,XX @@ enum { bool virt_is_acpi_enabled(RISCVVirtState *s); bool virt_is_iommu_sys_enabled(RISCVVirtState *s); void virt_acpi_setup(RISCVVirtState *vms); -uint32_t imsic_num_bits(uint32_t count); /* * The virt machine physical address space used by some of the devices diff --git a/hw/riscv/aia.c b/hw/riscv/aia.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/riscv/aia.c @@ -XXX,XX +XXX,XX @@ +/* + * QEMU RISC-V Advanced Interrupt Architecture (AIA) + * + * Copyright (C) 2019 Western Digital Corporation or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/kvm.h" +#include "hw/intc/riscv_aplic.h" +#include "hw/intc/riscv_imsic.h" + +#include "aia.h" + +uint32_t imsic_num_bits(uint32_t count) +{ + uint32_t ret = 0; + + while (BIT(ret) < count) { + ret++; + } + + return ret; +} + +DeviceState *riscv_create_aia(bool msimode, int aia_guests, + const MemMapEntry *aplic_m, + const MemMapEntry *aplic_s, + const MemMapEntry *imsic_m, + const MemMapEntry *imsic_s, + int socket, int base_hartid, int hart_count, + uint32_t num_msis, uint32_t num_prio_bits) +{ + int i; + hwaddr addr = 0; + uint32_t guest_bits; + DeviceState *aplic_s_dev = NULL; + DeviceState *aplic_m_dev = NULL; + + if (msimode) { + if (!kvm_enabled()) { + /* Per-socket M-level IMSICs */ + addr = imsic_m->base + socket * (1U << IMSIC_MMIO_GROUP_MIN_SHIFT); + for (i = 0; i < hart_count; i++) { + riscv_imsic_create(addr + i * IMSIC_HART_SIZE(0), + base_hartid + i, true, 1, + num_msis); + } + } + + /* Per-socket S-level IMSICs */ + guest_bits = imsic_num_bits(aia_guests + 1); + addr = imsic_s->base + socket * (1U << IMSIC_MMIO_GROUP_MIN_SHIFT); + for (i = 0; i < hart_count; i++) { + riscv_imsic_create(addr + i * IMSIC_HART_SIZE(guest_bits), + base_hartid + i, false, 1 + aia_guests, + num_msis); + } + } + + if (!kvm_enabled()) { + /* Per-socket M-level APLIC */ + aplic_m_dev = riscv_aplic_create(aplic_m->base + + socket * aplic_m->size, + aplic_m->size, + (msimode) ? 0 : base_hartid, + (msimode) ? 0 : hart_count, + VIRT_IRQCHIP_NUM_SOURCES, + num_prio_bits, + msimode, true, NULL); + } + + /* Per-socket S-level APLIC */ + aplic_s_dev = riscv_aplic_create(aplic_s->base + + socket * aplic_s->size, + aplic_s->size, + (msimode) ? 0 : base_hartid, + (msimode) ? 0 : hart_count, + VIRT_IRQCHIP_NUM_SOURCES, + num_prio_bits, + msimode, false, aplic_m_dev); + + if (kvm_enabled() && msimode) { + riscv_aplic_set_kvm_msicfgaddr(RISCV_APLIC(aplic_s_dev), addr); + } + + return kvm_enabled() ? aplic_s_dev : aplic_m_dev; +} 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 @@ #include "system/kvm.h" #include "system/reset.h" +#include "aia.h" + #define ACPI_BUILD_TABLE_SIZE 0x20000 #define ACPI_BUILD_INTC_ID(socket, index) ((socket << 24) | (index)) 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/virtio/virtio-iommu.h" #include "hw/uefi/var-service-api.h" +#include "aia.h" + /* KVM AIA only supports APLIC MSI. APLIC Wired is always emulated by QEMU. */ static bool virt_use_kvm_aia_aplic_imsic(RISCVVirtAIAType aia_type) { @@ -XXX,XX +XXX,XX @@ static void create_fdt_socket_plic(RISCVVirtState *s, } } -uint32_t imsic_num_bits(uint32_t count) -{ - uint32_t ret = 0; - - while (BIT(ret) < count) { - ret++; - } - - return ret; -} - static void create_fdt_one_imsic(RISCVVirtState *s, hwaddr base_addr, uint32_t *intc_phandles, uint32_t msi_phandle, bool m_mode, uint32_t imsic_guest_bits) @@ -XXX,XX +XXX,XX @@ static DeviceState *virt_create_plic(const MemMapEntry *memmap, int socket, memmap[VIRT_PLIC].size); } -static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests, - const MemMapEntry *memmap, int socket, - int base_hartid, int hart_count) -{ - int i; - hwaddr addr = 0; - uint32_t guest_bits; - DeviceState *aplic_s = NULL; - DeviceState *aplic_m = NULL; - bool msimode = aia_type == VIRT_AIA_TYPE_APLIC_IMSIC; - - if (msimode) { - if (!kvm_enabled()) { - /* Per-socket M-level IMSICs */ - addr = memmap[VIRT_IMSIC_M].base + - socket * VIRT_IMSIC_GROUP_MAX_SIZE; - for (i = 0; i < hart_count; i++) { - riscv_imsic_create(addr + i * IMSIC_HART_SIZE(0), - base_hartid + i, true, 1, - VIRT_IRQCHIP_NUM_MSIS); - } - } - - /* Per-socket S-level IMSICs */ - guest_bits = imsic_num_bits(aia_guests + 1); - addr = memmap[VIRT_IMSIC_S].base + socket * VIRT_IMSIC_GROUP_MAX_SIZE; - for (i = 0; i < hart_count; i++) { - riscv_imsic_create(addr + i * IMSIC_HART_SIZE(guest_bits), - base_hartid + i, false, 1 + aia_guests, - VIRT_IRQCHIP_NUM_MSIS); - } - } - - if (!kvm_enabled()) { - /* Per-socket M-level APLIC */ - aplic_m = riscv_aplic_create(memmap[VIRT_APLIC_M].base + - socket * memmap[VIRT_APLIC_M].size, - memmap[VIRT_APLIC_M].size, - (msimode) ? 0 : base_hartid, - (msimode) ? 0 : hart_count, - VIRT_IRQCHIP_NUM_SOURCES, - VIRT_IRQCHIP_NUM_PRIO_BITS, - msimode, true, NULL); - } - - /* Per-socket S-level APLIC */ - aplic_s = riscv_aplic_create(memmap[VIRT_APLIC_S].base + - socket * memmap[VIRT_APLIC_S].size, - memmap[VIRT_APLIC_S].size, - (msimode) ? 0 : base_hartid, - (msimode) ? 0 : hart_count, - VIRT_IRQCHIP_NUM_SOURCES, - VIRT_IRQCHIP_NUM_PRIO_BITS, - msimode, false, aplic_m); - - if (kvm_enabled() && msimode) { - riscv_aplic_set_kvm_msicfgaddr(RISCV_APLIC(aplic_s), addr); - } - - return kvm_enabled() ? aplic_s : aplic_m; -} - static void create_platform_bus(RISCVVirtState *s, DeviceState *irqchip) { DeviceState *dev; @@ -XXX,XX +XXX,XX @@ static void virt_machine_init(MachineState *machine) s->irqchip[i] = virt_create_plic(s->memmap, i, base_hartid, hart_count); } else { - s->irqchip[i] = virt_create_aia(s->aia_type, s->aia_guests, - s->memmap, i, base_hartid, - hart_count); + s->irqchip[i] = riscv_create_aia(s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC, + s->aia_guests, + &s->memmap[VIRT_APLIC_M], + &s->memmap[VIRT_APLIC_S], + &s->memmap[VIRT_IMSIC_M], + &s->memmap[VIRT_IMSIC_S], + i, base_hartid, hart_count, + VIRT_IRQCHIP_NUM_MSIS, + VIRT_IRQCHIP_NUM_PRIO_BITS); } /* Try to use different IRQCHIP instance based device type */ 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('aia.c')) 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')) -- 2.54.0
From: Joel Stanley <joel@jms.id.au> Instead of hard coding the number of IRQ sources used by the APLIC pass it in as a parameter. This allows other machines to configure this as required. The maximum number of sources is 1023. Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20260630024952.1520546-5-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/aia.h | 3 +-- include/hw/riscv/virt.h | 1 + hw/riscv/aia.c | 8 ++++++-- hw/riscv/virt-acpi-build.c | 25 ++++++++++++++++--------- hw/riscv/virt.c | 2 ++ 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/hw/riscv/aia.h b/hw/riscv/aia.h index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/aia.h +++ b/hw/riscv/aia.h @@ -XXX,XX +XXX,XX @@ #include "exec/hwaddr.h" -#define VIRT_IRQCHIP_NUM_SOURCES 96 - uint32_t imsic_num_bits(uint32_t count); DeviceState *riscv_create_aia(bool msimode, int aia_guests, + uint16_t num_sources, const MemMapEntry *aplic_m, const MemMapEntry *aplic_s, const MemMapEntry *imsic_m, diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -XXX,XX +XXX,XX @@ struct RISCVVirtState { struct GPEXHost *gpex_host; OnOffAuto iommu_sys; uint16_t pci_iommu_bdf; + uint16_t num_sources; }; enum { diff --git a/hw/riscv/aia.c b/hw/riscv/aia.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/aia.c +++ b/hw/riscv/aia.c @@ -XXX,XX +XXX,XX @@ uint32_t imsic_num_bits(uint32_t count) } DeviceState *riscv_create_aia(bool msimode, int aia_guests, + uint16_t num_sources, const MemMapEntry *aplic_m, const MemMapEntry *aplic_s, const MemMapEntry *imsic_m, @@ -XXX,XX +XXX,XX @@ DeviceState *riscv_create_aia(bool msimode, int aia_guests, DeviceState *aplic_s_dev = NULL; DeviceState *aplic_m_dev = NULL; + /* The RISC-V Advanced Interrupt Architecture, Chapter 1.2. Limits */ + g_assert(num_sources <= 1023); + if (msimode) { if (!kvm_enabled()) { /* Per-socket M-level IMSICs */ @@ -XXX,XX +XXX,XX @@ DeviceState *riscv_create_aia(bool msimode, int aia_guests, aplic_m->size, (msimode) ? 0 : base_hartid, (msimode) ? 0 : hart_count, - VIRT_IRQCHIP_NUM_SOURCES, + num_sources, num_prio_bits, msimode, true, NULL); } @@ -XXX,XX +XXX,XX @@ DeviceState *riscv_create_aia(bool msimode, int aia_guests, aplic_s->size, (msimode) ? 0 : base_hartid, (msimode) ? 0 : hart_count, - VIRT_IRQCHIP_NUM_SOURCES, + num_sources, num_prio_bits, msimode, false, aplic_m_dev); 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 acpi_dsdt_add_cpus(Aml *scope, RISCVVirtState *s) } static void acpi_dsdt_add_plic_aplic(Aml *scope, uint8_t socket_count, + uint16_t num_sources, uint64_t mmio_base, uint64_t mmio_size, const char *hid) { @@ -XXX,XX +XXX,XX @@ static void acpi_dsdt_add_plic_aplic(Aml *scope, uint8_t socket_count, uint32_t gsi_base; uint8_t socket; + /* The RISC-V Advanced Interrupt Architecture, Chapter 1.2. Limits */ + g_assert(num_sources <= 1023); + for (socket = 0; socket < socket_count; socket++) { plic_aplic_addr = mmio_base + mmio_size * socket; - gsi_base = VIRT_IRQCHIP_NUM_SOURCES * socket; + gsi_base = num_sources * socket; Aml *dev = aml_device("IC%.02X", socket); aml_append(dev, aml_name_decl("_HID", aml_string("%s", hid))); aml_append(dev, aml_name_decl("_UID", aml_int(socket))); @@ -XXX,XX +XXX,XX @@ static void build_dsdt(GArray *table_data, socket_count = riscv_socket_count(ms); if (s->aia_type == VIRT_AIA_TYPE_NONE) { - acpi_dsdt_add_plic_aplic(scope, socket_count, memmap[VIRT_PLIC].base, - memmap[VIRT_PLIC].size, "RSCV0001"); + acpi_dsdt_add_plic_aplic(scope, socket_count, s->num_sources, + memmap[VIRT_PLIC].base, + memmap[VIRT_PLIC].size, + "RSCV0001"); } else { - acpi_dsdt_add_plic_aplic(scope, socket_count, memmap[VIRT_APLIC_S].base, + acpi_dsdt_add_plic_aplic(scope, socket_count, s->num_sources, + memmap[VIRT_APLIC_S].base, memmap[VIRT_APLIC_S].size, "RSCV0002"); } @@ -XXX,XX +XXX,XX @@ static void build_dsdt(GArray *table_data, } else if (socket_count == 2) { virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base, memmap[VIRT_VIRTIO].size, - VIRTIO_IRQ + VIRT_IRQCHIP_NUM_SOURCES, 0, + VIRTIO_IRQ + s->num_sources, 0, VIRTIO_COUNT); - acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + VIRT_IRQCHIP_NUM_SOURCES); + acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + s->num_sources); } else { virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base, memmap[VIRT_VIRTIO].size, - VIRTIO_IRQ + VIRT_IRQCHIP_NUM_SOURCES, 0, + VIRTIO_IRQ + s->num_sources, 0, VIRTIO_COUNT); - acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + VIRT_IRQCHIP_NUM_SOURCES * 2); + acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + s->num_sources * 2); } aml_append(dsdt, scope); @@ -XXX,XX +XXX,XX @@ static void build_madt(GArray *table_data, for (socket = 0; socket < riscv_socket_count(ms); socket++) { aplic_addr = s->memmap[VIRT_APLIC_S].base + s->memmap[VIRT_APLIC_S].size * socket; - gsi_base = VIRT_IRQCHIP_NUM_SOURCES * socket; + gsi_base = s->num_sources * socket; build_append_int_noprefix(table_data, 0x1A, 1); /* Type */ build_append_int_noprefix(table_data, 36, 1); /* Length */ build_append_int_noprefix(table_data, 1, 1); /* Version */ 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 virt_machine_init(MachineState *machine) } else { s->irqchip[i] = riscv_create_aia(s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC, s->aia_guests, + s->num_sources, &s->memmap[VIRT_APLIC_M], &s->memmap[VIRT_APLIC_S], &s->memmap[VIRT_IMSIC_M], @@ -XXX,XX +XXX,XX @@ static void virt_machine_instance_init(Object *obj) s->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); s->acpi = ON_OFF_AUTO_AUTO; s->iommu_sys = ON_OFF_AUTO_AUTO; + s->num_sources = VIRT_IRQCHIP_NUM_SOURCES; } static char *virt_get_aia_guests(Object *obj, Error **errp) -- 2.54.0
From: Joel Stanley <joel@jms.id.au> riscv_create_aia() currently hard-codes the M-IMSIC at one 4 KiB page per hart and gives callers no way to widen it. Add an m_imsic_stride parameter that supplies the per hart byte stride directly. The virt machine passes IMSIC_HART_SIZE(0) (= 4 KiB), preserving its existing compact layout. The parameter only changes how the slots are spaced, with the rest of each slot reserved. This allows future platforms that have different layouts to control the stride. Reviewed-by: Philippe Mathieu-Daudé <philmd@mailo.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20260630024952.1520546-6-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/aia.h | 1 + hw/riscv/aia.c | 3 ++- hw/riscv/virt.c | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/riscv/aia.h b/hw/riscv/aia.h index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/aia.h +++ b/hw/riscv/aia.h @@ -XXX,XX +XXX,XX @@ uint32_t imsic_num_bits(uint32_t count); DeviceState *riscv_create_aia(bool msimode, int aia_guests, + uint32_t m_imsic_stride, uint16_t num_sources, const MemMapEntry *aplic_m, const MemMapEntry *aplic_s, diff --git a/hw/riscv/aia.c b/hw/riscv/aia.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/aia.c +++ b/hw/riscv/aia.c @@ -XXX,XX +XXX,XX @@ uint32_t imsic_num_bits(uint32_t count) } DeviceState *riscv_create_aia(bool msimode, int aia_guests, + uint32_t m_imsic_stride, uint16_t num_sources, const MemMapEntry *aplic_m, const MemMapEntry *aplic_s, @@ -XXX,XX +XXX,XX @@ DeviceState *riscv_create_aia(bool msimode, int aia_guests, /* Per-socket M-level IMSICs */ addr = imsic_m->base + socket * (1U << IMSIC_MMIO_GROUP_MIN_SHIFT); for (i = 0; i < hart_count; i++) { - riscv_imsic_create(addr + i * IMSIC_HART_SIZE(0), + riscv_imsic_create(addr + i * m_imsic_stride, base_hartid + i, true, 1, num_msis); } 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 virt_machine_init(MachineState *machine) } else { s->irqchip[i] = riscv_create_aia(s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC, s->aia_guests, + IMSIC_HART_SIZE(0), s->num_sources, &s->memmap[VIRT_APLIC_M], &s->memmap[VIRT_APLIC_S], -- 2.54.0
From: Nicholas Piggin <npiggin@gmail.com> Ascalon supports Zkr and the SEED CSR. Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20260630024952.1520546-7-joel@jms.id.au> 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 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.ext_zba = true, .cfg.ext_zbb = true, .cfg.ext_zbs = true, + .cfg.ext_zkr = true, .cfg.ext_zkt = true, .cfg.ext_zvbb = true, .cfg.ext_zvbc = true, -- 2.54.0
From: Joel Stanley <joel@jms.id.au> The Tenstorrent Atlantis platform is a collaboration between Tenstorrent and CoreLab Technology. It is based on the Atlantis SoC, which includes the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology. The Tenstorrent Ascalon-X is a high performance 64-bit RVA23 compliant RISC-V CPU. Add the tt-atlantis machine containing serial console, interrupt controllers, and device tree support. The Atlantis boot images loaded from include OpenSBI and an initial DTB that is passed to OpenSBI. This is approximated in the model by having QEMU build the device tree rather than load a DTB image directly. Subsequent stages may use the modified DTB provided by OpenSBI or opt to supply their own. qemu-system-riscv64 -M tt-atlantis -m 512M \ -kernel Image -initrd rootfs.cpio -nographic Co-Developed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260630024952.1520546-8-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- MAINTAINERS | 12 + docs/system/riscv/tt_atlantis.rst | 41 +++ docs/system/target-riscv.rst | 1 + include/hw/riscv/tt_atlantis.h | 50 +++ hw/riscv/tt_atlantis.c | 523 ++++++++++++++++++++++++++++++ hw/riscv/Kconfig | 10 + hw/riscv/meson.build | 1 + 7 files changed, 638 insertions(+) create mode 100644 docs/system/riscv/tt_atlantis.rst create mode 100644 include/hw/riscv/tt_atlantis.h create mode 100644 hw/riscv/tt_atlantis.c diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ F: hw/*/*sifive*.c F: include/hw/*/*sifive*.h F: tests/functional/riscv64/test_sifive_u.py +Tenstorrent Machines +M: Joel Stanley <joel@jms.id.au> +M: Nicholas Piggin <npiggin@gmail.com> +R: Anirudh Srinivasan <asrinivasan@oss.tenstorrent.com> +R: Michael Ellerman <mpe@kernel.org> +R: Portia Stephens <portias@oss.tenstorrent.com> +L: qemu-riscv@nongnu.org +S: Supported +F: docs/system/riscv/tt_*.rst +F: hw/riscv/tt_*.c +F: include/hw/riscv/tt_*.h + AMD Microblaze-V Generic Board M: Sai Pavan Boddu <sai.pavan.boddu@amd.com> S: Maintained diff --git a/docs/system/riscv/tt_atlantis.rst b/docs/system/riscv/tt_atlantis.rst new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/docs/system/riscv/tt_atlantis.rst @@ -XXX,XX +XXX,XX @@ +Tenstorrent Atlantis (``tt-atlantis``) +====================================== + +The Tenstorrent Atlantis platform is a collaboration between Tenstorrent +and CoreLab Technology. It is based on the Atlantis SoC, which includes +the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology. + +The Tenstorrent Ascalon-X is a high performance 64-bit RVA23 compliant +RISC-V CPU. + +tt-atlantis QEMU model features +------------------------------- + +* 8-core Ascalon-X CPU Cluster +* RISC-V compliant Advanced Interrupt Architecture +* 16550A compatible UART + +Known limitations +----------------- + +The QEMU tt-atlantis machine does not yet model every device on the +real platform. Notably: + +* There is no PCI host bridge, so virtio-pci devices cannot be + attached. Boots that need block storage must use ``-initrd`` with + an initramfs. +* The DesignWare UART is modelled with QEMU's ns16550-compatible + ``serial_mm`` device; DesignWare-specific registers beyond that + set return 0. + +Supported software +------------------ + +The Tenstorrent Ascalon CPUs avoid proprietary or non-standard +extensions, so compatibility with existing software is generally +good. The QEMU tt-atlantis machine works with upstream OpenSBI +and Linux with default configurations. + +The development board hardware will require some implementation +specific setup in firmware which is being developed and may +become a requirement or option for the tt-atlantis machine. 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 riscv/mips riscv/shakti-c riscv/sifive_u + riscv/tt_atlantis riscv/virt riscv/xiangshan-kunminghu diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/riscv/tt_atlantis.h @@ -XXX,XX +XXX,XX @@ +/* + * Tenstorrent Atlantis RISC-V System on Chip + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright 2025 Tenstorrent, Joel Stanley <joel@jms.id.au> + */ + +#ifndef HW_RISCV_TT_ATLANTIS_H +#define HW_RISCV_TT_ATLANTIS_H + +#include "hw/core/boards.h" +#include "hw/core/sysbus.h" +#include "hw/intc/riscv_imsic.h" +#include "hw/riscv/riscv_hart.h" + +#define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis") +OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisState, TT_ATLANTIS_MACHINE) + +struct TTAtlantisState { + /*< private >*/ + MachineState parent; + + /*< public >*/ + Notifier machine_done; + const MemMapEntry *memmap; + + RISCVHartArrayState soc; + DeviceState *irqchip; + + int fdt_size; +}; + +enum { + TT_ATL_UART1_IRQ = 39, +}; + +enum { + TT_ATL_ACLINT, + TT_ATL_BOOTROM, + TT_ATL_DDR_LO, + TT_ATL_DDR_HI, + TT_ATL_MAPLIC, + TT_ATL_MIMSIC, + TT_ATL_SAPLIC, + TT_ATL_SIMSIC, + TT_ATL_UART1, +}; + +#endif diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/riscv/tt_atlantis.c @@ -XXX,XX +XXX,XX @@ +/* + * Tenstorrent Atlantis RISC-V System on Chip + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright 2025 Tenstorrent, Joel Stanley <joel@jms.id.au> + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qemu/units.h" + +#include "hw/core/boards.h" +#include "hw/core/loader.h" +#include "hw/core/sysbus.h" + +#include "target/riscv/cpu.h" +#include "target/riscv/pmu.h" + +#include "hw/riscv/boot.h" +#include "hw/riscv/fdt-common.h" +#include "hw/riscv/machines-qom.h" +#include "hw/riscv/riscv_hart.h" + +#include "hw/char/serial-mm.h" +#include "hw/intc/riscv_aclint.h" +#include "hw/misc/unimp.h" + +#include "system/system.h" +#include "system/device_tree.h" + +#include "hw/riscv/tt_atlantis.h" + +#include "aia.h" + +#define TT_IRQCHIP_NUM_MSIS 255 +#define TT_IRQCHIP_NUM_SOURCES 128 +#define TT_IRQCHIP_NUM_PRIO_BITS 3 +#define TT_IRQCHIP_GUESTS 63 /* aia_guests, gives guest_index_bits=6 */ +#define TT_IRQCHIP_MIMSIC_STRIDE 0x40000 + +#define TT_ACLINT_MTIME_SIZE 0x8050 +#define TT_ACLINT_MTIME 0x0 +#define TT_ACLINT_MTIMECMP 0x8000 +#define TT_ACLINT_TIMEBASE_FREQ 1000000000 + +static const MemMapEntry tt_atlantis_memmap[] = { + /* Keep sorted with :'<,'>!sort -g -k 4 */ + [TT_ATL_DDR_LO] = { 0x00000000, 0x80000000 }, + [TT_ATL_BOOTROM] = { 0x80000000, 0x2000 }, + [TT_ATL_MIMSIC] = { 0xa0000000, 0x200000 }, + [TT_ATL_ACLINT] = { 0xa2180000, 0x10000 }, + [TT_ATL_SIMSIC] = { 0xa4000000, 0x200000 }, + [TT_ATL_MAPLIC] = { 0xcc000000, 0x4000000 }, + [TT_ATL_UART1] = { 0xd4110000, 0x10000 }, + [TT_ATL_SAPLIC] = { 0xe8000000, 0x4000000 }, + [TT_ATL_DDR_HI] = { 0x100000000, 0x1000000000 }, +}; + +static uint32_t fdt_phandle = 1; +static uint32_t next_phandle(void) +{ + return fdt_phandle++; +} + +static void create_fdt_memory(TTAtlantisState *s) +{ + void *fdt = MACHINE(s)->fdt; + hwaddr size_lo = MACHINE(s)->ram_size; + hwaddr size_hi = 0; + + if (size_lo > s->memmap[TT_ATL_DDR_LO].size) { + size_lo = s->memmap[TT_ATL_DDR_LO].size; + size_hi = MACHINE(s)->ram_size - size_lo; + } + + create_fdt_socket_memory(fdt, s->memmap[TT_ATL_DDR_LO].base, size_lo, + 0, false); + if (size_hi) { + /* + * The first part of the HI address is aliased at the LO address + * so do not include that as usable memory. Is there any way + * (or good reason) to describe that aliasing 2GB with DT? + */ + create_fdt_socket_memory(fdt, s->memmap[TT_ATL_DDR_HI].base + size_lo, + size_hi, 0, false); + } +} + +static void create_fdt_aclint(TTAtlantisState *s, uint32_t *intc_phandles) +{ + void *fdt = MACHINE(s)->fdt; + g_autofree char *name = NULL; + g_autofree uint32_t *aclint_mtimer_cells = NULL; + uint32_t aclint_cells_size; + hwaddr addr; + + aclint_mtimer_cells = g_new0(uint32_t, s->soc.num_harts * 2); + + for (int cpu = 0; cpu < s->soc.num_harts; cpu++) { + aclint_mtimer_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); + aclint_mtimer_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER); + } + aclint_cells_size = s->soc.num_harts * sizeof(uint32_t) * 2; + + addr = s->memmap[TT_ATL_ACLINT].base; + + name = g_strdup_printf("/soc/mtimer@%"HWADDR_PRIX, addr); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,aclint-mtimer"); + qemu_fdt_setprop_sized_cells(fdt, name, "reg", + 2, addr + TT_ACLINT_MTIME, + 2, 0x1000, + 2, addr + TT_ACLINT_MTIMECMP, + 2, 0x1000); + qemu_fdt_setprop(fdt, name, "interrupts-extended", + aclint_mtimer_cells, aclint_cells_size); +} + +static void create_fdt_one_imsic(void *fdt, const MemMapEntry *mem, int cpus, + uint32_t *intc_phandles, uint32_t msi_phandle, + int irq_line, uint32_t imsic_guest_bits) +{ + g_autofree char *name = NULL; + g_autofree uint32_t *imsic_cells = g_new0(uint32_t, cpus * 2); + + for (int cpu = 0; cpu < cpus; cpu++) { + imsic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); + imsic_cells[cpu * 2 + 1] = cpu_to_be32(irq_line); + } + + name = g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIX, mem->base); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,imsics"); + + qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 0); + qemu_fdt_setprop(fdt, name, "interrupt-controller", NULL, 0); + qemu_fdt_setprop(fdt, name, "msi-controller", NULL, 0); + qemu_fdt_setprop(fdt, name, "interrupts-extended", + imsic_cells, sizeof(uint32_t) * cpus * 2); + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size); + qemu_fdt_setprop_cell(fdt, name, "riscv,num-ids", TT_IRQCHIP_NUM_MSIS); + + if (imsic_guest_bits) { + qemu_fdt_setprop_cell(fdt, name, "riscv,guest-index-bits", + imsic_guest_bits); + } + qemu_fdt_setprop_cell(fdt, name, "phandle", msi_phandle); +} + +static void create_fdt_one_aplic(void *fdt, + const MemMapEntry *mem, + uint32_t msi_phandle, + uint32_t *intc_phandles, + uint32_t aplic_phandle, + uint32_t aplic_child_phandle, + int irq_line, int num_harts) +{ + g_autofree char *name = + g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIX, mem->base); + g_autofree uint32_t *aplic_cells = g_new0(uint32_t, num_harts * 2); + + for (int cpu = 0; cpu < num_harts; cpu++) { + aplic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); + aplic_cells[cpu * 2 + 1] = cpu_to_be32(irq_line); + } + + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,aplic"); + qemu_fdt_setprop_cell(fdt, name, "#address-cells", 0); + qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 2); + qemu_fdt_setprop(fdt, name, "interrupt-controller", NULL, 0); + + qemu_fdt_setprop(fdt, name, "interrupts-extended", + aplic_cells, num_harts * sizeof(uint32_t) * 2); + qemu_fdt_setprop_cell(fdt, name, "msi-parent", msi_phandle); + + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size); + qemu_fdt_setprop_cell(fdt, name, "riscv,num-sources", + TT_IRQCHIP_NUM_SOURCES); + + if (aplic_child_phandle) { + qemu_fdt_setprop_cell(fdt, name, "riscv,children", + aplic_child_phandle); + qemu_fdt_setprop_cells(fdt, name, "riscv,delegation", + aplic_child_phandle, 1, TT_IRQCHIP_NUM_SOURCES); + } + + qemu_fdt_setprop_cell(fdt, name, "phandle", aplic_phandle); +} + +static void create_fdt_pmu(TTAtlantisState *s) +{ + char pmu_name[] = "/pmu"; + void *fdt = MACHINE(s)->fdt; + RISCVCPU *hart = &s->soc.harts[0]; + + qemu_fdt_add_subnode(fdt, pmu_name); + qemu_fdt_setprop_string(fdt, pmu_name, "compatible", "riscv,pmu"); + riscv_pmu_generate_fdt_node(fdt, hart->pmu_avail_ctrs, pmu_name); +} + +static void create_fdt_cpu(TTAtlantisState *s, const MemMapEntry *memmap, + uint32_t aplic_s_phandle, + uint32_t imsic_s_phandle) +{ + MachineState *ms = MACHINE(s); + void *fdt = MACHINE(s)->fdt; + g_autofree uint32_t *intc_phandles = g_new0(uint32_t, ms->smp.cpus); + + fdt_create_cpu_socket_subnode(fdt, TT_ACLINT_TIMEBASE_FREQ); + + create_fdt_socket_cpus(fdt, s->soc.harts, 0, s->soc.num_harts, + s->soc.hartid_base, &fdt_phandle, intc_phandles, + false, false); + + create_fdt_memory(s); + + create_fdt_aclint(s, intc_phandles); + + uint32_t imsic_guest_bits = imsic_num_bits(TT_IRQCHIP_GUESTS + 1); + + /* M-level IMSIC node */ + uint32_t msi_m_phandle = next_phandle(); + create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_MIMSIC], ms->smp.cpus, + intc_phandles, msi_m_phandle, + IRQ_M_EXT, imsic_guest_bits); + + /* S-level IMSIC node */ + create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_SIMSIC], ms->smp.cpus, + intc_phandles, imsic_s_phandle, + IRQ_S_EXT, imsic_guest_bits); + + uint32_t aplic_m_phandle = next_phandle(); + + /* M-level APLIC node */ + create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_MAPLIC], + msi_m_phandle, intc_phandles, + aplic_m_phandle, aplic_s_phandle, + IRQ_M_EXT, s->soc.num_harts); + + /* S-level APLIC node */ + create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_SAPLIC], + imsic_s_phandle, intc_phandles, + aplic_s_phandle, 0, + IRQ_S_EXT, s->soc.num_harts); +} + +static void create_fdt_uart(void *fdt, const MemMapEntry *mem, int irq, + int irqchip_phandle) +{ + g_autofree char *name = g_strdup_printf("/soc/serial@%"HWADDR_PRIX, + mem->base); + + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "ns16550a"); + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size); + qemu_fdt_setprop_cell(fdt, name, "reg-shift", 2); + qemu_fdt_setprop_cell(fdt, name, "reg-io-width", 4); + qemu_fdt_setprop_cell(fdt, name, "clock-frequency", 3686400); + qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", irqchip_phandle); + qemu_fdt_setprop_cells(fdt, name, "interrupts", irq, 0x4); + + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", name); + qemu_fdt_setprop_string(fdt, "/aliases", "serial0", name); +} + +static void create_fdt_rng(void *fdt) +{ + uint8_t rng_seed[32]; + + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); +} + +static void finalize_fdt(TTAtlantisState *s) +{ + uint32_t aplic_s_phandle = next_phandle(); + uint32_t imsic_s_phandle = next_phandle(); + void *fdt = MACHINE(s)->fdt; + + create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle); + + /* + * We want to do this, but the Linux aplic driver was broken before v6.16 + * + * qemu_fdt_setprop_cell(MACHINE(s)->fdt, "/soc", "interrupt-parent", + * aplic_s_phandle); + */ + + create_fdt_uart(fdt, &s->memmap[TT_ATL_UART1], TT_ATL_UART1_IRQ, + aplic_s_phandle); +} + +static void create_fdt(TTAtlantisState *s) +{ + MachineState *ms = MACHINE(s); + + ms->fdt = create_board_device_tree("Tenstorrent Atlantis RISC-V Machine", + "tenstorrent,atlantis", &s->fdt_size); + + qemu_fdt_add_subnode(ms->fdt, "/chosen"); + + create_fdt_rng(ms->fdt); + + qemu_fdt_add_subnode(ms->fdt, "/aliases"); + + create_fdt_pmu(s); +} + +static void load_fdt(TTAtlantisState *s) +{ + MachineState *ms = MACHINE(s); + char **node_path; + Error *err = NULL; + + ms->fdt = load_device_tree(ms->dtb, &s->fdt_size); + if (!ms->fdt) { + error_report("load_device_tree() failed"); + exit(1); + } + + qemu_fdt_add_path(ms->fdt, "/chosen"); + + /* Clear memory nodes and update with the specified RAM size */ + node_path = qemu_fdt_node_unit_path(ms->fdt, "memory", &err); + if (err) { + warn_report_err(err); + } else { + for (int i = 0; node_path[i]; i++) { + warn_report("Replacing device tree %s with the requested RAM size", + node_path[i]); + qemu_fdt_nop_node(ms->fdt, node_path[i]); + } + g_strfreev(node_path); + } + + create_fdt_memory(s); +} + +static void tt_atlantis_machine_done(Notifier *notifier, void *data) +{ + TTAtlantisState *s = container_of(notifier, TTAtlantisState, machine_done); + MachineState *machine = MACHINE(s); + hwaddr start_addr = s->memmap[TT_ATL_DDR_LO].base; + hwaddr mem_size; + target_ulong firmware_end_addr, kernel_start_addr; + const char *firmware_name = riscv_default_firmware_name(&s->soc); + uint64_t fdt_load_addr; + uint64_t kernel_entry; + RISCVBootInfo boot_info; + + /* + * A user provided dtb must include everything, including + * dynamic sysbus devices. Our FDT needs to be finalized. + */ + if (machine->dtb == NULL) { + finalize_fdt(s); + } + + mem_size = machine->ram_size; + if (mem_size > s->memmap[TT_ATL_DDR_LO].size) { + mem_size = s->memmap[TT_ATL_DDR_LO].size; + } + riscv_boot_info_init_discontig_mem(&boot_info, &s->soc, + s->memmap[TT_ATL_DDR_LO].base, + mem_size); + + firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info, + firmware_name, + &start_addr, NULL); + + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, + firmware_end_addr); + if (machine->kernel_filename) { + riscv_load_kernel(machine, &boot_info, kernel_start_addr, + true, NULL); + } + kernel_entry = boot_info.image_low_addr; + + fdt_load_addr = riscv_compute_fdt_addr(s->memmap[TT_ATL_DDR_LO].base, + s->memmap[TT_ATL_DDR_LO].size, + machine, &boot_info); + riscv_load_fdt(fdt_load_addr, machine->fdt); + + /* load the reset vector */ + riscv_setup_rom_reset_vec(machine, &s->soc, start_addr, + s->memmap[TT_ATL_BOOTROM].base, + s->memmap[TT_ATL_BOOTROM].size, + kernel_entry, + fdt_load_addr); +} + +static void tt_atlantis_machine_init(MachineState *machine) +{ + TTAtlantisState *s = TT_ATLANTIS_MACHINE(machine); + + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *ram_hi = g_new(MemoryRegion, 1); + MemoryRegion *ram_lo = g_new(MemoryRegion, 1); + MemoryRegion *bootrom = g_new(MemoryRegion, 1); + ram_addr_t lo_ram_size; + int hart_count = machine->smp.cpus; + + s->memmap = tt_atlantis_memmap; + + object_initialize_child(OBJECT(machine), "soc", &s->soc, + TYPE_RISCV_HART_ARRAY); + object_property_set_str(OBJECT(&s->soc), "cpu-type", machine->cpu_type, + &error_abort); + object_property_set_int(OBJECT(&s->soc), "hartid-base", 0, + &error_abort); + object_property_set_int(OBJECT(&s->soc), "num-harts", hart_count, + &error_abort); + object_property_set_int(OBJECT(&s->soc), "resetvec", + s->memmap[TT_ATL_BOOTROM].base, + &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); + + s->irqchip = riscv_create_aia(true, TT_IRQCHIP_GUESTS, + TT_IRQCHIP_MIMSIC_STRIDE, + TT_IRQCHIP_NUM_SOURCES, + &s->memmap[TT_ATL_MAPLIC], + &s->memmap[TT_ATL_SAPLIC], + &s->memmap[TT_ATL_MIMSIC], + &s->memmap[TT_ATL_SIMSIC], + 0, 0, hart_count, + TT_IRQCHIP_NUM_MSIS, + TT_IRQCHIP_NUM_PRIO_BITS); + + riscv_aclint_mtimer_create(s->memmap[TT_ATL_ACLINT].base, + TT_ACLINT_MTIME_SIZE, + 0, hart_count, + TT_ACLINT_MTIMECMP, + TT_ACLINT_MTIME, + TT_ACLINT_TIMEBASE_FREQ, true); + + /* + * DDR + * + * The high address is where RAM lives. It is always present and may be + * up to 64GB. The low address is an alias of the first 2GB of that RAM. + */ + if (machine->ram_size > s->memmap[TT_ATL_DDR_HI].size) { + char *sz = size_to_str(s->memmap[TT_ATL_DDR_HI].size); + error_report("RAM size is too large, maximum is %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } + + memory_region_init_alias(ram_hi, OBJECT(machine), "ram.high", machine->ram, + 0, machine->ram_size); + memory_region_add_subregion(system_memory, + s->memmap[TT_ATL_DDR_HI].base, ram_hi); + + lo_ram_size = MIN(machine->ram_size, s->memmap[TT_ATL_DDR_LO].size); + memory_region_init_alias(ram_lo, OBJECT(machine), "ram.low", machine->ram, + 0, lo_ram_size); + memory_region_add_subregion(system_memory, + s->memmap[TT_ATL_DDR_LO].base, ram_lo); + + /* Boot ROM */ + memory_region_init_rom(bootrom, NULL, "tt-atlantis.bootrom", + s->memmap[TT_ATL_BOOTROM].size, &error_fatal); + memory_region_add_subregion(system_memory, s->memmap[TT_ATL_BOOTROM].base, + bootrom); + + /* UART1, the soc console (UART0 is for the boot microcontroller) */ + serial_mm_init(system_memory, s->memmap[TT_ATL_UART1].base, 2, + qdev_get_gpio_in(s->irqchip, TT_ATL_UART1_IRQ), + 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); + /* + * Atlantis contains a DesignWare uart while the QEMU machine + * uses the serial_mm model with the base ns16550 register set. + * Linux's dw driver writes outside of serial_mm's 0x20 sized + * mapping and faults. + * + * Create an unimplemented device region so writes don't fault + * and reads return zero, which keeps Linux happy. + */ + create_unimplemented_device("tt-atlantis.uart0", + s->memmap[TT_ATL_UART1].base, + s->memmap[TT_ATL_UART1].size); + + /* Load or create device tree */ + if (machine->dtb) { + load_fdt(s); + } else { + create_fdt(s); + } + + s->machine_done.notify = tt_atlantis_machine_done; + qemu_add_machine_init_done_notifier(&s->machine_done); +} + +static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Tenstorrent Atlantis RISC-V SoC (Experimental)"; + mc->init = tt_atlantis_machine_init; + mc->max_cpus = 8; + mc->default_cpus = 8; + mc->default_ram_size = 4 * GiB; + mc->default_cpu_type = TYPE_RISCV_CPU_TT_ASCALON; + mc->block_default_type = IF_VIRTIO; + mc->no_cdrom = 1; + mc->default_ram_id = "tt_atlantis.ram"; +} + +static const TypeInfo tt_atlantis_types[] = { + { + .name = MACHINE_TYPE_NAME("tt-atlantis"), + .parent = TYPE_MACHINE, + .class_init = tt_atlantis_machine_class_init, + .instance_size = sizeof(TTAtlantisState), + .interfaces = riscv64_machine_interfaces, + }, +}; + +DEFINE_TYPES(tt_atlantis_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 SPIKE select RISCV_ACLINT select SIFIVE_PLIC +config TENSTORRENT + bool + default y + depends on RISCV64 + select RISCV_ACLINT + select RISCV_APLIC + select RISCV_IMSIC + select SERIAL_MM + select DEVICE_TREE + config XIANGSHAN_KUNMINGHU bool default y 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_SIFIVE_E', if_true: files('sifive_e.c')) riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c')) riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c')) riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c')) +riscv_ss.add(when: 'CONFIG_TENSTORRENT', if_true: files('tt_atlantis.c')) riscv_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) 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')) -- 2.54.0
From: Alistair Francis <alistair.francis@wdc.com> When using OpenSBI fw_dynamic on the Atlantis board OpenSBI fails to print any output, as it hits an error early on in the boot process and gets stuck in `sbi_hart_hang()`. The error occurs in the `sanitize_domain()` function inside OpenSBI. `sanitize_domain()` is called after a M-Mode OpenSBI Firmware and a generic coverall S-Mode RWX memory region are created. `sanitize_domain()` is checking that the next address is executable. If no next address is provided (which occurs on QEMU with an empty payload), then `dom->next_addr` will be 0. On most RISC-V boards address 0 will fall inside the coverall S-Mode RWX memory region and pass this check. On Atlantis the OpenSBI firmware is running at address 0, so this address falls inside the M-Mode only OpenSBI firmware region and fails the check. Once the check has failed OpenSBI aborts and the user doesn't see any messages. This can be fixed by either supplying a payload, or just manually forcing a non-zero address (actually just any address that isn't the OpenSBI firmware) for next_addr. This patch ensures that if no kernel is loaded we still specify a default kernel_entry so that OpenSBI happily boots and jumps to the first address in memory. Signed-off-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260630024952.1520546-9-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/tt_atlantis.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/tt_atlantis.c +++ b/hw/riscv/tt_atlantis.c @@ -XXX,XX +XXX,XX @@ static void tt_atlantis_machine_done(Notifier *notifier, void *data) if (machine->kernel_filename) { riscv_load_kernel(machine, &boot_info, kernel_start_addr, true, NULL); + kernel_entry = boot_info.image_low_addr; + } else { + /* If we aren't loading a payload, OpenSBI thinks we are trying to boot + * address 0, which fails `sbi_domain_check_addr()` as that is where + * OpenSBI is running. Instead point OpenSBI to the end of the region + * where it was loaded, which avoids the early hang, allowing the + * system to proceed with the OpenSBI boot output. + */ + kernel_entry = kernel_start_addr; } - kernel_entry = boot_info.image_low_addr; fdt_load_addr = riscv_compute_fdt_addr(s->memmap[TT_ATL_DDR_LO].base, s->memmap[TT_ATL_DDR_LO].size, -- 2.54.0
From: Nicholas Piggin <npiggin@gmail.com> Add OpenSBI and Linux boot tests for the tt-atlantis machine. Based on tests/functional/riscv64/test_sifive_u.py. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20260630024952.1520546-10-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- MAINTAINERS | 1 + tests/functional/riscv64/meson.build | 1 + tests/functional/riscv64/test_opensbi.py | 4 ++ tests/functional/riscv64/test_tt_atlantis.py | 57 ++++++++++++++++++++ 4 files changed, 63 insertions(+) create mode 100755 tests/functional/riscv64/test_tt_atlantis.py diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ S: Supported F: docs/system/riscv/tt_*.rst F: hw/riscv/tt_*.c F: include/hw/riscv/tt_*.h +F: tests/functional/riscv64/test_tt_*.py AMD Microblaze-V Generic Board M: Sai Pavan Boddu <sai.pavan.boddu@amd.com> 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_thorough = [ 'endianness', 'boston', 'sifive_u', + 'tt_atlantis', 'tuxrun', ] diff --git a/tests/functional/riscv64/test_opensbi.py b/tests/functional/riscv64/test_opensbi.py index XXXXXXX..XXXXXXX 100755 --- a/tests/functional/riscv64/test_opensbi.py +++ b/tests/functional/riscv64/test_opensbi.py @@ -XXX,XX +XXX,XX @@ def test_riscv_sifive_u(self): self.set_machine('sifive_u') self.boot_opensbi() + def test_riscv_tt_atlantis(self): + self.set_machine('tt-atlantis') + self.boot_opensbi() + def test_riscv_virt(self): self.set_machine('virt') self.boot_opensbi() diff --git a/tests/functional/riscv64/test_tt_atlantis.py b/tests/functional/riscv64/test_tt_atlantis.py new file mode 100755 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/functional/riscv64/test_tt_atlantis.py @@ -XXX,XX +XXX,XX @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a Tenstorrent Atlantis machine +# and checks the console +# +# Copyright (c) Linaro Ltd. +# Copyright 2026 Tenstorrent +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, LinuxKernelTest + + +class TTAtlantis(LinuxKernelTest): + + ASSET_KERNEL = Asset( + 'https://storage.tuxboot.com/kernels/6.11.9/riscv64/Image', + '174f8bb87f08961e54fa3fcd954a8e31f4645f6d6af4dd43983d5e9841490fb0') + ASSET_ROOTFS = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '9819da19e6eef291686fdd7b029ea00e764dc62f/rootfs/riscv64/' + 'rootfs.ext2.gz'), + 'b6ed95610310b7956f9bf20c4c9c0c05fea647900df441da9dfe767d24e8b28b') + + def do_test_riscv64_tt_atlantis(self, connect_disk): + self.set_machine('tt-atlantis') + kernel_path = self.ASSET_KERNEL.fetch() + rootfs_path = self.uncompress(self.ASSET_ROOTFS) + + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'earlycon=sbi ' + + if connect_disk: + kernel_command_line += 'root=/dev/vda panic=-1 noreboot rootwait ' + self.vm.add_args('-device', + 'virtio-blk,drive=drive0,serial=0x1234,bus=pcie.0') + self.vm.add_args('-drive', + f'file={rootfs_path},if=none,id=drive0,format=raw') + pattern = 'Boot successful.' + else: + kernel_command_line += 'panic=0 noreboot ' + pattern = 'Cannot open root device' + + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line, + '-no-reboot') + + self.vm.launch() + self.wait_for_console_pattern(pattern) + + def test_riscv64_tt_atlantis(self): + # tt-atlantis machine has no PCI host yet, so no disk + self.do_test_riscv64_tt_atlantis(False) + + +if __name__ == '__main__': + LinuxKernelTest.main() -- 2.54.0
From: Chris Rauer <crauer@google.com> Add a model for the Synopsys DesignWare Advanced I2C/SMBus Controller with sufficient functionality to be used by the Linux Designware I2C platform driver. This IP is used in the Tenstorrent Atlantis RISC-V SoC and will be added to the QEMU tt-atlantis machine. [npiggin: changelog, code cleanups and fixes as-per below link] Reviewed-by: Hao Wu <wuhaotsh@google.com> Signed-off-by: Chris Rauer <crauer@google.com> Link: https://lore.kernel.org/qemu-devel/20220110214755.810343-2-venture@google.com [jms: rebase and minor build fixes for class_init and reset callback] Link: https://lore.kernel.org/qemu-devel/20260507120524.111056-1-npiggin@gmail.com Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Corey Minyard <cminyard@mvista.com> Tested-by: Alano Song <AlanoSong@163.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20260630024952.1520546-11-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- MAINTAINERS | 8 + include/hw/i2c/designware_i2c.h | 56 +++ hw/i2c/designware_i2c.c | 745 ++++++++++++++++++++++++++++++++ hw/i2c/Kconfig | 5 + hw/i2c/meson.build | 1 + 5 files changed, 815 insertions(+) create mode 100644 include/hw/i2c/designware_i2c.h create mode 100644 hw/i2c/designware_i2c.c diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ S: Orphan F: hw/gpio/pcf8574.c F: include/hw/gpio/pcf8574.h +DesignWare I2C +M: Chris Rauer <crauer@google.com> +R: Alano Song <alanosong@163.com> +R: Joel Stanley <joel@jms.id.au> +S: Maintained +F: hw/i2c/designware_i2c.c +F: include/hw/i2c/designware_i2c.h + Generic Loader M: Alistair Francis <alistair@alistair23.me> S: Maintained diff --git a/include/hw/i2c/designware_i2c.h b/include/hw/i2c/designware_i2c.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/i2c/designware_i2c.h @@ -XXX,XX +XXX,XX @@ +/* + * DesignWare I2C Module. + * + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef DESIGNWARE_I2C_H +#define DESIGNWARE_I2C_H + +#include "qemu/fifo8.h" +#include "hw/i2c/i2c.h" +#include "hw/core/irq.h" +#include "hw/core/register.h" +#include "hw/core/sysbus.h" +#include "qom/object.h" + +#define DESIGNWARE_I2C_R_MAX (0x100 / 4) + +#define DESIGNWARE_I2C_RX_FIFO_SIZE 16 +#define DESIGNWARE_I2C_TX_FIFO_SIZE 16 + +typedef enum DesignWareI2CStatus { + DW_I2C_STATUS_IDLE, + DW_I2C_STATUS_SENDING_ADDRESS, + DW_I2C_STATUS_SENDING, + DW_I2C_STATUS_RECEIVING, +} DesignWareI2CStatus; + +#define TYPE_DESIGNWARE_I2C "designware-i2c" +OBJECT_DECLARE_SIMPLE_TYPE(DesignWareI2CState, DESIGNWARE_I2C) + +/* + * struct DesignWareI2CState - DesignWare I2C device state. + * @bus: The underlying I2C Bus + * @irq: Interrupt line fired on transaction events. + * @rx_fifo: The FIFO buffer for receiving in FIFO mode. + */ +struct DesignWareI2CState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + I2CBus *bus; + qemu_irq irq; + + uint32_t regs[DESIGNWARE_I2C_R_MAX]; + RegisterInfo regs_info[DESIGNWARE_I2C_R_MAX]; + + /* fifo8_num_used(rx_fifo) should always equal DW_IC_RXFLR */ + Fifo8 rx_fifo; + + DesignWareI2CStatus status; +}; + +#endif /* DESIGNWARE_I2C_H */ diff --git a/hw/i2c/designware_i2c.c b/hw/i2c/designware_i2c.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/i2c/designware_i2c.c @@ -XXX,XX +XXX,XX @@ +/* + * DesignWare I2C Module. + * + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "hw/i2c/designware_i2c.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/units.h" + +#ifndef DESIGNWARE_I2C_ERR_DEBUG +#define DESIGNWARE_I2C_ERR_DEBUG 0 +#endif + +REG32(DW_IC_CON, 0x00) /* I2C control */ + FIELD(DW_IC_CON, STOP_DET_IF_MASTER_ACTIV, 10, 1) + FIELD(DW_IC_CON, RX_FIFO_FULL_HLD_CTRL, 9, 1) + FIELD(DW_IC_CON, TX_EMPTY_CTRL, 8, 1) + FIELD(DW_IC_CON, STOP_IF_ADDRESSED, 7, 1) + FIELD(DW_IC_CON, SLAVE_DISABLE, 6, 1) + FIELD(DW_IC_CON, IC_RESTART_EN, 5, 1) + FIELD(DW_IC_CON, 10BITADDR_MASTER, 4, 1) + FIELD(DW_IC_CON, 10BITADDR_SLAVE, 3, 1) + FIELD(DW_IC_CON, SPEED, 1, 2) + FIELD(DW_IC_CON, MASTER_MODE, 0, 1) +REG32(DW_IC_TAR, 0x04) /* I2C target address */ + FIELD(DW_IC_TAR, IC_10BITADDR_MASTER, 12, 1) + FIELD(DW_IC_TAR, SPECIAL, 11, 1) + FIELD(DW_IC_TAR, GC_OR_START, 10, 1) + FIELD(DW_IC_TAR, ADDRESS, 0, 10) +REG32(DW_IC_SAR, 0x08) /* I2C slave address */ +REG32(DW_IC_DATA_CMD, 0x10) + FIELD(DW_IC_DATA_CMD, RESTART, 10, 1) + FIELD(DW_IC_DATA_CMD, STOP, 9, 1) + FIELD(DW_IC_DATA_CMD, CMD, 8, 1) + FIELD(DW_IC_DATA_CMD, DAT, 0, 8) +REG32(DW_IC_SS_SCL_HCNT, 0x14) /* Standard speed i2c clock scl high count */ +REG32(DW_IC_SS_SCL_LCNT, 0x18) /* Standard speed i2c clock scl low count */ +REG32(DW_IC_FS_SCL_HCNT, 0x1c) /* Fast or fast plus i2c clock scl high count */ +REG32(DW_IC_FS_SCL_LCNT, 0x20) /* Fast or fast plus i2c clock scl low count */ +REG32(DW_IC_INTR_STAT, 0x2c) +REG32(DW_IC_INTR_MASK, 0x30) /* I2C Interrupt Mask */ +REG32(DW_IC_RAW_INTR_STAT, 0x34) /* I2C raw interrupt status */ + /* DW_IC_INTR_STAT/INTR_MASK/RAW_INTR_STAT fields */ + SHARED_FIELD(DW_IC_INTR_RESTART_DET, 12, 1) + SHARED_FIELD(DW_IC_INTR_GEN_CALL, 11, 1) + SHARED_FIELD(DW_IC_INTR_START_DET, 10, 1) + SHARED_FIELD(DW_IC_INTR_STOP_DET, 9, 1) + SHARED_FIELD(DW_IC_INTR_ACTIVITY, 8, 1) + SHARED_FIELD(DW_IC_INTR_RX_DONE, 7, 1) + SHARED_FIELD(DW_IC_INTR_TX_ABRT, 6, 1) + SHARED_FIELD(DW_IC_INTR_RD_REQ, 5, 1) + SHARED_FIELD(DW_IC_INTR_TX_EMPTY, 4, 1) /* Hardware clear only. */ + SHARED_FIELD(DW_IC_INTR_TX_OVER, 3, 1) + SHARED_FIELD(DW_IC_INTR_RX_FULL, 2, 1) /* Hardware clear only. */ + SHARED_FIELD(DW_IC_INTR_RX_OVER, 1, 1) + SHARED_FIELD(DW_IC_INTR_RX_UNDER, 0, 1) + +#define DW_IC_INTR_ANY_MASK \ + (DW_IC_INTR_RESTART_DET_MASK | \ + DW_IC_INTR_GEN_CALL_MASK | \ + DW_IC_INTR_START_DET_MASK | \ + DW_IC_INTR_STOP_DET_MASK | \ + DW_IC_INTR_ACTIVITY_MASK | \ + DW_IC_INTR_RX_DONE_MASK | \ + DW_IC_INTR_TX_ABRT_MASK | \ + DW_IC_INTR_RD_REQ_MASK | \ + DW_IC_INTR_TX_EMPTY_MASK | \ + DW_IC_INTR_TX_OVER_MASK | \ + DW_IC_INTR_RX_FULL_MASK | \ + DW_IC_INTR_RX_OVER_MASK | \ + DW_IC_INTR_RX_UNDER_MASK) + +#define DW_IC_INTR_ANY_SW_CLEAR_MASK \ + (DW_IC_INTR_ANY_MASK & \ + ~(DW_IC_INTR_TX_EMPTY_MASK | \ + DW_IC_INTR_RX_FULL_MASK)) + +REG32(DW_IC_RX_TL, 0x38) /* I2C receive FIFO threshold */ +REG32(DW_IC_TX_TL, 0x3c) /* I2C transmit FIFO threshold */ +REG32(DW_IC_CLR_INTR, 0x40) +REG32(DW_IC_CLR_RX_UNDER, 0x44) +REG32(DW_IC_CLR_RX_OVER, 0x48) +REG32(DW_IC_CLR_TX_OVER, 0x4c) +REG32(DW_IC_CLR_RD_REQ, 0x50) +REG32(DW_IC_CLR_TX_ABRT, 0x54) +REG32(DW_IC_CLR_RX_DONE, 0x58) +REG32(DW_IC_CLR_ACTIVITY, 0x5c) +REG32(DW_IC_CLR_STOP_DET, 0x60) +REG32(DW_IC_CLR_START_DET, 0x64) +REG32(DW_IC_CLR_GEN_CALL, 0x68) +REG32(DW_IC_ENABLE, 0x6c) /* I2C enable */ + FIELD(DW_IC_ENABLE, TX_CMD_BLOCK, 2, 1) + FIELD(DW_IC_ENABLE, ABORT, 1, 1) + FIELD(DW_IC_ENABLE, ENABLE, 0, 1) +REG32(DW_IC_STATUS, 0x70) /* I2C status */ + FIELD(DW_IC_STATUS, SLV_ACTIVITY, 6, 1) + FIELD(DW_IC_STATUS, MST_ACTIVITY, 5, 1) + FIELD(DW_IC_STATUS, RFF, 4, 1) + FIELD(DW_IC_STATUS, RFNE, 3, 1) + FIELD(DW_IC_STATUS, TFE, 2, 1) + FIELD(DW_IC_STATUS, TFNF, 1, 1) + FIELD(DW_IC_STATUS, ACTIVITY, 0, 1) +REG32(DW_IC_TXFLR, 0x74) /* I2C transmit fifo level */ +REG32(DW_IC_RXFLR, 0x78) /* I2C receive fifo level */ +REG32(DW_IC_SDA_HOLD, 0x7c) /* I2C SDA hold time length */ +REG32(DW_IC_TX_ABRT_SOURCE, 0x80) /* The I2C transmit abort source */ + FIELD(DW_IC_TX_ABRT_SOURCE, USER_ABRT, 16, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, SLVRD_INTX, 15, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, SLV_ARBLOST, 14, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, SLVFLUSH_TXFIFO, 13, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, ARB_LOST, 12, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, MASTER_DIS, 11, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, 10B_RD_NORSTRT, 10, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, SBYTE_NORSTRT, 9, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, HS_NORSTRT, 8, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, SBYTE_ACKDET, 7, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, HS_ACKDET, 6, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, GCALL_READ, 5, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, GCALL_NOACK, 4, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, TXDATA_NOACK, 3, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, 10ADDR2_NOACK, 2, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, 10ADDR1_NOACK, 1, 1) + FIELD(DW_IC_TX_ABRT_SOURCE, 7B_ADDR_NOACK, 0, 1) +REG32(DW_IC_SLV_DATA_NACK_ONLY, 0x84) +REG32(DW_IC_DMA_CR, 0x88) +REG32(DW_IC_DMA_TDLR, 0x8c) +REG32(DW_IC_DMA_RDLR, 0x90) +REG32(DW_IC_SDA_SETUP, 0x94) /* I2C SDA setup */ +REG32(DW_IC_ACK_GENERAL_CALL, 0x98) +REG32(DW_IC_ENABLE_STATUS, 0x9c) /* I2C enable status */ + FIELD(DW_IC_ENABLE_STATUS, SLV_RX_DATA_LOST, 2, 1) + FIELD(DW_IC_ENABLE_STATUS, SLV_DISABLED_WHILE_BUSY, 1, 1) + FIELD(DW_IC_ENABLE_STATUS, IC_EN, 0, 1) +REG32(DW_IC_FS_SPKLEN, 0xa0) /* I2C SS, FS or FM+ spike suppression limit */ +REG32(DW_IC_CLR_RESTART_DET, 0xa8) +REG32(DW_IC_SMBUS_INTR_MASK, 0xcc) /* SMBus Interrupt Mask */ +REG32(DW_IC_COMP_PARAM_1, 0xf4) /* Component parameter */ + FIELD(DW_IC_COMP_PARAM_1, TX_FIFO_SIZE, 16, 8) + FIELD(DW_IC_COMP_PARAM_1, RX_FIFO_SIZE, 8, 8) + FIELD(DW_IC_COMP_PARAM_1, HAS_ENCODED_PARAMS, 7, 1) + FIELD(DW_IC_COMP_PARAM_1, HAS_DMA, 6, 1) + FIELD(DW_IC_COMP_PARAM_1, INTR_IO, 5, 1) + FIELD(DW_IC_COMP_PARAM_1, HC_COUNT_VAL, 4, 1) + FIELD(DW_IC_COMP_PARAM_1, HIGH_SPEED_MODE, 2, 2) + FIELD(DW_IC_COMP_PARAM_1, APB_DATA_WIDTH_32, 0, 2) +REG32(DW_IC_COMP_VERSION, 0xf8) /* I2C component version */ +REG32(DW_IC_COMP_TYPE, 0xfc) /* I2C component type */ + +static void dw_i2c_update_irq(DesignWareI2CState *s) +{ + uint32_t intr = s->regs[R_DW_IC_RAW_INTR_STAT] & s->regs[R_DW_IC_INTR_MASK]; + + qemu_set_irq(s->irq, !!(intr & DW_IC_INTR_ANY_MASK)); +} + +static uint64_t dw_ic_data_cmd_reg_post_read(RegisterInfo *reg, uint64_t value) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque); + + g_assert(value == 0); + + if (s->status != DW_I2C_STATUS_RECEIVING) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Attempted to read from RX fifo when not in receive " + "state.\n", DEVICE(s)->canonical_path); + if (s->status != DW_I2C_STATUS_IDLE) { + SHARED_ARRAY_FIELD_DP32(s->regs, R_DW_IC_RAW_INTR_STAT, + DW_IC_INTR_RX_UNDER, 1); + dw_i2c_update_irq(s); + } + return 0; + } + + g_assert(s->regs[R_DW_IC_RXFLR] == fifo8_num_used(&s->rx_fifo)); + + if (fifo8_is_empty(&s->rx_fifo)) { + SHARED_ARRAY_FIELD_DP32(s->regs, R_DW_IC_RAW_INTR_STAT, DW_IC_INTR_RX_UNDER, 1); + dw_i2c_update_irq(s); + return 0; + } + + s->regs[R_DW_IC_RXFLR]--; + if (s->regs[R_DW_IC_RXFLR] <= s->regs[R_DW_IC_RX_TL]) { + SHARED_ARRAY_FIELD_DP32(s->regs, R_DW_IC_RAW_INTR_STAT, DW_IC_INTR_RX_FULL, 0); + dw_i2c_update_irq(s); + } + + return fifo8_pop(&s->rx_fifo); +} + +static uint64_t dw_ic_clr_intr_reg_post_read(RegisterInfo *reg, uint64_t value) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque); + + g_assert(value == 0); + + switch (reg->access->addr) { + case A_DW_IC_CLR_INTR: + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_ANY_SW_CLEAR_MASK; + break; + case A_DW_IC_CLR_RX_UNDER: + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_UNDER_MASK; + break; + case A_DW_IC_CLR_RX_OVER: + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_OVER_MASK; + break; + case A_DW_IC_CLR_TX_OVER: + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_TX_OVER_MASK; + break; + case A_DW_IC_CLR_RD_REQ: + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RD_REQ_MASK; + break; + case A_DW_IC_CLR_TX_ABRT: + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_TX_ABRT_MASK; + break; + case A_DW_IC_CLR_RX_DONE: + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_DONE_MASK; + break; + case A_DW_IC_CLR_ACTIVITY: + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_ACTIVITY_MASK; + break; + case A_DW_IC_CLR_STOP_DET: + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_STOP_DET_MASK; + break; + case A_DW_IC_CLR_START_DET: + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_START_DET_MASK; + break; + case A_DW_IC_CLR_GEN_CALL: + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_GEN_CALL_MASK; + break; + case A_DW_IC_CLR_RESTART_DET: + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RESTART_DET_MASK; + break; + default: + g_assert_not_reached(); + } + + dw_i2c_update_irq(s); + + return 0; +} + +static uint64_t dw_ic_intr_stat_reg_post_read(RegisterInfo *reg, uint64_t value) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque); + + g_assert(value == 0); + + return s->regs[R_DW_IC_RAW_INTR_STAT] & s->regs[R_DW_IC_INTR_MASK]; +} + +static uint64_t dw_ic_unsupported_reg_post_read(RegisterInfo *reg, uint64_t value) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque); + + qemu_log_mask(LOG_UNIMP, "%s: unsupported read - %s\n", + DEVICE(s)->canonical_path, reg->access->name); + + return 0; +} + +static uint64_t dw_ic_unsupported_reg_pre_write(RegisterInfo *reg, uint64_t value) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque); + + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - %s\n", + DEVICE(s)->canonical_path, reg->access->name); + + return 0; +} + +static uint64_t dw_ic_con_reg_pre_write(RegisterInfo *reg, uint64_t value) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque); + + if (s->regs[R_DW_IC_ENABLE] & R_DW_IC_ENABLE_ENABLE_MASK) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid setting to ic_con %d when ic_enable[0]==1\n", + DEVICE(s)->canonical_path, (int)value); + return s->regs[R_DW_IC_CON]; /* keep old value */ + } + + return value; +} + +static void dw_i2c_reset_to_idle(DesignWareI2CState *s) +{ + s->regs[R_DW_IC_ENABLE_STATUS] &= ~R_DW_IC_ENABLE_STATUS_IC_EN_MASK; + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_TX_EMPTY_MASK; + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_FULL_MASK; + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_UNDER_MASK; + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_OVER_MASK; + s->regs[R_DW_IC_RXFLR] = 0; + fifo8_reset(&s->rx_fifo); + s->regs[R_DW_IC_STATUS] &= ~R_DW_IC_STATUS_ACTIVITY_MASK; + s->status = DW_I2C_STATUS_IDLE; + dw_i2c_update_irq(s); +} + +static void dw_ic_tx_abort(DesignWareI2CState *s, uint32_t src) +{ + s->regs[R_DW_IC_TX_ABRT_SOURCE] |= src; + s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_TX_ABRT_MASK; + dw_i2c_reset_to_idle(s); + dw_i2c_update_irq(s); +} + +static void dw_ic_data_cmd_reg_post_write(RegisterInfo *reg, uint64_t value) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque); + int recv = !!(value & R_DW_IC_DATA_CMD_CMD_MASK); + + s->regs[R_DW_IC_DATA_CMD] = 0; /* Register has no storage */ + + if (!(s->regs[R_DW_IC_ENABLE] & R_DW_IC_ENABLE_ENABLE_MASK)) { + /* + * Controller is not enabled. The register_reset() path also lands + * here with value == 0, so silently ignore rather than reporting + * a spurious guest error. + */ + return; + } + + if (s->status == DW_I2C_STATUS_IDLE || + s->regs[R_DW_IC_RAW_INTR_STAT] & DW_IC_INTR_TX_ABRT_MASK) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Attempted to write to TX fifo when it is held in " + "reset\n", DEVICE(s)->canonical_path); + return; + } + + /* Send the address if it hasn't been sent yet. */ + if (s->status == DW_I2C_STATUS_SENDING_ADDRESS) { + int rv = i2c_start_transfer(s->bus, + ARRAY_FIELD_EX32(s->regs, DW_IC_TAR, ADDRESS), recv); + if (rv) { + dw_ic_tx_abort(s, R_DW_IC_TX_ABRT_SOURCE_7B_ADDR_NOACK_MASK); + return; + } + s->status = recv ? DW_I2C_STATUS_RECEIVING : DW_I2C_STATUS_SENDING; + } + + /* Send data */ + if (!recv) { + int rv = i2c_send(s->bus, FIELD_EX32(value, DW_IC_DATA_CMD, DAT)); + if (rv) { + i2c_end_transfer(s->bus); + dw_ic_tx_abort(s, R_DW_IC_TX_ABRT_SOURCE_TXDATA_NOACK_MASK); + return; + } + dw_i2c_update_irq(s); + } + + /* Restart command */ + if (value & R_DW_IC_DATA_CMD_RESTART_MASK && + s->regs[R_DW_IC_CON] & R_DW_IC_CON_IC_RESTART_EN_MASK) { + s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_RESTART_DET_MASK | + DW_IC_INTR_START_DET_MASK | + DW_IC_INTR_ACTIVITY_MASK; + s->regs[R_DW_IC_STATUS] |= R_DW_IC_STATUS_ACTIVITY_MASK; + dw_i2c_update_irq(s); + + if (i2c_start_transfer(s->bus, + ARRAY_FIELD_EX32(s->regs, DW_IC_TAR, ADDRESS), recv)) { + dw_ic_tx_abort(s, R_DW_IC_TX_ABRT_SOURCE_7B_ADDR_NOACK_MASK); + return; + } + + s->status = recv ? DW_I2C_STATUS_RECEIVING : DW_I2C_STATUS_SENDING; + } + + /* Receive data */ + if (recv) { + g_assert(s->regs[R_DW_IC_RXFLR] == fifo8_num_used(&s->rx_fifo)); + + if (!fifo8_is_full(&s->rx_fifo)) { + fifo8_push(&s->rx_fifo, i2c_recv(s->bus)); + s->regs[R_DW_IC_RXFLR]++; + } else { + s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_RX_OVER_MASK; + dw_i2c_update_irq(s); + } + + if (s->regs[R_DW_IC_RXFLR] > s->regs[R_DW_IC_RX_TL]) { + s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_RX_FULL_MASK; + dw_i2c_update_irq(s); + } + if (value & R_DW_IC_DATA_CMD_STOP_MASK) { + i2c_nack(s->bus); + } + } + + /* Stop command */ + if (value & R_DW_IC_DATA_CMD_STOP_MASK) { + s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_STOP_DET_MASK; + s->regs[R_DW_IC_STATUS] &= ~R_DW_IC_STATUS_ACTIVITY_MASK; + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_TX_EMPTY_MASK; + i2c_end_transfer(s->bus); + dw_i2c_update_irq(s); + } +} + +static void dw_ic_intr_mask_reg_post_write(RegisterInfo *reg, uint64_t value) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque); + + dw_i2c_update_irq(s); +} + +static uint64_t dw_ic_enable_reg_pre_write(RegisterInfo *reg, uint64_t value) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque); + + if (value & R_DW_IC_ENABLE_ENABLE_MASK && + !(s->regs[R_DW_IC_CON] & R_DW_IC_CON_SLAVE_DISABLE_MASK)) { + qemu_log_mask(LOG_UNIMP, + "%s: Designware I2C slave mode is not supported.\n", + DEVICE(s)->canonical_path); + return s->regs[R_DW_IC_ENABLE]; /* keep old value */ + } + + return value; +} + +static void dw_ic_enable_reg_post_write(RegisterInfo *reg, uint64_t value) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque); + + s->regs[R_DW_IC_ENABLE] = value & R_DW_IC_ENABLE_ENABLE_MASK; + + if (value & R_DW_IC_ENABLE_ABORT_MASK || value & R_DW_IC_ENABLE_TX_CMD_BLOCK_MASK) { + dw_ic_tx_abort(s, R_DW_IC_TX_ABRT_SOURCE_USER_ABRT_MASK); + return; + } + + if (value & R_DW_IC_ENABLE_ENABLE_MASK) { + s->regs[R_DW_IC_ENABLE_STATUS] |= R_DW_IC_ENABLE_STATUS_IC_EN_MASK; + s->regs[R_DW_IC_STATUS] |= R_DW_IC_STATUS_ACTIVITY_MASK; + s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_ACTIVITY_MASK | + DW_IC_INTR_START_DET_MASK | + DW_IC_INTR_TX_EMPTY_MASK; + s->status = DW_I2C_STATUS_SENDING_ADDRESS; + dw_i2c_update_irq(s); + } else if ((value & R_DW_IC_ENABLE_ENABLE_MASK) == 0) { + dw_i2c_reset_to_idle(s); + } +} + +static uint64_t dw_ic_rx_tl_reg_pre_write(RegisterInfo *reg, uint64_t value) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque); + + /* Note that a value of 0 for ic_rx_tl indicates a threshold of 1. */ + if (value > DESIGNWARE_I2C_RX_FIFO_SIZE - 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid setting to ic_rx_tl %d\n", + DEVICE(s)->canonical_path, (int)value); + return DESIGNWARE_I2C_RX_FIFO_SIZE - 1; + } + + return value; +} + +static void dw_ic_rx_tl_reg_post_write(RegisterInfo *reg, uint64_t value) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque); + + if (s->regs[R_DW_IC_RXFLR] > s->regs[R_DW_IC_RX_TL] && + s->regs[R_DW_IC_ENABLE] & R_DW_IC_ENABLE_ENABLE_MASK) { + s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_RX_FULL_MASK; + } else { + s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_FULL_MASK; + } + dw_i2c_update_irq(s); +} + +static uint64_t dw_ic_tx_tl_reg_pre_write(RegisterInfo *reg, uint64_t value) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque); + + /* + * Note that a value of 0 for ic_tx_tl indicates a threshold of 1. + * However, the tx threshold is not used in the model because commands are + * always sent out as soon as they are written. + */ + if (value > DESIGNWARE_I2C_TX_FIFO_SIZE - 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid setting to ic_tx_tl %d\n", + DEVICE(s)->canonical_path, (int)value); + return DESIGNWARE_I2C_TX_FIFO_SIZE - 1; + } + + return value; +} + +static const RegisterAccessInfo designware_i2c_regs_info[] = { + { .name = "DW_IC_CON", .addr = A_DW_IC_CON, + .reset = 0x7d, + .unimp = 0xfffffc00, + .pre_write = dw_ic_con_reg_pre_write, + },{ .name = "DW_IC_TAR", .addr = A_DW_IC_TAR, + .reset = 0x1055, + .unimp = 0xfffff000, + },{ .name = "DW_IC_SAR", .addr = A_DW_IC_SAR, + .reset = 0x55, + .unimp = 0xfffffc00, + .post_read = dw_ic_unsupported_reg_post_read, + .pre_write = dw_ic_unsupported_reg_pre_write, + },{ .name = "DW_IC_DATA_CMD", .addr = A_DW_IC_DATA_CMD, + .post_read = dw_ic_data_cmd_reg_post_read, + .post_write = dw_ic_data_cmd_reg_post_write, + },{ .name = "DW_IC_SS_SCL_HCNT", .addr = A_DW_IC_SS_SCL_HCNT, + .reset = 0x190, + .unimp = 0xffff0000, + },{ .name = "DW_IC_SS_SCL_LCNT", .addr = A_DW_IC_SS_SCL_LCNT, + .reset = 0x1d6, + .unimp = 0xffff0000, + },{ .name = "DW_IC_FS_SCL_HCNT", .addr = A_DW_IC_FS_SCL_HCNT, + .reset = 0x3c, + .unimp = 0xffff0000, + },{ .name = "DW_IC_FS_SCL_LCNT", .addr = A_DW_IC_FS_SCL_LCNT, + .reset = 0x82, + .unimp = 0xffff0000, + },{ .name = "DW_IC_INTR_STAT", .addr = A_DW_IC_INTR_STAT, + .ro = 0xffffffff, + .post_read = dw_ic_intr_stat_reg_post_read, + },{ .name = "DW_IC_INTR_MASK", .addr = A_DW_IC_INTR_MASK, + .reset = 0x8ff, + .unimp = 0xffff8000, + .post_write = dw_ic_intr_mask_reg_post_write, + },{ .name = "DW_IC_RAW_INTR_STAT", .addr = A_DW_IC_RAW_INTR_STAT, + .ro = 0xffffffff, + },{ .name = "DW_IC_RX_TL", .addr = A_DW_IC_RX_TL, + .pre_write = dw_ic_rx_tl_reg_pre_write, + .post_write = dw_ic_rx_tl_reg_post_write, + },{ .name = "DW_IC_TX_TL", .addr = A_DW_IC_TX_TL, + .pre_write = dw_ic_tx_tl_reg_pre_write, + },{ .name = "DW_IC_CLR_INTR", .addr = A_DW_IC_CLR_INTR, + .ro = 0xffffffff, + .post_read = dw_ic_clr_intr_reg_post_read, + },{ .name = "DW_IC_CLR_RX_UNDER", .addr = A_DW_IC_CLR_RX_UNDER, + .ro = 0xffffffff, + .post_read = dw_ic_clr_intr_reg_post_read, + },{ .name = "DW_IC_CLR_RX_OVER", .addr = A_DW_IC_CLR_RX_OVER, + .ro = 0xffffffff, + .post_read = dw_ic_clr_intr_reg_post_read, + },{ .name = "DW_IC_CLR_TX_OVER", .addr = A_DW_IC_CLR_TX_OVER, + .ro = 0xffffffff, + .post_read = dw_ic_clr_intr_reg_post_read, + },{ .name = "DW_IC_CLR_RD_REQ", .addr = A_DW_IC_CLR_RD_REQ, + .ro = 0xffffffff, + .post_read = dw_ic_clr_intr_reg_post_read, + },{ .name = "DW_IC_CLR_TX_ABRT", .addr = A_DW_IC_CLR_TX_ABRT, + .ro = 0xffffffff, + .post_read = dw_ic_clr_intr_reg_post_read, + },{ .name = "DW_IC_CLR_RX_DONE", .addr = A_DW_IC_CLR_RX_DONE, + .ro = 0xffffffff, + .post_read = dw_ic_clr_intr_reg_post_read, + },{ .name = "DW_IC_CLR_ACTIVITY", .addr = A_DW_IC_CLR_ACTIVITY, + .ro = 0xffffffff, + .post_read = dw_ic_clr_intr_reg_post_read, + },{ .name = "DW_IC_CLR_STOP_DET", .addr = A_DW_IC_CLR_STOP_DET, + .ro = 0xffffffff, + .post_read = dw_ic_clr_intr_reg_post_read, + },{ .name = "DW_IC_CLR_START_DET", .addr = A_DW_IC_CLR_START_DET, + .ro = 0xffffffff, + .post_read = dw_ic_clr_intr_reg_post_read, + },{ .name = "DW_IC_CLR_GEN_CALL", .addr = A_DW_IC_CLR_GEN_CALL, + .ro = 0xffffffff, + .post_read = dw_ic_clr_intr_reg_post_read, + },{ .name = "DW_IC_ENABLE", .addr = A_DW_IC_ENABLE, + .unimp = 0xfffffff8, + .pre_write = dw_ic_enable_reg_pre_write, + .post_write = dw_ic_enable_reg_post_write, + },{ .name = "DW_IC_STATUS", .addr = A_DW_IC_STATUS, + .reset = 0x6, + .ro = 0xffffffff, + },{ .name = "DW_IC_TXFLR", .addr = A_DW_IC_TXFLR, + .ro = 0xffffffff, + },{ .name = "DW_IC_RXFLR", .addr = A_DW_IC_RXFLR, + .ro = 0xffffffff, + },{ .name = "DW_IC_SDA_HOLD", .addr = A_DW_IC_SDA_HOLD, + .reset = 0x1, + .unimp = 0xff000000, + },{ .name = "DW_IC_TX_ABRT_SOURCE", .addr = A_DW_IC_TX_ABRT_SOURCE, + .ro = 0xffffffff, + },{ .name = "DW_IC_SLV_DATA_NACK_ONLY", .addr = A_DW_IC_SLV_DATA_NACK_ONLY, + .post_read = dw_ic_unsupported_reg_post_read, + .pre_write = dw_ic_unsupported_reg_pre_write, + },{ .name = "DW_IC_DMA_CR", .addr = A_DW_IC_DMA_CR, + .post_read = dw_ic_unsupported_reg_post_read, + .pre_write = dw_ic_unsupported_reg_pre_write, + },{ .name = "DW_IC_DMA_TDLR", .addr = A_DW_IC_DMA_TDLR, + .post_read = dw_ic_unsupported_reg_post_read, + .pre_write = dw_ic_unsupported_reg_pre_write, + },{ .name = "DW_IC_DMA_RDLR", .addr = A_DW_IC_DMA_RDLR, + .post_read = dw_ic_unsupported_reg_post_read, + .pre_write = dw_ic_unsupported_reg_pre_write, + },{ .name = "DW_IC_SDA_SETUP", .addr = A_DW_IC_SDA_SETUP, + .reset = 0x64, + .unimp = 0xffffff00, + },{ .name = "DW_IC_ACK_GENERAL_CALL", .addr = A_DW_IC_ACK_GENERAL_CALL, + .post_read = dw_ic_unsupported_reg_post_read, + .pre_write = dw_ic_unsupported_reg_pre_write, + },{ .name = "DW_IC_ENABLE_STATUS", .addr = A_DW_IC_ENABLE_STATUS, + .ro = 0xffffffff, + },{ .name = "DW_IC_FS_SPKLEN", .addr = A_DW_IC_FS_SPKLEN, + .reset = 0x2, + .ro = 0xffffff00, + },{ .name = "DW_IC_CLR_RESTART_DET", .addr = A_DW_IC_CLR_RESTART_DET, + .ro = 0xffffffff, + .post_read = dw_ic_clr_intr_reg_post_read, + },{ .name = "DW_IC_SMBUS_INTR_MASK", .addr = A_DW_IC_SMBUS_INTR_MASK, + /* No SMBus interrupts are implemented, Linux updates the mask */ + .reset = 0x7ff, + .unimp = 0xfffff800, + },{ .name = "DW_IC_COMP_PARAM_1", .addr = A_DW_IC_COMP_PARAM_1, + .reset = /* HAS_DMA and HC_COUNT_VAL are disabled */ + ((2 << R_DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32_SHIFT) | + R_DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE_MASK | + R_DW_IC_COMP_PARAM_1_INTR_IO_MASK | + R_DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS_MASK | + ((DESIGNWARE_I2C_RX_FIFO_SIZE - 1) + << R_DW_IC_COMP_PARAM_1_RX_FIFO_SIZE_SHIFT) | + ((DESIGNWARE_I2C_TX_FIFO_SIZE - 1) + << R_DW_IC_COMP_PARAM_1_TX_FIFO_SIZE_SHIFT)), + .ro = 0xffffffff, + },{ .name = "DW_IC_COMP_VERSION", .addr = A_DW_IC_COMP_VERSION, + .reset = 0x3132302a, + .ro = 0xffffffff, + },{ .name = "DW_IC_COMP_TYPE", .addr = A_DW_IC_COMP_TYPE, + .reset = 0x44570140, + .ro = 0xffffffff, + } +}; + +static const MemoryRegionOps designware_i2c_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void designware_i2c_enter_reset(Object *obj, ResetType type) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(obj); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs); ++i) { + register_reset(&s->regs_info[i]); + } + + fifo8_reset(&s->rx_fifo); + + s->status = DW_I2C_STATUS_IDLE; +} + +static void designware_i2c_hold_reset(Object *obj, ResetType type) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(obj); + + qemu_irq_lower(s->irq); +} + +static const VMStateDescription vmstate_designware_i2c = { + .name = TYPE_DESIGNWARE_I2C, + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, DesignWareI2CState, DESIGNWARE_I2C_R_MAX), + VMSTATE_FIFO8(rx_fifo, DesignWareI2CState), + VMSTATE_UINT32(status, DesignWareI2CState), + VMSTATE_END_OF_LIST(), + }, +}; + +static void designware_i2c_instance_init(Object *obj) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; + + fifo8_create(&s->rx_fifo, DESIGNWARE_I2C_RX_FIFO_SIZE); + + s->bus = i2c_init_bus(DEVICE(s), "i2c-bus"); + + memory_region_init(&s->iomem, obj, TYPE_DESIGNWARE_I2C, 4 * KiB); + reg_array = register_init_block32(DEVICE(obj), designware_i2c_regs_info, + ARRAY_SIZE(designware_i2c_regs_info), + s->regs_info, s->regs, + &designware_i2c_ops, + DESIGNWARE_I2C_ERR_DEBUG, + DESIGNWARE_I2C_R_MAX * 4); + memory_region_add_subregion(&s->iomem, 0, ®_array->mem); + + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); +} + +static void designware_i2c_finalize(Object *obj) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(obj); + + fifo8_destroy(&s->rx_fifo); +} + +static void designware_i2c_class_init(ObjectClass *klass, const void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "Designware I2C"; + dc->vmsd = &vmstate_designware_i2c; + rc->phases.enter = designware_i2c_enter_reset; + rc->phases.hold = designware_i2c_hold_reset; + + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); +} + +static const TypeInfo designware_i2c_types[] = { + { + .name = TYPE_DESIGNWARE_I2C, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(DesignWareI2CState), + .class_init = designware_i2c_class_init, + .instance_init = designware_i2c_instance_init, + .instance_finalize = designware_i2c_finalize, + }, +}; +DEFINE_TYPES(designware_i2c_types); diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/i2c/Kconfig +++ b/hw/i2c/Kconfig @@ -XXX,XX +XXX,XX @@ config ARM_SBCON_I2C bool select BITBANG_I2C +config DESIGNWARE_I2C + bool + select REGISTER + select I2C + config ACPI_SMBUS bool select SMBUS diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/i2c/meson.build +++ b/hw/i2c/meson.build @@ -XXX,XX +XXX,XX @@ i2c_ss.add(when: 'CONFIG_MPC_I2C', if_true: files('mpc_i2c.c')) i2c_ss.add(when: 'CONFIG_ALLWINNER_I2C', if_true: files('allwinner-i2c.c')) i2c_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('microbit_i2c.c')) i2c_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_smbus.c')) +i2c_ss.add(when: 'CONFIG_DESIGNWARE_I2C', if_true: files('designware_i2c.c')) i2c_ss.add(when: 'CONFIG_SMBUS_EEPROM', if_true: files('smbus_eeprom.c')) i2c_ss.add(when: 'CONFIG_ARM_SBCON_I2C', if_true: files('arm_sbcon_i2c.c')) i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c')) -- 2.54.0
From: Joel Stanley <joel@jms.id.au> Add DesignWare I2C controllers to the tt-atlantis machine. Provide a fixed clock in the device tree so that the Linux driver probes without WARNing. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20260630024952.1520546-12-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/tt_atlantis.h | 14 ++++++++ hw/riscv/tt_atlantis.c | 59 ++++++++++++++++++++++++++++++++++ hw/riscv/Kconfig | 1 + 3 files changed, 74 insertions(+) diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/tt_atlantis.h +++ b/include/hw/riscv/tt_atlantis.h @@ -XXX,XX +XXX,XX @@ #include "hw/core/boards.h" #include "hw/core/sysbus.h" +#include "hw/i2c/designware_i2c.h" #include "hw/intc/riscv_imsic.h" #include "hw/riscv/riscv_hart.h" #define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis") OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisState, TT_ATLANTIS_MACHINE) +#define TT_ATL_NUM_I2C 5 + struct TTAtlantisState { /*< private >*/ MachineState parent; @@ -XXX,XX +XXX,XX @@ struct TTAtlantisState { RISCVHartArrayState soc; DeviceState *irqchip; + DesignWareI2CState i2c[TT_ATL_NUM_I2C]; int fdt_size; }; enum { + TT_ATL_I2C0_IRQ = 33, + TT_ATL_I2C1_IRQ = 34, + TT_ATL_I2C2_IRQ = 35, + TT_ATL_I2C3_IRQ = 36, + TT_ATL_I2C4_IRQ = 37, TT_ATL_UART1_IRQ = 39, }; @@ -XXX,XX +XXX,XX @@ enum { TT_ATL_BOOTROM, TT_ATL_DDR_LO, TT_ATL_DDR_HI, + TT_ATL_I2C0, + TT_ATL_I2C1, + TT_ATL_I2C2, + TT_ATL_I2C3, + TT_ATL_I2C4, TT_ATL_MAPLIC, TT_ATL_MIMSIC, TT_ATL_SAPLIC, diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/tt_atlantis.c +++ b/hw/riscv/tt_atlantis.c @@ -XXX,XX +XXX,XX @@ static const MemMapEntry tt_atlantis_memmap[] = { [TT_ATL_ACLINT] = { 0xa2180000, 0x10000 }, [TT_ATL_SIMSIC] = { 0xa4000000, 0x200000 }, [TT_ATL_MAPLIC] = { 0xcc000000, 0x4000000 }, + [TT_ATL_I2C0] = { 0xd4040000, 0x10000 }, + [TT_ATL_I2C1] = { 0xd4050000, 0x10000 }, + [TT_ATL_I2C2] = { 0xd4060000, 0x10000 }, + [TT_ATL_I2C3] = { 0xd4070000, 0x10000 }, + [TT_ATL_I2C4] = { 0xd4080000, 0x10000 }, [TT_ATL_UART1] = { 0xd4110000, 0x10000 }, [TT_ATL_SAPLIC] = { 0xe8000000, 0x4000000 }, [TT_ATL_DDR_HI] = { 0x100000000, 0x1000000000 }, @@ -XXX,XX +XXX,XX @@ static void create_fdt_rng(void *fdt) qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); } +static void create_fdt_clk(void *fdt, const char *clock_name, + uint32_t freq, uint32_t phandle) +{ + g_autofree char *name = g_strdup_printf("/clocks/%s", clock_name); + + qemu_fdt_add_path(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "fixed-clock"); + qemu_fdt_setprop_string(fdt, name, "clock-output-names", clock_name); + qemu_fdt_setprop_cell(fdt, name, "#clock-cells", 0); + qemu_fdt_setprop_cell(fdt, name, "clock-frequency", freq); + qemu_fdt_setprop_cell(fdt, name, "phandle", phandle); +} + +static void create_fdt_i2c(void *fdt, const MemMapEntry *mem, uint32_t irq, + uint32_t irqchip_phandle, uint32_t clk_phandle) +{ + g_autofree char *name = g_strdup_printf("/soc/i2c@%"HWADDR_PRIX, mem->base); + + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "snps,designware-i2c"); + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size); + qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", irqchip_phandle); + qemu_fdt_setprop_cells(fdt, name, "interrupts", irq, 0x4); + qemu_fdt_setprop_cell(fdt, name, "clocks", clk_phandle); + qemu_fdt_setprop_cell(fdt, name, "clock-frequency", 100000); + qemu_fdt_setprop_cell(fdt, name, "#address-cells", 1); + qemu_fdt_setprop_cell(fdt, name, "#size-cells", 0); +} + static void finalize_fdt(TTAtlantisState *s) { uint32_t aplic_s_phandle = next_phandle(); uint32_t imsic_s_phandle = next_phandle(); + uint32_t periph_clk_phandle = next_phandle(); void *fdt = MACHINE(s)->fdt; create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle); @@ -XXX,XX +XXX,XX @@ static void finalize_fdt(TTAtlantisState *s) create_fdt_uart(fdt, &s->memmap[TT_ATL_UART1], TT_ATL_UART1_IRQ, aplic_s_phandle); + + create_fdt_clk(fdt, "periph-clk", 100000000, periph_clk_phandle); + + for (int i = 0; i < TT_ATL_NUM_I2C; i++) { + create_fdt_i2c(fdt, + &s->memmap[TT_ATL_I2C0 + i], + TT_ATL_I2C0_IRQ + i, + aplic_s_phandle, periph_clk_phandle); + } } static void create_fdt(TTAtlantisState *s) @@ -XXX,XX +XXX,XX @@ static void tt_atlantis_machine_init(MachineState *machine) s->memmap[TT_ATL_UART1].base, s->memmap[TT_ATL_UART1].size); + /* I2C */ + for (int i = 0; i < TT_ATL_NUM_I2C; i++) { + SysBusDevice *sbd; + + object_initialize_child(OBJECT(s), "i2c[*]", &s->i2c[i], + TYPE_DESIGNWARE_I2C); + sbd = SYS_BUS_DEVICE(&s->i2c[i]); + sysbus_realize(sbd, &error_fatal); + memory_region_add_subregion(system_memory, + s->memmap[TT_ATL_I2C0 + i].base, + sysbus_mmio_get_region(sbd, 0)); + sysbus_connect_irq(sbd, 0, + qdev_get_gpio_in(s->irqchip, TT_ATL_I2C0_IRQ + i)); + } + /* Load or create device tree */ if (machine->dtb) { load_fdt(s); 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 TENSTORRENT select RISCV_IMSIC select SERIAL_MM select DEVICE_TREE + select DESIGNWARE_I2C config XIANGSHAN_KUNMINGHU bool -- 2.54.0
From: Joel Stanley <joel@jms.id.au> Add an I2C RTC device and a temperature sensor. These are not present on the board but help for testing. The tmp105 is a lm75 compatible temperature sensor used by the SENSORS_LM75 Linux kernel driver. The ds1338 is a RTC device that is used by the RTC_DRV_DS1307 Linux kernel driver. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20260630024952.1520546-13-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/tt_atlantis.c | 27 +++++++++++++++++++++++++++ hw/riscv/Kconfig | 2 ++ 2 files changed, 29 insertions(+) diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/tt_atlantis.c +++ b/hw/riscv/tt_atlantis.c @@ -XXX,XX +XXX,XX @@ static const MemMapEntry tt_atlantis_memmap[] = { [TT_ATL_DDR_HI] = { 0x100000000, 0x1000000000 }, }; +static I2CBus *i2c_get_bus(TTAtlantisState *s, unsigned busnr) +{ + assert(busnr < TT_ATL_NUM_I2C); + + return s->i2c[busnr].bus; +} + static uint32_t fdt_phandle = 1; static uint32_t next_phandle(void) { @@ -XXX,XX +XXX,XX @@ static void create_fdt_i2c(void *fdt, const MemMapEntry *mem, uint32_t irq, qemu_fdt_setprop_cell(fdt, name, "#size-cells", 0); } +static void create_fdt_i2c_device(TTAtlantisState *s, int bus, + const char *compat, int addr) +{ + void *fdt = MACHINE(s)->fdt; + hwaddr base = s->memmap[TT_ATL_I2C0 + bus].base; + g_autofree char *name = g_strdup_printf("/soc/i2c@%"HWADDR_PRIX"/sensor@%x", + base, addr); + + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", compat); + qemu_fdt_setprop_cell(fdt, name, "reg", addr); +} + static void finalize_fdt(TTAtlantisState *s) { uint32_t aplic_s_phandle = next_phandle(); @@ -XXX,XX +XXX,XX @@ static void finalize_fdt(TTAtlantisState *s) TT_ATL_I2C0_IRQ + i, aplic_s_phandle, periph_clk_phandle); } + + create_fdt_i2c_device(s, 0, "dallas,ds1338", 0x6f); + create_fdt_i2c_device(s, 4, "ti,tmp105", 0x48); } static void create_fdt(TTAtlantisState *s) @@ -XXX,XX +XXX,XX @@ static void tt_atlantis_machine_init(MachineState *machine) qdev_get_gpio_in(s->irqchip, TT_ATL_I2C0_IRQ + i)); } + /* I2C peripherals: qemu specific */ + i2c_slave_create_simple(i2c_get_bus(s, 0), "ds1338", 0x6f); + i2c_slave_create_simple(i2c_get_bus(s, 4), "tmp105", 0x48); + /* Load or create device tree */ if (machine->dtb) { load_fdt(s); 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 TENSTORRENT select SERIAL_MM select DEVICE_TREE select DESIGNWARE_I2C + select DS1338 + select TMP105 config XIANGSHAN_KUNMINGHU bool -- 2.54.0
From: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> riscv-iommu spec: "If SADE is 1, the IOMMU updates A and D bits in first-stage PTEs atomically. If SADE is 0, the IOMMU causes a page-fault corresponding to the original access type if the A bit is 0 or if the memory access is a store and the D bit is 0.". Note that SADE=0 and A=0 will always cause a fault regardless of the original access type. Right now we're faulting in this case just for reads. Fixes: 0c54acb8243d ("hw/riscv: add RISC-V IOMMU base emulation") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3551 Signed-off-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Message-ID: <20260630211044.82894-1-daniel.barboza@oss.qualcomm.com> 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 int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, break; /* Read access check failed */ } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) { break; /* Write access check failed */ - } else if ((iotlb->perm & IOMMU_RO) && !ade && !(pte & PTE_A)) { + } else if (!ade && !(pte & PTE_A)) { break; /* Access bit not set */ } else if ((iotlb->perm & IOMMU_WO) && !ade && !(pte & PTE_D)) { break; /* Dirty bit not set */ -- 2.54.0