From 301c65f49f9602f39b9f3ce0ad9ff70d4bda7226 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Fri, 10 Nov 2023 09:37:16 -0800 Subject: [PATCH 1/6] linux-user/riscv: Add Zicboz block size to hwprobe Support for probing the Zicboz block size landed in Linux 6.6, which was released a few weeks ago. This provides the user-configured block size when Zicboz is enabled. Signed-off-by: Palmer Dabbelt Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231110173716.24423-1-palmer@rivosinc.com> Signed-off-by: Alistair Francis --- linux-user/syscall.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 16ca5ea7b6..e384e14248 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8808,6 +8808,8 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count) #define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0) #define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0) +#define RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE 6 + struct riscv_hwprobe { abi_llong key; abi_ullong value; @@ -8860,6 +8862,10 @@ static void risc_hwprobe_fill_pairs(CPURISCVState *env, case RISCV_HWPROBE_KEY_CPUPERF_0: __put_user(RISCV_HWPROBE_MISALIGNED_FAST, &pair->value); break; + case RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE: + value = cfg->ext_zicboz ? cfg->cboz_blocksize : 0; + __put_user(value, &pair->value); + break; default: __put_user(-1, &pair->key); break; From 7a87ba8956e59bec8cc4677c6aa5141e4c023a7d Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 10 Nov 2023 14:25:59 -0300 Subject: [PATCH 2/6] hw/riscv/virt.c: do create_fdt() earlier, add finalize_fdt() Commit 49554856f0 fixed a problem, where TPM devices were not appearing in the FDT, by delaying the FDT creation up until virt_machine_done(). This create a side effect (see gitlab #1925) - devices that need access to the '/chosen' FDT node during realize() stopped working because, at that point, we don't have a FDT. This happens because our FDT creation is monolithic, but it doesn't need to be. We can add the needed FDT components for realize() time and, at the same time, do another FDT round where we account for dynamic sysbus devices. In other words, the problem fixed by 49554856f0 could also be fixed by postponing only create_fdt_sockets() and its dependencies, leaving everything else from create_fdt() to be done during init(). Split the FDT creation in two parts: - create_fdt(), now moved back to virt_machine_init(), will create FDT nodes that doesn't depend on additional (dynamic) devices from the sysbus; - a new finalize_fdt() step is added, where create_fdt_sockets() and friends is executed, accounting for the dynamic sysbus devices that were added during realize(). This will make both use cases happy: TPM devices are still working as intended, and devices such as 'guest-loader' have a FDT to work on during realize(). Fixes: 49554856f0 ("riscv: Generate devicetree only after machine initialization is complete") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1925 Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20231110172559.73209-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 71 +++++++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index c7fc97e273..d2eac24156 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -962,7 +962,6 @@ static void create_fdt_uart(RISCVVirtState *s, const MemMapEntry *memmap, qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", UART0_IRQ, 0x4); } - qemu_fdt_add_subnode(ms->fdt, "/chosen"); qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", name); g_free(name); } @@ -1023,11 +1022,29 @@ static void create_fdt_fw_cfg(RISCVVirtState *s, const MemMapEntry *memmap) g_free(nodename); } +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; + + create_fdt_sockets(s, virt_memmap, &phandle, &irq_mmio_phandle, + &irq_pcie_phandle, &irq_virtio_phandle, + &msi_pcie_phandle); + + create_fdt_virtio(s, virt_memmap, irq_virtio_phandle); + + create_fdt_pcie(s, virt_memmap, irq_pcie_phandle, msi_pcie_phandle); + + create_fdt_reset(s, virt_memmap, &phandle); + + create_fdt_uart(s, virt_memmap, irq_mmio_phandle); + + create_fdt_rtc(s, virt_memmap, irq_mmio_phandle); +} + static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap) { MachineState *ms = MACHINE(s); - uint32_t phandle = 1, irq_mmio_phandle = 1, msi_pcie_phandle = 1; - uint32_t irq_pcie_phandle = 1, irq_virtio_phandle = 1; uint8_t rng_seed[32]; ms->fdt = create_device_tree(&s->fdt_size); @@ -1047,28 +1064,16 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap) qemu_fdt_setprop_cell(ms->fdt, "/soc", "#size-cells", 0x2); qemu_fdt_setprop_cell(ms->fdt, "/soc", "#address-cells", 0x2); - create_fdt_sockets(s, memmap, &phandle, &irq_mmio_phandle, - &irq_pcie_phandle, &irq_virtio_phandle, - &msi_pcie_phandle); - - create_fdt_virtio(s, memmap, irq_virtio_phandle); - - create_fdt_pcie(s, memmap, irq_pcie_phandle, msi_pcie_phandle); - - create_fdt_reset(s, memmap, &phandle); - - create_fdt_uart(s, memmap, irq_mmio_phandle); - - create_fdt_rtc(s, memmap, irq_mmio_phandle); - - create_fdt_flash(s, memmap); - create_fdt_fw_cfg(s, memmap); - create_fdt_pmu(s); + qemu_fdt_add_subnode(ms->fdt, "/chosen"); /* Pass seed to RNG */ qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); qemu_fdt_setprop(ms->fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); + + create_fdt_flash(s, memmap); + create_fdt_fw_cfg(s, memmap); + create_fdt_pmu(s); } static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, @@ -1257,15 +1262,12 @@ static void virt_machine_done(Notifier *notifier, void *data) uint64_t kernel_entry = 0; BlockBackend *pflash_blk0; - /* load/create device tree */ - if (machine->dtb) { - machine->fdt = load_device_tree(machine->dtb, &s->fdt_size); - if (!machine->fdt) { - error_report("load_device_tree() failed"); - exit(1); - } - } else { - create_fdt(s, memmap); + /* + * An user provided dtb must include everything, including + * dynamic sysbus devices. Our FDT needs to be finalized. + */ + if (machine->dtb == NULL) { + finalize_fdt(s); } /* @@ -1541,6 +1543,17 @@ static void virt_machine_init(MachineState *machine) } virt_flash_map(s, system_memory); + /* load/create device tree */ + if (machine->dtb) { + machine->fdt = load_device_tree(machine->dtb, &s->fdt_size); + if (!machine->fdt) { + error_report("load_device_tree() failed"); + exit(1); + } + } else { + create_fdt(s, memmap); + } + s->machine_done.notify = virt_machine_done; qemu_add_machine_init_done_notifier(&s->machine_done); } From 9bbf03275e1f02044a473634929724acb6b8eb32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chigot?= Date: Tue, 14 Nov 2023 13:39:13 +0100 Subject: [PATCH 3/6] target/riscv: don't verify ISA compatibility for zicntr and zihpm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The extensions zicntr and zihpm were officially added in the privilege instruction set specification 1.12. However, QEMU has been implemented them long before it and thus they are forced to be on during the cpu initialization to ensure compatibility (see riscv_cpu_init). riscv_cpu_disable_priv_spec_isa_exts was not updated when the above behavior was introduced, resulting in these extensions to be disabled after all. Signed-off-by: Clément Chigot Fixes: c004099330 ("target/riscv: add zicntr extension flag for TCG") Fixes: 0824121660 ("target/riscv: add zihpm extension flag for TCG") Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20231114123913.536194-1-chigot@adacore.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 08adad304d..8a35683a34 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -250,6 +250,15 @@ static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu) for (edata = isa_edata_arr; edata && edata->name; edata++) { if (isa_ext_is_enabled(cpu, edata->ext_enable_offset) && (env->priv_ver < edata->min_version)) { + /* + * These two extensions are always enabled as they were supported + * by QEMU before they were added as extensions in the ISA. + */ + if (!strcmp(edata->name, "zicntr") || + !strcmp(edata->name, "zihpm")) { + continue; + } + isa_ext_update_enabled(cpu, edata->ext_enable_offset, false); #ifndef CONFIG_USER_ONLY warn_report("disabling %s extension for hart 0x" TARGET_FMT_lx From a7472560ca5f7a61ef3a46b52118f680de81058c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Fri, 17 Nov 2023 09:28:42 +0100 Subject: [PATCH 4/6] riscv: Fix SiFive E CLINT clock frequency If you check the manual of SiFive E310 (https://cdn.sparkfun.com/assets/7/f/0/2/7/fe310-g002-manual-v19p05.pdf), you can see in Figure 1 that the CLINT is connected to the real time clock, which also feeds the AON peripheral (they share the same clock). In page 43, the docs also say that the timer registers of the CLINT count ticks from the rtcclk. I am currently playing with bare metal applications both in QEMU and a physical SiFive E310 board and I confirm that the CLINT clock in the physical board runs at 32.768 kHz. In QEMU, the same app produces a completely different outcome, as sometimes a new CLINT interrupt is triggered before finishing other tasks. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1978 Signed-off-by: Rom\ufffd\ufffdn C\ufffd\ufffdrdenas Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231117082840.55705-1-rcardenas.rod@gmail.com> Signed-off-by: Alistair Francis --- hw/riscv/sifive_e.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 0d37adc542..87d9602383 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -225,7 +225,7 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp) RISCV_ACLINT_SWI_SIZE, RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus, RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, - RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false); + SIFIVE_E_LFCLK_DEFAULT_FREQ, false); sifive_e_prci_create(memmap[SIFIVE_E_DEV_PRCI].base); /* AON */ From 82d53adfbb1aa0dbe7dac09b61ad86014efe81a7 Mon Sep 17 00:00:00 2001 From: Ivan Klokov Date: Tue, 21 Nov 2023 10:17:56 +0300 Subject: [PATCH 5/6] target/riscv/cpu_helper.c: Invalid exception on MMU translation stage According to RISCV privileged spec sect. 5.3.2 Virtual Address Translation Process access-fault exceptions may raise only after PMA/PMP check. Current implementation generates an access-fault for mbare mode even if there were no PMA/PMP errors. This patch removes the erroneous MMU mode check and generates an access-fault exception based on the pmp_violation flag only. Fixes: 1448689c7b ("target/riscv: Allow specifying MMU stage") Signed-off-by: Ivan Klokov Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231121071757.7178-2-ivan.klokov@syntacore.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index b7af69de53..9ff0952e46 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1143,47 +1143,31 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, bool two_stage_indirect) { CPUState *cs = env_cpu(env); - int page_fault_exceptions, vm; - uint64_t stap_mode; - - if (riscv_cpu_mxl(env) == MXL_RV32) { - stap_mode = SATP32_MODE; - } else { - stap_mode = SATP64_MODE; - } - - if (first_stage) { - vm = get_field(env->satp, stap_mode); - } else { - vm = get_field(env->hgatp, stap_mode); - } - - page_fault_exceptions = vm != VM_1_10_MBARE && !pmp_violation; switch (access_type) { case MMU_INST_FETCH: if (env->virt_enabled && !first_stage) { cs->exception_index = RISCV_EXCP_INST_GUEST_PAGE_FAULT; } else { - cs->exception_index = page_fault_exceptions ? - RISCV_EXCP_INST_PAGE_FAULT : RISCV_EXCP_INST_ACCESS_FAULT; + cs->exception_index = pmp_violation ? + RISCV_EXCP_INST_ACCESS_FAULT : RISCV_EXCP_INST_PAGE_FAULT; } break; case MMU_DATA_LOAD: if (two_stage && !first_stage) { cs->exception_index = RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT; } else { - cs->exception_index = page_fault_exceptions ? - RISCV_EXCP_LOAD_PAGE_FAULT : RISCV_EXCP_LOAD_ACCESS_FAULT; + cs->exception_index = pmp_violation ? + RISCV_EXCP_LOAD_ACCESS_FAULT : RISCV_EXCP_LOAD_PAGE_FAULT; } break; case MMU_DATA_STORE: if (two_stage && !first_stage) { cs->exception_index = RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT; } else { - cs->exception_index = page_fault_exceptions ? - RISCV_EXCP_STORE_PAGE_FAULT : - RISCV_EXCP_STORE_AMO_ACCESS_FAULT; + cs->exception_index = pmp_violation ? + RISCV_EXCP_STORE_AMO_ACCESS_FAULT : + RISCV_EXCP_STORE_PAGE_FAULT; } break; default: From 6bca4d7d1ff2b8857486c3ff31f5c6fc3e3984b4 Mon Sep 17 00:00:00 2001 From: Ivan Klokov Date: Tue, 21 Nov 2023 10:17:57 +0300 Subject: [PATCH 6/6] target/riscv/cpu_helper.c: Fix mxr bit behavior According to RISCV Specification sect 9.5 on two stage translation when V=1 the vsstatus(mstatus in QEMU's terms) field MXR, which makes execute-only pages readable, only overrides VS-stage page protection. Setting MXR at HS-level(mstatus_hs), however, overrides both VS-stage and G-stage execute-only permissions. The hypervisor extension changes the behavior of MXR\MPV\MPRV bits. Due to RISCV Specification sect. 9.4.1 when MPRV=1, explicit memory accesses are translated and protected, and endianness is applied, as though the current virtualization mode were set to MPV and the current nominal privilege mode were set to MPP. vsstatus.MXR makes readable those pages marked executable at the VS translation stage. Fixes: 36a18664ba ("target/riscv: Implement second stage MMU") Signed-off-by: Ivan Klokov Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20231121071757.7178-3-ivan.klokov@syntacore.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 9ff0952e46..e7e23b34f4 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1032,13 +1032,29 @@ restart: prot |= PAGE_WRITE; } if (pte & PTE_X) { - bool mxr; + bool mxr = false; - if (first_stage == true) { + /* + * Use mstatus for first stage or for the second stage without + * virt_enabled (MPRV+MPV) + */ + if (first_stage || !env->virt_enabled) { mxr = get_field(env->mstatus, MSTATUS_MXR); - } else { - mxr = get_field(env->vsstatus, MSTATUS_MXR); } + + /* MPRV+MPV case, check VSSTATUS */ + if (first_stage && two_stage && !env->virt_enabled) { + mxr |= get_field(env->vsstatus, MSTATUS_MXR); + } + + /* + * Setting MXR at HS-level overrides both VS-stage and G-stage + * execute-only permissions + */ + if (env->virt_enabled) { + mxr |= get_field(env->mstatus_hs, MSTATUS_MXR); + } + if (mxr) { prot |= PAGE_READ; }