:p
atchew
Login
From: Alistair Francis <alistair.francis@wdc.com> The following changes since commit c77283dd5d79149f4e7e9edd00f65416c648ee59: Merge tag 'pull-request-2025-07-02' of https://gitlab.com/thuth/qemu into staging (2025-07-03 06:01:41 -0400) are available in the Git repository at: https://github.com/alistair23/qemu.git tags/pull-riscv-to-apply-20250704 for you to fetch changes up to dc8bffc4eb0a93d3266cea1b17f8848dea5b915c: target: riscv: Add Svrsw60t59b extension support (2025-07-04 21:09:49 +1000) ---------------------------------------------------------------- Second RISC-V PR for 10.1 * sstc extension fixes * Fix zama16b order in isa_edata_arr * Profile handling fixes * Extend PMP region up to 64 * Remove capital 'Z' CPU properties * Add missing named features * Support atomic instruction fetch (Ziccif) * Add max_satp_mode from host cpu * Extend and configure PMP region count * Fix PPN field of Translation-reponse register * Use qemu_chr_fe_write_all() in DBCN_CONSOLE_WRITE_BYTE * Fix fcvt.s.bf16 NaN box checking * Avoid infinite delay of async xmit function * Device tree reg cleanups * Add Kunminghu CPU and platform * Fix missing exit TB flow for ldff_trans * Fix migration failure when aia is configured as aplic-imsic * Fix MEPC/SEPC bit masking for IALIGN * Add a property to set vill bit on reserved usage of vsetvli instruction * Add Svrsw60t59b extension support ---------------------------------------------------------------- Alexandre Ghiti (1): target: riscv: Add Svrsw60t59b extension support Anton Blanchard (1): target/riscv: Fix fcvt.s.bf16 NaN box checking Charalampos Mitrodimas (2): target/riscv: Fix MEPC/SEPC bit masking for IALIGN tests/tcg/riscv64: Add test for MEPC bit masking Daniel Henrique Barboza (9): target/riscv/cpu.c: fix zama16b order in isa_edata_arr[] target/riscv/tcg: restrict satp_mode changes in cpu_set_profile target/riscv/tcg: decouple profile enablement from user prop target/riscv: add profile->present flag target/riscv: remove capital 'Z' CPU properties target/riscv/cpu.c: add 'sdtrig' in riscv,isa target/riscv/cpu.c: add 'ssstrict' to riscv, isa target/riscv/cpu.c: do better with 'named features' doc target/riscv: use qemu_chr_fe_write_all() in DBCN_CONSOLE_WRITE_BYTE Florian Lugou (1): hw/char: sifive_uart: Avoid infinite delay of async xmit function Huang Borong (2): target/riscv: Add BOSC's Xiangshan Kunminghu CPU hw/riscv: Initial support for BOSC's Xiangshan Kunminghu FPGA prototype Jay Chang (2): target/riscv: Extend PMP region up to 64 target/riscv: Make PMP region count configurable Jim Shu (5): target/riscv: Add the checking into stimecmp write function. hw/intc: riscv_aclint: Fix mtime write for sstc extension target/riscv: Fix VSTIP bit in sstc extension. target/riscv: Enable/Disable S/VS-mode Timer when STCE bit is changed target/riscv: support atomic instruction fetch (Ziccif) Joel Stanley (12): hw/riscv/virt: Fix clint base address type hw/riscv/virt: Use setprop_sized_cells for clint hw/riscv/virt: Use setprop_sized_cells for memory hw/riscv/virt: Use setprop_sized_cells for aplic hw/riscv/virt: Use setprop_sized_cells for aclint hw/riscv/virt: Use setprop_sized_cells for plic hw/riscv/virt: Use setprop_sized_cells for virtio hw/riscv/virt: Use setprop_sized_cells for reset hw/riscv/virt: Use setprop_sized_cells for uart hw/riscv/virt: Use setprop_sized_cells for rtc hw/riscv/virt: Use setprop_sized_cells for iommu hw/riscv/virt: Use setprop_sized_cells for pcie Max Chou (1): target/riscv: rvv: Fix missing exit TB flow for ldff_trans Meng Zhuo (1): target/riscv/kvm: add max_satp_mode from host cpu Nutty Liu (1): hw/riscv/riscv-iommu: Fix PPN field of Translation-reponse register Vasilis Liaskovitis (1): target/riscv: Add a property to set vill bit on reserved usage of vsetvli instruction liu.xuemei1@zte.com.cn (1): migration: Fix migration failure when aia is configured as aplic-imsic MAINTAINERS | 7 + docs/system/riscv/xiangshan-kunminghu.rst | 39 +++++ docs/system/target-riscv.rst | 1 + configs/devices/riscv64-softmmu/default.mak | 1 + hw/riscv/riscv-iommu-bits.h | 1 + include/hw/riscv/xiangshan_kmh.h | 68 +++++++++ target/riscv/cpu-qom.h | 1 + target/riscv/cpu.h | 19 ++- target/riscv/cpu_bits.h | 63 +++++++- target/riscv/helper.h | 2 +- target/riscv/internals.h | 27 ++++ target/riscv/time_helper.h | 1 + target/riscv/cpu_cfg_fields.h.inc | 3 + hw/char/sifive_uart.c | 6 +- hw/intc/riscv_aclint.c | 5 + hw/intc/riscv_aplic.c | 12 +- hw/intc/riscv_imsic.c | 10 +- hw/riscv/riscv-iommu.c | 9 +- hw/riscv/virt.c | 66 ++++----- hw/riscv/xiangshan_kmh.c | 220 ++++++++++++++++++++++++++++ target/riscv/cpu.c | 144 +++++++++++++++--- target/riscv/cpu_helper.c | 3 +- target/riscv/csr.c | 192 +++++++++++++++++++++++- target/riscv/fpu_helper.c | 2 +- target/riscv/kvm/kvm-cpu.c | 18 ++- target/riscv/machine.c | 3 +- target/riscv/op_helper.c | 4 +- target/riscv/pmp.c | 28 ++-- target/riscv/riscv-qmp-cmds.c | 2 +- target/riscv/tcg/tcg-cpu.c | 186 +++++++++++------------ target/riscv/time_helper.c | 65 +++++++- target/riscv/translate.c | 46 ++++-- target/riscv/vector_helper.c | 12 +- target/riscv/insn_trans/trans_rvv.c.inc | 10 +- hw/riscv/Kconfig | 9 ++ hw/riscv/meson.build | 1 + tests/data/acpi/riscv64/virt/RHCT | Bin 400 -> 416 bytes tests/tcg/riscv64/Makefile.softmmu-target | 4 + tests/tcg/riscv64/test-mepc-masking.S | 73 +++++++++ 39 files changed, 1151 insertions(+), 212 deletions(-) create mode 100644 docs/system/riscv/xiangshan-kunminghu.rst create mode 100644 include/hw/riscv/xiangshan_kmh.h create mode 100644 hw/riscv/xiangshan_kmh.c create mode 100644 tests/tcg/riscv64/test-mepc-masking.S
From: Jim Shu <jim.shu@sifive.com> Preparation commit to let aclint timer to use stimecmp write function. Aclint timer doesn't call sstc() predicate so we need to check inside the stimecmp write function. Signed-off-by: Jim Shu <jim.shu@sifive.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250519143518.11086-2-jim.shu@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/time_helper.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/time_helper.c +++ b/target/riscv/time_helper.c @@ -XXX,XX +XXX,XX @@ void riscv_timer_write_timecmp(CPURISCVState *env, QEMUTimer *timer, { uint64_t diff, ns_diff, next; RISCVAclintMTimerState *mtimer = env->rdtime_fn_arg; - uint32_t timebase_freq = mtimer->timebase_freq; - uint64_t rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta; + uint32_t timebase_freq; + uint64_t rtc_r; + + if (!riscv_cpu_cfg(env)->ext_sstc || !env->rdtime_fn || + !env->rdtime_fn_arg || !get_field(env->menvcfg, MENVCFG_STCE)) { + /* S/VS Timer IRQ depends on sstc extension, rdtime_fn(), and STCE. */ + return; + } + + if (timer_irq == MIP_VSTIP && + (!riscv_has_ext(env, RVH) || !get_field(env->henvcfg, HENVCFG_STCE))) { + /* VS Timer IRQ also depends on RVH and henvcfg.STCE. */ + return; + } + + timebase_freq = mtimer->timebase_freq; + rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta; if (timecmp <= rtc_r) { /* -- 2.50.0
From: Jim Shu <jim.shu@sifive.com> When changing the mtime value, the period of [s|vs]timecmp timers should also be updated, similar to the period of mtimecmp timer. The period of the stimecmp timer is the time until the next S-mode timer IRQ. The value is calculated as "stimecmp - time". [1] It is equal to "stimecmp - mtime" since the time CSR is a read-only shadow of the memory-mapped mtime register. Thus, changing mtime value will update the period of stimecmp timer. Similarly, the period of vstimecmp timer is calculated as "vstimecmp - (mtime + htimedelta)" [2], so changing mtime value will update the period of vstimecmp timer. [1] RISC-V Priv spec ch 9.1.1. Supervisor Timer (stimecmp) Register A supervisor timer interrupt becomes pending, as reflected in the STIP bit in the mip and sip registers whenever time contains a value greater than or equal to stimecmp. [2] RISC-V Priv spec ch19.2.1. Virtual Supervisor Timer (vstimecmp) Register A virtual supervisor timer interrupt becomes pending, as reflected in the VSTIP bit in the hip register, whenever (time + htimedelta), truncated to 64 bits, contains a value greater than or equal to vstimecmp Signed-off-by: Jim Shu <jim.shu@sifive.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250519143518.11086-3-jim.shu@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/intc/riscv_aclint.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/riscv_aclint.c +++ b/hw/intc/riscv_aclint.c @@ -XXX,XX +XXX,XX @@ #include "qemu/module.h" #include "hw/sysbus.h" #include "target/riscv/cpu.h" +#include "target/riscv/time_helper.h" #include "hw/qdev-properties.h" #include "hw/intc/riscv_aclint.h" #include "qemu/timer.h" @@ -XXX,XX +XXX,XX @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), mtimer->hartid_base + i, mtimer->timecmp[i]); + riscv_timer_write_timecmp(env, env->stimer, env->stimecmp, 0, MIP_STIP); + riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp, + env->htimedelta, MIP_VSTIP); + } return; } -- 2.50.0
From: Jim Shu <jim.shu@sifive.com> VSTIP is only writable when both [mh]envcfg.STCE is enabled, or it will revert it's defined behavior as if sstc extension is not implemented. Signed-off-by: Jim Shu <jim.shu@sifive.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250519143518.11086-4-jim.shu@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 9 ++++++++- 1 file changed, 8 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 rmw_mip64(CPURISCVState *env, int csrno, if (riscv_cpu_cfg(env)->ext_sstc && (env->priv == PRV_M) && get_field(env->menvcfg, MENVCFG_STCE)) { /* sstc extension forbids STIP & VSTIP to be writeable in mip */ - mask = mask & ~(MIP_STIP | MIP_VSTIP); + + /* STIP is not writable when menvcfg.STCE is enabled. */ + mask = mask & ~MIP_STIP; + + /* VSTIP is not writable when both [mh]envcfg.STCE are enabled. */ + if (get_field(env->henvcfg, HENVCFG_STCE)) { + mask = mask & ~MIP_VSTIP; + } } if (mask) { -- 2.50.0
From: Jim Shu <jim.shu@sifive.com> Updating STCE will enable/disable SSTC in S-mode or/and VS-mode, so we also need to update S/VS-mode Timer and S/VSTIP bits in $mip CSR. Signed-off-by: Jim Shu <jim.shu@sifive.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250519143518.11086-5-jim.shu@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/time_helper.h | 1 + target/riscv/csr.c | 46 ++++++++++++++++++++++++++++++++++++++ target/riscv/time_helper.c | 46 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) diff --git a/target/riscv/time_helper.h b/target/riscv/time_helper.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/time_helper.h +++ b/target/riscv/time_helper.h @@ -XXX,XX +XXX,XX @@ void riscv_timer_write_timecmp(CPURISCVState *env, QEMUTimer *timer, uint64_t timecmp, uint64_t delta, uint32_t timer_irq); +void riscv_timer_stce_changed(CPURISCVState *env, bool is_m_mode, bool enable); void riscv_timer_init(RISCVCPU *cpu); #endif 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; + bool stce_changed = false; if (riscv_cpu_mxl(env) == MXL_RV64) { mask |= (cfg->ext_svpbmt ? MENVCFG_PBMTE : 0) | @@ -XXX,XX +XXX,XX @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, if ((val & MENVCFG_DTE) == 0) { env->mstatus &= ~MSTATUS_SDT; } + + if (cfg->ext_sstc && + ((env->menvcfg & MENVCFG_STCE) != (val & MENVCFG_STCE))) { + stce_changed = true; + } } env->menvcfg = (env->menvcfg & ~mask) | (val & mask); + + if (stce_changed) { + riscv_timer_stce_changed(env, true, !!(val & MENVCFG_STCE)); + } + return write_henvcfg(env, CSR_HENVCFG, env->henvcfg, ra); } @@ -XXX,XX +XXX,XX @@ static RISCVException write_menvcfgh(CPURISCVState *env, int csrno, (cfg->ext_smcdeleg ? MENVCFG_CDE : 0) | (cfg->ext_ssdbltrp ? MENVCFG_DTE : 0); uint64_t valh = (uint64_t)val << 32; + bool stce_changed = false; + + if (cfg->ext_sstc && + ((env->menvcfg & MENVCFG_STCE) != (valh & MENVCFG_STCE))) { + stce_changed = true; + } if ((valh & MENVCFG_DTE) == 0) { env->mstatus &= ~MSTATUS_SDT; } env->menvcfg = (env->menvcfg & ~mask) | (valh & mask); + + if (stce_changed) { + riscv_timer_stce_changed(env, true, !!(valh & MENVCFG_STCE)); + } + return write_henvcfgh(env, CSR_HENVCFGH, env->henvcfg >> 32, ra); } @@ -XXX,XX +XXX,XX @@ static RISCVException read_henvcfg(CPURISCVState *env, int csrno, static RISCVException write_henvcfg(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE; RISCVException ret; + bool stce_changed = false; ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG); if (ret != RISCV_EXCP_NONE) { @@ -XXX,XX +XXX,XX @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, get_field(val, HENVCFG_PMM) != PMM_FIELD_RESERVED) { mask |= HENVCFG_PMM; } + + if (cfg->ext_sstc && + ((env->henvcfg & HENVCFG_STCE) != (val & HENVCFG_STCE))) { + stce_changed = true; + } } env->henvcfg = val & mask; @@ -XXX,XX +XXX,XX @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, env->vsstatus &= ~MSTATUS_SDT; } + if (stce_changed) { + riscv_timer_stce_changed(env, false, !!(val & HENVCFG_STCE)); + } + return RISCV_EXCP_NONE; } @@ -XXX,XX +XXX,XX @@ static RISCVException read_henvcfgh(CPURISCVState *env, int csrno, static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); uint64_t mask = env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE | HENVCFG_DTE); uint64_t valh = (uint64_t)val << 32; RISCVException ret; + bool stce_changed = false; ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG); if (ret != RISCV_EXCP_NONE) { return ret; } + + if (cfg->ext_sstc && + ((env->henvcfg & HENVCFG_STCE) != (valh & HENVCFG_STCE))) { + stce_changed = true; + } + env->henvcfg = (env->henvcfg & 0xFFFFFFFF) | (valh & mask); if ((env->henvcfg & HENVCFG_DTE) == 0) { env->vsstatus &= ~MSTATUS_SDT; } + + if (stce_changed) { + riscv_timer_stce_changed(env, false, !!(val & HENVCFG_STCE)); + } + return RISCV_EXCP_NONE; } diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/time_helper.c +++ b/target/riscv/time_helper.c @@ -XXX,XX +XXX,XX @@ void riscv_timer_write_timecmp(CPURISCVState *env, QEMUTimer *timer, timer_mod(timer, next); } +/* + * When disabling xenvcfg.STCE, the S/VS Timer may be disabled at the same time. + * It is safe to call this function regardless of whether the timer has been + * deleted or not. timer_del() will do nothing if the timer has already + * been deleted. + */ +static void riscv_timer_disable_timecmp(CPURISCVState *env, QEMUTimer *timer, + uint32_t timer_irq) +{ + /* Disable S-mode Timer IRQ and HW-based STIP */ + if ((timer_irq == MIP_STIP) && !get_field(env->menvcfg, MENVCFG_STCE)) { + riscv_cpu_update_mip(env, timer_irq, BOOL_TO_MASK(0)); + timer_del(timer); + return; + } + + /* Disable VS-mode Timer IRQ and HW-based VSTIP */ + if ((timer_irq == MIP_VSTIP) && + (!get_field(env->menvcfg, MENVCFG_STCE) || + !get_field(env->henvcfg, HENVCFG_STCE))) { + env->vstime_irq = 0; + riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(0)); + timer_del(timer); + return; + } +} + +/* Enable or disable S/VS-mode Timer when xenvcfg.STCE is changed */ +void riscv_timer_stce_changed(CPURISCVState *env, bool is_m_mode, bool enable) +{ + if (enable) { + riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp, + env->htimedelta, MIP_VSTIP); + } else { + riscv_timer_disable_timecmp(env, env->vstimer, MIP_VSTIP); + } + + if (is_m_mode) { + if (enable) { + riscv_timer_write_timecmp(env, env->stimer, env->stimecmp, 0, MIP_STIP); + } else { + riscv_timer_disable_timecmp(env, env->stimer, MIP_STIP); + } + } +} + void riscv_timer_init(RISCVCPU *cpu) { CPURISCVState *env; -- 2.50.0
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Put it after zalrsc and before zawrs. Cc: qemu-trivial@nongnu.org Fixes: a60ce58fd9 ("target/riscv: Support Zama16b extension") Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250522113344.823294-1-dbarboza@ventanamicro.com> 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 @@ const RISCVIsaExtData isa_edata_arr[] = { 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), - ISA_EXT_DATA_ENTRY(zama16b, PRIV_VERSION_1_13_0, ext_zama16b), ISA_EXT_DATA_ENTRY(zalrsc, PRIV_VERSION_1_12_0, ext_zalrsc), + ISA_EXT_DATA_ENTRY(zama16b, PRIV_VERSION_1_13_0, ext_zama16b), ISA_EXT_DATA_ENTRY(zawrs, PRIV_VERSION_1_12_0, ext_zawrs), ISA_EXT_DATA_ENTRY(zfa, PRIV_VERSION_1_12_0, ext_zfa), ISA_EXT_DATA_ENTRY(zfbfmin, PRIV_VERSION_1_12_0, ext_zfbfmin), -- 2.50.0
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> We're changing 'mmu' to true regardless of whether the profile is being enabled or not, and at the same time we're changing satp_mode to profile->enabled. This will promote a situation where we'll set mmu=on without a virtual memory mode, which is a mistake. Only touch 'mmu' and satp_mode if the profile is being enabled. Suggested-by: Andrew Jones <ajones@ventanamicro.com> Fixes: 55398025e7 ("target/riscv: add satp_mode profile support") Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Andrew Jones <ajones@ventanamicro.com> Reviewed-by: Björn Töpel <bjorn@rivosinc.com> Tested-by: Björn Töpel <bjorn@rivosinc.com> Message-ID: <20250528184407.1451983-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/tcg/tcg-cpu.c | 14 +++++++------- 1 file changed, 7 insertions(+), 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 void cpu_set_profile(Object *obj, Visitor *v, const char *name, if (profile->enabled) { cpu->env.priv_ver = profile->priv_spec; - } #ifndef CONFIG_USER_ONLY - if (profile->satp_mode != RISCV_PROFILE_ATTR_UNUSED) { - object_property_set_bool(obj, "mmu", true, NULL); - const char *satp_prop = satp_mode_str(profile->satp_mode, - riscv_cpu_is_32bit(cpu)); - object_property_set_bool(obj, satp_prop, profile->enabled, NULL); - } + if (profile->satp_mode != RISCV_PROFILE_ATTR_UNUSED) { + object_property_set_bool(obj, "mmu", true, NULL); + const char *satp_prop = satp_mode_str(profile->satp_mode, + riscv_cpu_is_32bit(cpu)); + object_property_set_bool(obj, satp_prop, true, NULL); + } #endif + } for (i = 0; misa_bits[i] != 0; i++) { uint32_t bit = misa_bits[i]; -- 2.50.0
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> We have code in riscv_cpu_add_profiles() to enable a profile right away in case a CPU chose the profile during its cpu_init(). But we're using the user callback option to do so, setting profile->user_set. Create a new helper that does all the grunt work to enable/disable a given profile. Use this new helper in the cases where we want a CPU to be compatible to a certain profile, leaving the user callback to be used exclusively by users. Fixes: fba92a92e3 ("target/riscv: add 'rva22u64' CPU") Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Andrew Jones <ajones@ventanamicro.com> Reviewed-by: Björn Töpel <bjorn@rivosinc.com> Tested-by: Björn Töpel <bjorn@rivosinc.com> Message-ID: <20250528184407.1451983-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/tcg/tcg-cpu.c | 127 +++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 60 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 bool riscv_cpu_is_generic(Object *cpu_obj) return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL; } +static void riscv_cpu_set_profile(RISCVCPU *cpu, + RISCVCPUProfile *profile, + bool enabled) +{ + int i, ext_offset; + + if (profile->u_parent != NULL) { + riscv_cpu_set_profile(cpu, profile->u_parent, enabled); + } + + if (profile->s_parent != NULL) { + riscv_cpu_set_profile(cpu, profile->s_parent, enabled); + } + + profile->enabled = enabled; + + if (profile->enabled) { + cpu->env.priv_ver = profile->priv_spec; + +#ifndef CONFIG_USER_ONLY + if (profile->satp_mode != RISCV_PROFILE_ATTR_UNUSED) { + object_property_set_bool(OBJECT(cpu), "mmu", true, NULL); + const char *satp_prop = satp_mode_str(profile->satp_mode, + riscv_cpu_is_32bit(cpu)); + object_property_set_bool(OBJECT(cpu), satp_prop, true, NULL); + } +#endif + } + + for (i = 0; misa_bits[i] != 0; i++) { + uint32_t bit = misa_bits[i]; + + if (!(profile->misa_ext & bit)) { + continue; + } + + if (bit == RVI && !profile->enabled) { + /* + * Disabling profiles will not disable the base + * ISA RV64I. + */ + continue; + } + + cpu_misa_ext_add_user_opt(bit, profile->enabled); + riscv_cpu_write_misa_bit(cpu, bit, profile->enabled); + } + + for (i = 0; profile->ext_offsets[i] != RISCV_PROFILE_EXT_LIST_END; i++) { + 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); + } + + cpu_cfg_ext_add_user_opt(ext_offset, profile->enabled); + isa_ext_update_enabled(cpu, ext_offset, profile->enabled); + } +} + /* * We'll get here via the following path: * @@ -XXX,XX +XXX,XX @@ static void cpu_set_profile(Object *obj, Visitor *v, const char *name, RISCVCPUProfile *profile = opaque; RISCVCPU *cpu = RISCV_CPU(obj); bool value; - int i, ext_offset; if (riscv_cpu_is_vendor(obj)) { error_setg(errp, "Profile %s is not available for vendor CPUs", @@ -XXX,XX +XXX,XX @@ static void cpu_set_profile(Object *obj, Visitor *v, const char *name, } profile->user_set = true; - profile->enabled = value; - - if (profile->u_parent != NULL) { - object_property_set_bool(obj, profile->u_parent->name, - profile->enabled, NULL); - } - - if (profile->s_parent != NULL) { - object_property_set_bool(obj, profile->s_parent->name, - profile->enabled, NULL); - } - - if (profile->enabled) { - cpu->env.priv_ver = profile->priv_spec; - -#ifndef CONFIG_USER_ONLY - if (profile->satp_mode != RISCV_PROFILE_ATTR_UNUSED) { - object_property_set_bool(obj, "mmu", true, NULL); - const char *satp_prop = satp_mode_str(profile->satp_mode, - riscv_cpu_is_32bit(cpu)); - object_property_set_bool(obj, satp_prop, true, NULL); - } -#endif - } - - for (i = 0; misa_bits[i] != 0; i++) { - uint32_t bit = misa_bits[i]; - - if (!(profile->misa_ext & bit)) { - continue; - } - if (bit == RVI && !profile->enabled) { - /* - * Disabling profiles will not disable the base - * ISA RV64I. - */ - continue; - } - - cpu_misa_ext_add_user_opt(bit, profile->enabled); - riscv_cpu_write_misa_bit(cpu, bit, profile->enabled); - } - - for (i = 0; profile->ext_offsets[i] != RISCV_PROFILE_EXT_LIST_END; i++) { - 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); - } - - cpu_cfg_ext_add_user_opt(ext_offset, profile->enabled); - isa_ext_update_enabled(cpu, ext_offset, profile->enabled); - } + riscv_cpu_set_profile(cpu, profile, value); } static void cpu_get_profile(Object *obj, Visitor *v, const char *name, @@ -XXX,XX +XXX,XX @@ static void cpu_get_profile(Object *obj, Visitor *v, const char *name, static void riscv_cpu_add_profiles(Object *cpu_obj) { for (int i = 0; riscv_profiles[i] != NULL; i++) { - const RISCVCPUProfile *profile = riscv_profiles[i]; + RISCVCPUProfile *profile = riscv_profiles[i]; object_property_add(cpu_obj, profile->name, "bool", cpu_get_profile, cpu_set_profile, @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_add_profiles(Object *cpu_obj) * case. */ if (profile->enabled) { - object_property_set_bool(cpu_obj, profile->name, true, NULL); + riscv_cpu_set_profile(RISCV_CPU(cpu_obj), profile, true); } } } -- 2.50.0
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Björn reported in [1] a case where a rv64 CPU is going through the profile code path to enable satp mode. In this case,the amount of extensions on top of the rv64 CPU made it compliant with the RVA22S64 profile during the validation of CPU 0. When the subsequent CPUs were initialized the static profile object has the 'enable' flag set, enabling the profile code path for those CPUs. This happens because we are initializing and realizing each CPU before going to the next, i.e. init and realize CPU0, then init and realize CPU1 and so on. If we change any persistent state during the validation of CPU N it will interfere with the init/realization of CPU N+1. We're using the 'enabled' profile flag to do two distinct things: inform cpu_init() that we want profile extensions to be enabled, and telling QMP that a profile is currently enabled in the CPU. We want to be flexible enough to recognize profile support for all CPUs that has the extension prerequisites, but we do not want to force the profile code path if a profile wasn't set too. Add a new 'present' flag for profiles that will coexist with the 'enabled' flag. Enabling a profile means "we want to switch on all its mandatory extensions". A profile is 'present' if we asserted during validation that the CPU has the needed prerequisites. This means that the case reported by Björn now results in RVA22S64.enabled=false and RVA22S64.present=true. QMP will recognize it as a RVA22 compliant CPU and we won't force the CPU into the profile path. [1] https://lore.kernel.org/qemu-riscv/87y0usiz22.fsf@all.your.base.are.belong.to.us/ Reported-by: Björn Töpel <bjorn@kernel.org> Fixes: 2af005d610 ("target/riscv/tcg: validate profiles during finalize") Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Andrew Jones <ajones@ventanamicro.com> Reviewed-by: Björn Töpel <bjorn@rivosinc.com> Tested-by: Björn Töpel <bjorn@rivosinc.com> Message-ID: <20250528184407.1451983-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 15 +++++++++++++++ target/riscv/riscv-qmp-cmds.c | 2 +- target/riscv/tcg/tcg-cpu.c | 11 +++-------- 3 files changed, 19 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 @@ typedef struct riscv_cpu_profile { struct riscv_cpu_profile *s_parent; const char *name; uint32_t misa_ext; + /* + * The profile is enabled/disabled via command line or + * via cpu_init(). Enabling a profile will add all its + * mandatory extensions in the CPU during init(). + */ bool enabled; + /* + * The profile is present in the CPU, i.e. the current set of + * CPU extensions complies with it. A profile can be enabled + * and not present (e.g. the user disabled a mandatory extension) + * and the other way around (e.g. all mandatory extensions are + * present in a non-profile CPU). + * + * QMP uses this flag. + */ + bool present; bool user_set; int priv_spec; int satp_mode; 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_profiles_qdict(Object *obj, QDict *qdict_out) for (int i = 0; riscv_profiles[i] != NULL; i++) { profile = riscv_profiles[i]; - value = QOBJECT(qbool_from_bool(profile->enabled)); + value = QOBJECT(qbool_from_bool(profile->present)); qdict_put_obj(qdict_out, profile->name, value); } 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_check_parent_profile(RISCVCPU *cpu, RISCVCPUProfile *profile, RISCVCPUProfile *parent) { - const char *parent_name; - bool parent_enabled; - - if (!profile->enabled || !parent) { + if (!profile->present || !parent) { return; } - parent_name = parent->name; - parent_enabled = object_property_get_bool(OBJECT(cpu), parent_name, NULL); - profile->enabled = parent_enabled; + profile->present = parent->present; } static void riscv_cpu_validate_profile(RISCVCPU *cpu, @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_validate_profile(RISCVCPU *cpu, } } - profile->enabled = profile_impl; + profile->present = profile_impl; riscv_cpu_check_parent_profile(cpu, profile, profile->u_parent); riscv_cpu_check_parent_profile(cpu, profile, profile->s_parent); -- 2.50.0
From: Jay Chang <jay.chang@sifive.com> According to the RISC-V Privileged Specification (version >1.12), RV32 supports 16 CSRs (pmpcfg0–pmpcfg15) to configure 64 PMP regions (pmpaddr0–pmpaddr63). Signed-off-by: Jay Chang <jay.chang@sifive.com> Reviewed-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20250522081236.4050-2-jay.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_bits.h | 60 +++++++++++++++++++ target/riscv/csr.c | 124 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 182 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -XXX,XX +XXX,XX @@ #define CSR_PMPCFG1 0x3a1 #define CSR_PMPCFG2 0x3a2 #define CSR_PMPCFG3 0x3a3 +#define CSR_PMPCFG4 0x3a4 +#define CSR_PMPCFG5 0x3a5 +#define CSR_PMPCFG6 0x3a6 +#define CSR_PMPCFG7 0x3a7 +#define CSR_PMPCFG8 0x3a8 +#define CSR_PMPCFG9 0x3a9 +#define CSR_PMPCFG10 0x3aa +#define CSR_PMPCFG11 0x3ab +#define CSR_PMPCFG12 0x3ac +#define CSR_PMPCFG13 0x3ad +#define CSR_PMPCFG14 0x3ae +#define CSR_PMPCFG15 0x3af #define CSR_PMPADDR0 0x3b0 #define CSR_PMPADDR1 0x3b1 #define CSR_PMPADDR2 0x3b2 @@ -XXX,XX +XXX,XX @@ #define CSR_PMPADDR13 0x3bd #define CSR_PMPADDR14 0x3be #define CSR_PMPADDR15 0x3bf +#define CSR_PMPADDR16 0x3c0 +#define CSR_PMPADDR17 0x3c1 +#define CSR_PMPADDR18 0x3c2 +#define CSR_PMPADDR19 0x3c3 +#define CSR_PMPADDR20 0x3c4 +#define CSR_PMPADDR21 0x3c5 +#define CSR_PMPADDR22 0x3c6 +#define CSR_PMPADDR23 0x3c7 +#define CSR_PMPADDR24 0x3c8 +#define CSR_PMPADDR25 0x3c9 +#define CSR_PMPADDR26 0x3ca +#define CSR_PMPADDR27 0x3cb +#define CSR_PMPADDR28 0x3cc +#define CSR_PMPADDR29 0x3cd +#define CSR_PMPADDR30 0x3ce +#define CSR_PMPADDR31 0x3cf +#define CSR_PMPADDR32 0x3d0 +#define CSR_PMPADDR33 0x3d1 +#define CSR_PMPADDR34 0x3d2 +#define CSR_PMPADDR35 0x3d3 +#define CSR_PMPADDR36 0x3d4 +#define CSR_PMPADDR37 0x3d5 +#define CSR_PMPADDR38 0x3d6 +#define CSR_PMPADDR39 0x3d7 +#define CSR_PMPADDR40 0x3d8 +#define CSR_PMPADDR41 0x3d9 +#define CSR_PMPADDR42 0x3da +#define CSR_PMPADDR43 0x3db +#define CSR_PMPADDR44 0x3dc +#define CSR_PMPADDR45 0x3dd +#define CSR_PMPADDR46 0x3de +#define CSR_PMPADDR47 0x3df +#define CSR_PMPADDR48 0x3e0 +#define CSR_PMPADDR49 0x3e1 +#define CSR_PMPADDR50 0x3e2 +#define CSR_PMPADDR51 0x3e3 +#define CSR_PMPADDR52 0x3e4 +#define CSR_PMPADDR53 0x3e5 +#define CSR_PMPADDR54 0x3e6 +#define CSR_PMPADDR55 0x3e7 +#define CSR_PMPADDR56 0x3e8 +#define CSR_PMPADDR57 0x3e9 +#define CSR_PMPADDR58 0x3ea +#define CSR_PMPADDR59 0x3eb +#define CSR_PMPADDR60 0x3ec +#define CSR_PMPADDR61 0x3ed +#define CSR_PMPADDR62 0x3ee +#define CSR_PMPADDR63 0x3ef /* RNMI */ #define CSR_MNSCRATCH 0x740 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 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_PMPCFG1] = { "pmpcfg1", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG2] = { "pmpcfg2", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG3] = { "pmpcfg3", pmp, read_pmpcfg, write_pmpcfg }, + [CSR_PMPCFG4] = { "pmpcfg4", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG5] = { "pmpcfg5", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG6] = { "pmpcfg6", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG7] = { "pmpcfg7", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG8] = { "pmpcfg8", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG9] = { "pmpcfg9", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG10] = { "pmpcfg10", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG11] = { "pmpcfg11", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG12] = { "pmpcfg12", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG13] = { "pmpcfg13", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG14] = { "pmpcfg14", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG15] = { "pmpcfg15", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_PMPADDR0] = { "pmpaddr0", pmp, read_pmpaddr, write_pmpaddr }, [CSR_PMPADDR1] = { "pmpaddr1", pmp, read_pmpaddr, write_pmpaddr }, [CSR_PMPADDR2] = { "pmpaddr2", pmp, read_pmpaddr, write_pmpaddr }, @@ -XXX,XX +XXX,XX @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_PMPADDR11] = { "pmpaddr11", pmp, read_pmpaddr, write_pmpaddr }, [CSR_PMPADDR12] = { "pmpaddr12", pmp, read_pmpaddr, write_pmpaddr }, [CSR_PMPADDR13] = { "pmpaddr13", pmp, read_pmpaddr, write_pmpaddr }, - [CSR_PMPADDR14] = { "pmpaddr14", pmp, read_pmpaddr, write_pmpaddr }, - [CSR_PMPADDR15] = { "pmpaddr15", pmp, read_pmpaddr, write_pmpaddr }, + [CSR_PMPADDR14] = { "pmpaddr14", pmp, read_pmpaddr, write_pmpaddr }, + [CSR_PMPADDR15] = { "pmpaddr15", pmp, read_pmpaddr, write_pmpaddr }, + [CSR_PMPADDR16] = { "pmpaddr16", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR17] = { "pmpaddr17", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR18] = { "pmpaddr18", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR19] = { "pmpaddr19", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR20] = { "pmpaddr20", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR21] = { "pmpaddr21", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR22] = { "pmpaddr22", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR23] = { "pmpaddr23", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR24] = { "pmpaddr24", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR25] = { "pmpaddr25", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR26] = { "pmpaddr26", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR27] = { "pmpaddr27", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR28] = { "pmpaddr28", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR29] = { "pmpaddr29", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR30] = { "pmpaddr30", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR31] = { "pmpaddr31", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR32] = { "pmpaddr32", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR33] = { "pmpaddr33", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR34] = { "pmpaddr34", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR35] = { "pmpaddr35", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR36] = { "pmpaddr36", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR37] = { "pmpaddr37", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR38] = { "pmpaddr38", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR39] = { "pmpaddr39", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR40] = { "pmpaddr40", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR41] = { "pmpaddr41", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR42] = { "pmpaddr42", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR43] = { "pmpaddr43", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR44] = { "pmpaddr44", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR45] = { "pmpaddr45", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR46] = { "pmpaddr46", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR47] = { "pmpaddr47", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR48] = { "pmpaddr48", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR49] = { "pmpaddr49", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR50] = { "pmpaddr50", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR51] = { "pmpaddr51", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR52] = { "pmpaddr52", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR53] = { "pmpaddr53", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR54] = { "pmpaddr54", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR55] = { "pmpaddr55", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR56] = { "pmpaddr56", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR57] = { "pmpaddr57", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR58] = { "pmpaddr58", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR59] = { "pmpaddr59", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR60] = { "pmpaddr60", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR61] = { "pmpaddr61", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR62] = { "pmpaddr62", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR63] = { "pmpaddr63", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Debug CSRs */ [CSR_TSELECT] = { "tselect", debug, read_tselect, write_tselect }, -- 2.50.0
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> These properties were deprecated in QEMU 8.2, commit 8043effd9b. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250530134608.1806922-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 1 - target/riscv/cpu.c | 17 ----------------- target/riscv/tcg/tcg-cpu.c | 31 +------------------------------ 3 files changed, 1 insertion(+), 48 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_extensions[]; extern const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[]; extern const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[]; extern const RISCVCPUMultiExtConfig riscv_cpu_named_features[]; -extern const RISCVCPUMultiExtConfig riscv_cpu_deprecated_exts[]; 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_named_features[] = { { }, }; -/* Deprecated entries marked for future removal */ -const RISCVCPUMultiExtConfig riscv_cpu_deprecated_exts[] = { - MULTI_EXT_CFG_BOOL("Zifencei", ext_zifencei, true), - 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("Zawrs", ext_zawrs, true), - MULTI_EXT_CFG_BOOL("Zfa", ext_zfa, true), - 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("Zve64f", ext_zve64f, false), - MULTI_EXT_CFG_BOOL("Zve64d", ext_zve64d, false), - - { }, -}; - 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_add_profiles(Object *cpu_obj) } } -static bool cpu_ext_is_deprecated(const char *ext_name) -{ - return isupper(ext_name[0]); -} - -/* - * String will be allocated in the heap. Caller is responsible - * for freeing it. - */ -static char *cpu_ext_to_lower(const char *ext_name) -{ - char *ret = g_malloc0(strlen(ext_name) + 1); - - strcpy(ret, ext_name); - ret[0] = tolower(ret[0]); - - return ret; -} - static void cpu_set_multi_ext_cfg(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -XXX,XX +XXX,XX @@ static void cpu_set_multi_ext_cfg(Object *obj, Visitor *v, const char *name, return; } - if (cpu_ext_is_deprecated(multi_ext_cfg->name)) { - g_autofree char *lower = cpu_ext_to_lower(multi_ext_cfg->name); - - warn_report("CPU property '%s' is deprecated. Please use '%s' instead", - multi_ext_cfg->name, lower); - } - cpu_cfg_ext_add_user_opt(multi_ext_cfg->offset, value); prev_val = isa_ext_is_enabled(cpu, multi_ext_cfg->offset); @@ -XXX,XX +XXX,XX @@ static void cpu_add_multi_ext_prop(Object *cpu_obj, const RISCVCPUMultiExtConfig *multi_cfg) { bool generic_cpu = riscv_cpu_is_generic(cpu_obj); - bool deprecated_ext = cpu_ext_is_deprecated(multi_cfg->name); object_property_add(cpu_obj, multi_cfg->name, "bool", cpu_get_multi_ext_cfg, cpu_set_multi_ext_cfg, NULL, (void *)multi_cfg); - if (!generic_cpu || deprecated_ext) { + if (!generic_cpu) { return; } @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_add_user_properties(Object *obj) riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_vendor_exts); riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_experimental_exts); - riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_deprecated_exts); - riscv_cpu_add_profiles(obj); } -- 2.50.0
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> We have support for sdtrig for awhile but we are not advertising it. It is enabled by default via the 'debug' flag. Use the same flag to also advertise sdtrig. Add an exception in disable_priv_spec_isa_exts() to avoid spamming warnings for 'sdtrig' for vendor CPUs like sifive_u. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250604174329.1147549-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 1 + target/riscv/tcg/tcg-cpu.c | 9 +++++++++ tests/data/acpi/riscv64/virt/RHCT | Bin 400 -> 406 bytes 3 files changed, 10 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 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zvkt, PRIV_VERSION_1_12_0, ext_zvkt), 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), 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) continue; } + /* + * cpu.debug = true is marked as 'sdtrig', priv spec 1.12. + * Skip this warning since existing CPUs with older priv + * spec and debug = true will be impacted. + */ + if (!strcmp(edata->name, "sdtrig")) { + continue; + } + isa_ext_update_enabled(cpu, edata->ext_enable_offset, false); /* diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT index XXXXXXX..XXXXXXX 100644 Binary files a/tests/data/acpi/riscv64/virt/RHCT and b/tests/data/acpi/riscv64/virt/RHCT differ -- 2.50.0
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> 'ssstrict' is a RVA23 profile-defined extension defined as follows: "No non-conforming extensions are present. Attempts to execute unimplemented opcodes or access unimplemented CSRs in the standard or reserved encoding spaces raises an illegal instruction exception that results in a contained trap to the supervisor-mode trap handler." In short, we need to throw an exception when accessing unimplemented CSRs or opcodes. We do that, so let's advertise it. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Andrew Jones <ajones@ventanamicro.com> Message-ID: <20250529202315.1684198-3-dbarboza@ventanamicro.com> Message-ID: <20250604174329.1147549-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 1 + tests/data/acpi/riscv64/virt/RHCT | Bin 406 -> 416 bytes 2 files 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(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_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), diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT index XXXXXXX..XXXXXXX 100644 Binary files a/tests/data/acpi/riscv64/virt/RHCT and b/tests/data/acpi/riscv64/virt/RHCT differ -- 2.50.0
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Most of the named features are added directly in isa_edata_arr[], some of them are also added in riscv_cpu_named_features(). There is a reason for that, and the existing docs can do better explaining it. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20250529202315.1684198-4-dbarboza@ventanamicro.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250604174329.1147549-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 14 ++++++++++++-- 1 file changed, 12 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 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. They have riscv,isa strings - * and priv_ver like regular extensions. + * 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), { }, -- 2.50.0
From: Jim Shu <jim.shu@sifive.com> Support 4-byte atomic instruction fetch when instruction is natural aligned. Current implementation is not atomic because it loads instruction twice for first and last 2 bytes. We load 4 bytes at once to keep the atomicity. This instruction preload method only applys when instruction is 4-byte aligned. If instruction is unaligned, it could be across pages so that preload will trigger additional page fault. We encounter this issue when doing pressure test of enabling & disabling Linux kernel ftrace. Ftrace with kernel preemption requires concurrent modification and execution of instruction, so non-atomic instruction fetch will cause the race condition. We may fetch the wrong instruction which is the mixing of 2 instructions. Also, RISC-V Profile wants to provide this feature by HW. RVA20U64 Ziccif protects the atomicity of instruction fetch when it is natural aligned. This commit depends on the atomic read support of translator_ld in the commit 6a9dfe1984b0c593fb0ddb52d4e70832e6201dd6. Signed-off-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250508094838.19394-1-jim.shu@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/translate.c | 46 +++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -XXX,XX +XXX,XX @@ const RISCVDecoder decoder_table[] = { const size_t decoder_table_size = ARRAY_SIZE(decoder_table); -static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) +static void decode_opc(CPURISCVState *env, DisasContext *ctx) { + uint32_t opcode; + bool pc_is_4byte_align = ((ctx->base.pc_next % 4) == 0); + ctx->virt_inst_excp = false; - ctx->cur_insn_len = insn_len(opcode); + if (pc_is_4byte_align) { + /* + * Load 4 bytes at once to make instruction fetch atomically. + * + * Note: When pc is 4-byte aligned, 4-byte instruction wouldn't be + * across pages. We could preload 4 bytes instruction no matter + * real one is 2 or 4 bytes. Instruction preload wouldn't trigger + * additional page fault. + */ + opcode = translator_ldl(env, &ctx->base, ctx->base.pc_next); + } else { + /* + * For unaligned pc, instruction preload may trigger additional + * page fault so we only load 2 bytes here. + */ + opcode = (uint32_t) translator_lduw(env, &ctx->base, ctx->base.pc_next); + } + ctx->ol = ctx->xl; + + ctx->cur_insn_len = insn_len((uint16_t)opcode); /* Check for compressed insn */ if (ctx->cur_insn_len == 2) { - ctx->opcode = opcode; + ctx->opcode = (uint16_t)opcode; /* * The Zca extension is added as way to refer to instructions in the C * extension that do not include the floating-point loads and stores @@ -XXX,XX +XXX,XX @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) return; } } else { - uint32_t opcode32 = opcode; - opcode32 = deposit32(opcode32, 16, 16, - translator_lduw(env, &ctx->base, - ctx->base.pc_next + 2)); - ctx->opcode = opcode32; + if (!pc_is_4byte_align) { + /* Load last 2 bytes of instruction here */ + opcode = deposit32(opcode, 16, 16, + translator_lduw(env, &ctx->base, + ctx->base.pc_next + 2)); + } + ctx->opcode = opcode; for (guint i = 0; i < ctx->decoders->len; ++i) { riscv_cpu_decode_fn func = g_ptr_array_index(ctx->decoders, i); - if (func(ctx, opcode32)) { + if (func(ctx, opcode)) { return; } } @@ -XXX,XX +XXX,XX @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPURISCVState *env = cpu_env(cpu); - uint16_t opcode16 = translator_lduw(env, &ctx->base, ctx->base.pc_next); - ctx->ol = ctx->xl; - decode_opc(env, ctx, opcode16); + decode_opc(env, ctx); ctx->base.pc_next += ctx->cur_insn_len; /* -- 2.50.0
From: Meng Zhuo <mengzhuo@iscas.ac.cn> This patch adds max_satp_mode from host kvm cpu setting. Tested on: Milkv Megrez (Eswin 7700x) Reviewed-by: Andrew Jones <ajones@ventanamicro.com> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2931 Signed-off-by: Meng Zhuo <mengzhuo@iscas.ac.cn> Message-ID: <20250606034250.181707-1-mengzhuo@iscas.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/kvm/kvm-cpu.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) 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_riscv_destroy_scratch_vcpu(KVMScratchCPU *scratch) close(scratch->kvmfd); } +static void kvm_riscv_init_max_satp_mode(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) +{ + struct kvm_one_reg reg; + int ret; + + reg.id = RISCV_CONFIG_REG(satp_mode); + reg.addr = (uint64_t)&cpu->cfg.max_satp_mode; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to retrieve satp mode from host, error %d", ret); + } +} + static void kvm_riscv_init_machine_ids(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) { struct kvm_one_reg reg; @@ -XXX,XX +XXX,XX @@ static void riscv_init_kvm_registers(Object *cpu_obj) kvm_riscv_init_machine_ids(cpu, &kvmcpu); kvm_riscv_init_misa_ext_mask(cpu, &kvmcpu); kvm_riscv_init_cfg(cpu, &kvmcpu); + kvm_riscv_init_max_satp_mode(cpu, &kvmcpu); kvm_riscv_destroy_scratch_vcpu(&kvmcpu); } @@ -XXX,XX +XXX,XX @@ static bool kvm_cpu_realize(CPUState *cs, Error **errp) } } - return true; + return true; } void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp) -- 2.50.0
From: Jay Chang <jay.chang@sifive.com> Previously, the number of PMP regions was hardcoded to 16 in QEMU. This patch replaces the fixed value with a new `pmp_regions` field, allowing platforms to configure the number of PMP regions. If no specific value is provided, the default number of PMP regions remains 16 to preserve the existing behavior. A new CPU parameter num-pmp-regions has been introduced to the QEMU command line. For example: -cpu rv64, g=true, c=true, pmp=true, num-pmp-regions=8 Signed-off-by: Jay Chang <jay.chang@sifive.com> Reviewed-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250606072525.17313-3-jay.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 3 +- target/riscv/cpu_cfg_fields.h.inc | 1 + target/riscv/cpu.c | 48 +++++++++++++++++++++++++++++-- target/riscv/csr.c | 5 +++- target/riscv/machine.c | 3 +- target/riscv/pmp.c | 28 ++++++++++++------ 6 files changed, 74 insertions(+), 14 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -XXX,XX +XXX,XX @@ extern RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[]; #define MMU_USER_IDX 3 -#define MAX_RISCV_PMPS (16) +#define MAX_RISCV_PMPS (64) +#define OLD_MAX_RISCV_PMPS (16) #if !defined(CONFIG_USER_ONLY) #include "pmp.h" diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -XXX,XX +XXX,XX @@ TYPED_FIELD(uint16_t, elen, 0) TYPED_FIELD(uint16_t, cbom_blocksize, 0) TYPED_FIELD(uint16_t, cbop_blocksize, 0) TYPED_FIELD(uint16_t, cboz_blocksize, 0) +TYPED_FIELD(uint8_t, pmp_regions, 0) TYPED_FIELD(int8_t, max_satp_mode, -1) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_init(Object *obj) cpu->cfg.cbom_blocksize = 64; cpu->cfg.cbop_blocksize = 64; cpu->cfg.cboz_blocksize = 64; + cpu->cfg.pmp_regions = 16; cpu->env.vext_ver = VEXT_VERSION_1_00_0; cpu->cfg.max_satp_mode = -1; @@ -XXX,XX +XXX,XX @@ static const PropertyInfo prop_pmp = { .set = prop_pmp_set, }; +static void prop_num_pmp_regions_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + uint8_t value; + + visit_type_uint8(v, name, &value, errp); + + if (cpu->cfg.pmp_regions != value && riscv_cpu_is_vendor(obj)) { + cpu_set_prop_err(cpu, name, errp); + return; + } + + if (cpu->env.priv_ver < PRIV_VERSION_1_12_0 && value > OLD_MAX_RISCV_PMPS) { + error_setg(errp, "Number of PMP regions exceeds maximum available"); + return; + } else if (value > MAX_RISCV_PMPS) { + error_setg(errp, "Number of PMP regions exceeds maximum available"); + return; + } + + cpu_option_add_user_setting(name, value); + cpu->cfg.pmp_regions = value; +} + +static void prop_num_pmp_regions_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint8_t value = RISCV_CPU(obj)->cfg.pmp_regions; + + visit_type_uint8(v, name, &value, errp); +} + +static const PropertyInfo prop_num_pmp_regions = { + .type = "uint8", + .description = "num-pmp-regions", + .get = prop_num_pmp_regions_get, + .set = prop_num_pmp_regions_set, +}; + static int priv_spec_from_str(const char *priv_spec_str) { int priv_version = -1; @@ -XXX,XX +XXX,XX @@ static const Property riscv_cpu_properties[] = { {.name = "mmu", .info = &prop_mmu}, {.name = "pmp", .info = &prop_pmp}, + {.name = "num-pmp-regions", .info = &prop_num_pmp_regions}, {.name = "priv_spec", .info = &prop_priv_spec}, {.name = "vext_spec", .info = &prop_vext_spec}, @@ -XXX,XX +XXX,XX @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.max_satp_mode = VM_1_10_MBARE, .cfg.ext_zifencei = true, .cfg.ext_zicsr = true, - .cfg.pmp = true + .cfg.pmp = true, + .cfg.pmp_regions = 8 ), DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_U, TYPE_RISCV_VENDOR_CPU, @@ -XXX,XX +XXX,XX @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.ext_zifencei = true, .cfg.ext_zicsr = true, .cfg.mmu = true, - .cfg.pmp = true + .cfg.pmp = true, + .cfg.pmp_regions = 8 ), #if defined(TARGET_RISCV32) || \ 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 dbltrp_hmode(CPURISCVState *env, int csrno) static RISCVException pmp(CPURISCVState *env, int csrno) { if (riscv_cpu_cfg(env)->pmp) { - if (csrno <= CSR_PMPCFG3) { + int max_pmpcfg = (env->priv_ver >= PRIV_VERSION_1_12_0) ? ++ CSR_PMPCFG15 : CSR_PMPCFG3; + + if (csrno <= max_pmpcfg) { uint32_t reg_index = csrno - CSR_PMPCFG0; /* TODO: RV128 restriction check */ diff --git a/target/riscv/machine.c b/target/riscv/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -XXX,XX +XXX,XX @@ static int pmp_post_load(void *opaque, int version_id) RISCVCPU *cpu = opaque; CPURISCVState *env = &cpu->env; int i; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; - for (i = 0; i < MAX_RISCV_PMPS; i++) { + for (i = 0; i < pmp_regions; i++) { pmp_update_rule_addr(env, i); } pmp_update_rule_nums(env); 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 @@ uint32_t pmp_get_num_rules(CPURISCVState *env) */ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) { - if (pmp_index < MAX_RISCV_PMPS) { + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; + + if (pmp_index < pmp_regions) { return env->pmp_state.pmp[pmp_index].cfg_reg; } @@ -XXX,XX +XXX,XX @@ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) */ static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) { - if (pmp_index < MAX_RISCV_PMPS) { + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; + + if (pmp_index < pmp_regions) { if (env->pmp_state.pmp[pmp_index].cfg_reg == val) { /* no change */ return false; @@ -XXX,XX +XXX,XX @@ void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index) void pmp_update_rule_nums(CPURISCVState *env) { int i; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; env->pmp_state.num_rules = 0; - for (i = 0; i < MAX_RISCV_PMPS; i++) { + for (i = 0; i < pmp_regions; i++) { const uint8_t a_field = pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); if (PMP_AMATCH_OFF != a_field) { @@ -XXX,XX +XXX,XX @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr, int pmp_size = 0; hwaddr s = 0; hwaddr e = 0; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; /* Short cut if no rules */ if (0 == pmp_get_num_rules(env)) { @@ -XXX,XX +XXX,XX @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr, * 1.10 draft priv spec states there is an implicit order * from low to high */ - for (i = 0; i < MAX_RISCV_PMPS; i++) { + for (i = 0; i < pmp_regions; i++) { s = pmp_is_in_range(env, i, addr); e = pmp_is_in_range(env, i, addr + pmp_size - 1); @@ -XXX,XX +XXX,XX @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, { trace_pmpaddr_csr_write(env->mhartid, addr_index, val); bool is_next_cfg_tor = false; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; - if (addr_index < MAX_RISCV_PMPS) { + if (addr_index < pmp_regions) { if (env->pmp_state.pmp[addr_index].addr_reg == val) { /* no change */ return; @@ -XXX,XX +XXX,XX @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, * In TOR mode, need to check the lock bit of the next pmp * (if there is a next). */ - if (addr_index + 1 < MAX_RISCV_PMPS) { + if (addr_index + 1 < pmp_regions) { uint8_t pmp_cfg = env->pmp_state.pmp[addr_index + 1].cfg_reg; is_next_cfg_tor = PMP_AMATCH_TOR == pmp_get_a_field(pmp_cfg); @@ -XXX,XX +XXX,XX @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index) { target_ulong val = 0; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; - if (addr_index < MAX_RISCV_PMPS) { + if (addr_index < pmp_regions) { val = env->pmp_state.pmp[addr_index].addr_reg; trace_pmpaddr_csr_read(env->mhartid, addr_index, val); } else { @@ -XXX,XX +XXX,XX @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val) { int i; uint64_t mask = MSECCFG_MMWP | MSECCFG_MML; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; /* Update PMM field only if the value is valid according to Zjpm v1.0 */ if (riscv_cpu_cfg(env)->ext_smmpm && riscv_cpu_mxl(env) == MXL_RV64 && @@ -XXX,XX +XXX,XX @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val) /* RLB cannot be enabled if it's already 0 and if any regions are locked */ if (!MSECCFG_RLB_ISSET(env)) { - for (i = 0; i < MAX_RISCV_PMPS; i++) { + for (i = 0; i < pmp_regions; i++) { if (pmp_is_locked(env, i)) { val &= ~MSECCFG_RLB; break; @@ -XXX,XX +XXX,XX @@ target_ulong pmp_get_tlb_size(CPURISCVState *env, hwaddr addr) hwaddr tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1); hwaddr tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1; int i; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; /* * If PMP is not supported or there are no PMP rules, the TLB page will not @@ -XXX,XX +XXX,XX @@ target_ulong pmp_get_tlb_size(CPURISCVState *env, hwaddr addr) return TARGET_PAGE_SIZE; } - for (i = 0; i < MAX_RISCV_PMPS; i++) { + for (i = 0; i < pmp_regions; i++) { if (pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg) == PMP_AMATCH_OFF) { continue; } -- 2.50.0
From: Nutty Liu <liujingqi@lanxincomputing.com> The original implementation incorrectly performed a bitwise AND operation between the PPN of iova and PPN Mask, leading to an incorrect PPN field in Translation-reponse register. The PPN of iova should be set entirely in the PPN field of Translation-reponse register. Also remove the code that was used to clear S field since this field is already zero. Signed-off-by: Nutty Liu <liujingqi@lanxincomputing.com> Reviewed-by: Tomasz Jeznach <tjeznach@rivosinc.com> Message-ID: <20250605124848.1248-1-liujingqi@lanxincomputing.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu.c | 6 +----- 1 file changed, 1 insertion(+), 5 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 void riscv_iommu_process_dbg(RISCVIOMMUState *s) iova = RISCV_IOMMU_TR_RESPONSE_FAULT | (((uint64_t) fault) << 10); } else { iova = iotlb.translated_addr & ~iotlb.addr_mask; - iova >>= TARGET_PAGE_BITS; - iova &= RISCV_IOMMU_TR_RESPONSE_PPN; - - /* We do not support superpages (> 4kbs) for now */ - iova &= ~RISCV_IOMMU_TR_RESPONSE_S; + iova = set_field(0, RISCV_IOMMU_TR_RESPONSE_PPN, PPN_DOWN(iova)); } riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE, iova); } -- 2.50.0
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> The SBI spec states, for console write byte: "This is a blocking SBI call and it will only return after writing the specified byte to the debug console. It will also return, with SBI_ERR_FAILED, if there are I/O errors." Being a blocker call will either succeed writing the byte or error out, it's feasible to use the blocking qemu_chr_fe_write_all() instead of qemu_chr_fe_write(). Last but not the least, we will duck possible changes in qemu_chr_fe_write() where ret = 0 will have a 'zero byte written' semantic [1] - something that we're not ready to deal in this current state. [1] https://lore.kernel.org/qemu-devel/CAFEAcA_kEndvNtw4EHySXWwQPoGs029yAzZGGBcV=zGHaj7KUQ@mail.gmail.com/ Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20250605094456.1385105-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/kvm/kvm-cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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_riscv_handle_sbi_dbcn(CPUState *cs, struct kvm_run *run) break; case SBI_EXT_DBCN_CONSOLE_WRITE_BYTE: ch = run->riscv_sbi.args[0]; - ret = qemu_chr_fe_write(serial_hd(0)->be, &ch, sizeof(ch)); + ret = qemu_chr_fe_write_all(serial_hd(0)->be, &ch, sizeof(ch)); if (ret < 0) { error_report("SBI_EXT_DBCN_CONSOLE_WRITE_BYTE: error when " -- 2.50.0
From: Anton Blanchard <antonb@tenstorrent.com> fcvt.s.bf16 uses the FP16 check_nanbox_h() which returns an FP16 quiet NaN. Add check_nanbox_bf16() which returns a BF16 quiet NaN. Signed-off-by: Anton Blanchard <antonb@tenstorrent.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250501114253.594887-1-antonb@tenstorrent.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/internals.h | 16 ++++++++++++++++ target/riscv/fpu_helper.c | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/target/riscv/internals.h b/target/riscv/internals.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -XXX,XX +XXX,XX @@ static inline float16 check_nanbox_h(CPURISCVState *env, uint64_t f) } } +static inline float16 check_nanbox_bf16(CPURISCVState *env, uint64_t f) +{ + /* Disable nanbox check when enable zfinx */ + if (env_archcpu(env)->cfg.ext_zfinx) { + return (uint16_t)f; + } + + uint64_t mask = MAKE_64BIT_MASK(16, 48); + + if (likely((f & mask) == mask)) { + return (uint16_t)f; + } else { + return 0x7FC0u; /* default qnan */ + } +} + #ifndef CONFIG_USER_ONLY /* Our implementation of SysemuCPUOps::has_work */ bool riscv_cpu_has_work(CPUState *cs); diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -XXX,XX +XXX,XX @@ uint64_t helper_fcvt_bf16_s(CPURISCVState *env, uint64_t rs1) uint64_t helper_fcvt_s_bf16(CPURISCVState *env, uint64_t rs1) { - float16 frs1 = check_nanbox_h(env, rs1); + float16 frs1 = check_nanbox_bf16(env, rs1); return nanbox_s(env, bfloat16_to_float32(frs1, &env->fp_status)); } -- 2.50.0
From: Florian Lugou <florian.lugou@provenrun.com> The current handler for TXFIFO writes schedules an async callback to pop characters from the queue. When software writes to TXFIFO faster than the async callback delay (100ns), the timer may be pushed back while the previous character has not be dequeued yet. This happens in particular when using -icount with small shift values. This is especially worrysome when software repetitively issues amoor.w instructions (as suggested by SiFive specification) and the FIFO is full, leading to the callback being infinitly pushed back. This commit fixes the issue by never pushing back the timer, only updating it if it is not already active. Signed-off-by: Florian Lugou <florian.lugou@provenrun.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250605101255.797162-1-florian.lugou@provenrun.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/char/sifive_uart.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 @@ static void sifive_uart_write_tx_fifo(SiFiveUARTState *s, const uint8_t *buf, s->txfifo |= SIFIVE_UART_TXFIFO_FULL; } - timer_mod(s->fifo_trigger_handle, current_time + - TX_INTERRUPT_TRIGGER_DELAY_NS); + if (!timer_pending(s->fifo_trigger_handle)) { + timer_mod(s->fifo_trigger_handle, current_time + + TX_INTERRUPT_TRIGGER_DELAY_NS); + } } static uint64_t -- 2.50.0
From: Joel Stanley <joel@jms.id.au> The address is a hardware address, so use hwaddr for consistency with the rest of the machine. Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20250604025450.85327-2-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 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_socket_clint(RISCVVirtState *s, int cpu; g_autofree char *clint_name = NULL; g_autofree uint32_t *clint_cells = NULL; - unsigned long clint_addr; + hwaddr clint_addr; MachineState *ms = MACHINE(s); static const char * const clint_compat[2] = { "sifive,clint0", "riscv,clint0" @@ -XXX,XX +XXX,XX @@ static void create_fdt_socket_clint(RISCVVirtState *s, } clint_addr = s->memmap[VIRT_CLINT].base + - (s->memmap[VIRT_CLINT].size * socket); - clint_name = g_strdup_printf("/soc/clint@%lx", clint_addr); + s->memmap[VIRT_CLINT].size * socket; + clint_name = g_strdup_printf("/soc/clint@%"HWADDR_PRIx, clint_addr); qemu_fdt_add_subnode(ms->fdt, clint_name); qemu_fdt_setprop_string_array(ms->fdt, clint_name, "compatible", (char **)&clint_compat, -- 2.50.0
From: Joel Stanley <joel@jms.id.au> The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20250604025450.85327-3-joel@jms.id.au> 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_socket_clint(RISCVVirtState *s, qemu_fdt_setprop_string_array(ms->fdt, clint_name, "compatible", (char **)&clint_compat, ARRAY_SIZE(clint_compat)); - qemu_fdt_setprop_cells(ms->fdt, clint_name, "reg", - 0x0, clint_addr, 0x0, s->memmap[VIRT_CLINT].size); + qemu_fdt_setprop_sized_cells(ms->fdt, clint_name, "reg", + 2, clint_addr, 2, s->memmap[VIRT_CLINT].size); qemu_fdt_setprop(ms->fdt, clint_name, "interrupts-extended", clint_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4); riscv_socket_fdt_write_id(ms, clint_name, socket); -- 2.50.0
From: Joel Stanley <joel@jms.id.au> Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20250604025450.85327-4-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -XXX,XX +XXX,XX @@ static void create_fdt_socket_memory(RISCVVirtState *s, int socket) size = riscv_socket_mem_size(ms, socket); mem_name = g_strdup_printf("/memory@%"HWADDR_PRIx, addr); qemu_fdt_add_subnode(ms->fdt, mem_name); - qemu_fdt_setprop_cells(ms->fdt, mem_name, "reg", - addr >> 32, addr, size >> 32, size); + qemu_fdt_setprop_sized_cells(ms->fdt, mem_name, "reg", 2, addr, 2, size); qemu_fdt_setprop_string(ms->fdt, mem_name, "device_type", "memory"); riscv_socket_fdt_write_id(ms, mem_name, socket); } -- 2.50.0
From: Joel Stanley <joel@jms.id.au> The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20250604025450.85327-5-joel@jms.id.au> 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_one_aplic(RISCVVirtState *s, int socket, qemu_fdt_setprop_cell(ms->fdt, aplic_name, "msi-parent", msi_phandle); } - qemu_fdt_setprop_cells(ms->fdt, aplic_name, "reg", - 0x0, aplic_addr, 0x0, aplic_size); + qemu_fdt_setprop_sized_cells(ms->fdt, aplic_name, "reg", + 2, aplic_addr, 2, aplic_size); qemu_fdt_setprop_cell(ms->fdt, aplic_name, "riscv,num-sources", VIRT_IRQCHIP_NUM_SOURCES); -- 2.50.0
From: Joel Stanley <joel@jms.id.au> The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20250604025450.85327-6-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 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_socket_aclint(RISCVVirtState *s, qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "riscv,aclint-mswi"); - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, addr, 0x0, RISCV_ACLINT_SWI_SIZE); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", + 2, addr, 2, RISCV_ACLINT_SWI_SIZE); qemu_fdt_setprop(ms->fdt, name, "interrupts-extended", aclint_mswi_cells, aclint_cells_size); qemu_fdt_setprop(ms->fdt, name, "interrupt-controller", NULL, 0); @@ -XXX,XX +XXX,XX @@ static void create_fdt_socket_aclint(RISCVVirtState *s, qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "riscv,aclint-mtimer"); - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, addr + RISCV_ACLINT_DEFAULT_MTIME, - 0x0, size - RISCV_ACLINT_DEFAULT_MTIME, - 0x0, addr + RISCV_ACLINT_DEFAULT_MTIMECMP, - 0x0, RISCV_ACLINT_DEFAULT_MTIME); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", + 2, addr + RISCV_ACLINT_DEFAULT_MTIME, + 2, size - RISCV_ACLINT_DEFAULT_MTIME, + 2, addr + RISCV_ACLINT_DEFAULT_MTIMECMP, + 2, RISCV_ACLINT_DEFAULT_MTIME); qemu_fdt_setprop(ms->fdt, name, "interrupts-extended", aclint_mtimer_cells, aclint_cells_size); riscv_socket_fdt_write_id(ms, name, socket); @@ -XXX,XX +XXX,XX @@ static void create_fdt_socket_aclint(RISCVVirtState *s, qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "riscv,aclint-sswi"); - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, addr, 0x0, s->memmap[VIRT_ACLINT_SSWI].size); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", + 2, addr, 2, s->memmap[VIRT_ACLINT_SSWI].size); qemu_fdt_setprop(ms->fdt, name, "interrupts-extended", aclint_sswi_cells, aclint_cells_size); qemu_fdt_setprop(ms->fdt, name, "interrupt-controller", NULL, 0); -- 2.50.0
From: Joel Stanley <joel@jms.id.au> The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20250604025450.85327-7-joel@jms.id.au> 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_socket_plic(RISCVVirtState *s, s->soc[socket].num_harts * sizeof(uint32_t) * 4); } - qemu_fdt_setprop_cells(ms->fdt, plic_name, "reg", - 0x0, plic_addr, 0x0, s->memmap[VIRT_PLIC].size); + 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); -- 2.50.0
From: Joel Stanley <joel@jms.id.au> The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20250604025450.85327-8-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 4 +--- 1 file changed, 1 insertion(+), 3 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_virtio(RISCVVirtState *s, uint32_t irq_virtio_phandle) qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "virtio,mmio"); - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, addr, - 0x0, size); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", 2, addr, 2, size); qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_virtio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { -- 2.50.0
From: Joel Stanley <joel@jms.id.au> The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20250604025450.85327-9-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 5 +++-- 1 file changed, 3 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_reset(RISCVVirtState *s, uint32_t *phandle) qemu_fdt_setprop_string_array(ms->fdt, name, "compatible", (char **)&compat, ARRAY_SIZE(compat)); } - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, s->memmap[VIRT_TEST].base, 0x0, s->memmap[VIRT_TEST].size); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", + 2, s->memmap[VIRT_TEST].base, + 2, s->memmap[VIRT_TEST].size); qemu_fdt_setprop_cell(ms->fdt, name, "phandle", test_phandle); test_phandle = qemu_fdt_get_phandle(ms->fdt, name); g_free(name); -- 2.50.0
From: Joel Stanley <joel@jms.id.au> The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20250604025450.85327-10-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 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_uart(RISCVVirtState *s, s->memmap[VIRT_UART0].base); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "ns16550a"); - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, s->memmap[VIRT_UART0].base, - 0x0, s->memmap[VIRT_UART0].size); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", + 2, s->memmap[VIRT_UART0].base, + 2, s->memmap[VIRT_UART0].size); qemu_fdt_setprop_cell(ms->fdt, name, "clock-frequency", 3686400); qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_mmio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { -- 2.50.0
From: Joel Stanley <joel@jms.id.au> The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20250604025450.85327-11-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 5 +++-- 1 file changed, 3 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_rtc(RISCVVirtState *s, qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "google,goldfish-rtc"); - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, s->memmap[VIRT_RTC].base, 0x0, s->memmap[VIRT_RTC].size); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", + 2, s->memmap[VIRT_RTC].base, + 2, s->memmap[VIRT_RTC].size); qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_mmio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { -- 2.50.0
From: Joel Stanley <joel@jms.id.au> The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20250604025450.85327-12-joel@jms.id.au> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -XXX,XX +XXX,XX @@ static void create_fdt_iommu_sys(RISCVVirtState *s, uint32_t irq_chip, qemu_fdt_setprop_cell(fdt, iommu_node, "#iommu-cells", 1); qemu_fdt_setprop_cell(fdt, iommu_node, "phandle", iommu_phandle); - qemu_fdt_setprop_cells(fdt, iommu_node, "reg", - addr >> 32, addr, size >> 32, size); + qemu_fdt_setprop_sized_cells(fdt, iommu_node, "reg", 2, addr, 2, size); qemu_fdt_setprop_cell(fdt, iommu_node, "interrupt-parent", irq_chip); qemu_fdt_setprop_cells(fdt, iommu_node, "interrupts", -- 2.50.0
From: Joel Stanley <joel@jms.id.au> The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Joel Stanley <joel@jms.id.au> Message-ID: <20250604025450.85327-13-joel@jms.id.au> 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_pcie(RISCVVirtState *s, if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { qemu_fdt_setprop_cell(ms->fdt, name, "msi-parent", msi_pcie_phandle); } - qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0, - s->memmap[VIRT_PCIE_ECAM].base, 0, s->memmap[VIRT_PCIE_ECAM].size); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", 2, + s->memmap[VIRT_PCIE_ECAM].base, 2, s->memmap[VIRT_PCIE_ECAM].size); qemu_fdt_setprop_sized_cells(ms->fdt, name, "ranges", 1, FDT_PCI_RANGE_IOPORT, 2, 0, 2, s->memmap[VIRT_PCIE_PIO].base, 2, s->memmap[VIRT_PCIE_PIO].size, -- 2.50.0
From: Huang Borong <3543977024@qq.com> Add a CPU entry for the Xiangshan Kunminghu CPU, an open-source, high-performance RISC-V processor. More details can be found at: https://github.com/OpenXiangShan/XiangShan Note: The ISA extensions supported by the Xiangshan Kunminghu CPU are categorized based on four RISC-V specifications: Volume I: Unprivileged Architecture, Volume II: Privileged Architecture, AIA, and RVA23. The extensions within each category are organized according to the chapter order in the specifications. Signed-off-by: Yu Hu <huyu@bosc.ac.cn> Signed-off-by: Ran Wang <wangran@bosc.ac.cn> Signed-off-by: Borong Huang <3543977024@qq.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250425122212.364-1-wangran@bosc.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu-qom.h | 1 + target/riscv/cpu.c | 58 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -XXX,XX +XXX,XX @@ #define TYPE_RISCV_CPU_VEYRON_V1 RISCV_CPU_TYPE_NAME("veyron-v1") #define TYPE_RISCV_CPU_TT_ASCALON RISCV_CPU_TYPE_NAME("tt-ascalon") #define TYPE_RISCV_CPU_XIANGSHAN_NANHU RISCV_CPU_TYPE_NAME("xiangshan-nanhu") +#define TYPE_RISCV_CPU_XIANGSHAN_KMH RISCV_CPU_TYPE_NAME("xiangshan-kunminghu") #define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host") OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) 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.max_satp_mode = VM_1_10_SV39, ), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_XIANGSHAN_KMH, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVG | RVC | RVB | RVS | RVU | RVH | RVV, + .priv_spec = PRIV_VERSION_1_13_0, + /* + * The RISC-V Instruction Set Manual: Volume I + * Unprivileged Architecture + */ + .cfg.ext_zicntr = true, + .cfg.ext_zihpm = true, + .cfg.ext_zihintntl = true, + .cfg.ext_zihintpause = true, + .cfg.ext_zimop = true, + .cfg.ext_zcmop = true, + .cfg.ext_zicond = true, + .cfg.ext_zawrs = true, + .cfg.ext_zacas = true, + .cfg.ext_zfh = true, + .cfg.ext_zfa = true, + .cfg.ext_zcb = true, + .cfg.ext_zbc = true, + .cfg.ext_zvfh = true, + .cfg.ext_zkn = true, + .cfg.ext_zks = true, + .cfg.ext_zkt = true, + .cfg.ext_zvbb = true, + .cfg.ext_zvkt = true, + /* + * The RISC-V Instruction Set Manual: Volume II + * Privileged Architecture + */ + .cfg.ext_smstateen = true, + .cfg.ext_smcsrind = true, + .cfg.ext_sscsrind = true, + .cfg.ext_svnapot = true, + .cfg.ext_svpbmt = true, + .cfg.ext_svinval = true, + .cfg.ext_sstc = true, + .cfg.ext_sscofpmf = true, + .cfg.ext_ssdbltrp = true, + .cfg.ext_ssnpm = true, + .cfg.ext_smnpm = true, + .cfg.ext_smmpm = true, + .cfg.ext_sspm = true, + .cfg.ext_supm = true, + /* The RISC-V Advanced Interrupt Architecture */ + .cfg.ext_smaia = true, + .cfg.ext_ssaia = true, + /* RVA23 Profiles */ + .cfg.ext_zicbom = true, + .cfg.ext_zicbop = true, + .cfg.ext_zicboz = true, + .cfg.ext_svade = true, + .cfg.mmu = true, + .cfg.pmp = true, + .cfg.max_satp_mode = VM_1_10_SV48, + ), + #if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE128, TYPE_RISCV_DYNAMIC_CPU, .cfg.max_satp_mode = VM_1_10_SV57, -- 2.50.0
From: Huang Borong <3543977024@qq.com> This implementation provides emulation for the Xiangshan Kunminghu FPGA prototype platform, including support for UART, CLINT, IMSIC, and APLIC devices. More details can be found at https://github.com/OpenXiangShan/XiangShan Signed-off-by: qinshaoqing <qinshaoqing@bosc.ac.cn> Signed-off-by: Yang Wang <wangyang@bosc.ac.cn> Signed-off-by: Yu Hu <819258943@qq.com> Signed-off-by: Ran Wang <wangran@bosc.ac.cn> Signed-off-by: Borong Huang <3543977024@qq.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20250617074222.17618-1-wangran@bosc.ac.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- MAINTAINERS | 7 + docs/system/riscv/xiangshan-kunminghu.rst | 39 ++++ docs/system/target-riscv.rst | 1 + configs/devices/riscv64-softmmu/default.mak | 1 + include/hw/riscv/xiangshan_kmh.h | 68 ++++++ hw/riscv/xiangshan_kmh.c | 220 ++++++++++++++++++++ hw/riscv/Kconfig | 9 + hw/riscv/meson.build | 1 + 8 files changed, 346 insertions(+) create mode 100644 docs/system/riscv/xiangshan-kunminghu.rst create mode 100644 include/hw/riscv/xiangshan_kmh.h create mode 100644 hw/riscv/xiangshan_kmh.c diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ S: Maintained F: hw/riscv/microblaze-v-generic.c F: docs/system/riscv/microblaze-v-generic.rst +Xiangshan Kunminghu +M: Ran Wang <wangran@bosc.ac.cn> +S: Maintained +F: docs/system/riscv/xiangshan-kunminghu.rst +F: hw/riscv/xiangshan_kmh.c +F: include/hw/riscv/xiangshan_kmh.h + RX Machines ----------- rx-gdbsim diff --git a/docs/system/riscv/xiangshan-kunminghu.rst b/docs/system/riscv/xiangshan-kunminghu.rst new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/docs/system/riscv/xiangshan-kunminghu.rst @@ -XXX,XX +XXX,XX @@ +BOSC Xiangshan Kunminghu FPGA prototype platform (``xiangshan-kunminghu``) +========================================================================== +The ``xiangshan-kunminghu`` machine is compatible with our FPGA prototype +platform. + +XiangShan is an open-source high-performance RISC-V processor project. +The third generation processor is called Kunminghu. Kunminghu is a 64-bit +RV64GCBSUHV processor core. More information can be found in our Github +repository: +https://github.com/OpenXiangShan/XiangShan + +Supported devices +----------------- +The ``xiangshan-kunminghu`` machine supports the following devices: + +* Up to 16 xiangshan-kunminghu cores +* Core Local Interruptor (CLINT) +* Incoming MSI Controller (IMSIC) +* Advanced Platform-Level Interrupt Controller (APLIC) +* 1 UART + +Boot options +------------ +The ``xiangshan-kunminghu`` machine can start using the standard ``-bios`` +functionality for loading the boot image. You need to compile and link +the firmware, kernel, and Device Tree (FDT) into a single binary file, +such as ``fw_payload.bin``. + +Running +------- +Below is an example command line for running the ``xiangshan-kunminghu`` +machine: + +.. code-block:: bash + + $ qemu-system-riscv64 -machine xiangshan-kunminghu \ + -smp 16 -m 16G \ + -bios path/to/opensbi/platform/generic/firmware/fw_payload.bin \ + -nographic 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/shakti-c riscv/sifive_u riscv/virt + riscv/xiangshan-kunminghu RISC-V CPU firmware ------------------- diff --git a/configs/devices/riscv64-softmmu/default.mak b/configs/devices/riscv64-softmmu/default.mak index XXXXXXX..XXXXXXX 100644 --- a/configs/devices/riscv64-softmmu/default.mak +++ b/configs/devices/riscv64-softmmu/default.mak @@ -XXX,XX +XXX,XX @@ # CONFIG_RISCV_VIRT=n # CONFIG_MICROCHIP_PFSOC=n # CONFIG_SHAKTI_C=n +# CONFIG_XIANGSHAN_KUNMINGHU=n diff --git a/include/hw/riscv/xiangshan_kmh.h b/include/hw/riscv/xiangshan_kmh.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/riscv/xiangshan_kmh.h @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * QEMU RISC-V Board Compatible with the Xiangshan Kunminghu + * FPGA prototype platform + * + * Copyright (c) 2025 Beijing Institute of Open Source Chip (BOSC) + * + */ + +#ifndef HW_XIANGSHAN_KMH_H +#define HW_XIANGSHAN_KMH_H + +#include "hw/boards.h" +#include "hw/riscv/riscv_hart.h" + +#define XIANGSHAN_KMH_MAX_CPUS 16 + +typedef struct XiangshanKmhSoCState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + RISCVHartArrayState cpus; + DeviceState *irqchip; + MemoryRegion rom; +} XiangshanKmhSoCState; + +#define TYPE_XIANGSHAN_KMH_SOC "xiangshan.kunminghu.soc" +DECLARE_INSTANCE_CHECKER(XiangshanKmhSoCState, XIANGSHAN_KMH_SOC, + TYPE_XIANGSHAN_KMH_SOC) + +typedef struct XiangshanKmhState { + /*< private >*/ + MachineState parent_obj; + + /*< public >*/ + XiangshanKmhSoCState soc; +} XiangshanKmhState; + +#define TYPE_XIANGSHAN_KMH_MACHINE MACHINE_TYPE_NAME("xiangshan-kunminghu") +DECLARE_INSTANCE_CHECKER(XiangshanKmhState, XIANGSHAN_KMH_MACHINE, + TYPE_XIANGSHAN_KMH_MACHINE) + +enum { + XIANGSHAN_KMH_ROM, + XIANGSHAN_KMH_UART0, + XIANGSHAN_KMH_CLINT, + XIANGSHAN_KMH_APLIC_M, + XIANGSHAN_KMH_APLIC_S, + XIANGSHAN_KMH_IMSIC_M, + XIANGSHAN_KMH_IMSIC_S, + XIANGSHAN_KMH_DRAM, +}; + +enum { + XIANGSHAN_KMH_UART0_IRQ = 10, +}; + +/* Indicating Timebase-freq (1MHZ) */ +#define XIANGSHAN_KMH_CLINT_TIMEBASE_FREQ 1000000 + +#define XIANGSHAN_KMH_IMSIC_NUM_IDS 255 +#define XIANGSHAN_KMH_IMSIC_NUM_GUESTS 7 +#define XIANGSHAN_KMH_IMSIC_GUEST_BITS 3 + +#define XIANGSHAN_KMH_APLIC_NUM_SOURCES 96 + +#endif diff --git a/hw/riscv/xiangshan_kmh.c b/hw/riscv/xiangshan_kmh.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/riscv/xiangshan_kmh.c @@ -XXX,XX +XXX,XX @@ +/* + * QEMU RISC-V Board Compatible with the Xiangshan Kunminghu + * FPGA prototype platform + * + * Copyright (c) 2025 Beijing Institute of Open Source Chip (BOSC) + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Provides a board compatible with the Xiangshan Kunminghu + * FPGA prototype platform: + * + * 0) UART (16550A) + * 1) CLINT (Core-Local Interruptor) + * 2) IMSIC (Incoming MSI Controller) + * 3) APLIC (Advanced Platform-Level Interrupt Controller) + * + * More information can be found in our Github repository: + * https://github.com/OpenXiangShan/XiangShan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "system/address-spaces.h" +#include "hw/boards.h" +#include "hw/char/serial-mm.h" +#include "hw/intc/riscv_aclint.h" +#include "hw/intc/riscv_aplic.h" +#include "hw/intc/riscv_imsic.h" +#include "hw/qdev-properties.h" +#include "hw/riscv/boot.h" +#include "hw/riscv/xiangshan_kmh.h" +#include "hw/riscv/riscv_hart.h" +#include "system/system.h" + +static const MemMapEntry xiangshan_kmh_memmap[] = { + [XIANGSHAN_KMH_ROM] = { 0x1000, 0xF000 }, + [XIANGSHAN_KMH_UART0] = { 0x310B0000, 0x10000 }, + [XIANGSHAN_KMH_CLINT] = { 0x38000000, 0x10000 }, + [XIANGSHAN_KMH_APLIC_M] = { 0x31100000, 0x4000 }, + [XIANGSHAN_KMH_APLIC_S] = { 0x31120000, 0x4000 }, + [XIANGSHAN_KMH_IMSIC_M] = { 0x3A800000, 0x10000 }, + [XIANGSHAN_KMH_IMSIC_S] = { 0x3B000000, 0x80000 }, + [XIANGSHAN_KMH_DRAM] = { 0x80000000, 0x0 }, +}; + +static DeviceState *xiangshan_kmh_create_aia(uint32_t num_harts) +{ + int i; + const MemMapEntry *memmap = xiangshan_kmh_memmap; + hwaddr addr = 0; + DeviceState *aplic_m = NULL; + + /* M-level IMSICs */ + addr = memmap[XIANGSHAN_KMH_IMSIC_M].base; + for (i = 0; i < num_harts; i++) { + riscv_imsic_create(addr + i * IMSIC_HART_SIZE(0), i, true, + 1, XIANGSHAN_KMH_IMSIC_NUM_IDS); + } + + /* S-level IMSICs */ + addr = memmap[XIANGSHAN_KMH_IMSIC_S].base; + for (i = 0; i < num_harts; i++) { + riscv_imsic_create(addr + + i * IMSIC_HART_SIZE(XIANGSHAN_KMH_IMSIC_GUEST_BITS), + i, false, 1 + XIANGSHAN_KMH_IMSIC_GUEST_BITS, + XIANGSHAN_KMH_IMSIC_NUM_IDS); + } + + /* M-level APLIC */ + aplic_m = riscv_aplic_create(memmap[XIANGSHAN_KMH_APLIC_M].base, + memmap[XIANGSHAN_KMH_APLIC_M].size, + 0, 0, XIANGSHAN_KMH_APLIC_NUM_SOURCES, + 1, true, true, NULL); + + /* S-level APLIC */ + riscv_aplic_create(memmap[XIANGSHAN_KMH_APLIC_S].base, + memmap[XIANGSHAN_KMH_APLIC_S].size, + 0, 0, XIANGSHAN_KMH_APLIC_NUM_SOURCES, + 1, true, false, aplic_m); + + return aplic_m; +} + +static void xiangshan_kmh_soc_realize(DeviceState *dev, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + XiangshanKmhSoCState *s = XIANGSHAN_KMH_SOC(dev); + const MemMapEntry *memmap = xiangshan_kmh_memmap; + MemoryRegion *system_memory = get_system_memory(); + uint32_t num_harts = ms->smp.cpus; + + qdev_prop_set_uint32(DEVICE(&s->cpus), "num-harts", num_harts); + qdev_prop_set_uint32(DEVICE(&s->cpus), "hartid-base", 0); + qdev_prop_set_string(DEVICE(&s->cpus), "cpu-type", + TYPE_RISCV_CPU_XIANGSHAN_KMH); + sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_fatal); + + /* AIA */ + s->irqchip = xiangshan_kmh_create_aia(num_harts); + + /* UART */ + serial_mm_init(system_memory, memmap[XIANGSHAN_KMH_UART0].base, 2, + qdev_get_gpio_in(s->irqchip, XIANGSHAN_KMH_UART0_IRQ), + 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); + + /* CLINT */ + riscv_aclint_swi_create(memmap[XIANGSHAN_KMH_CLINT].base, + 0, num_harts, false); + riscv_aclint_mtimer_create(memmap[XIANGSHAN_KMH_CLINT].base + + RISCV_ACLINT_SWI_SIZE, + RISCV_ACLINT_DEFAULT_MTIMER_SIZE, + 0, num_harts, RISCV_ACLINT_DEFAULT_MTIMECMP, + RISCV_ACLINT_DEFAULT_MTIME, + XIANGSHAN_KMH_CLINT_TIMEBASE_FREQ, true); + + /* ROM */ + memory_region_init_rom(&s->rom, OBJECT(dev), "xiangshan.kunminghu.rom", + memmap[XIANGSHAN_KMH_ROM].size, &error_fatal); + memory_region_add_subregion(system_memory, + memmap[XIANGSHAN_KMH_ROM].base, &s->rom); +} + +static void xiangshan_kmh_soc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = xiangshan_kmh_soc_realize; + dc->user_creatable = false; +} + +static void xiangshan_kmh_soc_instance_init(Object *obj) +{ + XiangshanKmhSoCState *s = XIANGSHAN_KMH_SOC(obj); + + object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY); +} + +static const TypeInfo xiangshan_kmh_soc_info = { + .name = TYPE_XIANGSHAN_KMH_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(XiangshanKmhSoCState), + .instance_init = xiangshan_kmh_soc_instance_init, + .class_init = xiangshan_kmh_soc_class_init, +}; + +static void xiangshan_kmh_soc_register_types(void) +{ + type_register_static(&xiangshan_kmh_soc_info); +} +type_init(xiangshan_kmh_soc_register_types) + +static void xiangshan_kmh_machine_init(MachineState *machine) +{ + XiangshanKmhState *s = XIANGSHAN_KMH_MACHINE(machine); + const MemMapEntry *memmap = xiangshan_kmh_memmap; + MemoryRegion *system_memory = get_system_memory(); + hwaddr start_addr = memmap[XIANGSHAN_KMH_DRAM].base; + + /* Initialize SoC */ + object_initialize_child(OBJECT(machine), "soc", &s->soc, + TYPE_XIANGSHAN_KMH_SOC); + qdev_realize(DEVICE(&s->soc), NULL, &error_fatal); + + /* Register RAM */ + memory_region_add_subregion(system_memory, + memmap[XIANGSHAN_KMH_DRAM].base, + machine->ram); + + /* 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); + } + + /* Note: dtb has been integrated into firmware(OpenSBI) when compiling */ +} + +static void xiangshan_kmh_machine_class_init(ObjectClass *klass, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(klass); + static const char *const valid_cpu_types[] = { + TYPE_RISCV_CPU_XIANGSHAN_KMH, + NULL + }; + + mc->desc = "RISC-V Board compatible with the Xiangshan " \ + "Kunminghu FPGA prototype platform"; + mc->init = xiangshan_kmh_machine_init; + mc->max_cpus = XIANGSHAN_KMH_MAX_CPUS; + mc->default_cpu_type = TYPE_RISCV_CPU_XIANGSHAN_KMH; + mc->valid_cpu_types = valid_cpu_types; + mc->default_ram_id = "xiangshan.kunminghu.ram"; +} + +static const TypeInfo xiangshan_kmh_machine_info = { + .name = TYPE_XIANGSHAN_KMH_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(XiangshanKmhState), + .class_init = xiangshan_kmh_machine_class_init, +}; + +static void xiangshan_kmh_machine_register_types(void) +{ + type_register_static(&xiangshan_kmh_machine_info); +} +type_init(xiangshan_kmh_machine_register_types) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -XXX,XX +XXX,XX @@ config SPIKE select HTIF select RISCV_ACLINT select SIFIVE_PLIC + +config XIANGSHAN_KUNMINGHU + bool + default y + depends on RISCV64 + select RISCV_ACLINT + select RISCV_APLIC + select RISCV_IMSIC + select SERIAL_MM 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_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')) riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c')) +riscv_ss.add(when: 'CONFIG_XIANGSHAN_KUNMINGHU', if_true: files('xiangshan_kmh.c')) hw_arch += {'riscv': riscv_ss} -- 2.50.0
From: Max Chou <max.chou@sifive.com> According to the V spec, the vector fault-only-first load instructions may change the VL CSR. So the ldff_trans TCG translation function should generate the lookup_and_goto_ptr flow as the vsetvl/vsetvli translation function to make sure the vl_eq_vlmax TB flag is correct. Signed-off-by: Max Chou <max.chou@sifive.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20250627133013.443997-1-max.chou@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/insn_trans/trans_rvv.c.inc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -XXX,XX +XXX,XX @@ static bool ldff_trans(uint32_t vd, uint32_t rs1, uint32_t data, fn(dest, mask, base, tcg_env, desc); finalize_rvv_inst(s); + + /* vector unit-stride fault-only-first load may modify vl CSR */ + gen_update_pc(s, s->cur_insn_len); + lookup_and_goto_ptr(s); + s->base.is_jmp = DISAS_NORETURN; + return true; } -- 2.50.0
From: "liu.xuemei1@zte.com.cn" <liu.xuemei1@zte.com.cn> Address an error in migration when aia is configured as 'aplic-imsic' in riscv kvm vm by adding riscv_aplic_state_needed() and riscv_imsic_state_needed() to determine whether the corresponding sates are needed. Previously, the fields in the vmsds of 'riscv_aplic' and 'riscv_imsic' can only be initialized under certain special conditions in commit 95a97b3fd2. However, the corresponding ses of these vmsds are inserted into the savevm_state.handlers unconditionally. This led to migration failure characterized by uninitialized fields when save vm state: qemu-system-riscv64: ../migration/vmstate.c:433: vmstate_save_state_v: Assertion 'first_elem || !n_elems || !size' failed. Fixes: 95a97b3fd2 ("target/riscv: update APLIC and IMSIC to support KVM AIA") Signed-off-by: Xuemei Liu <liu.xuemei1@zte.com.cn> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20250616150034827wuHs_ffe3Qm8cqFXT7HeW@zte.com.cn> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/intc/riscv_aplic.c | 12 ++++++++++-- hw/intc/riscv_imsic.c | 10 ++++++++-- 2 files changed, 18 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 const Property riscv_aplic_properties[] = { DEFINE_PROP_BOOL("mmode", RISCVAPLICState, mmode, 0), }; +static bool riscv_aplic_state_needed(void *opaque) +{ + RISCVAPLICState *aplic = opaque; + + return riscv_use_emulated_aplic(aplic->msimode); +} + static const VMStateDescription vmstate_riscv_aplic = { .name = "riscv_aplic", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, + .needed = riscv_aplic_state_needed, .fields = (const VMStateField[]) { VMSTATE_UINT32(domaincfg, RISCVAPLICState), VMSTATE_UINT32(mmsicfgaddr, RISCVAPLICState), 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 Property riscv_imsic_properties[] = { DEFINE_PROP_UINT32("num-irqs", RISCVIMSICState, num_irqs, 0), }; +static bool riscv_imsic_state_needed(void *opaque) +{ + return !kvm_irqchip_in_kernel(); +} + static const VMStateDescription vmstate_riscv_imsic = { .name = "riscv_imsic", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, + .needed = riscv_imsic_state_needed, .fields = (const VMStateField[]) { VMSTATE_VARRAY_UINT32(eidelivery, RISCVIMSICState, num_pages, 0, -- 2.50.0
From: Charalampos Mitrodimas <charmitro@posteo.net> According to the RISC-V Privileged Architecture specification, the low bit of MEPC/SEPC must always be zero. When IALIGN=32, the two low bits must be zero. This commit fixes the behavior of MEPC/SEPC CSR reads and writes, and the implicit reads by MRET/SRET instructions to properly mask the lowest bit(s) based on whether the C extension is enabled: - When C extension is enabled (IALIGN=16): mask bit 0 - When C extension is disabled (IALIGN=32): mask bits [1:0] Previously, when vectored mode bits from STVEC (which sets bit 0 for vectored mode) were written to MEPC, the bits would not be cleared correctly, causing incorrect behavior on MRET. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2855 Signed-off-by: Charalampos Mitrodimas <charmitro@posteo.net> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20250703182157.281320-2-charmitro@posteo.net> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/internals.h | 11 +++++++++++ target/riscv/csr.c | 8 ++++---- target/riscv/op_helper.c | 4 ++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/target/riscv/internals.h b/target/riscv/internals.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -XXX,XX +XXX,XX @@ static inline float16 check_nanbox_bf16(CPURISCVState *env, uint64_t f) } } +static inline target_ulong get_xepc_mask(CPURISCVState *env) +{ + /* When IALIGN=32, both low bits must be zero. + * When IALIGN=16 (has C extension), only bit 0 must be zero. */ + if (riscv_has_ext(env, RVC)) { + return ~(target_ulong)1; + } else { + return ~(target_ulong)3; + } +} + #ifndef CONFIG_USER_ONLY /* Our implementation of SysemuCPUOps::has_work */ bool riscv_cpu_has_work(CPUState *cs); 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_mscratch(CPURISCVState *env, int csrno, static RISCVException read_mepc(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->mepc; + *val = env->mepc & get_xepc_mask(env); return RISCV_EXCP_NONE; } static RISCVException write_mepc(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { - env->mepc = val; + env->mepc = val & get_xepc_mask(env); return RISCV_EXCP_NONE; } @@ -XXX,XX +XXX,XX @@ static RISCVException write_sscratch(CPURISCVState *env, int csrno, static RISCVException read_sepc(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->sepc; + *val = env->sepc & get_xepc_mask(env); return RISCV_EXCP_NONE; } static RISCVException write_sepc(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { - env->sepc = val; + env->sepc = val & get_xepc_mask(env); return RISCV_EXCP_NONE; } diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -XXX,XX +XXX,XX @@ target_ulong helper_sret(CPURISCVState *env) riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - target_ulong retpc = env->sepc; + target_ulong retpc = env->sepc & get_xepc_mask(env); if (!riscv_cpu_allow_16bit_insn(&env_archcpu(env)->cfg, env->priv_ver, env->misa_ext) && (retpc & 0x3)) { @@ -XXX,XX +XXX,XX @@ static target_ulong ssdbltrp_mxret(CPURISCVState *env, target_ulong mstatus, target_ulong helper_mret(CPURISCVState *env) { - target_ulong retpc = env->mepc; + target_ulong retpc = env->mepc & get_xepc_mask(env); uint64_t mstatus = env->mstatus; target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); -- 2.50.0
From: Charalampos Mitrodimas <charmitro@posteo.net> Add a regression test to verify that MEPC properly masks the lower bits when an address with mode bits is written to it, as required by the RISC-V Privileged Architecture specification. The test sets STVEC to an address with bit 0 set (vectored mode), triggers an illegal instruction exception, copies STVEC to MEPC in the trap handler, and verifies that MEPC masks bits [1:0] correctly for IALIGN=32. Without the fix, MEPC retains the mode bits (returns non-zero/FAIL). With the fix, MEPC clears bits [1:0] (returns 0/PASS). Signed-off-by: Charalampos Mitrodimas <charmitro@posteo.net> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20250703182157.281320-3-charmitro@posteo.net> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- tests/tcg/riscv64/Makefile.softmmu-target | 4 ++ tests/tcg/riscv64/test-mepc-masking.S | 73 +++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 tests/tcg/riscv64/test-mepc-masking.S diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target index XXXXXXX..XXXXXXX 100644 --- a/tests/tcg/riscv64/Makefile.softmmu-target +++ b/tests/tcg/riscv64/Makefile.softmmu-target @@ -XXX,XX +XXX,XX @@ EXTRA_RUNS += run-issue1060 run-issue1060: issue1060 $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<) +EXTRA_RUNS += run-test-mepc-masking +run-test-mepc-masking: test-mepc-masking + $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<) + # We don't currently support the multiarch system tests undefine MULTIARCH_TESTS diff --git a/tests/tcg/riscv64/test-mepc-masking.S b/tests/tcg/riscv64/test-mepc-masking.S new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/tcg/riscv64/test-mepc-masking.S @@ -XXX,XX +XXX,XX @@ +/* + * Test for MEPC masking bug fix + * + * This test verifies that MEPC properly masks the lower bits according + * to the RISC-V specification when vectored mode bits from STVEC are + * written to MEPC. + */ + + .option norvc + + .text + .global _start +_start: + /* Set up machine trap vector */ + lla t0, machine_trap_handler + csrw mtvec, t0 + + /* Set STVEC with vectored mode (mode bits = 01) */ + li t0, 0x80004001 + csrw stvec, t0 + + /* Clear medeleg to handle exceptions in M-mode */ + csrw medeleg, zero + + /* Trigger illegal instruction exception */ + .word 0xffffffff + +test_completed: + /* Exit with result in a0 */ + /* a0 = 0: success (bits [1:0] were masked) */ + /* a0 != 0: failure (some bits were not masked) */ + j _exit + +machine_trap_handler: + /* Check if illegal instruction (mcause = 2) */ + csrr t0, mcause + li t1, 2 + bne t0, t1, skip_test + + /* Test: Copy STVEC (with mode bits) to MEPC */ + csrr t0, stvec /* t0 = 0x80004001 */ + csrw mepc, t0 /* Write to MEPC */ + csrr t1, mepc /* Read back MEPC */ + + /* Check if bits [1:0] are masked (IALIGN=32 without RVC) */ + andi a0, t1, 3 /* a0 = 0 if both bits masked correctly */ + + /* Set correct return address */ + lla t0, test_completed + csrw mepc, t0 + +skip_test: + mret + +/* Exit with semihosting */ +_exit: + lla a1, semiargs + li t0, 0x20026 /* ADP_Stopped_ApplicationExit */ + sd t0, 0(a1) + sd a0, 8(a1) + li a0, 0x20 /* TARGET_SYS_EXIT_EXTENDED */ + + /* Semihosting call sequence */ + .balign 16 + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + j . + + .data + .balign 8 +semiargs: + .space 16 -- 2.50.0
From: Vasilis Liaskovitis <vliaskovitis@suse.com> Usage of vsetvli instruction is reserved if VLMAX is changed when vsetvli rs1 and rd arguments are x0. In this case, if the new property is true, only the vill bit will be set. See https://github.com/riscv/riscv-isa-manual/blob/main/src/v-st-ext.adoc#avl-encoding According to the spec, the above use cases are reserved, and "Implementations may set vill in either case." Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2422 Signed-off-by: Vasilis Liaskovitis <vliaskovitis@suse.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20250618213542.22873-1-vliaskovitis@suse.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/helper.h | 2 +- target/riscv/cpu_cfg_fields.h.inc | 1 + target/riscv/cpu.c | 1 + target/riscv/vector_helper.c | 12 +++++++++++- target/riscv/insn_trans/trans_rvv.c.inc | 4 ++-- 5 files changed, 16 insertions(+), 4 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_FLAGS_3(hyp_hsv_d, TCG_CALL_NO_WG, void, env, tl, tl) #endif /* Vector functions */ -DEF_HELPER_3(vsetvl, tl, env, tl, tl) +DEF_HELPER_4(vsetvl, tl, env, tl, tl, tl) DEF_HELPER_5(vle8_v, void, ptr, ptr, tl, env, i32) DEF_HELPER_5(vle16_v, void, ptr, ptr, tl, env, i32) DEF_HELPER_5(vle32_v, void, ptr, ptr, tl, env, i32) diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -XXX,XX +XXX,XX @@ BOOL_FIELD(ext_supm) BOOL_FIELD(rvv_ta_all_1s) BOOL_FIELD(rvv_ma_all_1s) BOOL_FIELD(rvv_vl_half_avl) +BOOL_FIELD(rvv_vsetvl_x0_vill) /* Named features */ BOOL_FIELD(ext_svade) BOOL_FIELD(ext_zic64b) 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 Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("rvv_ta_all_1s", RISCVCPU, cfg.rvv_ta_all_1s, false), DEFINE_PROP_BOOL("rvv_ma_all_1s", RISCVCPU, cfg.rvv_ma_all_1s, false), DEFINE_PROP_BOOL("rvv_vl_half_avl", RISCVCPU, cfg.rvv_vl_half_avl, false), + DEFINE_PROP_BOOL("rvv_vsetvl_x0_vill", RISCVCPU, cfg.rvv_vsetvl_x0_vill, false), /* * write_misa() is marked as experimental for now so mark 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 @@ #include <math.h> target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1, - target_ulong s2) + target_ulong s2, target_ulong x0) { int vlmax, vl; RISCVCPU *cpu = env_archcpu(env); @@ -XXX,XX +XXX,XX @@ target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1, } else { vl = vlmax; } + + if (cpu->cfg.rvv_vsetvl_x0_vill && x0 && (env->vl != vl)) { + /* only set vill bit. */ + env->vill = 1; + env->vtype = 0; + env->vl = 0; + env->vstart = 0; + return 0; + } + env->vl = vl; env->vtype = s2; env->vstart = 0; diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -XXX,XX +XXX,XX @@ static bool do_vsetvl(DisasContext *s, int rd, int rs1, TCGv s2) s1 = get_gpr(s, rs1, EXT_ZERO); } - gen_helper_vsetvl(dst, tcg_env, s1, s2); + gen_helper_vsetvl(dst, tcg_env, s1, s2, tcg_constant_tl((int) (rd == 0 && rs1 == 0))); gen_set_gpr(s, rd, dst); finalize_rvv_inst(s); @@ -XXX,XX +XXX,XX @@ static bool do_vsetivli(DisasContext *s, int rd, TCGv s1, TCGv s2) dst = dest_gpr(s, rd); - gen_helper_vsetvl(dst, tcg_env, s1, s2); + gen_helper_vsetvl(dst, tcg_env, s1, s2, tcg_constant_tl(0)); gen_set_gpr(s, rd, dst); finalize_rvv_inst(s); gen_update_pc(s, s->cur_insn_len); -- 2.50.0
From: Alexandre Ghiti <alexghiti@rivosinc.com> The Svrsw60t59b extension allows to free the PTE reserved bits 60 and 59 for software to use. Reviewed-by: Deepak Gupta <debug@rivosinc.com> Signed-off-by: Alexandre Ghiti <alexghiti@rivosinc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Nutty Liu<liujingqi@lanxincomputing.com> Message-ID: <20250702-dev-alex-svrsw60b59b_v2-v2-1-504ddf0f8530@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu-bits.h | 1 + target/riscv/cpu_bits.h | 3 ++- target/riscv/cpu_cfg_fields.h.inc | 1 + hw/riscv/riscv-iommu.c | 3 ++- target/riscv/cpu.c | 2 ++ target/riscv/cpu_helper.c | 3 ++- target/riscv/tcg/tcg-cpu.c | 8 ++++++++ 7 files changed, 18 insertions(+), 3 deletions(-) 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_pq_record { #define RISCV_IOMMU_CAP_SV39 BIT_ULL(9) #define RISCV_IOMMU_CAP_SV48 BIT_ULL(10) #define RISCV_IOMMU_CAP_SV57 BIT_ULL(11) +#define RISCV_IOMMU_CAP_SVRSW60T59B BIT_ULL(14) #define RISCV_IOMMU_CAP_SV32X4 BIT_ULL(16) #define RISCV_IOMMU_CAP_SV39X4 BIT_ULL(17) #define RISCV_IOMMU_CAP_SV48X4 BIT_ULL(18) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -XXX,XX +XXX,XX @@ typedef enum { #define PTE_SOFT 0x300 /* Reserved for Software */ #define PTE_PBMT 0x6000000000000000ULL /* Page-based memory types */ #define PTE_N 0x8000000000000000ULL /* NAPOT translation */ -#define PTE_RESERVED 0x1FC0000000000000ULL /* Reserved bits */ +#define PTE_RESERVED(svrsw60t59b) \ + (svrsw60t59b ? 0x07C0000000000000ULL : 0x1FC0000000000000ULL) /* Reserved bits */ #define PTE_ATTR (PTE_N | PTE_PBMT) /* All attributes bits */ /* Page table PPN shift amount */ diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -XXX,XX +XXX,XX @@ BOOL_FIELD(ext_svadu) BOOL_FIELD(ext_svinval) BOOL_FIELD(ext_svnapot) BOOL_FIELD(ext_svpbmt) +BOOL_FIELD(ext_svrsw60t59b) BOOL_FIELD(ext_svvptc) BOOL_FIELD(ext_svukte) BOOL_FIELD(ext_zdinx) 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) } if (s->enable_g_stage) { s->cap |= RISCV_IOMMU_CAP_SV32X4 | RISCV_IOMMU_CAP_SV39X4 | - RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4; + RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4 | + RISCV_IOMMU_CAP_SVRSW60T59B; } if (s->hpm_cntrs > 0) { diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -XXX,XX +XXX,XX @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(svinval, PRIV_VERSION_1_12_0, ext_svinval), 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_EXT_DATA_ENTRY(svvptc, PRIV_VERSION_1_13_0, ext_svvptc), ISA_EXT_DATA_ENTRY(xtheadba, PRIV_VERSION_1_11_0, ext_xtheadba), @@ -XXX,XX +XXX,XX @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { 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), 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, bool svade = riscv_cpu_cfg(env)->ext_svade; bool svadu = riscv_cpu_cfg(env)->ext_svadu; bool adue = svadu ? env->menvcfg & MENVCFG_ADUE : !svade; + bool svrsw60t59b = riscv_cpu_cfg(env)->ext_svrsw60t59b; if (first_stage && two_stage && env->virt_enabled) { pbmte = pbmte && (env->henvcfg & HENVCFG_PBMTE); @@ -XXX,XX +XXX,XX @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, if (riscv_cpu_sxl(env) == MXL_RV32) { ppn = pte >> PTE_PPN_SHIFT; } else { - if (pte & PTE_RESERVED) { + if (pte & PTE_RESERVED(svrsw60t59b)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: reserved bits set in PTE: " "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", __func__, pte_addr, pte); diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) cpu->cfg.ext_ssctr = false; } + if (cpu->cfg.ext_svrsw60t59b && + (!cpu->cfg.mmu || mcc->def->misa_mxl_max == MXL_RV32)) { + error_setg(errp, "svrsw60t59b is not supported on RV32 and MMU-less platforms"); + return; + } + /* * Disable isa extensions based on priv spec after we * validated and set everything we need. @@ -XXX,XX +XXX,XX @@ static void riscv_init_max_cpu_extensions(Object *obj) if (env->misa_mxl != MXL_RV32) { isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcf), false); + } else { + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_svrsw60t59b), false); } /* -- 2.50.0
The following changes since commit 3e9793ab01904144c204589811e0e879109a9713: Merge tag 'qga-pull-2024-12-18' of https://github.com/kostyanf14/qemu into staging (2024-12-18 20:24:59 -0500) are available in the Git repository at: https://github.com/alistair23/qemu.git tags/pull-riscv-to-apply-20241220 for you to fetch changes up to 2fc8f50eadad5dcc99bc5de1333808b9de47a097: target/riscv: add support for RV64 Xiangshan Nanhu CPU (2024-12-20 11:22:47 +1000) ---------------------------------------------------------------- RISC-V PR for 10.0 * Correct the validness check of iova * Fix APLIC in_clrip and clripnum write emulation * Support riscv-iommu-sys device * Add Tenstorrent Ascalon CPU * Add AIA userspace irqchip_split support * Add Microblaze V generic board * Upgrade ACPI SPCR table to support SPCR table revision 4 format * Remove tswap64() calls from HTIF * Support 64-bit address of initrd * Introduce svukte ISA extension * Support ssstateen extension * Support for RV64 Xiangshan Nanhu CPU ---------------------------------------------------------------- Anton Blanchard (1): target/riscv: Add Tenstorrent Ascalon CPU Daniel Henrique Barboza (15): hw/riscv/riscv-iommu.c: add riscv_iommu_instance_init() hw/riscv/riscv-iommu: parametrize CAP.IGS hw/riscv/virt.c, riscv-iommu-sys.c: add MSIx support hw/riscv/riscv-iommu: implement reset protocol docs/specs: add riscv-iommu-sys information hw/intc/riscv_aplic: rename is_kvm_aia() hw/riscv/virt.c: reduce virt_use_kvm_aia() usage hw/riscv/virt.c: rename helper to virt_use_kvm_aia_aplic_imsic() target/riscv/kvm: consider irqchip_split() in aia_create() hw/riscv/virt.c, riscv_aplic.c: add 'emulated_aplic' helpers hw/intc/riscv_aplic: add kvm_msicfgaddr for split mode aplic-imsic target/riscv/kvm: remove irqchip_split() restriction docs: update riscv/virt.rst with kernel-irqchip=split support target/riscv/tcg: hide warn for named feats when disabling via priv_ver target/riscv: add ssstateen Fea.Wang (6): target/riscv: Add svukte extension capability variable target/riscv: Support senvcfg[UKTE] bit when svukte extension is enabled target/riscv: Support hstatus[HUKTE] bit when svukte extension is enabled target/riscv: Check memory access to meet svukte rule target/riscv: Expose svukte ISA extension target/riscv: Check svukte is not enabled in RV32 Jason Chien (1): hw/riscv/riscv-iommu.c: Correct the validness check of iova Jim Shu (3): hw/riscv: Support to load DTB after 3GB memory on 64-bit system. hw/riscv: Add a new struct RISCVBootInfo hw/riscv: Add the checking if DTB overlaps to kernel or initrd MollyChen (1): target/riscv: add support for RV64 Xiangshan Nanhu CPU Philippe Mathieu-Daudé (5): MAINTAINERS: Cover RISC-V HTIF interface hw/char/riscv_htif: Explicit little-endian implementation hw/char/riscv_htif: Clarify MemoryRegionOps expect 32-bit accesses target/riscv: Include missing headers in 'vector_internals.h' target/riscv: Include missing headers in 'internals.h' Sai Pavan Boddu (1): hw/riscv: Add Microblaze V generic board Sia Jee Heng (3): qtest: allow SPCR acpi table changes hw/acpi: Upgrade ACPI SPCR table to support SPCR table revision 4 format tests/qtest/bios-tables-test: Update virt SPCR golden reference for RISC-V Sunil V L (1): hw/riscv/virt: Add IOMMU as platform device if the option is set Tomasz Jeznach (1): hw/riscv: add riscv-iommu-sys platform device Yong-Xuan Wang (1): hw/intc/riscv_aplic: Fix APLIC in_clrip and clripnum write emulation MAINTAINERS | 8 + docs/specs/index.rst | 1 + docs/specs/riscv-aia.rst | 83 ++++++++++ docs/specs/riscv-iommu.rst | 30 +++- docs/system/riscv/microblaze-v-generic.rst | 42 +++++ docs/system/riscv/virt.rst | 17 ++ docs/system/target-riscv.rst | 1 + hw/riscv/riscv-iommu-bits.h | 6 + hw/riscv/riscv-iommu.h | 5 + include/hw/acpi/acpi-defs.h | 7 +- include/hw/acpi/aml-build.h | 2 +- include/hw/intc/riscv_aplic.h | 8 + include/hw/riscv/boot.h | 28 +++- include/hw/riscv/iommu.h | 10 +- include/hw/riscv/virt.h | 6 +- target/riscv/cpu-qom.h | 2 + target/riscv/cpu_bits.h | 2 + target/riscv/cpu_cfg.h | 2 + target/riscv/internals.h | 3 + target/riscv/vector_internals.h | 1 + hw/acpi/aml-build.c | 20 ++- hw/arm/virt-acpi-build.c | 8 +- hw/char/riscv_htif.c | 15 +- hw/intc/riscv_aplic.c | 74 +++++++-- hw/loongarch/acpi-build.c | 6 +- hw/riscv/boot.c | 100 +++++++---- hw/riscv/microblaze-v-generic.c | 184 +++++++++++++++++++++ hw/riscv/microchip_pfsoc.c | 13 +- hw/riscv/opentitan.c | 4 +- hw/riscv/riscv-iommu-pci.c | 21 +++ hw/riscv/riscv-iommu-sys.c | 256 +++++++++++++++++++++++++++++ hw/riscv/riscv-iommu.c | 137 ++++++++++----- hw/riscv/sifive_e.c | 4 +- hw/riscv/sifive_u.c | 18 +- hw/riscv/spike.c | 14 +- hw/riscv/virt-acpi-build.c | 12 +- hw/riscv/virt.c | 159 +++++++++++++++--- target/riscv/cpu.c | 101 ++++++++++++ target/riscv/cpu_helper.c | 55 +++++++ target/riscv/csr.c | 7 + target/riscv/kvm/kvm-cpu.c | 43 ++--- target/riscv/tcg/tcg-cpu.c | 27 ++- hw/riscv/Kconfig | 8 + hw/riscv/meson.build | 3 +- hw/riscv/trace-events | 4 + tests/data/acpi/riscv64/virt/SPCR | Bin 80 -> 90 bytes 46 files changed, 1380 insertions(+), 177 deletions(-) create mode 100644 docs/specs/riscv-aia.rst create mode 100644 docs/system/riscv/microblaze-v-generic.rst create mode 100644 hw/riscv/microblaze-v-generic.c create mode 100644 hw/riscv/riscv-iommu-sys.c
From: Jason Chien <jason.chien@sifive.com> From RISCV IOMMU spec section 2.1.3: When SXL is 1, the following rules apply: - If the first-stage is not Bare, then a page fault corresponding to the original access type occurs if the IOVA has bits beyond bit 31 set to 1. - If the second-stage is not Bare, then a guest page fault corresponding to the original access type occurs if the incoming GPA has bits beyond bit 33 set to 1. From RISCV IOMMU spec section 2.3 step 17: Use the process specified in Section "Two-Stage Address Translation" of the RISC-V Privileged specification to determine the GPA accessed by the transaction. From RISCV IOMMU spec section 2.3 step 19: Use the second-stage address translation process specified in Section "Two-Stage Address Translation" of the RISC-V Privileged specification to translate the GPA A to determine the SPA accessed by the transaction. This commit adds the iova check with the following rules: - For Sv32, Sv32x4, Sv39x4, Sv48x4 and Sv57x4, the iova must be zero extended. - For Sv39, Sv48 and Sv57, the iova must be signed extended with most significant bit. Signed-off-by: Jason Chien <jason.chien@sifive.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20241114065617.25133-1-jason.chien@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 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 int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, /* Address range check before first level lookup */ if (!sc[pass].step) { - const uint64_t va_mask = (1ULL << (va_skip + va_bits)) - 1; - if ((addr & va_mask) != addr) { - return RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED; + const uint64_t va_len = va_skip + va_bits; + const uint64_t va_mask = (1ULL << va_len) - 1; + + if (pass == S_STAGE && va_len > 32) { + target_ulong mask, masked_msbs; + + mask = (1L << (TARGET_LONG_BITS - (va_len - 1))) - 1; + masked_msbs = (addr >> (va_len - 1)) & mask; + + if (masked_msbs != 0 && masked_msbs != mask) { + return (iotlb->perm & IOMMU_WO) ? + RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S : + RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S; + } + } else { + if ((addr & va_mask) != addr) { + return (iotlb->perm & IOMMU_WO) ? + RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS : + RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS; + } } } -- 2.47.1
From: Yong-Xuan Wang <yongxuan.wang@sifive.com> In the section "4.7 Precise effects on interrupt-pending bits" of the RISC-V AIA specification defines that: "If the source mode is Level1 or Level0 and the interrupt domain is configured in MSI delivery mode (domaincfg.DM = 1): The pending bit is cleared whenever the rectified input value is low, when the interrupt is forwarded by MSI, or by a relevant write to an in_clrip register or to clripnum." Update the riscv_aplic_set_pending() to match the spec. Fixes: bf31cf06eb ("hw/intc/riscv_aplic: Fix setipnum_le write emulation for APLIC MSI-mode") Signed-off-by: Yong-Xuan Wang <yongxuan.wang@sifive.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241029085349.30412-1-yongxuan.wang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/intc/riscv_aplic.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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_set_pending(RISCVAPLICState *aplic, if ((sm == APLIC_SOURCECFG_SM_LEVEL_HIGH) || (sm == APLIC_SOURCECFG_SM_LEVEL_LOW)) { - if (!aplic->msimode || (aplic->msimode && !pending)) { + if (!aplic->msimode) { return; } + if (aplic->msimode && !pending) { + goto noskip_write_pending; + } if ((aplic->state[irq] & APLIC_ISTATE_INPUT) && (sm == APLIC_SOURCECFG_SM_LEVEL_LOW)) { return; @@ -XXX,XX +XXX,XX @@ static void riscv_aplic_set_pending(RISCVAPLICState *aplic, } } +noskip_write_pending: riscv_aplic_set_pending_raw(aplic, irq, pending); } -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Move all the static initializion of the device to an init() function, leaving only the dynamic initialization to be done during realize. With this change s->cap is initialized with RISCV_IOMMU_CAP_DBG during init(), and realize() will increment s->cap with the extra caps. This will allow callers to add IOMMU capabilities before the realization. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241106133407.604587-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu.c | 71 +++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 32 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 const MemoryRegionOps riscv_iommu_trap_ops = { } }; +static void riscv_iommu_instance_init(Object *obj) +{ + RISCVIOMMUState *s = RISCV_IOMMU(obj); + + /* Enable translation debug interface */ + s->cap = RISCV_IOMMU_CAP_DBG; + + /* Report QEMU target physical address space limits */ + s->cap = set_field(s->cap, RISCV_IOMMU_CAP_PAS, + TARGET_PHYS_ADDR_SPACE_BITS); + + /* TODO: method to report supported PID bits */ + s->pid_bits = 8; /* restricted to size of MemTxAttrs.pid */ + s->cap |= RISCV_IOMMU_CAP_PD8; + + /* register storage */ + s->regs_rw = 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); + + /* Mark all registers read-only */ + memset(s->regs_ro, 0xff, RISCV_IOMMU_REG_SIZE); + + /* Device translation context cache */ + s->ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash, + riscv_iommu_ctx_equal, + g_free, NULL); + + s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash, + riscv_iommu_iot_equal, + g_free, NULL); + + s->iommus.le_next = NULL; + s->iommus.le_prev = NULL; + QLIST_INIT(&s->spaces); +} + static void riscv_iommu_realize(DeviceState *dev, Error **errp) { RISCVIOMMUState *s = RISCV_IOMMU(dev); - s->cap = s->version & RISCV_IOMMU_CAP_VERSION; + s->cap |= s->version & RISCV_IOMMU_CAP_VERSION; if (s->enable_msi) { s->cap |= RISCV_IOMMU_CAP_MSI_FLAT | RISCV_IOMMU_CAP_MSI_MRIF; } @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp) s->cap |= RISCV_IOMMU_CAP_SV32X4 | RISCV_IOMMU_CAP_SV39X4 | RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4; } - /* Enable translation debug interface */ - s->cap |= RISCV_IOMMU_CAP_DBG; - - /* Report QEMU target physical address space limits */ - s->cap = set_field(s->cap, RISCV_IOMMU_CAP_PAS, - TARGET_PHYS_ADDR_SPACE_BITS); - - /* TODO: method to report supported PID bits */ - s->pid_bits = 8; /* restricted to size of MemTxAttrs.pid */ - s->cap |= RISCV_IOMMU_CAP_PD8; /* Out-of-reset translation mode: OFF (DMA disabled) BARE (passthrough) */ s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, s->enable_off ? RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE); - /* register storage */ - s->regs_rw = 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); - - /* Mark all registers read-only */ - memset(s->regs_ro, 0xff, RISCV_IOMMU_REG_SIZE); - /* * Register complete MMIO space, including MSI/PBA registers. * Note, PCIDevice implementation will add overlapping MR for MSI/PBA, @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->trap_mr, OBJECT(dev), &riscv_iommu_trap_ops, s, "riscv-iommu-trap", ~0ULL); address_space_init(&s->trap_as, &s->trap_mr, "riscv-iommu-trap-as"); - - /* Device translation context cache */ - s->ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash, - riscv_iommu_ctx_equal, - g_free, NULL); - - s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash, - riscv_iommu_iot_equal, - g_free, NULL); - - s->iommus.le_next = NULL; - s->iommus.le_prev = NULL; - QLIST_INIT(&s->spaces); } static void riscv_iommu_unrealize(DeviceState *dev) @@ -XXX,XX +XXX,XX @@ static const TypeInfo riscv_iommu_info = { .name = TYPE_RISCV_IOMMU, .parent = TYPE_DEVICE, .instance_size = sizeof(RISCVIOMMUState), + .instance_init = riscv_iommu_instance_init, .class_init = riscv_iommu_class_init, }; -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Interrupt Generation Support (IGS) is a capability that is tied to the interrupt deliver mechanism, not with the core IOMMU emulation. We should allow device implementations to set IGS as they wish. A new helper is added to make it easier for device impls to set IGS. Use it in our existing IOMMU device (riscv-iommu-pci) to set RISCV_IOMMU_CAPS_IGS_MSI. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241106133407.604587-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu-bits.h | 6 ++++++ hw/riscv/riscv-iommu.h | 4 ++++ hw/riscv/riscv-iommu-pci.c | 1 + hw/riscv/riscv-iommu.c | 5 +++++ 4 files changed, 16 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_pq_record { #define RISCV_IOMMU_CAP_PD17 BIT_ULL(39) #define RISCV_IOMMU_CAP_PD20 BIT_ULL(40) +enum riscv_iommu_igs_modes { + RISCV_IOMMU_CAP_IGS_MSI = 0, + RISCV_IOMMU_CAP_IGS_WSI, + RISCV_IOMMU_CAP_IGS_BOTH +}; + /* 5.4 Features control register (32bits) */ #define RISCV_IOMMU_REG_FCTL 0x0008 #define RISCV_IOMMU_FCTL_BE BIT(0) 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 @@ #include "qom/object.h" #include "hw/riscv/iommu.h" +#include "hw/riscv/riscv-iommu-bits.h" + +typedef enum riscv_iommu_igs_modes riscv_iommu_igs_mode; struct RISCVIOMMUState { /*< private >*/ @@ -XXX,XX +XXX,XX @@ struct RISCVIOMMUState { void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus, Error **errp); +void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode); /* private helpers */ diff --git a/hw/riscv/riscv-iommu-pci.c b/hw/riscv/riscv-iommu-pci.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu-pci.c +++ b/hw/riscv/riscv-iommu-pci.c @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_pci_init(Object *obj) qdev_alias_all_properties(DEVICE(iommu), obj); iommu->icvec_avail_vectors = RISCV_IOMMU_PCI_ICVEC_VECTORS; + riscv_iommu_set_cap_igs(iommu, RISCV_IOMMU_CAP_IGS_MSI); } static const Property riscv_iommu_pci_properties[] = { 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 const MemoryRegionOps riscv_iommu_trap_ops = { } }; +void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode) +{ + s->cap = set_field(s->cap, RISCV_IOMMU_CAP_IGS, mode); +} + static void riscv_iommu_instance_init(Object *obj) { RISCVIOMMUState *s = RISCV_IOMMU(obj); -- 2.47.1
From: Tomasz Jeznach <tjeznach@rivosinc.com> This device models the RISC-V IOMMU as a sysbus device. The same design decisions taken in the riscv-iommu-pci device were kept, namely the existence of 4 vectors are available for each interrupt cause. The WSIs are emitted using the input of the s->notify() callback as a index to an IRQ list. The IRQ list starts at 'base_irq' and goes until base_irq + 3. This means that boards must have 4 contiguous IRQ lines available, starting from 'base_irq'. Signed-off-by: Tomasz Jeznach <tjeznach@rivosinc.com> Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241106133407.604587-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/iommu.h | 4 ++ hw/riscv/riscv-iommu-sys.c | 128 +++++++++++++++++++++++++++++++++++++ hw/riscv/riscv-iommu.c | 3 +- hw/riscv/meson.build | 2 +- 4 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 hw/riscv/riscv-iommu-sys.c diff --git a/include/hw/riscv/iommu.h b/include/hw/riscv/iommu.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/iommu.h +++ b/include/hw/riscv/iommu.h @@ -XXX,XX +XXX,XX @@ typedef struct RISCVIOMMUSpace RISCVIOMMUSpace; OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStatePci, RISCV_IOMMU_PCI) typedef struct RISCVIOMMUStatePci RISCVIOMMUStatePci; +#define TYPE_RISCV_IOMMU_SYS "riscv-iommu-device" +OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStateSys, RISCV_IOMMU_SYS) +typedef struct RISCVIOMMUStateSys RISCVIOMMUStateSys; + #endif diff --git a/hw/riscv/riscv-iommu-sys.c b/hw/riscv/riscv-iommu-sys.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/riscv/riscv-iommu-sys.c @@ -XXX,XX +XXX,XX @@ +/* + * QEMU emulation of an RISC-V IOMMU Platform Device + * + * Copyright (C) 2022-2023 Rivos Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/pci/pci_bus.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/host-utils.h" +#include "qemu/module.h" +#include "qom/object.h" + +#include "riscv-iommu.h" + +#define RISCV_IOMMU_SYSDEV_ICVEC_VECTORS 0x3333 + +/* RISC-V IOMMU System Platform Device Emulation */ + +struct RISCVIOMMUStateSys { + SysBusDevice parent; + uint64_t addr; + uint32_t base_irq; + DeviceState *irqchip; + RISCVIOMMUState iommu; + qemu_irq irqs[RISCV_IOMMU_INTR_COUNT]; +}; + +static void riscv_iommu_sysdev_notify(RISCVIOMMUState *iommu, + unsigned vector) +{ + RISCVIOMMUStateSys *s = container_of(iommu, RISCVIOMMUStateSys, iommu); + uint32_t fctl = riscv_iommu_reg_get32(iommu, RISCV_IOMMU_REG_FCTL); + + /* We do not support MSIs yet */ + if (!(fctl & RISCV_IOMMU_FCTL_WSI)) { + return; + } + + qemu_irq_pulse(s->irqs[vector]); +} + +static void riscv_iommu_sys_realize(DeviceState *dev, Error **errp) +{ + RISCVIOMMUStateSys *s = RISCV_IOMMU_SYS(dev); + SysBusDevice *sysdev = SYS_BUS_DEVICE(s); + PCIBus *pci_bus; + qemu_irq irq; + + qdev_realize(DEVICE(&s->iommu), NULL, errp); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iommu.regs_mr); + if (s->addr) { + sysbus_mmio_map(SYS_BUS_DEVICE(s), 0, s->addr); + } + + pci_bus = (PCIBus *) object_resolve_path_type("", TYPE_PCI_BUS, NULL); + if (pci_bus) { + riscv_iommu_pci_setup_iommu(&s->iommu, pci_bus, errp); + } + + s->iommu.notify = riscv_iommu_sysdev_notify; + + /* 4 IRQs are defined starting from s->base_irq */ + for (int i = 0; i < RISCV_IOMMU_INTR_COUNT; i++) { + sysbus_init_irq(sysdev, &s->irqs[i]); + irq = qdev_get_gpio_in(s->irqchip, s->base_irq + i); + sysbus_connect_irq(sysdev, i, irq); + } +} + +static void riscv_iommu_sys_init(Object *obj) +{ + RISCVIOMMUStateSys *s = RISCV_IOMMU_SYS(obj); + RISCVIOMMUState *iommu = &s->iommu; + + object_initialize_child(obj, "iommu", iommu, TYPE_RISCV_IOMMU); + qdev_alias_all_properties(DEVICE(iommu), obj); + + iommu->icvec_avail_vectors = RISCV_IOMMU_SYSDEV_ICVEC_VECTORS; + riscv_iommu_set_cap_igs(iommu, RISCV_IOMMU_CAP_IGS_WSI); +} + +static Property riscv_iommu_sys_properties[] = { + DEFINE_PROP_UINT64("addr", RISCVIOMMUStateSys, addr, 0), + DEFINE_PROP_UINT32("base-irq", RISCVIOMMUStateSys, base_irq, 0), + DEFINE_PROP_LINK("irqchip", RISCVIOMMUStateSys, irqchip, + TYPE_DEVICE, DeviceState *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void riscv_iommu_sys_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = riscv_iommu_sys_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, riscv_iommu_sys_properties); +} + +static const TypeInfo riscv_iommu_sys = { + .name = TYPE_RISCV_IOMMU_SYS, + .parent = TYPE_SYS_BUS_DEVICE, + .class_init = riscv_iommu_sys_class_init, + .instance_init = riscv_iommu_sys_init, + .instance_size = sizeof(RISCVIOMMUStateSys), +}; + +static void riscv_iommu_register_sys(void) +{ + type_register_static(&riscv_iommu_sys); +} + +type_init(riscv_iommu_register_sys) 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 uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type) static void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type) { - const uint32_t fctl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FCTL); uint32_t ipsr, icvec, vector; - if (fctl & RISCV_IOMMU_FCTL_WSI || !s->notify) { + if (!s->notify) { return; } 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_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_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_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files('riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c')) hw_arch += {'riscv': riscv_ss} -- 2.47.1
From: Sunil V L <sunilvl@ventanamicro.com> Add a new machine option called 'iommu-sys' that enables a riscv-iommu-sys platform device for the 'virt' machine. The option is default 'off'. The device will use IRQs 36 to 39. We will not support both riscv-iommu-sys and riscv-iommu-pci devices in the same board in this first implementation. If a riscv-iommu-pci device is added in the command line we will disable the riscv-iommu-sys device. Signed-off-by: Sunil V L <sunilvl@ventanamicro.com> Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241106133407.604587-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/iommu.h | 2 + include/hw/riscv/virt.h | 6 ++- hw/riscv/virt.c | 104 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 109 insertions(+), 3 deletions(-) diff --git a/include/hw/riscv/iommu.h b/include/hw/riscv/iommu.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/iommu.h +++ b/include/hw/riscv/iommu.h @@ -XXX,XX +XXX,XX @@ typedef struct RISCVIOMMUStatePci RISCVIOMMUStatePci; OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStateSys, RISCV_IOMMU_SYS) typedef struct RISCVIOMMUStateSys RISCVIOMMUStateSys; +#define FDT_IRQ_TYPE_EDGE_LOW 1 + #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 @@ struct RISCVVirtState { OnOffAuto acpi; const MemMapEntry *memmap; struct GPEXHost *gpex_host; + OnOffAuto iommu_sys; }; enum { @@ -XXX,XX +XXX,XX @@ enum { VIRT_PCIE_MMIO, VIRT_PCIE_PIO, VIRT_PLATFORM_BUS, - VIRT_PCIE_ECAM + VIRT_PCIE_ECAM, + VIRT_IOMMU_SYS, }; enum { @@ -XXX,XX +XXX,XX @@ enum { VIRTIO_IRQ = 1, /* 1 to 8 */ VIRTIO_COUNT = 8, PCIE_IRQ = 0x20, /* 32 to 35 */ + IOMMU_SYS_IRQ = 0x24, /* 36-39 */ VIRT_PLATFORM_BUS_IRQ = 64, /* 64 to 95 */ }; @@ -XXX,XX +XXX,XX @@ enum { 1 + FDT_APLIC_INT_CELLS) 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); 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 "target/riscv/pmu.h" #include "hw/riscv/riscv_hart.h" #include "hw/riscv/iommu.h" +#include "hw/riscv/riscv-iommu-bits.h" #include "hw/riscv/virt.h" #include "hw/riscv/boot.h" #include "hw/riscv/numa.h" @@ -XXX,XX +XXX,XX @@ static const MemMapEntry virt_memmap[] = { [VIRT_CLINT] = { 0x2000000, 0x10000 }, [VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 }, [VIRT_PCIE_PIO] = { 0x3000000, 0x10000 }, + [VIRT_IOMMU_SYS] = { 0x3010000, 0x1000 }, [VIRT_PLATFORM_BUS] = { 0x4000000, 0x2000000 }, [VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) }, [VIRT_APLIC_M] = { 0xc000000, APLIC_SIZE(VIRT_CPUS_MAX) }, @@ -XXX,XX +XXX,XX @@ static void create_fdt_virtio(RISCVVirtState *s, const MemMapEntry *memmap, static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, uint32_t irq_pcie_phandle, - uint32_t msi_pcie_phandle) + uint32_t msi_pcie_phandle, + uint32_t iommu_sys_phandle) { g_autofree char *name = NULL; MachineState *ms = MACHINE(s); @@ -XXX,XX +XXX,XX @@ static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, 2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.size); + if (virt_is_iommu_sys_enabled(s)) { + qemu_fdt_setprop_cells(ms->fdt, name, "iommu-map", + 0, iommu_sys_phandle, 0, 0, 0, + iommu_sys_phandle, 0, 0xffff); + } + create_pcie_irq_map(s, ms->fdt, name, irq_pcie_phandle); } @@ -XXX,XX +XXX,XX @@ static void create_fdt_virtio_iommu(RISCVVirtState *s, uint16_t bdf) bdf + 1, iommu_phandle, bdf + 1, 0xffff - bdf); } +static void create_fdt_iommu_sys(RISCVVirtState *s, uint32_t irq_chip, + uint32_t *iommu_sys_phandle) +{ + const char comp[] = "riscv,iommu"; + void *fdt = MACHINE(s)->fdt; + uint32_t iommu_phandle; + g_autofree char *iommu_node = NULL; + hwaddr addr = s->memmap[VIRT_IOMMU_SYS].base; + hwaddr size = s->memmap[VIRT_IOMMU_SYS].size; + uint32_t iommu_irq_map[RISCV_IOMMU_INTR_COUNT] = { + IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_CQ, + IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_FQ, + IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_PM, + IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_PQ, + }; + + iommu_node = g_strdup_printf("/soc/iommu@%x", + (unsigned int) s->memmap[VIRT_IOMMU_SYS].base); + iommu_phandle = qemu_fdt_alloc_phandle(fdt); + qemu_fdt_add_subnode(fdt, iommu_node); + + qemu_fdt_setprop(fdt, iommu_node, "compatible", comp, sizeof(comp)); + qemu_fdt_setprop_cell(fdt, iommu_node, "#iommu-cells", 1); + qemu_fdt_setprop_cell(fdt, iommu_node, "phandle", iommu_phandle); + + qemu_fdt_setprop_cells(fdt, iommu_node, "reg", + addr >> 32, addr, size >> 32, size); + qemu_fdt_setprop_cell(fdt, iommu_node, "interrupt-parent", irq_chip); + + qemu_fdt_setprop_cells(fdt, iommu_node, "interrupts", + iommu_irq_map[0], FDT_IRQ_TYPE_EDGE_LOW, + iommu_irq_map[1], FDT_IRQ_TYPE_EDGE_LOW, + iommu_irq_map[2], FDT_IRQ_TYPE_EDGE_LOW, + iommu_irq_map[3], FDT_IRQ_TYPE_EDGE_LOW); + + *iommu_sys_phandle = iommu_phandle; +} + static void create_fdt_iommu(RISCVVirtState *s, uint16_t bdf) { const char comp[] = "riscv,pci-iommu"; @@ -XXX,XX +XXX,XX @@ static void finalize_fdt(RISCVVirtState *s) { uint32_t phandle = 1, irq_mmio_phandle = 1, msi_pcie_phandle = 1; uint32_t irq_pcie_phandle = 1, irq_virtio_phandle = 1; + uint32_t iommu_sys_phandle = 1; create_fdt_sockets(s, virt_memmap, &phandle, &irq_mmio_phandle, &irq_pcie_phandle, &irq_virtio_phandle, @@ -XXX,XX +XXX,XX @@ static void finalize_fdt(RISCVVirtState *s) create_fdt_virtio(s, virt_memmap, irq_virtio_phandle); - create_fdt_pcie(s, virt_memmap, irq_pcie_phandle, msi_pcie_phandle); + if (virt_is_iommu_sys_enabled(s)) { + create_fdt_iommu_sys(s, irq_mmio_phandle, &iommu_sys_phandle); + } + create_fdt_pcie(s, virt_memmap, irq_pcie_phandle, msi_pcie_phandle, + iommu_sys_phandle); create_fdt_reset(s, virt_memmap, &phandle); @@ -XXX,XX +XXX,XX @@ static void virt_machine_init(MachineState *machine) create_fdt(s, memmap); } + if (virt_is_iommu_sys_enabled(s)) { + DeviceState *iommu_sys = qdev_new(TYPE_RISCV_IOMMU_SYS); + + object_property_set_uint(OBJECT(iommu_sys), "addr", + s->memmap[VIRT_IOMMU_SYS].base, + &error_fatal); + object_property_set_uint(OBJECT(iommu_sys), "base-irq", + IOMMU_SYS_IRQ, + &error_fatal); + object_property_set_link(OBJECT(iommu_sys), "irqchip", + OBJECT(mmio_irqchip), + &error_fatal); + + sysbus_realize_and_unref(SYS_BUS_DEVICE(iommu_sys), &error_fatal); + } + s->machine_done.notify = virt_machine_done; qemu_add_machine_init_done_notifier(&s->machine_done); } @@ -XXX,XX +XXX,XX @@ static void virt_machine_instance_init(Object *obj) s->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); s->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); s->acpi = ON_OFF_AUTO_AUTO; + s->iommu_sys = ON_OFF_AUTO_AUTO; } static char *virt_get_aia_guests(Object *obj, Error **errp) @@ -XXX,XX +XXX,XX @@ static void virt_set_aclint(Object *obj, bool value, Error **errp) s->have_aclint = value; } +bool virt_is_iommu_sys_enabled(RISCVVirtState *s) +{ + return s->iommu_sys == ON_OFF_AUTO_ON; +} + +static void virt_get_iommu_sys(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + OnOffAuto iommu_sys = s->iommu_sys; + + visit_type_OnOffAuto(v, name, &iommu_sys, errp); +} + +static void virt_set_iommu_sys(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &s->iommu_sys, errp); +} + bool virt_is_acpi_enabled(RISCVVirtState *s) { return s->acpi != ON_OFF_AUTO_OFF; @@ -XXX,XX +XXX,XX @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, DeviceState *dev) { MachineClass *mc = MACHINE_GET_CLASS(machine); + RISCVVirtState *s = RISCV_VIRT_MACHINE(machine); if (device_is_dynamic_sysbus(mc, dev) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) || object_dynamic_cast(OBJECT(dev), TYPE_RISCV_IOMMU_PCI)) { + s->iommu_sys = ON_OFF_AUTO_OFF; return HOTPLUG_HANDLER(machine); } @@ -XXX,XX +XXX,XX @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, if (object_dynamic_cast(OBJECT(dev), TYPE_RISCV_IOMMU_PCI)) { create_fdt_iommu(s, pci_get_bdf(PCI_DEVICE(dev))); + s->iommu_sys = ON_OFF_AUTO_OFF; } } @@ -XXX,XX +XXX,XX @@ static void virt_machine_class_init(ObjectClass *oc, void *data) NULL, NULL); object_class_property_set_description(oc, "acpi", "Enable ACPI"); + + object_class_property_add(oc, "iommu-sys", "OnOffAuto", + virt_get_iommu_sys, virt_set_iommu_sys, + NULL, NULL); + object_class_property_set_description(oc, "iommu-sys", + "Enable IOMMU platform device"); } static const TypeInfo virt_machine_typeinfo = { -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> MSIx support is added in the RISC-V IOMMU platform device by including the required MSIx facilities to alow software to properly setup the MSIx subsystem. We took inspiration of what is being done in the riscv-iommu-pci device, mainly msix_init() and msix_notify(), while keeping in mind that riscv-iommu-sys isn't a true PCI device and we don't need to copy/paste all the contents of these MSIx functions. Two extra MSI MemoryRegions were added: 'msix-table' and 'msix-pba'. They are used to manage r/w of the MSI table and Pending Bit Array (PBA) respectively. Both are subregions of the main IOMMU memory region, iommu->regs_mr, initialized during riscv_iommu_realize(), and each one has their own handlers for MSIx reads and writes. This is the expected memory map when using this device in the 'virt' machine: 0000000003010000-0000000003010fff (prio 0, i/o): riscv-iommu-regs 0000000003010300-000000000301034f (prio 0, i/o): msix-table 0000000003010400-0000000003010407 (prio 0, i/o): msix-pba We're now able to set IGS to RISCV_IOMMU_CAP_IGS_BOTH, and userspace is free to decide which interrupt model to use. Enabling MSIx support for this device in the 'virt' machine requires adding 'msi-parent' in the iommu-sys DT. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241106133407.604587-6-dbarboza@ventanamicro.com> [ Changes by AF: - Used PRIx64 in trace ] Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu-sys.c | 116 +++++++++++++++++++++++++++++++++++-- hw/riscv/virt.c | 6 +- hw/riscv/trace-events | 2 + 3 files changed, 119 insertions(+), 5 deletions(-) diff --git a/hw/riscv/riscv-iommu-sys.c b/hw/riscv/riscv-iommu-sys.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu-sys.c +++ b/hw/riscv/riscv-iommu-sys.c @@ -XXX,XX +XXX,XX @@ #include "qemu/host-utils.h" #include "qemu/module.h" #include "qom/object.h" +#include "exec/exec-all.h" +#include "trace.h" #include "riscv-iommu.h" #define RISCV_IOMMU_SYSDEV_ICVEC_VECTORS 0x3333 +#define RISCV_IOMMU_PCI_MSIX_VECTORS 5 + /* RISC-V IOMMU System Platform Device Emulation */ struct RISCVIOMMUStateSys { @@ -XXX,XX +XXX,XX @@ struct RISCVIOMMUStateSys { uint32_t base_irq; DeviceState *irqchip; RISCVIOMMUState iommu; + + /* Wired int support */ qemu_irq irqs[RISCV_IOMMU_INTR_COUNT]; + + /* Memory Regions for MSIX table and pending bit entries. */ + MemoryRegion msix_table_mmio; + MemoryRegion msix_pba_mmio; + uint8_t *msix_table; + uint8_t *msix_pba; +}; + +static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr, + unsigned size) +{ + RISCVIOMMUStateSys *s = opaque; + + g_assert(addr + size <= RISCV_IOMMU_PCI_MSIX_VECTORS * PCI_MSIX_ENTRY_SIZE); + return pci_get_long(s->msix_table + addr); +} + +static void msix_table_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + RISCVIOMMUStateSys *s = opaque; + + g_assert(addr + size <= RISCV_IOMMU_PCI_MSIX_VECTORS * PCI_MSIX_ENTRY_SIZE); + pci_set_long(s->msix_table + addr, val); +} + +static const MemoryRegionOps msix_table_mmio_ops = { + .read = msix_table_mmio_read, + .write = msix_table_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .max_access_size = 4, + }, +}; + +static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, + unsigned size) +{ + RISCVIOMMUStateSys *s = opaque; + + return pci_get_long(s->msix_pba + addr); +} + +static void msix_pba_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ +} + +static const MemoryRegionOps msix_pba_mmio_ops = { + .read = msix_pba_mmio_read, + .write = msix_pba_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .max_access_size = 4, + }, }; +static void riscv_iommu_sysdev_init_msi(RISCVIOMMUStateSys *s, + uint32_t n_vectors) +{ + RISCVIOMMUState *iommu = &s->iommu; + uint32_t table_size = table_size = n_vectors * PCI_MSIX_ENTRY_SIZE; + uint32_t table_offset = RISCV_IOMMU_REG_MSI_CONFIG; + uint32_t pba_size = QEMU_ALIGN_UP(n_vectors, 64) / 8; + uint32_t pba_offset = RISCV_IOMMU_REG_MSI_CONFIG + 256; + + s->msix_table = g_malloc0(table_size); + s->msix_pba = g_malloc0(pba_size); + + memory_region_init_io(&s->msix_table_mmio, OBJECT(s), &msix_table_mmio_ops, + s, "msix-table", table_size); + memory_region_add_subregion(&iommu->regs_mr, table_offset, + &s->msix_table_mmio); + + memory_region_init_io(&s->msix_pba_mmio, OBJECT(s), &msix_pba_mmio_ops, s, + "msix-pba", pba_size); + memory_region_add_subregion(&iommu->regs_mr, pba_offset, + &s->msix_pba_mmio); +} + +static void riscv_iommu_sysdev_send_MSI(RISCVIOMMUStateSys *s, + uint32_t vector) +{ + uint8_t *table_entry = s->msix_table + vector * PCI_MSIX_ENTRY_SIZE; + uint64_t msi_addr = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR); + uint32_t msi_data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA); + MemTxResult result; + + address_space_stl_le(&address_space_memory, msi_addr, + msi_data, MEMTXATTRS_UNSPECIFIED, &result); + trace_riscv_iommu_sys_msi_sent(vector, msi_addr, msi_data, result); +} + static void riscv_iommu_sysdev_notify(RISCVIOMMUState *iommu, unsigned vector) { RISCVIOMMUStateSys *s = container_of(iommu, RISCVIOMMUStateSys, iommu); uint32_t fctl = riscv_iommu_reg_get32(iommu, RISCV_IOMMU_REG_FCTL); - /* We do not support MSIs yet */ - if (!(fctl & RISCV_IOMMU_FCTL_WSI)) { + if (fctl & RISCV_IOMMU_FCTL_WSI) { + qemu_irq_pulse(s->irqs[vector]); + trace_riscv_iommu_sys_irq_sent(vector); return; } - qemu_irq_pulse(s->irqs[vector]); + riscv_iommu_sysdev_send_MSI(s, vector); } static void riscv_iommu_sys_realize(DeviceState *dev, Error **errp) @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_sys_realize(DeviceState *dev, Error **errp) irq = qdev_get_gpio_in(s->irqchip, s->base_irq + i); sysbus_connect_irq(sysdev, i, irq); } + + riscv_iommu_sysdev_init_msi(s, RISCV_IOMMU_PCI_MSIX_VECTORS); } static void riscv_iommu_sys_init(Object *obj) @@ -XXX,XX +XXX,XX @@ static void riscv_iommu_sys_init(Object *obj) qdev_alias_all_properties(DEVICE(iommu), obj); iommu->icvec_avail_vectors = RISCV_IOMMU_SYSDEV_ICVEC_VECTORS; - riscv_iommu_set_cap_igs(iommu, RISCV_IOMMU_CAP_IGS_WSI); + riscv_iommu_set_cap_igs(iommu, RISCV_IOMMU_CAP_IGS_BOTH); } static Property riscv_iommu_sys_properties[] = { 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_virtio_iommu(RISCVVirtState *s, uint16_t bdf) } static void create_fdt_iommu_sys(RISCVVirtState *s, uint32_t irq_chip, + uint32_t msi_phandle, uint32_t *iommu_sys_phandle) { const char comp[] = "riscv,iommu"; @@ -XXX,XX +XXX,XX @@ static void create_fdt_iommu_sys(RISCVVirtState *s, uint32_t irq_chip, iommu_irq_map[2], FDT_IRQ_TYPE_EDGE_LOW, iommu_irq_map[3], FDT_IRQ_TYPE_EDGE_LOW); + qemu_fdt_setprop_cell(fdt, iommu_node, "msi-parent", msi_phandle); + *iommu_sys_phandle = iommu_phandle; } @@ -XXX,XX +XXX,XX @@ static void finalize_fdt(RISCVVirtState *s) create_fdt_virtio(s, virt_memmap, irq_virtio_phandle); if (virt_is_iommu_sys_enabled(s)) { - create_fdt_iommu_sys(s, irq_mmio_phandle, &iommu_sys_phandle); + create_fdt_iommu_sys(s, irq_mmio_phandle, msi_pcie_phandle, + &iommu_sys_phandle); } create_fdt_pcie(s, virt_memmap, irq_pcie_phandle, msi_pcie_phandle, iommu_sys_phandle); diff --git a/hw/riscv/trace-events b/hw/riscv/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/trace-events +++ b/hw/riscv/trace-events @@ -XXX,XX +XXX,XX @@ riscv_iommu_icvec_write(uint32_t orig, uint32_t actual) "ICVEC write: incoming 0 riscv_iommu_ats(const char *id, unsigned b, unsigned d, unsigned f, uint64_t iova) "%s: translate request %04x:%02x.%u iova: 0x%"PRIx64 riscv_iommu_ats_inval(const char *id) "%s: dev-iotlb invalidate" riscv_iommu_ats_prgr(const char *id) "%s: dev-iotlb page request group response" +riscv_iommu_sys_irq_sent(uint32_t vector) "IRQ sent to vector %u" +riscv_iommu_sys_msi_sent(uint32_t vector, uint64_t msi_addr, uint32_t msi_data, uint32_t result) "MSI sent to vector %u msi_addr 0x%"PRIx64" msi_data 0x%x result %u" -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Add a riscv_iommu_reset() helper in the base emulation code that implements the expected reset behavior as defined by the riscv-iommu spec. Devices can then use this helper in their own reset callbacks. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241106133407.604587-7-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/riscv-iommu.h | 1 + include/hw/riscv/iommu.h | 6 ++++-- hw/riscv/riscv-iommu-pci.c | 20 ++++++++++++++++++++ hw/riscv/riscv-iommu-sys.c | 20 ++++++++++++++++++++ hw/riscv/riscv-iommu.c | 35 +++++++++++++++++++++++++++++++++++ hw/riscv/trace-events | 2 ++ 6 files changed, 82 insertions(+), 2 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 { void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus, Error **errp); void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode); +void riscv_iommu_reset(RISCVIOMMUState *s); /* private helpers */ diff --git a/include/hw/riscv/iommu.h b/include/hw/riscv/iommu.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/riscv/iommu.h +++ b/include/hw/riscv/iommu.h @@ -XXX,XX +XXX,XX @@ typedef struct RISCVIOMMUState RISCVIOMMUState; typedef struct RISCVIOMMUSpace RISCVIOMMUSpace; #define TYPE_RISCV_IOMMU_PCI "riscv-iommu-pci" -OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStatePci, RISCV_IOMMU_PCI) +OBJECT_DECLARE_TYPE(RISCVIOMMUStatePci, RISCVIOMMUPciClass, RISCV_IOMMU_PCI) typedef struct RISCVIOMMUStatePci RISCVIOMMUStatePci; +typedef struct RISCVIOMMUPciClass RISCVIOMMUPciClass; #define TYPE_RISCV_IOMMU_SYS "riscv-iommu-device" -OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStateSys, RISCV_IOMMU_SYS) +OBJECT_DECLARE_TYPE(RISCVIOMMUStateSys, RISCVIOMMUSysClass, RISCV_IOMMU_SYS) typedef struct RISCVIOMMUStateSys RISCVIOMMUStateSys; +typedef struct RISCVIOMMUSysClass RISCVIOMMUSysClass; #define FDT_IRQ_TYPE_EDGE_LOW 1 diff --git a/hw/riscv/riscv-iommu-pci.c b/hw/riscv/riscv-iommu-pci.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu-pci.c +++ b/hw/riscv/riscv-iommu-pci.c @@ -XXX,XX +XXX,XX @@ #include "cpu_bits.h" #include "riscv-iommu.h" #include "riscv-iommu-bits.h" +#include "trace.h" /* RISC-V IOMMU PCI Device Emulation */ #define RISCV_PCI_CLASS_SYSTEM_IOMMU 0x0806 @@ -XXX,XX +XXX,XX @@ typedef struct RISCVIOMMUStatePci { RISCVIOMMUState iommu; /* common IOMMU state */ } RISCVIOMMUStatePci; +struct RISCVIOMMUPciClass { + /*< public >*/ + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; + /* interrupt delivery callback */ static void riscv_iommu_pci_notify(RISCVIOMMUState *iommu, unsigned vector) { @@ -XXX,XX +XXX,XX @@ static const Property riscv_iommu_pci_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void riscv_iommu_pci_reset_hold(Object *obj, ResetType type) +{ + RISCVIOMMUStatePci *pci = RISCV_IOMMU_PCI(obj); + RISCVIOMMUState *iommu = &pci->iommu; + + riscv_iommu_reset(iommu); + + trace_riscv_iommu_pci_reset_hold(type); +} + static void riscv_iommu_pci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = riscv_iommu_pci_reset_hold; k->realize = riscv_iommu_pci_realize; k->exit = riscv_iommu_pci_exit; diff --git a/hw/riscv/riscv-iommu-sys.c b/hw/riscv/riscv-iommu-sys.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu-sys.c +++ b/hw/riscv/riscv-iommu-sys.c @@ -XXX,XX +XXX,XX @@ struct RISCVIOMMUStateSys { uint8_t *msix_pba; }; +struct RISCVIOMMUSysClass { + /*< public >*/ + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; + static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr, unsigned size) { @@ -XXX,XX +XXX,XX @@ static Property riscv_iommu_sys_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void riscv_iommu_sys_reset_hold(Object *obj, ResetType type) +{ + RISCVIOMMUStateSys *sys = RISCV_IOMMU_SYS(obj); + RISCVIOMMUState *iommu = &sys->iommu; + + riscv_iommu_reset(iommu); + + trace_riscv_iommu_sys_reset_hold(type); +} + static void riscv_iommu_sys_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = riscv_iommu_sys_reset_hold; + dc->realize = riscv_iommu_sys_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); device_class_set_props(dc, riscv_iommu_sys_properties); 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_unrealize(DeviceState *dev) g_hash_table_unref(s->ctx_cache); } +void riscv_iommu_reset(RISCVIOMMUState *s) +{ + uint32_t reg_clr; + int ddtp_mode; + + /* + * Clear DDTP while setting DDTP_mode back to user + * initial setting. + */ + ddtp_mode = s->enable_off ? + RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE; + s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, ddtp_mode); + riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, s->ddtp); + + reg_clr = RISCV_IOMMU_CQCSR_CQEN | RISCV_IOMMU_CQCSR_CIE | + RISCV_IOMMU_CQCSR_CQON | RISCV_IOMMU_CQCSR_BUSY; + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, 0, reg_clr); + + reg_clr = RISCV_IOMMU_FQCSR_FQEN | RISCV_IOMMU_FQCSR_FIE | + RISCV_IOMMU_FQCSR_FQON | RISCV_IOMMU_FQCSR_BUSY; + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, 0, reg_clr); + + reg_clr = RISCV_IOMMU_PQCSR_PQEN | RISCV_IOMMU_PQCSR_PIE | + RISCV_IOMMU_PQCSR_PQON | RISCV_IOMMU_PQCSR_BUSY; + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, 0, reg_clr); + + riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0, + RISCV_IOMMU_TR_REQ_CTL_GO_BUSY); + + riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_IPSR, 0); + + g_hash_table_remove_all(s->ctx_cache); + g_hash_table_remove_all(s->iot_cache); +} + static const Property riscv_iommu_properties[] = { DEFINE_PROP_UINT32("version", RISCVIOMMUState, version, RISCV_IOMMU_SPEC_DOT_VER), diff --git a/hw/riscv/trace-events b/hw/riscv/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/trace-events +++ b/hw/riscv/trace-events @@ -XXX,XX +XXX,XX @@ riscv_iommu_ats_inval(const char *id) "%s: dev-iotlb invalidate" riscv_iommu_ats_prgr(const char *id) "%s: dev-iotlb page request group response" riscv_iommu_sys_irq_sent(uint32_t vector) "IRQ sent to vector %u" riscv_iommu_sys_msi_sent(uint32_t vector, uint64_t msi_addr, uint32_t msi_data, uint32_t result) "MSI sent to vector %u msi_addr 0x%"PRIx64" msi_data 0x%x result %u" +riscv_iommu_sys_reset_hold(int reset_type) "reset type %d" +riscv_iommu_pci_reset_hold(int reset_type) "reset type %d" -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241106133407.604587-8-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- docs/specs/riscv-iommu.rst | 30 +++++++++++++++++++++++++++--- docs/system/riscv/virt.rst | 10 ++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/docs/specs/riscv-iommu.rst b/docs/specs/riscv-iommu.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/specs/riscv-iommu.rst +++ b/docs/specs/riscv-iommu.rst @@ -XXX,XX +XXX,XX @@ RISC-V IOMMU support for RISC-V machines QEMU implements a RISC-V IOMMU emulation based on the RISC-V IOMMU spec version 1.0 `iommu1.0`_. -The emulation includes a PCI reference device, riscv-iommu-pci, that QEMU -RISC-V boards can use. The 'virt' RISC-V machine is compatible with this -device. +The emulation includes a PCI reference device (riscv-iommu-pci) and a platform +bus device (riscv-iommu-sys) that QEMU RISC-V boards can use. The 'virt' +RISC-V machine is compatible with both devices. riscv-iommu-pci reference device -------------------------------- @@ -XXX,XX +XXX,XX @@ Several options are available to control the capabilities of the device, namely: - "s-stage": enable s-stage support - "g-stage": enable g-stage support +riscv-iommu-sys device +---------------------- + +This device implements the RISC-V IOMMU emulation as a platform bus device that +RISC-V boards can use. + +For the 'virt' board the device is disabled by default. To enable it use the +'iommu-sys' machine option: + +.. code-block:: bash + + $ qemu-system-riscv64 -M virt,iommu-sys=on (...) + +There is no options to configure the capabilities of this device in the 'virt' +board using the QEMU command line. The device is configured with the following +riscv-iommu options: + +- "ioatc-limit": default value (2Mb) +- "intremap": enabled +- "ats": enabled +- "off": on (DMA disabled) +- "s-stage": enabled +- "g-stage": enabled + .. _iommu1.0: https://github.com/riscv-non-isa/riscv-iommu/releases/download/v1.0/riscv-iommu.pdf .. _linux-v8: https://lore.kernel.org/linux-riscv/cover.1718388908.git.tjeznach@rivosinc.com/ diff --git a/docs/system/riscv/virt.rst b/docs/system/riscv/virt.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/riscv/virt.rst +++ b/docs/system/riscv/virt.rst @@ -XXX,XX +XXX,XX @@ command line: $ qemu-system-riscv64 -M virt -device riscv-iommu-pci (...) +It also has support for the riscv-iommu-sys platform device: + +.. code-block:: bash + + $ qemu-system-riscv64 -M virt,iommu-sys=on (...) + Refer to :ref:`riscv-iommu` for more information on how the RISC-V IOMMU support works. @@ -XXX,XX +XXX,XX @@ The following machine-specific options are supported: having AIA IMSIC (i.e. "aia=aplic-imsic" selected). When not specified, the default number of per-HART VS-level AIA IMSIC pages is 0. +- iommu-sys=[on|off] + + Enables the riscv-iommu-sys platform device. Defaults to 'off'. + Running Linux kernel -------------------- -- 2.47.1
From: Anton Blanchard <antonb@tenstorrent.com> Add a CPU entry for the Tenstorrent Ascalon CPU, a series of 2 wide to 8 wide RV64 cores. More details can be found at https://tenstorrent.com/ip/tt-ascalon Signed-off-by: Anton Blanchard <antonb@tenstorrent.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20241113110459.1607299-1-antonb@tenstorrent.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu-qom.h | 1 + target/riscv/cpu.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -XXX,XX +XXX,XX @@ #define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54") #define TYPE_RISCV_CPU_THEAD_C906 RISCV_CPU_TYPE_NAME("thead-c906") #define TYPE_RISCV_CPU_VEYRON_V1 RISCV_CPU_TYPE_NAME("veyron-v1") +#define TYPE_RISCV_CPU_TT_ASCALON RISCV_CPU_TYPE_NAME("tt-ascalon") #define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host") OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) 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 rv64_veyron_v1_cpu_init(Object *obj) #endif } +/* Tenstorrent Ascalon */ +static void rv64_tt_ascalon_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + RISCVCPU *cpu = RISCV_CPU(obj); + + riscv_cpu_set_misa_ext(env, RVG | RVC | RVS | RVU | RVH | RVV); + env->priv_ver = PRIV_VERSION_1_13_0; + + /* Enable ISA extensions */ + cpu->cfg.mmu = true; + cpu->cfg.vlenb = 256 >> 3; + cpu->cfg.elen = 64; + cpu->env.vext_ver = VEXT_VERSION_1_00_0; + cpu->cfg.rvv_ma_all_1s = true; + cpu->cfg.rvv_ta_all_1s = true; + cpu->cfg.misa_w = true; + cpu->cfg.pmp = true; + cpu->cfg.cbom_blocksize = 64; + cpu->cfg.cbop_blocksize = 64; + cpu->cfg.cboz_blocksize = 64; + cpu->cfg.ext_zic64b = true; + cpu->cfg.ext_zicbom = true; + cpu->cfg.ext_zicbop = true; + cpu->cfg.ext_zicboz = true; + cpu->cfg.ext_zicntr = true; + cpu->cfg.ext_zicond = true; + cpu->cfg.ext_zicsr = true; + cpu->cfg.ext_zifencei = true; + cpu->cfg.ext_zihintntl = true; + cpu->cfg.ext_zihintpause = true; + cpu->cfg.ext_zihpm = true; + cpu->cfg.ext_zimop = true; + cpu->cfg.ext_zawrs = true; + cpu->cfg.ext_zfa = true; + cpu->cfg.ext_zfbfmin = true; + cpu->cfg.ext_zfh = true; + cpu->cfg.ext_zfhmin = true; + cpu->cfg.ext_zcb = true; + cpu->cfg.ext_zcmop = true; + cpu->cfg.ext_zba = true; + cpu->cfg.ext_zbb = true; + cpu->cfg.ext_zbs = true; + cpu->cfg.ext_zkt = true; + cpu->cfg.ext_zvbb = true; + cpu->cfg.ext_zvbc = true; + cpu->cfg.ext_zvfbfmin = true; + cpu->cfg.ext_zvfbfwma = true; + cpu->cfg.ext_zvfh = true; + cpu->cfg.ext_zvfhmin = true; + cpu->cfg.ext_zvkng = true; + cpu->cfg.ext_smaia = true; + cpu->cfg.ext_smstateen = true; + cpu->cfg.ext_ssaia = true; + cpu->cfg.ext_sscofpmf = true; + cpu->cfg.ext_sstc = true; + cpu->cfg.ext_svade = true; + cpu->cfg.ext_svinval = true; + cpu->cfg.ext_svnapot = true; + cpu->cfg.ext_svpbmt = true; + +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(cpu, VM_1_10_SV57); +#endif +} + #ifdef CONFIG_TCG static void rv128_base_cpu_init(Object *obj) { @@ -XXX,XX +XXX,XX @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_U54, MXL_RV64, rv64_sifive_u_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SHAKTI_C, MXL_RV64, rv64_sifive_u_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_THEAD_C906, MXL_RV64, rv64_thead_c906_cpu_init), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_TT_ASCALON, MXL_RV64, rv64_tt_ascalon_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_VEYRON_V1, MXL_RV64, rv64_veyron_v1_cpu_init), #ifdef CONFIG_TCG DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE128, MXL_RV128, rv128_base_cpu_init), -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> The helper is_kvm_aia() is checking not only for AIA, but for aplic-imsic (i.e. "aia=aplic-imsic" in 'virt' RISC-V machine) with an in-kernel chip present. Rename it to be a bit clear what the helper is doing since we'll add more AIA helpers in the next patches. Make the helper public because the 'virt' machine will use it as well. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241119191706.718860-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/intc/riscv_aplic.h | 1 + hw/intc/riscv_aplic.c | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/hw/intc/riscv_aplic.h b/include/hw/intc/riscv_aplic.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/intc/riscv_aplic.h +++ b/include/hw/intc/riscv_aplic.h @@ -XXX,XX +XXX,XX @@ struct RISCVAPLICState { }; void riscv_aplic_add_child(DeviceState *parent, DeviceState *child); +bool riscv_is_kvm_aia_aplic_imsic(bool msimode); DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, uint32_t hartid_base, uint32_t num_harts, uint32_t num_sources, 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 @@ * KVM AIA only supports APLIC MSI, fallback to QEMU emulation if we want to use * APLIC Wired. */ -static bool is_kvm_aia(bool msimode) +bool riscv_is_kvm_aia_aplic_imsic(bool msimode) { return kvm_irqchip_in_kernel() && msimode; } @@ -XXX,XX +XXX,XX @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) uint32_t i; RISCVAPLICState *aplic = RISCV_APLIC(dev); - if (!is_kvm_aia(aplic->msimode)) { + if (!riscv_is_kvm_aia_aplic_imsic(aplic->msimode)) { aplic->bitfield_words = (aplic->num_irqs + 31) >> 5; aplic->sourcecfg = g_new0(uint32_t, aplic->num_irqs); aplic->state = g_new0(uint32_t, aplic->num_irqs); @@ -XXX,XX +XXX,XX @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) * have IRQ lines delegated by their parent APLIC. */ if (!aplic->parent) { - if (kvm_enabled() && is_kvm_aia(aplic->msimode)) { + if (kvm_enabled() && riscv_is_kvm_aia_aplic_imsic(aplic->msimode)) { qdev_init_gpio_in(dev, riscv_kvm_aplic_request, aplic->num_irqs); } else { qdev_init_gpio_in(dev, riscv_aplic_request, aplic->num_irqs); @@ -XXX,XX +XXX,XX @@ DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - if (!is_kvm_aia(msimode)) { + if (!riscv_is_kvm_aia_aplic_imsic(msimode)) { sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); } -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> In create_fdt_sockets() we have the following pattern: if (kvm_enabled() && virt_use_kvm_aia(s)) { (... do stuff ...) } else { (... do other stuff ...) } if (kvm_enabled() && virt_use_kvm_aia(s)) { (... do more stuff ...) } else { (... do more other stuff) } Do everything in a single if/else clause to reduce the usage of virt_use_kvm_aia() helper and to make the code a bit less repetitive. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241119191706.718860-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 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_sockets(RISCVVirtState *s, const MemMapEntry *memmap, msi_m_phandle, msi_s_phandle, phandle, &intc_phandles[0], xplic_phandles, ms->smp.cpus); + + *irq_mmio_phandle = xplic_phandles[0]; + *irq_virtio_phandle = xplic_phandles[0]; + *irq_pcie_phandle = xplic_phandles[0]; } else { phandle_pos = ms->smp.cpus; for (socket = (socket_count - 1); socket >= 0; socket--) { @@ -XXX,XX +XXX,XX @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, s->soc[socket].num_harts); } } - } - if (kvm_enabled() && virt_use_kvm_aia(s)) { - *irq_mmio_phandle = xplic_phandles[0]; - *irq_virtio_phandle = xplic_phandles[0]; - *irq_pcie_phandle = xplic_phandles[0]; - } else { for (socket = 0; socket < socket_count; socket++) { if (socket == 0) { *irq_mmio_phandle = xplic_phandles[socket]; -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Similar to the riscv_is_kvm_aia_aplic_imsic() helper from riscv_aplic.c, the existing virt_use_kvm_aia() is testing for KVM aia=aplic-imsic with in-kernel irqchip enabled. It is not checking for a generic AIA support. Rename the helper to virt_use_kvm_aia_aplic_imsic() to reflect what the helper is doing, and use the existing riscv_is_kvm_aia_aplic_imsic() to obscure details such as the presence of the in-kernel irqchip. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241119191706.718860-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 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 @@ #include "hw/virtio/virtio-iommu.h" /* KVM AIA only supports APLIC MSI. APLIC Wired is always emulated by QEMU. */ -static bool virt_use_kvm_aia(RISCVVirtState *s) +static bool virt_use_kvm_aia_aplic_imsic(RISCVVirtAIAType aia_type) { - return kvm_irqchip_in_kernel() && s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC; + bool msimode = aia_type == VIRT_AIA_TYPE_APLIC_IMSIC; + + return riscv_is_kvm_aia_aplic_imsic(msimode); } static bool virt_aclint_allowed(void) @@ -XXX,XX +XXX,XX @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, *msi_pcie_phandle = msi_s_phandle; } - /* KVM AIA only has one APLIC instance */ - if (kvm_enabled() && virt_use_kvm_aia(s)) { + /* KVM AIA aplic-imsic only has one APLIC instance */ + if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) { create_fdt_socket_aplic(s, memmap, 0, msi_m_phandle, msi_s_phandle, phandle, &intc_phandles[0], xplic_phandles, @@ -XXX,XX +XXX,XX @@ static void virt_machine_init(MachineState *machine) } } - if (kvm_enabled() && virt_use_kvm_aia(s)) { + if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) { kvm_riscv_aia_create(machine, IMSIC_MMIO_GROUP_MIN_SHIFT, VIRT_IRQCHIP_NUM_SOURCES, VIRT_IRQCHIP_NUM_MSIS, memmap[VIRT_APLIC_S].base, -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Before adding support to kernel-irqchip=split when using KVM AIA we need to change how we create the in-kernel AIA device. In the use case we have so far, i.e. in-kernel irqchip without split mode, both the s-mode APLIC and IMSIC controllers are provided by the irqchip. In irqchip_split() mode we'll emulate the s-mode APLIC controller, which will send MSIs to the in-kernel IMSIC controller. To do that we need to change kvm_riscv_aia_create() to not create the in-kernel s-mode APLIC controller. In the kernel source arch/riscv/kvm/aia_aplic.c, function kvm_riscv_aia_aplic_init(), we verify that the APLIC controller won't be instantiated by KVM if we do not set 'nr_sources', which is set via KVM_DEV_RISCV_AIA_CONFIG_SRCS. For QEMU this means that we should not set 'aia_irq_num' during kvm_riscv_aia_create() in irqchip_split() mode. In this same condition, skip KVM_DEV_RISCV_AIA_ADDR_APLIC as well since it is used to set the base address for the in-kernel APLIC controller. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241119191706.718860-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/kvm/kvm-cpu.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 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 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, } } - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, - KVM_DEV_RISCV_AIA_CONFIG_SRCS, - &aia_irq_num, true, NULL); - if (ret < 0) { - error_report("KVM AIA: failed to set number of input irq lines"); - exit(1); - } + /* + * Skip APLIC creation in KVM if we're running split mode. + * This is done by leaving KVM_DEV_RISCV_AIA_CONFIG_SRCS + * unset. We can also skip KVM_DEV_RISCV_AIA_ADDR_APLIC + * since KVM won't be using it. + */ + if (!kvm_kernel_irqchip_split()) { + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, + KVM_DEV_RISCV_AIA_CONFIG_SRCS, + &aia_irq_num, true, NULL); + if (ret < 0) { + error_report("KVM AIA: failed to set number of input irq lines"); + exit(1); + } + + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_ADDR, + KVM_DEV_RISCV_AIA_ADDR_APLIC, + &aplic_base, true, NULL); + if (ret < 0) { + error_report("KVM AIA: failed to set the base address of APLIC"); + exit(1); + } + } ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, KVM_DEV_RISCV_AIA_CONFIG_IDS, @@ -XXX,XX +XXX,XX @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, exit(1); } - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_ADDR, - KVM_DEV_RISCV_AIA_ADDR_APLIC, - &aplic_base, true, NULL); - if (ret < 0) { - error_report("KVM AIA: failed to set the base address of APLIC"); - exit(1); - } - for (socket = 0; socket < socket_count; socket++) { socket_imsic_base = imsic_base + socket * (1U << group_shift); hart_count = riscv_socket_hart_count(machine, socket); -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> The current logic to determine if we don't need an emulated APLIC controller, i.e. KVM will provide for us, is to determine if we're running KVM, with in-kernel irqchip support, and running aia=aplic-imsic. This is modelled by riscv_is_kvm_aia_aplic_imsic() and virt_use_kvm_aia_aplic_imsic(). This won't suffice to support irqchip_split() mode: it will match exactly the same conditions as the one above, but setting the irqchip to 'split' mode will now require us to emulate an APLIC s-mode controller, like we're doing with 'aia=aplic'. Create a new riscv_use_emulated_aplic() helper that will encapsulate this logic. Replace the uses of "riscv_is_kvm_aia_aplic_imsic()" with this helper every time we're taking a decision on emulate an APLIC controller or not. Do the same in virt.c with virt_use_emulated_aplic(). Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241119191706.718860-6-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/intc/riscv_aplic.h | 1 + hw/intc/riscv_aplic.c | 24 +++++++++++++++++++++--- hw/riscv/virt.c | 14 ++++++++++++-- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/include/hw/intc/riscv_aplic.h b/include/hw/intc/riscv_aplic.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/intc/riscv_aplic.h +++ b/include/hw/intc/riscv_aplic.h @@ -XXX,XX +XXX,XX @@ struct RISCVAPLICState { void riscv_aplic_add_child(DeviceState *parent, DeviceState *child); bool riscv_is_kvm_aia_aplic_imsic(bool msimode); +bool riscv_use_emulated_aplic(bool msimode); DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, uint32_t hartid_base, uint32_t num_harts, uint32_t num_sources, 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 "target/riscv/cpu.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" +#include "sysemu/tcg.h" #include "kvm/kvm_riscv.h" #include "migration/vmstate.h" @@ -XXX,XX +XXX,XX @@ bool riscv_is_kvm_aia_aplic_imsic(bool msimode) return kvm_irqchip_in_kernel() && msimode; } +bool riscv_use_emulated_aplic(bool msimode) +{ +#ifdef CONFIG_KVM + if (tcg_enabled()) { + return true; + } + + if (!riscv_is_kvm_aia_aplic_imsic(msimode)) { + return true; + } + + return kvm_kernel_irqchip_split(); +#else + return true; +#endif +} + static bool riscv_aplic_irq_rectified_val(RISCVAPLICState *aplic, uint32_t irq) { @@ -XXX,XX +XXX,XX @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) uint32_t i; RISCVAPLICState *aplic = RISCV_APLIC(dev); - if (!riscv_is_kvm_aia_aplic_imsic(aplic->msimode)) { + if (riscv_use_emulated_aplic(aplic->msimode)) { aplic->bitfield_words = (aplic->num_irqs + 31) >> 5; aplic->sourcecfg = g_new0(uint32_t, aplic->num_irqs); aplic->state = g_new0(uint32_t, aplic->num_irqs); @@ -XXX,XX +XXX,XX @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) * have IRQ lines delegated by their parent APLIC. */ if (!aplic->parent) { - if (kvm_enabled() && riscv_is_kvm_aia_aplic_imsic(aplic->msimode)) { + if (kvm_enabled() && !riscv_use_emulated_aplic(aplic->msimode)) { qdev_init_gpio_in(dev, riscv_kvm_aplic_request, aplic->num_irqs); } else { qdev_init_gpio_in(dev, riscv_aplic_request, aplic->num_irqs); @@ -XXX,XX +XXX,XX @@ DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - if (!riscv_is_kvm_aia_aplic_imsic(msimode)) { + if (riscv_use_emulated_aplic(msimode)) { sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 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 bool virt_use_kvm_aia_aplic_imsic(RISCVVirtAIAType aia_type) return riscv_is_kvm_aia_aplic_imsic(msimode); } +static bool virt_use_emulated_aplic(RISCVVirtAIAType aia_type) +{ + bool msimode = aia_type == VIRT_AIA_TYPE_APLIC_IMSIC; + + return riscv_use_emulated_aplic(msimode); +} + static bool virt_aclint_allowed(void) { return tcg_enabled() || qtest_enabled(); @@ -XXX,XX +XXX,XX @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, *msi_pcie_phandle = msi_s_phandle; } - /* KVM AIA aplic-imsic only has one APLIC instance */ - if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) { + /* + * With KVM AIA aplic-imsic, using an irqchip without split + * mode, we'll use only one APLIC instance. + */ + if (!virt_use_emulated_aplic(s->aia_type)) { create_fdt_socket_aplic(s, memmap, 0, msi_m_phandle, msi_s_phandle, phandle, &intc_phandles[0], xplic_phandles, -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> The last step to enable KVM AIA aplic-imsic with irqchip in split mode is to deal with how MSIs are going to be sent. In our current design we don't allow an APLIC controller to send MSIs unless it's on m-mode. And we also do not allow Supervisor MSI address configuration via the 'smsiaddrcfg' and 'smsiaddrcfgh' registers unless it's also a m-mode APLIC controller. Add a new RISCVACPLICState attribute called 'kvm_msicfgaddr'. This attribute represents the base configuration address for MSIs, in our case the base addr of the IMSIC controller. This attribute is being set only when running irqchip_split() mode with aia=aplic-imsic. During riscv_aplic_msi_send() we'll check if the attribute was set to skip the check for a m-mode APLIC controller and to change the resulting MSI addr by adding kvm_msicfgaddr right before address_space_stl_le(). Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241119191706.718860-7-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/intc/riscv_aplic.h | 6 +++++ hw/intc/riscv_aplic.c | 42 +++++++++++++++++++++++++++-------- hw/riscv/virt.c | 6 ++++- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/include/hw/intc/riscv_aplic.h b/include/hw/intc/riscv_aplic.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/intc/riscv_aplic.h +++ b/include/hw/intc/riscv_aplic.h @@ -XXX,XX +XXX,XX @@ struct RISCVAPLICState { uint32_t num_irqs; bool msimode; bool mmode; + + /* To support KVM aia=aplic-imsic with irqchip split mode */ + bool kvm_splitmode; + uint32_t kvm_msicfgaddr; + uint32_t kvm_msicfgaddrH; }; void riscv_aplic_add_child(DeviceState *parent, DeviceState *child); bool riscv_is_kvm_aia_aplic_imsic(bool msimode); bool riscv_use_emulated_aplic(bool msimode); +void riscv_aplic_set_kvm_msicfgaddr(RISCVAPLICState *aplic, hwaddr addr); DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, uint32_t hartid_base, uint32_t num_harts, uint32_t num_sources, 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 @@ bool riscv_use_emulated_aplic(bool msimode) #endif } +void riscv_aplic_set_kvm_msicfgaddr(RISCVAPLICState *aplic, hwaddr addr) +{ +#ifdef CONFIG_KVM + if (riscv_use_emulated_aplic(aplic->msimode)) { + aplic->kvm_msicfgaddr = extract64(addr, 0, 32); + aplic->kvm_msicfgaddrH = extract64(addr, 32, 32); + } +#endif +} + static bool riscv_aplic_irq_rectified_val(RISCVAPLICState *aplic, uint32_t irq) { @@ -XXX,XX +XXX,XX @@ static void riscv_aplic_msi_send(RISCVAPLICState *aplic, uint32_t lhxs, lhxw, hhxs, hhxw, group_idx, msicfgaddr, msicfgaddrH; aplic_m = aplic; - while (aplic_m && !aplic_m->mmode) { - aplic_m = aplic_m->parent; - } - if (!aplic_m) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: m-level APLIC not found\n", - __func__); - return; + + if (!aplic->kvm_splitmode) { + while (aplic_m && !aplic_m->mmode) { + aplic_m = aplic_m->parent; + } + if (!aplic_m) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: m-level APLIC not found\n", + __func__); + return; + } } if (aplic->mmode) { @@ -XXX,XX +XXX,XX @@ static void riscv_aplic_msi_send(RISCVAPLICState *aplic, addr |= (uint64_t)(guest_idx & APLIC_xMSICFGADDR_PPN_HART(lhxs)); addr <<= APLIC_xMSICFGADDR_PPN_SHIFT; + if (aplic->kvm_splitmode) { + addr |= aplic->kvm_msicfgaddr; + addr |= ((uint64_t)aplic->kvm_msicfgaddrH << 32); + } + address_space_stl_le(&address_space_memory, addr, eiid, MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { @@ -XXX,XX +XXX,XX @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) memory_region_init_io(&aplic->mmio, OBJECT(dev), &riscv_aplic_ops, aplic, TYPE_RISCV_APLIC, aplic->aperture_size); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &aplic->mmio); + + if (kvm_enabled()) { + aplic->kvm_splitmode = true; + } } /* @@ -XXX,XX +XXX,XX @@ static const Property riscv_aplic_properties[] = { static const VMStateDescription vmstate_riscv_aplic = { .name = "riscv_aplic", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (const VMStateField[]) { VMSTATE_UINT32(domaincfg, RISCVAPLICState), VMSTATE_UINT32(mmsicfgaddr, RISCVAPLICState), @@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_riscv_aplic = { VMSTATE_UINT32(smsicfgaddr, RISCVAPLICState), VMSTATE_UINT32(smsicfgaddrH, RISCVAPLICState), VMSTATE_UINT32(genmsi, RISCVAPLICState), + VMSTATE_UINT32(kvm_msicfgaddr, RISCVAPLICState), + VMSTATE_UINT32(kvm_msicfgaddrH, RISCVAPLICState), VMSTATE_VARRAY_UINT32(sourcecfg, RISCVAPLICState, num_irqs, 0, vmstate_info_uint32, uint32_t), diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -XXX,XX +XXX,XX @@ static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests, int base_hartid, int hart_count) { int i; - hwaddr addr; + hwaddr addr = 0; uint32_t guest_bits; DeviceState *aplic_s = NULL; DeviceState *aplic_m = NULL; @@ -XXX,XX +XXX,XX @@ static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests, 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; } -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Remove the 'irqchip_split()' restriction in kvm_arch_init() now that we have support for "-accel kvm,kernel-irqchip=split". Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241119191706.718860-8-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/kvm/kvm-cpu.c | 5 ----- 1 file changed, 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_init(MachineState *ms, KVMState *s) int kvm_arch_irqchip_create(KVMState *s) { - if (kvm_kernel_irqchip_split()) { - error_report("-machine kernel_irqchip=split is not supported on RISC-V."); - exit(1); - } - /* * We can create the VAIA using the newer device control API. */ -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Also add a new page, docs/specs/riscv-aia.rst, where we're documenting the state of AIA support in QEMU w.r.t the controllers being emulated or not depending on the AIA and accelerator settings. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241119191706.718860-9-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- docs/specs/index.rst | 1 + docs/specs/riscv-aia.rst | 83 ++++++++++++++++++++++++++++++++++++++ docs/system/riscv/virt.rst | 7 ++++ 3 files changed, 91 insertions(+) create mode 100644 docs/specs/riscv-aia.rst diff --git a/docs/specs/index.rst b/docs/specs/index.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -XXX,XX +XXX,XX @@ guest hardware that is specific to QEMU. rapl-msr rocker riscv-iommu + riscv-aia diff --git a/docs/specs/riscv-aia.rst b/docs/specs/riscv-aia.rst new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/docs/specs/riscv-aia.rst @@ -XXX,XX +XXX,XX @@ +.. _riscv-aia: + +RISC-V AIA support for RISC-V machines +====================================== + +AIA (Advanced Interrupt Architecture) support is implemented in the ``virt`` +RISC-V machine for TCG and KVM accelerators. + +The support consists of two main modes: + +- "aia=aplic": adds one or more APLIC (Advanced Platform Level Interrupt Controller) + devices +- "aia=aplic-imsic": adds one or more APLIC device and an IMSIC (Incoming MSI + Controller) device for each CPU + +From an user standpoint, these modes will behave the same regardless of the accelerator +used. From a developer standpoint the accelerator settings will change what it being +emulated in userspace versus what is being emulated by an in-kernel irqchip. + +When running TCG, all controllers are emulated in userspace, including machine mode +(m-mode) APLIC and IMSIC (when applicable). + +When running KVM: + +- no m-mode is provided, so there is no m-mode APLIC or IMSIC emulation regardless of + the AIA mode chosen +- with "aia=aplic", s-mode APLIC will be emulated by userspace +- with "aia=aplic-imsic" there are two possibilities. If no additional KVM option + is provided there will be no APLIC or IMSIC emulation in userspace, and the virtual + machine will use the provided in-kernel APLIC and IMSIC controllers. If the user + chooses to use the irqchip in split mode via "-accel kvm,kernel-irqchip=split", + s-mode APLIC will be emulated while using the s-mode IMSIC from the irqchip + +The following table summarizes how the AIA and accelerator options defines what +we will emulate in userspace: + + +.. list-table:: How AIA and accel options changes controller emulation + :widths: 25 25 25 25 25 25 25 + :header-rows: 1 + + * - Accel + - Accel props + - AIA type + - APLIC m-mode + - IMSIC m-mode + - APLIC s-mode + - IMSIC s-mode + * - tcg + - --- + - aplic + - emul + - n/a + - emul + - n/a + * - tcg + - --- + - aplic-imsic + - emul + - emul + - emul + - emul + * - kvm + - --- + - aplic + - n/a + - n/a + - emul + - n/a + * - kvm + - none + - aplic-imsic + - n/a + - n/a + - in-kernel + - in-kernel + * - kvm + - irqchip=split + - aplic-imsic + - n/a + - n/a + - emul + - in-kernel diff --git a/docs/system/riscv/virt.rst b/docs/system/riscv/virt.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/riscv/virt.rst +++ b/docs/system/riscv/virt.rst @@ -XXX,XX +XXX,XX @@ The following machine-specific options are supported: MSIs. When not specified, this option is assumed to be "none" which selects SiFive PLIC to handle wired interrupts. + This option also interacts with '-accel kvm'. When using "aia=aplic-imsic" + with KVM, it is possible to set the use of the kernel irqchip in split mode + by using "-accel kvm,kernel-irqchip=split". In this case the ``virt`` machine + will emulate the APLIC controller instead of using the APLIC controller from + the irqchip. See :ref:`riscv-aia` for more details on all available AIA + modes. + - aia-guests=nnn The number of per-HART VS-level AIA IMSIC pages to be emulated for a guest -- 2.47.1
From: Sai Pavan Boddu <sai.pavan.boddu@amd.com> Add a basic board with interrupt controller (intc), timer, serial (uartlite), small memory called LMB@0 (128kB) and DDR@0x80000000 (configured via command line eg. -m 2g). This is basic configuration which matches HW generated out of AMD Vivado (design tools). But initial configuration is going beyond what it is configured by default because validation should be done on other configurations too. That's why wire also additional uart16500, axi ethernet(with axi dma). GPIOs, i2c and qspi is also listed for completeness. IRQ map is: (addr) 0 - timer (0x41c00000) 1 - uartlite (0x40600000) 2 - i2c (0x40800000) 3 - qspi (0x44a00000) 4 - uart16550 (0x44a10000) 5 - emaclite (0x40e00000) 6 - timer2 (0x41c10000) 7 - axi emac (0x40c00000) 8 - axi dma (0x41e00000) 9 - axi dma 10 - gpio (0x40000000) 11 - gpio2 (0x40010000) 12 - gpio3 (0x40020000) Signed-off-by: Sai Pavan Boddu <sai.pavan.boddu@amd.com> Signed-off-by: Michal Simek <michal.simek@amd.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241125134739.18189-1-sai.pavan.boddu@amd.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- MAINTAINERS | 6 + docs/system/riscv/microblaze-v-generic.rst | 42 +++++ docs/system/target-riscv.rst | 1 + hw/riscv/microblaze-v-generic.c | 184 +++++++++++++++++++++ hw/riscv/Kconfig | 8 + hw/riscv/meson.build | 1 + 6 files changed, 242 insertions(+) create mode 100644 docs/system/riscv/microblaze-v-generic.rst create mode 100644 hw/riscv/microblaze-v-generic.c diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ F: docs/system/riscv/sifive_u.rst F: hw/*/*sifive*.c F: include/hw/*/*sifive*.h +AMD Microblaze-V Generic Board +M: Sai Pavan Boddu <sai.pavan.boddu@amd.com> +S: Maintained +F: hw/riscv/microblaze-v-generic.c +F: docs/system/riscv/microblaze-v-generic.rst + RX Machines ----------- rx-gdbsim diff --git a/docs/system/riscv/microblaze-v-generic.rst b/docs/system/riscv/microblaze-v-generic.rst new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/docs/system/riscv/microblaze-v-generic.rst @@ -XXX,XX +XXX,XX @@ +Microblaze-V generic board (``amd-microblaze-v-generic``) +========================================================= +The AMD MicroBlaze™ V processor is a soft-core RISC-V processor IP for AMD +adaptive SoCs and FPGAs. The MicroBlaze™ V processor is based on the 32-bit (or +64-bit) RISC-V instruction set architecture (ISA) and contains interfaces +compatible with the classic MicroBlaze™ V processor (i.e it is a drop in +replacement for the classic MicroBlaze™ processor in existing RTL designs). +More information can be found in below document. + +https://docs.amd.com/r/en-US/ug1629-microblaze-v-user-guide/MicroBlaze-V-Architecture + +The MicroBlaze™ V generic board in QEMU has following supported devices: + + - timer + - uartlite + - uart16550 + - emaclite + - timer2 + - axi emac + - axi dma + +The MicroBlaze™ V core in QEMU has the following configuration: + + - RV32I base integer instruction set + - "Zicsr" Control and Status register instructions + - "Zifencei" instruction-fetch + - Extensions: m, a, f, c + +Running +""""""" +Below is an example command line for launching mainline U-boot +(xilinx_mbv32_defconfig) on the Microblaze-V generic board. + +.. code-block:: bash + + $ qemu-system-riscv32 -M amd-microblaze-v-generic \ + -display none \ + -device loader,addr=0x80000000,file=u-boot-spl.bin,cpu-num=0 \ + -device loader,addr=0x80200000,file=u-boot.img \ + -serial mon:stdio \ + -device loader,addr=0x83000000,file=system.dtb \ + -m 2g diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/target-riscv.rst +++ b/docs/system/target-riscv.rst @@ -XXX,XX +XXX,XX @@ undocumented; you can get a complete list by running .. toctree:: :maxdepth: 1 + riscv/microblaze-v-generic riscv/microchip-icicle-kit riscv/shakti-c riscv/sifive_u diff --git a/hw/riscv/microblaze-v-generic.c b/hw/riscv/microblaze-v-generic.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/riscv/microblaze-v-generic.c @@ -XXX,XX +XXX,XX @@ +/* + * QEMU model of Microblaze V generic board. + * + * based on hw/microblaze/petalogix_ml605_mmu.c + * + * Copyright (c) 2011 Michal Simek <monstr@monstr.eu> + * Copyright (c) 2011 PetaLogix + * Copyright (c) 2009 Edgar E. Iglesias. + * Copyright (C) 2024, Advanced Micro Devices, Inc. + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Written by Sai Pavan Boddu <sai.pavan.boddu@amd.com + * and by Michal Simek <michal.simek@amd.com>. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "cpu.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "net/net.h" +#include "hw/boards.h" +#include "hw/char/serial-mm.h" +#include "exec/address-spaces.h" +#include "hw/char/xilinx_uartlite.h" +#include "hw/misc/unimp.h" + +#define LMB_BRAM_SIZE (128 * KiB) +#define MEMORY_BASEADDR 0x80000000 +#define INTC_BASEADDR 0x41200000 +#define TIMER_BASEADDR 0x41c00000 +#define TIMER_BASEADDR2 0x41c10000 +#define UARTLITE_BASEADDR 0x40600000 +#define ETHLITE_BASEADDR 0x40e00000 +#define UART16550_BASEADDR 0x44a10000 +#define AXIENET_BASEADDR 0x40c00000 +#define AXIDMA_BASEADDR 0x41e00000 +#define GPIO_BASEADDR 0x40000000 +#define GPIO_BASEADDR2 0x40010000 +#define GPIO_BASEADDR3 0x40020000 +#define I2C_BASEADDR 0x40800000 +#define QSPI_BASEADDR 0x44a00000 + +#define TIMER_IRQ 0 +#define UARTLITE_IRQ 1 +#define UART16550_IRQ 4 +#define ETHLITE_IRQ 5 +#define TIMER_IRQ2 6 +#define AXIENET_IRQ 7 +#define AXIDMA_IRQ1 8 +#define AXIDMA_IRQ0 9 + +static void mb_v_generic_init(MachineState *machine) +{ + ram_addr_t ram_size = machine->ram_size; + DeviceState *dev, *dma, *eth0; + Object *ds, *cs; + int i; + RISCVCPU *cpu; + hwaddr ddr_base = MEMORY_BASEADDR; + MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1); + MemoryRegion *phys_ram = g_new(MemoryRegion, 1); + qemu_irq irq[32]; + MemoryRegion *sysmem = get_system_memory(); + + cpu = RISCV_CPU(object_new(machine->cpu_type)); + object_property_set_bool(OBJECT(cpu), "h", false, NULL); + object_property_set_bool(OBJECT(cpu), "d", false, NULL); + qdev_realize(DEVICE(cpu), NULL, &error_abort); + /* Attach emulated BRAM through the LMB. */ + memory_region_init_ram(phys_lmb_bram, NULL, + "mb_v.lmb_bram", LMB_BRAM_SIZE, + &error_fatal); + memory_region_add_subregion(sysmem, 0x00000000, phys_lmb_bram); + + memory_region_init_ram(phys_ram, NULL, "mb_v.ram", + ram_size, &error_fatal); + memory_region_add_subregion(sysmem, ddr_base, phys_ram); + + dev = qdev_new("xlnx.xps-intc"); + qdev_prop_set_uint32(dev, "kind-of-intr", + 1 << UARTLITE_IRQ); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + qdev_get_gpio_in(DEVICE(cpu), 11)); + for (i = 0; i < 32; i++) { + irq[i] = qdev_get_gpio_in(dev, i); + } + + /* Uartlite */ + dev = qdev_new(TYPE_XILINX_UARTLITE); + qdev_prop_set_chr(dev, "chardev", serial_hd(0)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, UARTLITE_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[UARTLITE_IRQ]); + + /* Full uart */ + serial_mm_init(sysmem, UART16550_BASEADDR + 0x1000, 2, + irq[UART16550_IRQ], 115200, serial_hd(1), + DEVICE_LITTLE_ENDIAN); + + /* 2 timers at irq 0 @ 100 Mhz. */ + dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_uint32(dev, "one-timer-only", 0); + qdev_prop_set_uint32(dev, "clock-frequency", 100000000); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]); + + /* 2 timers at irq 3 @ 100 Mhz. */ + dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_uint32(dev, "one-timer-only", 0); + qdev_prop_set_uint32(dev, "clock-frequency", 100000000); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR2); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ2]); + + /* Emaclite */ + dev = qdev_new("xlnx.xps-ethernetlite"); + qemu_configure_nic_device(dev, true, NULL); + qdev_prop_set_uint32(dev, "tx-ping-pong", 0); + qdev_prop_set_uint32(dev, "rx-ping-pong", 0); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ETHLITE_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[ETHLITE_IRQ]); + + /* axi ethernet and dma initialization. */ + eth0 = qdev_new("xlnx.axi-ethernet"); + dma = qdev_new("xlnx.axi-dma"); + + /* FIXME: attach to the sysbus instead */ + object_property_add_child(qdev_get_machine(), "xilinx-eth", OBJECT(eth0)); + object_property_add_child(qdev_get_machine(), "xilinx-dma", OBJECT(dma)); + + ds = object_property_get_link(OBJECT(dma), + "axistream-connected-target", NULL); + cs = object_property_get_link(OBJECT(dma), + "axistream-control-connected-target", NULL); + qemu_configure_nic_device(eth0, true, NULL); + qdev_prop_set_uint32(eth0, "rxmem", 0x1000); + qdev_prop_set_uint32(eth0, "txmem", 0x1000); + object_property_set_link(OBJECT(eth0), "axistream-connected", ds, + &error_abort); + object_property_set_link(OBJECT(eth0), "axistream-control-connected", cs, + &error_abort); + sysbus_realize_and_unref(SYS_BUS_DEVICE(eth0), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(eth0), 0, AXIENET_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(eth0), 0, irq[AXIENET_IRQ]); + + ds = object_property_get_link(OBJECT(eth0), + "axistream-connected-target", NULL); + cs = object_property_get_link(OBJECT(eth0), + "axistream-control-connected-target", NULL); + qdev_prop_set_uint32(dma, "freqhz", 100000000); + object_property_set_link(OBJECT(dma), "axistream-connected", ds, + &error_abort); + object_property_set_link(OBJECT(dma), "axistream-control-connected", cs, + &error_abort); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dma), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dma), 0, AXIDMA_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dma), 0, irq[AXIDMA_IRQ0]); + sysbus_connect_irq(SYS_BUS_DEVICE(dma), 1, irq[AXIDMA_IRQ1]); + + /* unimplemented devices */ + create_unimplemented_device("gpio", GPIO_BASEADDR, 0x10000); + create_unimplemented_device("gpio2", GPIO_BASEADDR2, 0x10000); + create_unimplemented_device("gpio3", GPIO_BASEADDR3, 0x10000); + create_unimplemented_device("i2c", I2C_BASEADDR, 0x10000); + create_unimplemented_device("qspi", QSPI_BASEADDR, 0x10000); +} + +static void mb_v_generic_machine_init(MachineClass *mc) +{ + mc->desc = "AMD Microblaze-V generic platform"; + mc->init = mb_v_generic_init; + mc->min_cpus = 1; + mc->max_cpus = 1; + mc->default_cpu_type = TYPE_RISCV_CPU_BASE; + mc->default_cpus = 1; +} + +DEFINE_MACHINE("amd-microblaze-v-generic", mb_v_generic_machine_init) 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 MICROCHIP_PFSOC select SIFIVE_PLIC select UNIMP +config MICROBLAZE_V + bool + default y + depends on RISCV32 || RISCV64 + select XILINX + select XILINX_AXI + select XILINX_ETHLITE + config OPENTITAN 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_SPIKE', if_true: files('spike.c')) riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.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_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c')) hw_arch += {'riscv': riscv_ss} -- 2.47.1
From: Sia Jee Heng <jeeheng.sia@starfivetech.com> Signed-off-by: Sia Jee Heng <jeeheng.sia@starfivetech.com> Reviewed-by: Sunil V L <sunilvl@ventanamicro.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241028015744.624943-2-jeeheng.sia@starfivetech.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- tests/qtest/bios-tables-test-allowed-diff.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/riscv64/virt/SPCR", -- 2.47.1
From: Sia Jee Heng <jeeheng.sia@starfivetech.com> Update the SPCR table to accommodate the SPCR Table revision 4 [1]. The SPCR table has been modified to adhere to the revision 4 format [2]. [1]: https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table [2]: https://github.com/acpica/acpica/pull/931 Signed-off-by: Sia Jee Heng <jeeheng.sia@starfivetech.com> Reviewed-by: Sunil V L <sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Message-ID: <20241028015744.624943-3-jeeheng.sia@starfivetech.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/acpi/acpi-defs.h | 7 +++++-- include/hw/acpi/aml-build.h | 2 +- hw/acpi/aml-build.c | 20 ++++++++++++++++---- hw/arm/virt-acpi-build.c | 8 ++++++-- hw/loongarch/acpi-build.c | 6 +++++- hw/riscv/virt-acpi-build.c | 12 +++++++++--- 6 files changed, 42 insertions(+), 13 deletions(-) diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -XXX,XX +XXX,XX @@ typedef struct AcpiSpcrData { uint8_t flow_control; uint8_t terminal_type; uint8_t language; - uint8_t reserved1; uint16_t pci_device_id; /* Must be 0xffff if not PCI device */ uint16_t pci_vendor_id; /* Must be 0xffff if not PCI device */ uint8_t pci_bus; @@ -XXX,XX +XXX,XX @@ typedef struct AcpiSpcrData { uint8_t pci_function; uint32_t pci_flags; uint8_t pci_segment; - uint32_t reserved2; + uint32_t uart_clk_freq; + uint32_t precise_baudrate; + uint32_t namespace_string_length; + uint32_t namespace_string_offset; + char namespace_string[]; } AcpiSpcrData; #define ACPI_FADT_ARM_PSCI_COMPLIANT (1 << 0) diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -XXX,XX +XXX,XX @@ void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog, void build_spcr(GArray *table_data, BIOSLinker *linker, const AcpiSpcrData *f, const uint8_t rev, - const char *oem_id, const char *oem_table_id); + const char *oem_id, const char *oem_table_id, const char *name); #endif diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index XXXXXXX..XXXXXXX 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -XXX,XX +XXX,XX @@ static void build_processor_hierarchy_node(GArray *tbl, uint32_t flags, void build_spcr(GArray *table_data, BIOSLinker *linker, const AcpiSpcrData *f, const uint8_t rev, - const char *oem_id, const char *oem_table_id) + const char *oem_id, const char *oem_table_id, const char *name) { AcpiTable table = { .sig = "SPCR", .rev = rev, .oem_id = oem_id, .oem_table_id = oem_table_id }; @@ -XXX,XX +XXX,XX @@ void build_spcr(GArray *table_data, BIOSLinker *linker, build_append_int_noprefix(table_data, f->pci_flags, 4); /* PCI Segment */ build_append_int_noprefix(table_data, f->pci_segment, 1); - /* Reserved */ - build_append_int_noprefix(table_data, 0, 4); - + if (rev < 4) { + /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); + } else { + /* UartClkFreq */ + build_append_int_noprefix(table_data, f->uart_clk_freq, 4); + /* PreciseBaudrate */ + build_append_int_noprefix(table_data, f->precise_baudrate, 4); + /* NameSpaceStringLength */ + build_append_int_noprefix(table_data, f->namespace_string_length, 2); + /* NameSpaceStringOffset */ + build_append_int_noprefix(table_data, f->namespace_string_offset, 2); + /* NamespaceString[] */ + g_array_append_vals(table_data, name, f->namespace_string_length); + } acpi_table_end(linker, &table); } /* diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -XXX,XX +XXX,XX @@ spcr_setup(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) .pci_flags = 0, .pci_segment = 0, }; - - build_spcr(table_data, linker, &serial, 2, vms->oem_id, vms->oem_table_id); + /* + * Passing NULL as the SPCR Table for Revision 2 doesn't support + * NameSpaceString. + */ + build_spcr(table_data, linker, &serial, 2, vms->oem_id, vms->oem_table_id, + NULL); } /* diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c index XXXXXXX..XXXXXXX 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/acpi-build.c @@ -XXX,XX +XXX,XX @@ spcr_setup(GArray *table_data, BIOSLinker *linker, MachineState *machine) }; lvms = LOONGARCH_VIRT_MACHINE(machine); + /* + * Passing NULL as the SPCR Table for Revision 2 doesn't support + * NameSpaceString. + */ build_spcr(table_data, linker, &serial, 2, lvms->oem_id, - lvms->oem_table_id); + lvms->oem_table_id, NULL); } typedef 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 @@ acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, /* * Serial Port Console Redirection Table (SPCR) - * Rev: 1.07 + * Rev: 1.10 */ static void spcr_setup(GArray *table_data, BIOSLinker *linker, RISCVVirtState *s) { + const char name[] = "."; AcpiSpcrData serial = { - .interface_type = 0, /* 16550 compatible */ + .interface_type = 0x12, /* 16550 compatible */ .base_addr.id = AML_AS_SYSTEM_MEMORY, .base_addr.width = 32, .base_addr.offset = 0, @@ -XXX,XX +XXX,XX @@ spcr_setup(GArray *table_data, BIOSLinker *linker, RISCVVirtState *s) .pci_function = 0, .pci_flags = 0, .pci_segment = 0, + .uart_clk_freq = 0, + .precise_baudrate = 0, + .namespace_string_length = sizeof(name), + .namespace_string_offset = 88, }; - build_spcr(table_data, linker, &serial, 2, s->oem_id, s->oem_table_id); + build_spcr(table_data, linker, &serial, 4, s->oem_id, s->oem_table_id, + name); } /* RHCT Node[N] starts at offset 56 */ -- 2.47.1
From: Sia Jee Heng <jeeheng.sia@starfivetech.com> Update the virt SPCR golden reference file for RISC-V to accommodate the SPCR Table revision 4 [1], utilizing the iasl binary compiled from the latest ACPICA repository. The SPCR table has been modified to adhere to the revision 4 format [2]. [1]: https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table [2]: https://github.com/acpica/acpica/pull/931 Diffs from iasl: /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20200925 (64-bit version) * Copyright (c) 2000 - 2020 Intel Corporation * - * Disassembly of tests/data/acpi/riscv64/virt/SPCR, Wed Aug 28 18:28:19 2024 + * Disassembly of /tmp/aml-MN0NS2, Wed Aug 28 18:28:19 2024 * * ACPI Data Table [SPCR] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue */ [000h 0000 4] Signature : "SPCR" [Serial Port Console Redirection table] -[004h 0004 4] Table Length : 00000050 -[008h 0008 1] Revision : 02 -[009h 0009 1] Checksum : B9 +[004h 0004 4] Table Length : 0000005A +[008h 0008 1] Revision : 04 +[009h 0009 1] Checksum : 13 [00Ah 0010 6] Oem ID : "BOCHS " [010h 0016 8] Oem Table ID : "BXPC " [018h 0024 4] Oem Revision : 00000001 [01Ch 0028 4] Asl Compiler ID : "BXPC" [020h 0032 4] Asl Compiler Revision : 00000001 -[024h 0036 1] Interface Type : 00 +[024h 0036 1] Interface Type : 12 [025h 0037 3] Reserved : 000000 [028h 0040 12] Serial Port Register : [Generic Address Structure] [028h 0040 1] Space ID : 00 [SystemMemory] [029h 0041 1] Bit Width : 20 [02Ah 0042 1] Bit Offset : 00 [02Bh 0043 1] Encoded Access Width : 01 [Byte Access:8] [02Ch 0044 8] Address : 0000000010000000 [034h 0052 1] Interrupt Type : 10 [035h 0053 1] PCAT-compatible IRQ : 00 [036h 0054 4] Interrupt : 0000000A [03Ah 0058 1] Baud Rate : 07 [03Bh 0059 1] Parity : 00 [03Ch 0060 1] Stop Bits : 01 [03Dh 0061 1] Flow Control : 00 [03Eh 0062 1] Terminal Type : 00 [04Ch 0076 1] Reserved : 00 [040h 0064 2] PCI Device ID : FFFF [042h 0066 2] PCI Vendor ID : FFFF [044h 0068 1] PCI Bus : 00 [045h 0069 1] PCI Device : 00 [046h 0070 1] PCI Function : 00 [047h 0071 4] PCI Flags : 00000000 [04Bh 0075 1] PCI Segment : 00 -[04Ch 0076 4] Reserved : 00000000 +[04Ch 0076 004h] Uart Clock Freq : 00000000 +[050h 0080 004h] Precise Baud rate : 00000000 +[054h 0084 002h] NameSpaceStringLength : 0002 +[056h 0086 002h] NameSpaceStringOffset : 0058 +[058h 0088 002h] NamespaceString : "." -Raw Table Data: Length 80 (0x50) +Raw Table Data: Length 90 (0x5A) - 0000: 53 50 43 52 50 00 00 00 02 B9 42 4F 43 48 53 20 // SPCRP.....BOCHS + 0000: 53 50 43 52 5A 00 00 00 04 13 42 4F 43 48 53 20 // SPCRZ.....BOCHS 0010: 42 58 50 43 20 20 20 20 01 00 00 00 42 58 50 43 // BXPC ....BXPC - 0020: 01 00 00 00 00 00 00 00 00 20 00 01 00 00 00 10 // ......... ...... + 0020: 01 00 00 00 12 00 00 00 00 20 00 01 00 00 00 10 // ......... ...... 0030: 00 00 00 00 10 00 0A 00 00 00 07 00 01 00 00 03 // ................ 0040: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 // ................ + 0050: 00 00 00 00 02 00 58 00 2E 00 // ......X... Signed-off-by: Sia Jee Heng <jeeheng.sia@starfivetech.com> Reviewed-by: Sunil V L <sunilvl@ventanamicro.com> Message-ID: <20241028015744.624943-4-jeeheng.sia@starfivetech.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- tests/qtest/bios-tables-test-allowed-diff.h | 1 - tests/data/acpi/riscv64/virt/SPCR | Bin 80 -> 90 bytes 2 files changed, 1 deletion(-) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,2 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/riscv64/virt/SPCR", diff --git a/tests/data/acpi/riscv64/virt/SPCR b/tests/data/acpi/riscv64/virt/SPCR index XXXXXXX..XXXXXXX 100644 Binary files a/tests/data/acpi/riscv64/virt/SPCR and b/tests/data/acpi/riscv64/virt/SPCR differ -- 2.47.1
From: Philippe Mathieu-Daudé <philmd@linaro.org> The HTIF interface is RISC-V specific, add it within the MAINTAINERS section covering hw/riscv/. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20241129154304.34946-2-philmd@linaro.org> 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: configs/targets/riscv* F: docs/system/target-riscv.rst F: target/riscv/ +F: hw/char/riscv_htif.c F: hw/riscv/ F: hw/intc/riscv* +F: include/hw/char/riscv_htif.h F: include/hw/riscv/ F: linux-user/host/riscv32/ F: linux-user/host/riscv64/ -- 2.47.1
From: Philippe Mathieu-Daudé <philmd@linaro.org> Since our RISC-V system emulation is only built for little endian, the HTIF device aims to interface with little endian memory accesses, thus we can explicit htif_mm_ops:endianness being DEVICE_LITTLE_ENDIAN. In that case tswap64() is equivalent to le64_to_cpu(), as in "convert this 64-bit little-endian value into host cpu order". Replace to simplify. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20241129154304.34946-3-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/char/riscv_htif.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c index XXXXXXX..XXXXXXX 100644 --- a/hw/char/riscv_htif.c +++ b/hw/char/riscv_htif.c @@ -XXX,XX +XXX,XX @@ #include "qemu/timer.h" #include "qemu/error-report.h" #include "exec/address-spaces.h" -#include "exec/tswap.h" +#include "qemu/bswap.h" #include "sysemu/dma.h" #include "sysemu/runstate.h" @@ -XXX,XX +XXX,XX @@ static void htif_handle_tohost_write(HTIFState *s, uint64_t val_written) } else { uint64_t syscall[8]; cpu_physical_memory_read(payload, syscall, sizeof(syscall)); - if (tswap64(syscall[0]) == PK_SYS_WRITE && - tswap64(syscall[1]) == HTIF_DEV_CONSOLE && - tswap64(syscall[3]) == HTIF_CONSOLE_CMD_PUTC) { + if (le64_to_cpu(syscall[0]) == PK_SYS_WRITE && + le64_to_cpu(syscall[1]) == HTIF_DEV_CONSOLE && + le64_to_cpu(syscall[3]) == HTIF_CONSOLE_CMD_PUTC) { uint8_t ch; - cpu_physical_memory_read(tswap64(syscall[2]), &ch, 1); + cpu_physical_memory_read(le64_to_cpu(syscall[2]), &ch, 1); /* * XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks @@ -XXX,XX +XXX,XX @@ static void htif_mm_write(void *opaque, hwaddr addr, static const MemoryRegionOps htif_mm_ops = { .read = htif_mm_read, .write = htif_mm_write, + .endianness = DEVICE_LITTLE_ENDIAN, }; HTIFState *htif_mm_init(MemoryRegion *address_space, Chardev *chr, -- 2.47.1
From: Philippe Mathieu-Daudé <philmd@linaro.org> Looking at htif_mm_ops[] read/write handlers, we notice they expect 32-bit values to accumulate into to the 'fromhost' and 'tohost' 64-bit variables. Explicit by setting the .impl min/max fields. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20241129154304.34946-4-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/char/riscv_htif.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c index XXXXXXX..XXXXXXX 100644 --- a/hw/char/riscv_htif.c +++ b/hw/char/riscv_htif.c @@ -XXX,XX +XXX,XX @@ static const MemoryRegionOps htif_mm_ops = { .read = htif_mm_read, .write = htif_mm_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, }; HTIFState *htif_mm_init(MemoryRegion *address_space, Chardev *chr, -- 2.47.1
From: Jim Shu <jim.shu@sifive.com> Larger initrd image will overlap the DTB at 3GB address. Since 64-bit system doesn't have 32-bit addressable issue, we just load DTB to the end of dram in 64-bit system. Signed-off-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20241120153935.24706-2-jim.shu@sifive.com> [ Changes by AF - Store fdt_load_addr_hi32 in the reset vector ] Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/boot.h | 2 +- hw/riscv/boot.c | 14 +++++++++----- hw/riscv/microchip_pfsoc.c | 4 ++-- hw/riscv/sifive_u.c | 8 +++++--- hw/riscv/spike.c | 4 ++-- hw/riscv/virt.c | 2 +- 6 files changed, 20 insertions(+), 14 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 @@ target_ulong riscv_load_kernel(MachineState *machine, bool load_initrd, symbol_fn_t sym_cb); uint64_t riscv_compute_fdt_addr(hwaddr dram_start, uint64_t dram_size, - MachineState *ms); + MachineState *ms, RISCVHartArrayState *harts); void riscv_load_fdt(hwaddr fdt_addr, void *fdt); void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts, hwaddr saddr, 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 @@ out: * The FDT is fdt_packed() during the calculation. */ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, - MachineState *ms) + MachineState *ms, RISCVHartArrayState *harts) { int ret = fdt_pack(ms->fdt); hwaddr dram_end, temp; @@ -XXX,XX +XXX,XX @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, /* * We should put fdt as far as possible to avoid kernel/initrd overwriting - * its content. But it should be addressable by 32 bit system as well. - * Thus, put it at an 2MB aligned address that less than fdt size from the - * end of dram or 3GB whichever is lesser. + * its content. But it should be addressable by 32 bit system as well in RV32. + * Thus, put it near to the end of dram in RV64, and put it near to the end + * of dram or 3GB whichever is lesser in RV32. */ - temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; + if (!riscv_is_32bit(harts)) { + temp = dram_end; + } else { + temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; + } return QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB); } 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) bool kernel_as_payload = false; target_ulong firmware_end_addr, kernel_start_addr; uint64_t kernel_entry; - uint32_t fdt_load_addr; + uint64_t fdt_load_addr; DriveInfo *dinfo = drive_get(IF_SD, 0, 0); /* Sanity check on RAM size */ @@ -XXX,XX +XXX,XX @@ static void microchip_icicle_kit_machine_init(MachineState *machine) /* Compute the fdt load address in dram */ fdt_load_addr = riscv_compute_fdt_addr(memmap[MICROCHIP_PFSOC_DRAM_LO].base, memmap[MICROCHIP_PFSOC_DRAM_LO].size, - machine); + machine, &s->soc.u_cpus); riscv_load_fdt(fdt_load_addr, machine->fdt); /* Load the 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) target_ulong firmware_end_addr, kernel_start_addr; const char *firmware_name; uint32_t start_addr_hi32 = 0x00000000; + uint32_t fdt_load_addr_hi32 = 0x00000000; int i; - uint32_t fdt_load_addr; + uint64_t fdt_load_addr; uint64_t kernel_entry; DriveInfo *dinfo; BlockBackend *blk; @@ -XXX,XX +XXX,XX @@ static void sifive_u_machine_init(MachineState *machine) fdt_load_addr = riscv_compute_fdt_addr(memmap[SIFIVE_U_DEV_DRAM].base, memmap[SIFIVE_U_DEV_DRAM].size, - machine); + machine, &s->soc.u_cpus); riscv_load_fdt(fdt_load_addr, machine->fdt); if (!riscv_is_32bit(&s->soc.u_cpus)) { start_addr_hi32 = (uint64_t)start_addr >> 32; + fdt_load_addr_hi32 = fdt_load_addr >> 32; } /* reset vector */ @@ -XXX,XX +XXX,XX @@ static void sifive_u_machine_init(MachineState *machine) start_addr, /* start: .dword */ start_addr_hi32, fdt_load_addr, /* fdt_laddr: .dword */ - 0x00000000, + fdt_load_addr_hi32, 0x00000000, /* fw_dyn: */ }; 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) hwaddr firmware_load_addr = memmap[SPIKE_DRAM].base; target_ulong kernel_start_addr; char *firmware_name; - uint32_t fdt_load_addr; + uint64_t fdt_load_addr; uint64_t kernel_entry; char *soc_name; int i, base_hartid, hart_count; @@ -XXX,XX +XXX,XX @@ static void spike_board_init(MachineState *machine) fdt_load_addr = riscv_compute_fdt_addr(memmap[SPIKE_DRAM].base, memmap[SPIKE_DRAM].size, - machine); + machine, &s->soc[0]); riscv_load_fdt(fdt_load_addr, machine->fdt); /* load the reset vector */ 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) fdt_load_addr = riscv_compute_fdt_addr(memmap[VIRT_DRAM].base, memmap[VIRT_DRAM].size, - machine); + machine, &s->soc[0]); riscv_load_fdt(fdt_load_addr, machine->fdt); /* load the reset vector */ -- 2.47.1
From: Jim Shu <jim.shu@sifive.com> Add a new struct RISCVBootInfo to sync boot information between multiple boot functions. Signed-off-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20241120153935.24706-3-jim.shu@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/boot.h | 25 ++++++++++----- hw/riscv/boot.c | 65 ++++++++++++++++++++++---------------- hw/riscv/microchip_pfsoc.c | 11 ++++--- hw/riscv/opentitan.c | 4 ++- hw/riscv/sifive_e.c | 4 ++- hw/riscv/sifive_u.c | 12 ++++--- hw/riscv/spike.c | 12 ++++--- hw/riscv/virt.c | 13 +++++--- 8 files changed, 90 insertions(+), 56 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 @@ #define RISCV32_BIOS_BIN "opensbi-riscv32-generic-fw_dynamic.bin" #define RISCV64_BIOS_BIN "opensbi-riscv64-generic-fw_dynamic.bin" +typedef struct RISCVBootInfo { + ssize_t kernel_size; + hwaddr image_low_addr; + hwaddr image_high_addr; + + bool is_32bit; +} RISCVBootInfo; + bool riscv_is_32bit(RISCVHartArrayState *harts); char *riscv_plic_hart_config_string(int hart_count); -target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts, +void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts); +target_ulong riscv_calc_kernel_start_addr(RISCVBootInfo *info, target_ulong firmware_end_addr); target_ulong riscv_find_and_load_firmware(MachineState *machine, const char *default_machine_firmware, @@ -XXX,XX +XXX,XX @@ char *riscv_find_firmware(const char *firmware_filename, target_ulong riscv_load_firmware(const char *firmware_filename, hwaddr *firmware_load_addr, symbol_fn_t sym_cb); -target_ulong riscv_load_kernel(MachineState *machine, - RISCVHartArrayState *harts, - target_ulong firmware_end_addr, - bool load_initrd, - symbol_fn_t sym_cb); -uint64_t riscv_compute_fdt_addr(hwaddr dram_start, uint64_t dram_size, - MachineState *ms, RISCVHartArrayState *harts); +void riscv_load_kernel(MachineState *machine, + RISCVBootInfo *info, + target_ulong kernel_start_addr, + bool load_initrd, + symbol_fn_t sym_cb); +uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, + MachineState *ms, RISCVBootInfo *info); void riscv_load_fdt(hwaddr fdt_addr, void *fdt); void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts, hwaddr saddr, 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) return g_strjoinv(",", (char **)vals); } -target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts, +void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts) +{ + info->kernel_size = 0; + info->is_32bit = riscv_is_32bit(harts); +} + +target_ulong riscv_calc_kernel_start_addr(RISCVBootInfo *info, target_ulong firmware_end_addr) { - if (riscv_is_32bit(harts)) { + if (info->is_32bit) { return QEMU_ALIGN_UP(firmware_end_addr, 4 * MiB); } else { return QEMU_ALIGN_UP(firmware_end_addr, 2 * MiB); @@ -XXX,XX +XXX,XX @@ target_ulong riscv_load_firmware(const char *firmware_filename, exit(1); } -static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) +static void riscv_load_initrd(MachineState *machine, RISCVBootInfo *info) { const char *filename = machine->initrd_filename; uint64_t mem_size = machine->ram_size; @@ -XXX,XX +XXX,XX @@ static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) * halfway into RAM, and for boards with 1GB of RAM or more we put * the initrd at 512MB. */ - start = kernel_entry + MIN(mem_size / 2, 512 * MiB); + start = info->image_low_addr + MIN(mem_size / 2, 512 * MiB); size = load_ramdisk(filename, start, mem_size - start); if (size == -1) { @@ -XXX,XX +XXX,XX @@ static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) } } -target_ulong riscv_load_kernel(MachineState *machine, - RISCVHartArrayState *harts, - target_ulong kernel_start_addr, - bool load_initrd, - symbol_fn_t sym_cb) +void riscv_load_kernel(MachineState *machine, + RISCVBootInfo *info, + target_ulong kernel_start_addr, + bool load_initrd, + symbol_fn_t sym_cb) { const char *kernel_filename = machine->kernel_filename; - uint64_t kernel_load_base, kernel_entry; + ssize_t kernel_size; void *fdt = machine->fdt; g_assert(kernel_filename != NULL); @@ -XXX,XX +XXX,XX @@ target_ulong riscv_load_kernel(MachineState *machine, * the (expected) load address load address. This allows kernels to have * separate SBI and ELF entry points (used by FreeBSD, for example). */ - if (load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, - NULL, &kernel_load_base, NULL, NULL, 0, - EM_RISCV, 1, 0, NULL, true, sym_cb) > 0) { - kernel_entry = kernel_load_base; + kernel_size = load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, NULL, + &info->image_low_addr, &info->image_high_addr, + NULL, 0, EM_RISCV, 1, 0, NULL, true, sym_cb); + if (kernel_size > 0) { + info->kernel_size = kernel_size; goto out; } - if (load_uimage_as(kernel_filename, &kernel_entry, NULL, NULL, - NULL, NULL, NULL) > 0) { + kernel_size = load_uimage_as(kernel_filename, &info->image_low_addr, + NULL, NULL, NULL, NULL, NULL); + if (kernel_size > 0) { + info->kernel_size = kernel_size; + info->image_high_addr = info->image_low_addr + kernel_size; goto out; } - if (load_image_targphys_as(kernel_filename, kernel_start_addr, - current_machine->ram_size, NULL) > 0) { - kernel_entry = kernel_start_addr; + kernel_size = load_image_targphys_as(kernel_filename, kernel_start_addr, + current_machine->ram_size, NULL); + if (kernel_size > 0) { + info->kernel_size = kernel_size; + info->image_low_addr = kernel_start_addr; + info->image_high_addr = info->image_low_addr + kernel_size; goto out; } @@ -XXX,XX +XXX,XX @@ target_ulong riscv_load_kernel(MachineState *machine, out: /* - * For 32 bit CPUs 'kernel_entry' can be sign-extended by + * For 32 bit CPUs 'image_low_addr' can be sign-extended by * load_elf_ram_sym(). */ - if (riscv_is_32bit(harts)) { - kernel_entry = extract64(kernel_entry, 0, 32); + if (info->is_32bit) { + info->image_low_addr = extract64(info->image_low_addr, 0, 32); } if (load_initrd && machine->initrd_filename) { - riscv_load_initrd(machine, kernel_entry); + riscv_load_initrd(machine, info); } if (fdt && machine->kernel_cmdline && *machine->kernel_cmdline) { qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", machine->kernel_cmdline); } - - return kernel_entry; } /* @@ -XXX,XX +XXX,XX @@ out: * The FDT is fdt_packed() during the calculation. */ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, - MachineState *ms, RISCVHartArrayState *harts) + MachineState *ms, RISCVBootInfo *info) { int ret = fdt_pack(ms->fdt); hwaddr dram_end, temp; @@ -XXX,XX +XXX,XX @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, * Thus, put it near to the end of dram in RV64, and put it near to the end * of dram or 3GB whichever is lesser in RV32. */ - if (!riscv_is_32bit(harts)) { + if (!info->is_32bit) { temp = dram_end; } else { temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; 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) uint64_t kernel_entry; uint64_t fdt_load_addr; DriveInfo *dinfo = drive_get(IF_SD, 0, 0); + RISCVBootInfo boot_info; /* Sanity check on RAM size */ if (machine->ram_size < mc->default_ram_size) { @@ -XXX,XX +XXX,XX @@ static void microchip_icicle_kit_machine_init(MachineState *machine) firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, &firmware_load_addr, NULL); + riscv_boot_info_init(&boot_info, &s->soc.u_cpus); if (kernel_as_payload) { - kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc.u_cpus, + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); - kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus, - kernel_start_addr, true, NULL); + riscv_load_kernel(machine, &boot_info, kernel_start_addr, + true, NULL); + kernel_entry = boot_info.image_low_addr; /* Compute the fdt load address in dram */ fdt_load_addr = riscv_compute_fdt_addr(memmap[MICROCHIP_PFSOC_DRAM_LO].base, memmap[MICROCHIP_PFSOC_DRAM_LO].size, - machine, &s->soc.u_cpus); + machine, &boot_info); riscv_load_fdt(fdt_load_addr, machine->fdt); /* Load the reset vector */ 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) OpenTitanState *s = OPENTITAN_MACHINE(machine); const MemMapEntry *memmap = ibex_memmap; MemoryRegion *sys_mem = get_system_memory(); + RISCVBootInfo boot_info; if (machine->ram_size != mc->default_ram_size) { char *sz = size_to_str(mc->default_ram_size); @@ -XXX,XX +XXX,XX @@ static void opentitan_machine_init(MachineState *machine) riscv_load_firmware(machine->firmware, &firmware_load_addr, NULL); } + riscv_boot_info_init(&boot_info, &s->soc.cpus); if (machine->kernel_filename) { - riscv_load_kernel(machine, &s->soc.cpus, + riscv_load_kernel(machine, &boot_info, memmap[IBEX_DEV_RAM].base, false, NULL); } diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -XXX,XX +XXX,XX @@ static void sifive_e_machine_init(MachineState *machine) SiFiveEState *s = RISCV_E_MACHINE(machine); MemoryRegion *sys_mem = get_system_memory(); int i; + RISCVBootInfo boot_info; if (machine->ram_size != mc->default_ram_size) { char *sz = size_to_str(mc->default_ram_size); @@ -XXX,XX +XXX,XX @@ static void sifive_e_machine_init(MachineState *machine) rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), memmap[SIFIVE_E_DEV_MROM].base, &address_space_memory); + riscv_boot_info_init(&boot_info, &s->soc.cpus); if (machine->kernel_filename) { - riscv_load_kernel(machine, &s->soc.cpus, + riscv_load_kernel(machine, &boot_info, memmap[SIFIVE_E_DEV_DTIM].base, false, NULL); } 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) BlockBackend *blk; DeviceState *flash_dev, *sd_dev, *card_dev; qemu_irq flash_cs, sd_cs; + RISCVBootInfo boot_info; /* Initialize SoC */ object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RISCV_U_SOC); @@ -XXX,XX +XXX,XX @@ static void sifive_u_machine_init(MachineState *machine) firmware_end_addr = riscv_find_and_load_firmware(machine, 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(&s->soc.u_cpus, + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); - - kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus, - kernel_start_addr, true, NULL); + riscv_load_kernel(machine, &boot_info, kernel_start_addr, + true, NULL); + kernel_entry = boot_info.image_low_addr; } else { /* * If dynamic firmware is used, it doesn't know where is the next mode @@ -XXX,XX +XXX,XX @@ static void sifive_u_machine_init(MachineState *machine) fdt_load_addr = riscv_compute_fdt_addr(memmap[SIFIVE_U_DEV_DRAM].base, memmap[SIFIVE_U_DEV_DRAM].size, - machine, &s->soc.u_cpus); + machine, &boot_info); riscv_load_fdt(fdt_load_addr, machine->fdt); if (!riscv_is_32bit(&s->soc.u_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_board_init(MachineState *machine) char *soc_name; int i, base_hartid, hart_count; bool htif_custom_base = false; + RISCVBootInfo boot_info; /* Check socket count limit */ if (SPIKE_SOCKETS_MAX < riscv_socket_count(machine)) { @@ -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(&s->soc[0], + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); - kernel_entry = riscv_load_kernel(machine, &s->soc[0], - kernel_start_addr, - true, htif_symbol_callback); + riscv_load_kernel(machine, &boot_info, kernel_start_addr, + true, htif_symbol_callback); + kernel_entry = boot_info.image_low_addr; } else { /* * If dynamic firmware is used, it doesn't know where is the next mode @@ -XXX,XX +XXX,XX @@ static void spike_board_init(MachineState *machine) fdt_load_addr = riscv_compute_fdt_addr(memmap[SPIKE_DRAM].base, memmap[SPIKE_DRAM].size, - machine, &s->soc[0]); + machine, &boot_info); riscv_load_fdt(fdt_load_addr, machine->fdt); /* load the reset vector */ 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) uint64_t fdt_load_addr; uint64_t kernel_entry = 0; BlockBackend *pflash_blk0; + RISCVBootInfo boot_info; /* * An user provided dtb must include everything, including @@ -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(&s->soc[0], + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); - - kernel_entry = riscv_load_kernel(machine, &s->soc[0], - kernel_start_addr, true, NULL); + 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(memmap[VIRT_DRAM].base, memmap[VIRT_DRAM].size, - machine, &s->soc[0]); + machine, &boot_info); riscv_load_fdt(fdt_load_addr, machine->fdt); /* load the reset vector */ -- 2.47.1
From: Jim Shu <jim.shu@sifive.com> DTB is placed to the end of memory, so we will check if the start address of DTB overlaps to the address of kernel/initrd. Signed-off-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20241120153935.24706-4-jim.shu@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- include/hw/riscv/boot.h | 3 +++ hw/riscv/boot.c | 25 ++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) 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 @@ typedef struct RISCVBootInfo { hwaddr image_low_addr; hwaddr image_high_addr; + hwaddr initrd_start; + ssize_t initrd_size; + bool is_32bit; } RISCVBootInfo; 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->kernel_size = 0; + info->initrd_size = 0; info->is_32bit = riscv_is_32bit(harts); } @@ -XXX,XX +XXX,XX @@ static void riscv_load_initrd(MachineState *machine, RISCVBootInfo *info) } } + info->initrd_start = start; + info->initrd_size = size; + /* Some RISC-V machines (e.g. opentitan) don't have a fdt. */ if (fdt) { end = start + size; @@ -XXX,XX +XXX,XX @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, int ret = fdt_pack(ms->fdt); hwaddr dram_end, temp; int fdtsize; + uint64_t dtb_start, dtb_start_limit; /* Should only fail if we've built a corrupted tree */ g_assert(ret == 0); @@ -XXX,XX +XXX,XX @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, exit(1); } + if (info->initrd_size) { + /* If initrd is successfully loaded, place DTB after it. */ + dtb_start_limit = info->initrd_start + info->initrd_size; + } else if (info->kernel_size) { + /* If only kernel is successfully loaded, place DTB after it. */ + dtb_start_limit = info->image_high_addr; + } else { + /* Otherwise, do not check DTB overlapping */ + dtb_start_limit = 0; + } + /* * A dram_size == 0, usually from a MemMapEntry[].size element, * means that the DRAM block goes all the way to ms->ram_size. @@ -XXX,XX +XXX,XX @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; } - return QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB); + 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"); + exit(1); + } + + return dtb_start; } /* -- 2.47.1
From: "Fea.Wang" <fea.wang@sifive.com> Refer to the draft of svukte extension from: https://github.com/riscv/riscv-isa-manual/pull/1564 Svukte provides a means to make user-mode accesses to supervisor memory raise page faults in constant time, mitigating attacks that attempt to discover the supervisor software's address-space layout. Signed-off-by: Fea.Wang <fea.wang@sifive.com> Reviewed-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241203034932.25185-2-fea.wang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_cfg.h | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -XXX,XX +XXX,XX @@ struct RISCVCPUConfig { bool ext_svnapot; bool ext_svpbmt; bool ext_svvptc; + bool ext_svukte; bool ext_zdinx; bool ext_zaamo; bool ext_zacas; -- 2.47.1
From: "Fea.Wang" <fea.wang@sifive.com> Svukte extension add UKTE bit, bit[8] in senvcfg CSR. The bit will be supported when the svukte extension is enabled. When senvcfg[UKTE] bit is set, the memory access from U-mode should do the svukte check only except HLV/HLVX/HSV H-mode instructions which depend on hstatus[HUKTE]. Signed-off-by: Fea.Wang <fea.wang@sifive.com> Reviewed-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241203034932.25185-3-fea.wang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_bits.h | 1 + target/riscv/csr.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -XXX,XX +XXX,XX @@ typedef enum RISCVException { #define SENVCFG_CBIE MENVCFG_CBIE #define SENVCFG_CBCFE MENVCFG_CBCFE #define SENVCFG_CBZE MENVCFG_CBZE +#define SENVCFG_UKTE BIT(8) #define HENVCFG_FIOM MENVCFG_FIOM #define HENVCFG_LPE MENVCFG_LPE 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_senvcfg(CPURISCVState *env, int csrno, mask |= SENVCFG_SSE; } + if (env_archcpu(env)->cfg.ext_svukte) { + mask |= SENVCFG_UKTE; + } + env->senvcfg = (env->senvcfg & ~mask) | (val & mask); return RISCV_EXCP_NONE; } -- 2.47.1
From: "Fea.Wang" <fea.wang@sifive.com> Svukte extension add HUKTE bit, bit[24] in hstatus CSR. The written value will be masked when the svukte extension is not enabled. When hstatus[HUKTE] bit is set, HLV/HLVX/HSV work in the U-mode should do svukte check. Signed-off-by: Fea.Wang <fea.wang@sifive.com> Reviewed-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241203034932.25185-4-fea.wang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_bits.h | 1 + target/riscv/csr.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -XXX,XX +XXX,XX @@ typedef enum { #define HSTATUS_VTVM 0x00100000 #define HSTATUS_VTW 0x00200000 #define HSTATUS_VTSR 0x00400000 +#define HSTATUS_HUKTE 0x01000000 #define HSTATUS_VSXL 0x300000000 #define HSTATUS32_WPRI 0xFF8FF87E 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_hstatus(CPURISCVState *env, int csrno, static RISCVException write_hstatus(CPURISCVState *env, int csrno, target_ulong val) { + if (!env_archcpu(env)->cfg.ext_svukte) { + val = val & (~HSTATUS_HUKTE); + } env->hstatus = val; if (riscv_cpu_mxl(env) != MXL_RV32 && get_field(val, HSTATUS_VSXL) != 2) { qemu_log_mask(LOG_UNIMP, -- 2.47.1
From: "Fea.Wang" <fea.wang@sifive.com> Follow the Svukte spec, do the memory access address checking 1. Include instruction fetches or explicit memory accesses 2. System run in effective privilege U or VU 3. Check senvcfg[UKTE] being set, or hstatus[HUKTE] being set if instruction is HLV, HLVX, HSV and execute from U mode to VU mode 4. Depend on Sv39 and check virtual addresses bit[SXLEN-1] 5. Raises a page-fault exception corresponding to the original access type. Ref: https://github.com/riscv/riscv-isa-manual/pull/1564/files Signed-off-by: Frank Chang <frank.chang@sifive.com> Signed-off-by: Fea.Wang <fea.wang@sifive.com> Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241203034932.25185-5-fea.wang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_helper.c | 55 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 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_pmp(CPURISCVState *env, int *prot, hwaddr addr, return TRANSLATE_SUCCESS; } +/* Returns 'true' if a svukte address check is needed */ +static bool do_svukte_check(CPURISCVState *env, bool first_stage, + int mode, bool virt) +{ + /* Svukte extension depends on Sv39. */ + if (!(env_archcpu(env)->cfg.ext_svukte || + !first_stage || + VM_1_10_SV39 != get_field(env->satp, SATP64_MODE))) { + return false; + } + + /* + * Check hstatus.HUKTE if the effective mode is switched to VU-mode by + * executing HLV/HLVX/HSV in U-mode. + * For other cases, check senvcfg.UKTE. + */ + if (env->priv == PRV_U && !env->virt_enabled && virt) { + if (!get_field(env->hstatus, HSTATUS_HUKTE)) { + return false; + } + } else if (!get_field(env->senvcfg, SENVCFG_UKTE)) { + return false; + } + + /* + * Svukte extension is qualified only in U or VU-mode. + * + * Effective mode can be switched to U or VU-mode by: + * - M-mode + mstatus.MPRV=1 + mstatus.MPP=U-mode. + * - Execute HLV/HLVX/HSV from HS-mode + hstatus.SPVP=0. + * - U-mode. + * - VU-mode. + * - Execute HLV/HLVX/HSV from U-mode + hstatus.HU=1. + */ + if (mode != PRV_U) { + return false; + } + + return true; +} + +static bool check_svukte_addr(CPURISCVState *env, vaddr addr) +{ + /* svukte extension excludes RV32 */ + uint32_t sxlen = 32 * riscv_cpu_sxl(env); + uint64_t high_bit = addr & (1UL << (sxlen - 1)); + return !high_bit; +} + /* * get_physical_address - get the physical address for this virtual address * @@ -XXX,XX +XXX,XX @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, MemTxResult res; MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; int mode = mmuidx_priv(mmu_idx); + bool virt = mmuidx_2stage(mmu_idx); bool use_background = false; hwaddr ppn; int napot_bits = 0; @@ -XXX,XX +XXX,XX @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, bool is_sstack_idx = ((mmu_idx & MMU_IDX_SS_WRITE) == MMU_IDX_SS_WRITE); bool sstack_page = false; + if (do_svukte_check(env, first_stage, mode, virt) && + !check_svukte_addr(env, addr)) { + return TRANSLATE_FAIL; + } + /* * Check if we should use the background registers for the two * stage translation. We don't need to check if we actually need -- 2.47.1
From: "Fea.Wang" <fea.wang@sifive.com> Add "svukte" in the ISA string when svukte extension is enabled. Signed-off-by: Fea.Wang <fea.wang@sifive.com> Reviewed-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: <20241203034932.25185-6-fea.wang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 2 ++ 1 file changed, 2 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 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(svinval, PRIV_VERSION_1_12_0, ext_svinval), 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(svukte, PRIV_VERSION_1_13_0, ext_svukte), ISA_EXT_DATA_ENTRY(svvptc, PRIV_VERSION_1_13_0, ext_svvptc), ISA_EXT_DATA_ENTRY(xtheadba, PRIV_VERSION_1_11_0, ext_xtheadba), ISA_EXT_DATA_ENTRY(xtheadbb, PRIV_VERSION_1_11_0, ext_xtheadbb), @@ -XXX,XX +XXX,XX @@ const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[] = { /* These are experimental so mark with 'x-' */ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { + MULTI_EXT_CFG_BOOL("x-svukte", ext_svukte, false), DEFINE_PROP_END_OF_LIST(), }; -- 2.47.1
From: "Fea.Wang" <fea.wang@sifive.com> The spec explicitly says svukte doesn't support RV32. So check that it is not enabled in RV32. Signed-off-by: Fea.Wang <fea.wang@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241203034932.25185-7-fea.wang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/tcg/tcg-cpu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -XXX,XX +XXX,XX @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } + if (mcc->misa_mxl_max == MXL_RV32 && cpu->cfg.ext_svukte) { + error_setg(errp, "svukte is not supported for RV32"); + return; + } + /* * Disable isa extensions based on priv spec after we * validated and set everything we need. -- 2.47.1
From: Philippe Mathieu-Daudé <philmd@linaro.org> Rather than relying on implicit includes, explicit them, in order to avoid when refactoring unrelated headers: target/riscv/vector_internals.h:36:12: error: call to undeclared function 'FIELD_EX32'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 36 | return FIELD_EX32(simd_data(desc), VDATA, NF); | ^ Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20241203200828.47311-2-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/vector_internals.h | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/vector_internals.h b/target/riscv/vector_internals.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/vector_internals.h +++ b/target/riscv/vector_internals.h @@ -XXX,XX +XXX,XX @@ #define TARGET_RISCV_VECTOR_INTERNALS_H #include "qemu/bitops.h" +#include "hw/registerfields.h" #include "cpu.h" #include "tcg/tcg-gvec-desc.h" #include "internals.h" -- 2.47.1
From: Philippe Mathieu-Daudé <philmd@linaro.org> Rather than relying on implicit includes, explicit them, in order to avoid when refactoring unrelated headers: target/riscv/internals.h:49:15: error: use of undeclared identifier 'PRV_S' 49 | ret = PRV_S; | ^ target/riscv/internals.h:93:9: error: call to undeclared function 'env_archcpu'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 93 | if (env_archcpu(env)->cfg.ext_zfinx) { | ^ target/riscv/internals.h:101:15: error: unknown type name 'float32'; did you mean 'float'? 101 | static inline float32 check_nanbox_s(CPURISCVState *env, uint64_t f) | ^~~~~~~ | float Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20241203200828.47311-3-philmd@linaro.org> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/internals.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/riscv/internals.h b/target/riscv/internals.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -XXX,XX +XXX,XX @@ #ifndef RISCV_CPU_INTERNALS_H #define RISCV_CPU_INTERNALS_H +#include "exec/cpu-common.h" #include "hw/registerfields.h" +#include "fpu/softfloat-types.h" +#include "target/riscv/cpu_bits.h" /* * The current MMU Modes are: -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Commit 68c9e54bea handled a situation where a warning was being shown when using the 'sifive_e' cpu when disabling the named extension zic64b. It makes little sense to show user warnings for named extensions that users can't control, and the solution taken was to disable zic64b manually in riscv_cpu_update_named_features(). This solution won't scale well when adding more named features, and can eventually end up repeating riscv_cpu_disable_priv_spec_isa_exts(). Change riscv_cpu_disable_priv_spec_isa_exts() to not show warnings when disabling a named feature. This will accomplish the same thing we're doing today while avoiding having two points where we're disabling exts via priv_ver mismatch. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20241113171755.978109-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/tcg/tcg-cpu.c | 13 ++++++++++--- 1 file changed, 10 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_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" TARGET_FMT_lx " 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.has_priv_1_12; + cpu->cfg.cboz_blocksize == 64; } static void riscv_cpu_validate_g(RISCVCPU *cpu) -- 2.47.1
From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> ssstateen is defined in RVA22 as: "Supervisor-mode view of the state-enable extension. The supervisor-mode (sstateen0-3) and hypervisor-mode (hstateen0-3) state-enable registers must be provided." Add ssstateen as a named feature that is available if we also have smstateen. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Andrew Jones <ajones@ventanamicro.com> Message-ID: <20241113171755.978109-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_cfg.h | 1 + target/riscv/cpu.c | 2 ++ target/riscv/tcg/tcg-cpu.c | 9 ++++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -XXX,XX +XXX,XX @@ struct RISCVCPUConfig { /* Named features */ bool ext_svade; bool ext_zic64b; + bool ext_ssstateen; /* * Always 'true' booleans for named features 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(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_EXT_DATA_ENTRY(ssstateen, PRIV_VERSION_1_12_0, ext_ssstateen), 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), @@ -XXX,XX +XXX,XX @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { */ const RISCVCPUMultiExtConfig riscv_cpu_named_features[] = { MULTI_EXT_CFG_BOOL("zic64b", ext_zic64b, true), + MULTI_EXT_CFG_BOOL("ssstateen", ext_ssstateen, true), DEFINE_PROP_END_OF_LIST(), }; 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_named_feat(RISCVCPU *cpu, uint32_t feat_offset) * All other named features are already enabled * in riscv_tcg_cpu_instance_init(). */ - if (feat_offset == CPU_CFG_OFFSET(ext_zic64b)) { + 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_ssstateen): + cpu->cfg.ext_smstateen = true; + break; } } @@ -XXX,XX +XXX,XX @@ static void riscv_cpu_update_named_features(RISCVCPU *cpu) cpu->cfg.ext_zic64b = cpu->cfg.cbom_blocksize == 64 && cpu->cfg.cbop_blocksize == 64 && cpu->cfg.cboz_blocksize == 64; + + cpu->cfg.ext_ssstateen = cpu->cfg.ext_smstateen; } static void riscv_cpu_validate_g(RISCVCPU *cpu) -- 2.47.1
From: MollyChen <xiaoou@iscas.ac.cn> Add a CPU entry for the RV64 XiangShan NANHU CPU which supports single-core and dual-core configurations. More details can be found at https://docs.xiangshan.cc/zh-cn/latest/integration/overview Signed-off-by: MollyChen <xiaoou@iscas.ac.cn> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20241205073622.46052-1-xiaoou@iscas.ac.cn> [ Changes by AF - Fixup code formatting ] Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu-qom.h | 1 + target/riscv/cpu.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index XXXXXXX..XXXXXXX 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -XXX,XX +XXX,XX @@ #define TYPE_RISCV_CPU_THEAD_C906 RISCV_CPU_TYPE_NAME("thead-c906") #define TYPE_RISCV_CPU_VEYRON_V1 RISCV_CPU_TYPE_NAME("veyron-v1") #define TYPE_RISCV_CPU_TT_ASCALON RISCV_CPU_TYPE_NAME("tt-ascalon") +#define TYPE_RISCV_CPU_XIANGSHAN_NANHU RISCV_CPU_TYPE_NAME("xiangshan-nanhu") #define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host") OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) 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 rv64_tt_ascalon_cpu_init(Object *obj) #endif } +static void rv64_xiangshan_nanhu_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + RISCVCPU *cpu = RISCV_CPU(obj); + + riscv_cpu_set_misa_ext(env, RVG | RVC | RVB | RVS | RVU); + env->priv_ver = PRIV_VERSION_1_12_0; + + /* Enable ISA extensions */ + cpu->cfg.ext_zbc = true; + cpu->cfg.ext_zbkb = true; + cpu->cfg.ext_zbkc = true; + cpu->cfg.ext_zbkx = true; + cpu->cfg.ext_zknd = true; + cpu->cfg.ext_zkne = true; + cpu->cfg.ext_zknh = true; + cpu->cfg.ext_zksed = true; + cpu->cfg.ext_zksh = true; + cpu->cfg.ext_svinval = true; + + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; + +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(cpu, VM_1_10_SV39); +#endif +} + #ifdef CONFIG_TCG static void rv128_base_cpu_init(Object *obj) { @@ -XXX,XX +XXX,XX @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_THEAD_C906, MXL_RV64, rv64_thead_c906_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_TT_ASCALON, MXL_RV64, rv64_tt_ascalon_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_VEYRON_V1, MXL_RV64, rv64_veyron_v1_cpu_init), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_XIANGSHAN_NANHU, + MXL_RV64, rv64_xiangshan_nanhu_cpu_init), #ifdef CONFIG_TCG DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE128, MXL_RV128, rv128_base_cpu_init), #endif /* CONFIG_TCG */ -- 2.47.1