aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2023-03-07 12:53:00 +0000
committerPeter Maydell <peter.maydell@linaro.org>2023-03-07 12:53:00 +0000
commit9832009d9dd2386664c15cc70f6e6bfe062be8bd (patch)
treeea3821c795d341ac30bd0e8e762a2ccdbdcbb058
parentc29a2f40cd5d1fdad4632b48343cd968db041a44 (diff)
parent47fc340010335bc2549bc1f07e5fd85d86a2b9f9 (diff)
downloadqemu-9832009d9dd2386664c15cc70f6e6bfe062be8bd.zip
qemu-9832009d9dd2386664c15cc70f6e6bfe062be8bd.tar.gz
qemu-9832009d9dd2386664c15cc70f6e6bfe062be8bd.tar.bz2
Merge tag 'pull-riscv-to-apply-20230306' of https://gitlab.com/palmer-dabbelt/qemu into staging
Sixth RISC-V PR for 8.0 * Support for the Zicbiom, ZCicboz, and Zicbop extensions. * OpenSBI has been updated to version 1.2, see <https://github.com/riscv-software-src/opensbi/releases/tag/v1.2> for the release notes. * Support for setting the virtual address width (ie, sv39/sv48/sv57) on the command line. * Support for ACPI on RISC-V. # -----BEGIN PGP SIGNATURE----- # # iQJHBAABCAAxFiEEKzw3R0RoQ7JKlDp6LhMZ81+7GIkFAmQGYGgTHHBhbG1lckBk # YWJiZWx0LmNvbQAKCRAuExnzX7sYidmyEAC6FEMbbFM5D++qR6w6xM6hXgzcrev6 # s1kyRRNVa45uSA78ti/Zi0hsDLNf7ZsNPndF0OIkkO5iAE0OVm3LU7tV1TqKcT82 # Dd9VXxe93zEmfnuJazHrMa54SXPhhnNdWHtKlZ6vBfZpbxgx0FFs50xkCsrM5LQZ # hYHxQUqPWQTvF2MdDHrxCuLcdKl+Wg3ysCcgRh2d049KUBrIu6vNaHC2+AGRjCbj # BkrGCkB82fTmVJjzAcVWQxLoAV12pCbJS4og1GtP8hA7WevtB39tbPin9siBKRZp # QBeiIsg0nebkpmZGrb+xWVwlIBNe9yYwJa0KmveQk8v7L5RIzjM1mtDL91VrVljC # KC2tfT570m0Iq2NoFMb3wd/kESHFzVDM/g+XYqRd4KSoiCNP/RbqYNQBwbMc31Tr # E27xfA1D8w2vem0Rk20x3KgPf1Z5OmGXjq6YObTpnAzG8cZlA37qKBP+ortt5aHX # GZSg3CAwknHHVajd4aaegkPsHxm1tRvoTfh38MwkPSNxaA9GD0nz0k9xaYDmeZ2L # olfanNsaQEwcVUId31+7sAENg1TZU0fnj879/nxkMUCazVTdL8/mz+IoTTx0QCST # 3+9ATWcyJUlmjbDKIs7kr1L+wJdvvHEJggPAbbPI8ekpXaLZvUYOT6ObzYKNAmwY # wELQBn8QKXcLVA== # =5gAt # -----END PGP SIGNATURE----- # gpg: Signature made Mon 06 Mar 2023 21:51:36 GMT # gpg: using RSA key 2B3C3747446843B24A943A7A2E1319F35FBB1889 # gpg: issuer "palmer@dabbelt.com" # gpg: Good signature from "Palmer Dabbelt <palmer@dabbelt.com>" [unknown] # gpg: aka "Palmer Dabbelt <palmerdabbelt@google.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 00CE 76D1 8349 60DF CE88 6DF8 EF4C A150 2CCB AB41 # Subkey fingerprint: 2B3C 3747 4468 43B2 4A94 3A7A 2E13 19F3 5FBB 1889 * tag 'pull-riscv-to-apply-20230306' of https://gitlab.com/palmer-dabbelt/qemu: (22 commits) MAINTAINERS: Add entry for RISC-V ACPI hw/riscv/virt.c: Initialize the ACPI tables hw/riscv/virt: virt-acpi-build.c: Add RHCT Table hw/riscv/virt: virt-acpi-build.c: Add RINTC in MADT hw/riscv/virt: Enable basic ACPI infrastructure hw/riscv/virt: Add memmap pointer to RiscVVirtState hw/riscv/virt: Add a switch to disable ACPI hw/riscv/virt: Add OEM_ID and OEM_TABLE_ID fields riscv: Correctly set the device-tree entry 'mmu-type' riscv: Introduce satp mode hw capabilities riscv: Allow user to set the satp mode riscv: Change type of valid_vm_1_10_[32|64] to bool riscv: Pass Object to register_cpu_props instead of DeviceState roms/opensbi: Upgrade from v1.1 to v1.2 gitlab/opensbi: Move to docker:stable hw: intc: Use cpu_by_arch_id to fetch CPU state target/riscv: cpu: Implement get_arch_id callback disas/riscv Fix ctzw disassemble hw/riscv/virt.c: add cbo[mz]-block-size fdt properties target/riscv: add Zicbop cbo.prefetch{i, r, m} placeholder ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--.gitlab-ci.d/opensbi.yml4
-rw-r--r--.gitlab-ci.d/opensbi/Dockerfile1
-rw-r--r--MAINTAINERS18
-rw-r--r--disas/riscv.c2
-rw-r--r--hw/intc/riscv_aclint.c16
-rw-r--r--hw/intc/riscv_aplic.c4
-rw-r--r--hw/intc/riscv_imsic.c6
-rw-r--r--hw/riscv/Kconfig1
-rw-r--r--hw/riscv/meson.build1
-rw-r--r--hw/riscv/virt-acpi-build.c416
-rw-r--r--hw/riscv/virt.c70
-rw-r--r--include/hw/riscv/virt.h6
-rw-r--r--pc-bios/opensbi-riscv32-generic-fw_dynamic.binbin117704 -> 123072 bytes
-rw-r--r--pc-bios/opensbi-riscv64-generic-fw_dynamic.binbin115344 -> 121800 bytes
m---------roms/opensbi0
-rw-r--r--target/riscv/cpu.c303
-rw-r--r--target/riscv/cpu.h29
-rw-r--r--target/riscv/csr.c29
-rw-r--r--target/riscv/helper.h5
-rw-r--r--target/riscv/insn32.decode16
-rw-r--r--target/riscv/insn_trans/trans_rvzicbo.c.inc57
-rw-r--r--target/riscv/op_helper.c135
-rw-r--r--target/riscv/translate.c1
23 files changed, 1060 insertions, 60 deletions
diff --git a/.gitlab-ci.d/opensbi.yml b/.gitlab-ci.d/opensbi.yml
index 04ed5a3..9a65146 100644
--- a/.gitlab-ci.d/opensbi.yml
+++ b/.gitlab-ci.d/opensbi.yml
@@ -42,9 +42,9 @@
docker-opensbi:
extends: .opensbi_job_rules
stage: containers
- image: docker:19.03.1
+ image: docker:stable
services:
- - docker:19.03.1-dind
+ - docker:stable-dind
variables:
GIT_DEPTH: 3
IMAGE_TAG: $CI_REGISTRY_IMAGE:opensbi-cross-build
diff --git a/.gitlab-ci.d/opensbi/Dockerfile b/.gitlab-ci.d/opensbi/Dockerfile
index 4ba8a4d..5ccf415 100644
--- a/.gitlab-ci.d/opensbi/Dockerfile
+++ b/.gitlab-ci.d/opensbi/Dockerfile
@@ -15,6 +15,7 @@ RUN apt update \
ca-certificates \
git \
make \
+ python3 \
wget \
&& \
\
diff --git a/MAINTAINERS b/MAINTAINERS
index da29661..5340de0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -999,12 +999,6 @@ S: Maintained
F: hw/ssi/xlnx-versal-ospi.c
F: include/hw/ssi/xlnx-versal-ospi.h
-ARM ACPI Subsystem
-M: Shannon Zhao <shannon.zhaosl@gmail.com>
-L: qemu-arm@nongnu.org
-S: Maintained
-F: hw/arm/virt-acpi-build.c
-
STM32F100
M: Alexandre Iooss <erdnaxe@crans.org>
L: qemu-arm@nongnu.org
@@ -1892,6 +1886,18 @@ F: docs/specs/acpi_nvdimm.rst
F: docs/specs/acpi_pci_hotplug.rst
F: docs/specs/acpi_hw_reduced_hotplug.rst
+ARM ACPI Subsystem
+M: Shannon Zhao <shannon.zhaosl@gmail.com>
+L: qemu-arm@nongnu.org
+S: Maintained
+F: hw/arm/virt-acpi-build.c
+
+RISC-V ACPI Subsystem
+M: Sunil V L <sunilvl@ventanamicro.com>
+L: qemu-riscv@nongnu.org
+S: Maintained
+F: hw/riscv/virt-acpi-build.c
+
ACPI/VIOT
M: Jean-Philippe Brucker <jean-philippe@linaro.org>
S: Supported
diff --git a/disas/riscv.c b/disas/riscv.c
index ddda687..54455aa 100644
--- a/disas/riscv.c
+++ b/disas/riscv.c
@@ -1645,7 +1645,7 @@ const rv_opcode_data opcode_data[] = {
{ "max", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
{ "maxu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
{ "clzw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
- { "clzw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+ { "ctzw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
{ "cpopw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
{ "slli.uw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
{ "add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c
index eee0464..b466a6a 100644
--- a/hw/intc/riscv_aclint.c
+++ b/hw/intc/riscv_aclint.c
@@ -130,7 +130,7 @@ static uint64_t riscv_aclint_mtimer_read(void *opaque, hwaddr addr,
addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) {
size_t hartid = mtimer->hartid_base +
((addr - mtimer->timecmp_base) >> 3);
- CPUState *cpu = qemu_get_cpu(hartid);
+ CPUState *cpu = cpu_by_arch_id(hartid);
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
if (!env) {
qemu_log_mask(LOG_GUEST_ERROR,
@@ -173,7 +173,7 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr,
addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) {
size_t hartid = mtimer->hartid_base +
((addr - mtimer->timecmp_base) >> 3);
- CPUState *cpu = qemu_get_cpu(hartid);
+ CPUState *cpu = cpu_by_arch_id(hartid);
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
if (!env) {
qemu_log_mask(LOG_GUEST_ERROR,
@@ -231,7 +231,7 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr,
/* Check if timer interrupt is triggered for each hart. */
for (i = 0; i < mtimer->num_harts; i++) {
- CPUState *cpu = qemu_get_cpu(mtimer->hartid_base + i);
+ CPUState *cpu = cpu_by_arch_id(mtimer->hartid_base + i);
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
if (!env) {
continue;
@@ -292,7 +292,7 @@ static void riscv_aclint_mtimer_realize(DeviceState *dev, Error **errp)
s->timecmp = g_new0(uint64_t, s->num_harts);
/* Claim timer interrupt bits */
for (i = 0; i < s->num_harts; i++) {
- RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(s->hartid_base + i));
+ RISCVCPU *cpu = RISCV_CPU(cpu_by_arch_id(s->hartid_base + i));
if (riscv_cpu_claim_interrupts(cpu, MIP_MTIP) < 0) {
error_report("MTIP already claimed");
exit(1);
@@ -372,7 +372,7 @@ DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size,
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
for (i = 0; i < num_harts; i++) {
- CPUState *cpu = qemu_get_cpu(hartid_base + i);
+ CPUState *cpu = cpu_by_arch_id(hartid_base + i);
RISCVCPU *rvcpu = RISCV_CPU(cpu);
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
riscv_aclint_mtimer_callback *cb =
@@ -407,7 +407,7 @@ static uint64_t riscv_aclint_swi_read(void *opaque, hwaddr addr,
if (addr < (swi->num_harts << 2)) {
size_t hartid = swi->hartid_base + (addr >> 2);
- CPUState *cpu = qemu_get_cpu(hartid);
+ CPUState *cpu = cpu_by_arch_id(hartid);
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
if (!env) {
qemu_log_mask(LOG_GUEST_ERROR,
@@ -430,7 +430,7 @@ static void riscv_aclint_swi_write(void *opaque, hwaddr addr, uint64_t value,
if (addr < (swi->num_harts << 2)) {
size_t hartid = swi->hartid_base + (addr >> 2);
- CPUState *cpu = qemu_get_cpu(hartid);
+ CPUState *cpu = cpu_by_arch_id(hartid);
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
if (!env) {
qemu_log_mask(LOG_GUEST_ERROR,
@@ -545,7 +545,7 @@ DeviceState *riscv_aclint_swi_create(hwaddr addr, uint32_t hartid_base,
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
for (i = 0; i < num_harts; i++) {
- CPUState *cpu = qemu_get_cpu(hartid_base + i);
+ CPUState *cpu = cpu_by_arch_id(hartid_base + i);
RISCVCPU *rvcpu = RISCV_CPU(cpu);
qdev_connect_gpio_out(dev, i,
diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c
index cfd007e..cd7efc4 100644
--- a/hw/intc/riscv_aplic.c
+++ b/hw/intc/riscv_aplic.c
@@ -833,7 +833,7 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp)
/* Claim the CPU interrupt to be triggered by this APLIC */
for (i = 0; i < aplic->num_harts; i++) {
- RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(aplic->hartid_base + i));
+ RISCVCPU *cpu = RISCV_CPU(cpu_by_arch_id(aplic->hartid_base + i));
if (riscv_cpu_claim_interrupts(cpu,
(aplic->mmode) ? MIP_MEIP : MIP_SEIP) < 0) {
error_report("%s already claimed",
@@ -966,7 +966,7 @@ DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size,
if (!msimode) {
for (i = 0; i < num_harts; i++) {
- CPUState *cpu = qemu_get_cpu(hartid_base + i);
+ CPUState *cpu = cpu_by_arch_id(hartid_base + i);
qdev_connect_gpio_out_named(dev, NULL, i,
qdev_get_gpio_in(DEVICE(cpu),
diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c
index 4d4d5b5..fea3385 100644
--- a/hw/intc/riscv_imsic.c
+++ b/hw/intc/riscv_imsic.c
@@ -316,8 +316,8 @@ static const MemoryRegionOps riscv_imsic_ops = {
static void riscv_imsic_realize(DeviceState *dev, Error **errp)
{
RISCVIMSICState *imsic = RISCV_IMSIC(dev);
- RISCVCPU *rcpu = RISCV_CPU(qemu_get_cpu(imsic->hartid));
- CPUState *cpu = qemu_get_cpu(imsic->hartid);
+ RISCVCPU *rcpu = RISCV_CPU(cpu_by_arch_id(imsic->hartid));
+ CPUState *cpu = cpu_by_arch_id(imsic->hartid);
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
imsic->num_eistate = imsic->num_pages * imsic->num_irqs;
@@ -413,7 +413,7 @@ DeviceState *riscv_imsic_create(hwaddr addr, uint32_t hartid, bool mmode,
uint32_t num_pages, uint32_t num_ids)
{
DeviceState *dev = qdev_new(TYPE_RISCV_IMSIC);
- CPUState *cpu = qemu_get_cpu(hartid);
+ CPUState *cpu = cpu_by_arch_id(hartid);
uint32_t i;
assert(!(addr & (IMSIC_MMIO_PAGE_SZ - 1)));
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 4550b3b..6528ebf 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -44,6 +44,7 @@ config RISCV_VIRT
select VIRTIO_MMIO
select FW_CFG_DMA
select PLATFORM_BUS
+ select ACPI
config SHAKTI_C
bool
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index ab6cae5..2f7ee81 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -9,5 +9,6 @@ riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c'))
riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c'))
riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c'))
riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c'))
+riscv_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c'))
hw_arch += {'riscv': riscv_ss}
diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c
new file mode 100644
index 0000000..82da0a2
--- /dev/null
+++ b/hw/riscv/virt-acpi-build.c
@@ -0,0 +1,416 @@
+/*
+ * Support for generating ACPI tables and passing them to Guests
+ *
+ * RISC-V virt ACPI generation
+ *
+ * Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2013 Red Hat Inc
+ * Copyright (c) 2015 HUAWEI TECHNOLOGIES CO.,LTD.
+ * Copyright (C) 2021-2023 Ventana Micro Systems Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * 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/acpi/acpi-defs.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/acpi/utils.h"
+#include "qapi/error.h"
+#include "sysemu/reset.h"
+#include "migration/vmstate.h"
+#include "hw/riscv/virt.h"
+#include "hw/riscv/numa.h"
+#include "hw/intc/riscv_aclint.h"
+
+#define ACPI_BUILD_TABLE_SIZE 0x20000
+
+typedef struct AcpiBuildState {
+ /* Copy of table in RAM (for patching) */
+ MemoryRegion *table_mr;
+ MemoryRegion *rsdp_mr;
+ MemoryRegion *linker_mr;
+ /* Is table patched? */
+ bool patched;
+} AcpiBuildState;
+
+static void acpi_align_size(GArray *blob, unsigned align)
+{
+ /*
+ * Align size to multiple of given size. This reduces the chance
+ * we need to change size in the future (breaking cross version migration).
+ */
+ g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
+}
+
+static void riscv_acpi_madt_add_rintc(uint32_t uid,
+ const CPUArchIdList *arch_ids,
+ GArray *entry)
+{
+ uint64_t hart_id = arch_ids->cpus[uid].arch_id;
+
+ build_append_int_noprefix(entry, 0x18, 1); /* Type */
+ build_append_int_noprefix(entry, 20, 1); /* Length */
+ build_append_int_noprefix(entry, 1, 1); /* Version */
+ build_append_int_noprefix(entry, 0, 1); /* Reserved */
+ build_append_int_noprefix(entry, 0x1, 4); /* Flags */
+ build_append_int_noprefix(entry, hart_id, 8); /* Hart ID */
+ build_append_int_noprefix(entry, uid, 4); /* ACPI Processor UID */
+}
+
+static void acpi_dsdt_add_cpus(Aml *scope, RISCVVirtState *s)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(s);
+ MachineState *ms = MACHINE(s);
+ const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
+
+ for (int i = 0; i < arch_ids->len; i++) {
+ Aml *dev;
+ GArray *madt_buf = g_array_new(0, 1, 1);
+
+ dev = aml_device("C%.03X", i);
+ aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0007")));
+ aml_append(dev, aml_name_decl("_UID",
+ aml_int(arch_ids->cpus[i].arch_id)));
+
+ /* build _MAT object */
+ riscv_acpi_madt_add_rintc(i, arch_ids, madt_buf);
+ aml_append(dev, aml_name_decl("_MAT",
+ aml_buffer(madt_buf->len,
+ (uint8_t *)madt_buf->data)));
+ g_array_free(madt_buf, true);
+
+ aml_append(scope, dev);
+ }
+}
+
+static void acpi_dsdt_add_fw_cfg(Aml *scope, const MemMapEntry *fw_cfg_memmap)
+{
+ Aml *dev = aml_device("FWCF");
+ aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
+
+ /* device present, functioning, decoding, not shown in UI */
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
+ aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
+
+ Aml *crs = aml_resource_template();
+ aml_append(crs, aml_memory32_fixed(fw_cfg_memmap->base,
+ fw_cfg_memmap->size, AML_READ_WRITE));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(scope, dev);
+}
+
+/* RHCT Node[N] starts at offset 56 */
+#define RHCT_NODE_ARRAY_OFFSET 56
+
+/*
+ * ACPI spec, Revision 6.5+
+ * 5.2.36 RISC-V Hart Capabilities Table (RHCT)
+ * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/16
+ * https://drive.google.com/file/d/1nP3nFiH4jkPMp6COOxP6123DCZKR-tia/view
+ */
+static void build_rhct(GArray *table_data,
+ BIOSLinker *linker,
+ RISCVVirtState *s)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(s);
+ MachineState *ms = MACHINE(s);
+ const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
+ size_t len, aligned_len;
+ uint32_t isa_offset, num_rhct_nodes;
+ RISCVCPU *cpu;
+ char *isa;
+
+ AcpiTable table = { .sig = "RHCT", .rev = 1, .oem_id = s->oem_id,
+ .oem_table_id = s->oem_table_id };
+
+ acpi_table_begin(&table, table_data);
+
+ build_append_int_noprefix(table_data, 0x0, 4); /* Reserved */
+
+ /* Time Base Frequency */
+ build_append_int_noprefix(table_data,
+ RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, 8);
+
+ /* ISA + N hart info */
+ num_rhct_nodes = 1 + ms->smp.cpus;
+
+ /* Number of RHCT nodes*/
+ build_append_int_noprefix(table_data, num_rhct_nodes, 4);
+
+ /* Offset to the RHCT node array */
+ build_append_int_noprefix(table_data, RHCT_NODE_ARRAY_OFFSET, 4);
+
+ /* ISA String Node */
+ isa_offset = table_data->len - table.table_offset;
+ build_append_int_noprefix(table_data, 0, 2); /* Type 0 */
+
+ cpu = &s->soc[0].harts[0];
+ isa = riscv_isa_string(cpu);
+ len = 8 + strlen(isa) + 1;
+ aligned_len = (len % 2) ? (len + 1) : len;
+
+ build_append_int_noprefix(table_data, aligned_len, 2); /* Length */
+ build_append_int_noprefix(table_data, 0x1, 2); /* Revision */
+
+ /* ISA string length including NUL */
+ build_append_int_noprefix(table_data, strlen(isa) + 1, 2);
+ g_array_append_vals(table_data, isa, strlen(isa) + 1); /* ISA string */
+
+ if (aligned_len != len) {
+ build_append_int_noprefix(table_data, 0x0, 1); /* Optional Padding */
+ }
+
+ /* Hart Info Node */
+ for (int i = 0; i < arch_ids->len; i++) {
+ build_append_int_noprefix(table_data, 0xFFFF, 2); /* Type */
+ build_append_int_noprefix(table_data, 16, 2); /* Length */
+ build_append_int_noprefix(table_data, 0x1, 2); /* Revision */
+ build_append_int_noprefix(table_data, 1, 2); /* Number of offsets */
+ build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */
+ build_append_int_noprefix(table_data, isa_offset, 4); /* Offsets[0] */
+ }
+
+ acpi_table_end(linker, &table);
+}
+
+/* FADT */
+static void build_fadt_rev6(GArray *table_data,
+ BIOSLinker *linker,
+ RISCVVirtState *s,
+ unsigned dsdt_tbl_offset)
+{
+ AcpiFadtData fadt = {
+ .rev = 6,
+ .minor_ver = 5,
+ .flags = 1 << ACPI_FADT_F_HW_REDUCED_ACPI,
+ .xdsdt_tbl_offset = &dsdt_tbl_offset,
+ };
+
+ build_fadt(table_data, linker, &fadt, s->oem_id, s->oem_table_id);
+}
+
+/* DSDT */
+static void build_dsdt(GArray *table_data,
+ BIOSLinker *linker,
+ RISCVVirtState *s)
+{
+ Aml *scope, *dsdt;
+ const MemMapEntry *memmap = s->memmap;
+ AcpiTable table = { .sig = "DSDT", .rev = 2, .oem_id = s->oem_id,
+ .oem_table_id = s->oem_table_id };
+
+
+ acpi_table_begin(&table, table_data);
+ dsdt = init_aml_allocator();
+
+ /*
+ * When booting the VM with UEFI, UEFI takes ownership of the RTC hardware.
+ * While UEFI can use libfdt to disable the RTC device node in the DTB that
+ * it passes to the OS, it cannot modify AML. Therefore, we won't generate
+ * the RTC ACPI device at all when using UEFI.
+ */
+ scope = aml_scope("\\_SB");
+ acpi_dsdt_add_cpus(scope, s);
+
+ acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]);
+
+ aml_append(dsdt, scope);
+
+ /* copy AML table into ACPI tables blob and patch header there */
+ g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
+
+ acpi_table_end(linker, &table);
+ free_aml_allocator();
+}
+
+/*
+ * ACPI spec, Revision 6.5+
+ * 5.2.12 Multiple APIC Description Table (MADT)
+ * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/15
+ * https://drive.google.com/file/d/1R6k4MshhN3WTT-hwqAquu5nX6xSEqK2l/view
+ */
+static void build_madt(GArray *table_data,
+ BIOSLinker *linker,
+ RISCVVirtState *s)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(s);
+ MachineState *ms = MACHINE(s);
+ const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
+
+ AcpiTable table = { .sig = "APIC", .rev = 6, .oem_id = s->oem_id,
+ .oem_table_id = s->oem_table_id };
+
+ acpi_table_begin(&table, table_data);
+ /* Local Interrupt Controller Address */
+ build_append_int_noprefix(table_data, 0, 4);
+ build_append_int_noprefix(table_data, 0, 4); /* MADT Flags */
+
+ /* RISC-V Local INTC structures per HART */
+ for (int i = 0; i < arch_ids->len; i++) {
+ riscv_acpi_madt_add_rintc(i, arch_ids, table_data);
+ }
+
+ acpi_table_end(linker, &table);
+}
+
+static void virt_acpi_build(RISCVVirtState *s, AcpiBuildTables *tables)
+{
+ GArray *table_offsets;
+ unsigned dsdt, xsdt;
+ GArray *tables_blob = tables->table_data;
+
+ table_offsets = g_array_new(false, true,
+ sizeof(uint32_t));
+
+ bios_linker_loader_alloc(tables->linker,
+ ACPI_BUILD_TABLE_FILE, tables_blob,
+ 64, false);
+
+ /* DSDT is pointed to by FADT */
+ dsdt = tables_blob->len;
+ build_dsdt(tables_blob, tables->linker, s);
+
+ /* FADT and others pointed to by XSDT */
+ acpi_add_table(table_offsets, tables_blob);
+ build_fadt_rev6(tables_blob, tables->linker, s, dsdt);
+
+ acpi_add_table(table_offsets, tables_blob);
+ build_madt(tables_blob, tables->linker, s);
+
+ acpi_add_table(table_offsets, tables_blob);
+ build_rhct(tables_blob, tables->linker, s);
+
+ /* XSDT is pointed to by RSDP */
+ xsdt = tables_blob->len;
+ build_xsdt(tables_blob, tables->linker, table_offsets, s->oem_id,
+ s->oem_table_id);
+
+ /* RSDP is in FSEG memory, so allocate it separately */
+ {
+ AcpiRsdpData rsdp_data = {
+ .revision = 2,
+ .oem_id = s->oem_id,
+ .xsdt_tbl_offset = &xsdt,
+ .rsdt_tbl_offset = NULL,
+ };
+ build_rsdp(tables->rsdp, tables->linker, &rsdp_data);
+ }
+
+ /*
+ * The align size is 128, warn if 64k is not enough therefore
+ * the align size could be resized.
+ */
+ if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) {
+ warn_report("ACPI table size %u exceeds %d bytes,"
+ " migration may not work",
+ tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2);
+ error_printf("Try removing some objects.");
+ }
+
+ acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE);
+
+ /* Clean up memory that's no longer used */
+ g_array_free(table_offsets, true);
+}
+
+static void acpi_ram_update(MemoryRegion *mr, GArray *data)
+{
+ uint32_t size = acpi_data_len(data);
+
+ /*
+ * Make sure RAM size is correct - in case it got changed
+ * e.g. by migration
+ */
+ memory_region_ram_resize(mr, size, &error_abort);
+
+ memcpy(memory_region_get_ram_ptr(mr), data->data, size);
+ memory_region_set_dirty(mr, 0, size);
+}
+
+static void virt_acpi_build_update(void *build_opaque)
+{
+ AcpiBuildState *build_state = build_opaque;
+ AcpiBuildTables tables;
+
+ /* No state to update or already patched? Nothing to do. */
+ if (!build_state || build_state->patched) {
+ return;
+ }
+
+ build_state->patched = true;
+
+ acpi_build_tables_init(&tables);
+
+ virt_acpi_build(RISCV_VIRT_MACHINE(qdev_get_machine()), &tables);
+
+ acpi_ram_update(build_state->table_mr, tables.table_data);
+ acpi_ram_update(build_state->rsdp_mr, tables.rsdp);
+ acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob);
+
+ acpi_build_tables_cleanup(&tables, true);
+}
+
+static void virt_acpi_build_reset(void *build_opaque)
+{
+ AcpiBuildState *build_state = build_opaque;
+ build_state->patched = false;
+}
+
+static const VMStateDescription vmstate_virt_acpi_build = {
+ .name = "virt_acpi_build",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(patched, AcpiBuildState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+void virt_acpi_setup(RISCVVirtState *s)
+{
+ AcpiBuildTables tables;
+ AcpiBuildState *build_state;
+
+ build_state = g_malloc0(sizeof *build_state);
+
+ acpi_build_tables_init(&tables);
+ virt_acpi_build(s, &tables);
+
+ /* Now expose it all to Guest */
+ build_state->table_mr = acpi_add_rom_blob(virt_acpi_build_update,
+ build_state, tables.table_data,
+ ACPI_BUILD_TABLE_FILE);
+ assert(build_state->table_mr != NULL);
+
+ build_state->linker_mr = acpi_add_rom_blob(virt_acpi_build_update,
+ build_state,
+ tables.linker->cmd_blob,
+ ACPI_BUILD_LOADER_FILE);
+
+ build_state->rsdp_mr = acpi_add_rom_blob(virt_acpi_build_update,
+ build_state, tables.rsdp,
+ ACPI_BUILD_RSDP_FILE);
+
+ qemu_register_reset(virt_acpi_build_reset, build_state);
+ virt_acpi_build_reset(build_state);
+ vmstate_register(NULL, 0, &vmstate_virt_acpi_build, build_state);
+
+ /*
+ * Clean up tables but don't free the memory: we track it
+ * in build_state.
+ */
+ acpi_build_tables_cleanup(&tables, false);
+}
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 4f81918..4e3efbe 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -49,6 +49,8 @@
#include "hw/pci/pci.h"
#include "hw/pci-host/gpex.h"
#include "hw/display/ramfb.h"
+#include "hw/acpi/aml-build.h"
+#include "qapi/qapi-visit-common.h"
/*
* The virt machine physical address space used by some of the devices
@@ -228,8 +230,9 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket,
int cpu;
uint32_t cpu_phandle;
MachineState *ms = MACHINE(s);
- char *name, *cpu_name, *core_name, *intc_name;
+ char *name, *cpu_name, *core_name, *intc_name, *sv_name;
bool is_32_bit = riscv_is_32bit(&s->soc[0]);
+ uint8_t satp_mode_max;
for (cpu = s->soc[socket].num_harts - 1; cpu >= 0; cpu--) {
RISCVCPU *cpu_ptr = &s->soc[socket].harts[cpu];
@@ -239,16 +242,29 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket,
cpu_name = g_strdup_printf("/cpus/cpu@%d",
s->soc[socket].hartid_base + cpu);
qemu_fdt_add_subnode(ms->fdt, cpu_name);
- if (cpu_ptr->cfg.mmu) {
- qemu_fdt_setprop_string(ms->fdt, cpu_name, "mmu-type",
- (is_32_bit) ? "riscv,sv32" : "riscv,sv48");
- } else {
- qemu_fdt_setprop_string(ms->fdt, cpu_name, "mmu-type",
- "riscv,none");
- }
+
+ satp_mode_max = satp_mode_max_from_map(
+ s->soc[socket].harts[cpu].cfg.satp_mode.map);
+ sv_name = g_strdup_printf("riscv,%s",
+ satp_mode_str(satp_mode_max, is_32_bit));
+ qemu_fdt_setprop_string(ms->fdt, cpu_name, "mmu-type", sv_name);
+ g_free(sv_name);
+
+
name = riscv_isa_string(cpu_ptr);
qemu_fdt_setprop_string(ms->fdt, cpu_name, "riscv,isa", name);
g_free(name);
+
+ if (cpu_ptr->cfg.ext_icbom) {
+ qemu_fdt_setprop_cell(ms->fdt, cpu_name, "riscv,cbom-block-size",
+ cpu_ptr->cfg.cbom_blocksize);
+ }
+
+ if (cpu_ptr->cfg.ext_icboz) {
+ qemu_fdt_setprop_cell(ms->fdt, cpu_name, "riscv,cboz-block-size",
+ cpu_ptr->cfg.cboz_blocksize);
+ }
+
qemu_fdt_setprop_string(ms->fdt, cpu_name, "compatible", "riscv");
qemu_fdt_setprop_string(ms->fdt, cpu_name, "status", "okay");
qemu_fdt_setprop_cell(ms->fdt, cpu_name, "reg",
@@ -1307,6 +1323,10 @@ static void virt_machine_done(Notifier *notifier, void *data)
if (kvm_enabled()) {
riscv_setup_direct_kernel(kernel_entry, fdt_load_addr);
}
+
+ if (virt_is_acpi_enabled(s)) {
+ virt_acpi_setup(s);
+ }
}
static void virt_machine_init(MachineState *machine)
@@ -1442,6 +1462,8 @@ static void virt_machine_init(MachineState *machine)
ROUND_UP(virt_high_pcie_memmap.base, virt_high_pcie_memmap.size);
}
+ s->memmap = virt_memmap;
+
/* register system main memory (actual RAM) */
memory_region_add_subregion(system_memory, memmap[VIRT_DRAM].base,
machine->ram);
@@ -1514,6 +1536,11 @@ static void virt_machine_init(MachineState *machine)
static void virt_machine_instance_init(Object *obj)
{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(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;
}
static char *virt_get_aia_guests(Object *obj, Error **errp)
@@ -1588,6 +1615,28 @@ static void virt_set_aclint(Object *obj, bool value, Error **errp)
s->have_aclint = value;
}
+bool virt_is_acpi_enabled(RISCVVirtState *s)
+{
+ return s->acpi != ON_OFF_AUTO_OFF;
+}
+
+static void virt_get_acpi(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+ OnOffAuto acpi = s->acpi;
+
+ visit_type_OnOffAuto(v, name, &acpi, errp);
+}
+
+static void virt_set_acpi(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+
+ visit_type_OnOffAuto(v, name, &s->acpi, errp);
+}
+
static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
DeviceState *dev)
{
@@ -1659,6 +1708,11 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
sprintf(str, "Set number of guest MMIO pages for AIA IMSIC. Valid value "
"should be between 0 and %d.", VIRT_IRQCHIP_MAX_GUESTS);
object_class_property_set_description(oc, "aia-guests", str);
+ object_class_property_add(oc, "acpi", "OnOffAuto",
+ virt_get_acpi, virt_set_acpi,
+ NULL, NULL);
+ object_class_property_set_description(oc, "acpi",
+ "Enable ACPI");
}
static const TypeInfo virt_machine_typeinfo = {
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
index b3d2613..e5c474b 100644
--- a/include/hw/riscv/virt.h
+++ b/include/hw/riscv/virt.h
@@ -56,6 +56,10 @@ struct RISCVVirtState {
bool have_aclint;
RISCVVirtAIAType aia_type;
int aia_guests;
+ char *oem_id;
+ char *oem_table_id;
+ OnOffAuto acpi;
+ const MemMapEntry *memmap;
};
enum {
@@ -121,4 +125,6 @@ enum {
#define FDT_APLIC_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + \
1 + FDT_APLIC_INT_CELLS)
+bool virt_is_acpi_enabled(RISCVVirtState *s);
+void virt_acpi_setup(RISCVVirtState *vms);
#endif
diff --git a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin
index 81bab1a..6a84258 100644
--- a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin
+++ b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin
Binary files differ
diff --git a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin
index 5eb0a74..80bdbf2 100644
--- a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin
+++ b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin
Binary files differ
diff --git a/roms/opensbi b/roms/opensbi
-Subproject 4489876e933d8ba0d8bc6c64bae71e295d45faa
+Subproject 6b5188ca14e59ce7bf71afe4e7d3d557c3d31bf
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 5bc0005..1e97473 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -28,6 +28,7 @@
#include "time_helper.h"
#include "exec/exec-all.h"
#include "qapi/error.h"
+#include "qapi/visitor.h"
#include "qemu/error-report.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
@@ -75,6 +76,8 @@ struct isa_ext_data {
static const struct isa_ext_data isa_edata_arr[] = {
ISA_EXT_DATA_ENTRY(h, false, PRIV_VERSION_1_12_0, ext_h),
ISA_EXT_DATA_ENTRY(v, false, PRIV_VERSION_1_10_0, ext_v),
+ ISA_EXT_DATA_ENTRY(zicbom, true, PRIV_VERSION_1_12_0, ext_icbom),
+ ISA_EXT_DATA_ENTRY(zicboz, true, PRIV_VERSION_1_12_0, ext_icboz),
ISA_EXT_DATA_ENTRY(zicond, true, PRIV_VERSION_1_12_0, ext_zicond),
ISA_EXT_DATA_ENTRY(zicsr, true, PRIV_VERSION_1_10_0, ext_icsr),
ISA_EXT_DATA_ENTRY(zifencei, true, PRIV_VERSION_1_10_0, ext_ifencei),
@@ -218,7 +221,7 @@ static const char * const riscv_intr_names[] = {
"reserved"
};
-static void register_cpu_props(DeviceState *dev);
+static void register_cpu_props(Object *obj);
const char *riscv_cpu_get_trap_name(target_ulong cause, bool async)
{
@@ -247,6 +250,89 @@ static void set_vext_version(CPURISCVState *env, int vext_ver)
env->vext_ver = vext_ver;
}
+#ifndef CONFIG_USER_ONLY
+static uint8_t satp_mode_from_str(const char *satp_mode_str)
+{
+ if (!strncmp(satp_mode_str, "mbare", 5)) {
+ return VM_1_10_MBARE;
+ }
+
+ if (!strncmp(satp_mode_str, "sv32", 4)) {
+ return VM_1_10_SV32;
+ }
+
+ if (!strncmp(satp_mode_str, "sv39", 4)) {
+ return VM_1_10_SV39;
+ }
+
+ if (!strncmp(satp_mode_str, "sv48", 4)) {
+ return VM_1_10_SV48;
+ }
+
+ if (!strncmp(satp_mode_str, "sv57", 4)) {
+ return VM_1_10_SV57;
+ }
+
+ if (!strncmp(satp_mode_str, "sv64", 4)) {
+ return VM_1_10_SV64;
+ }
+
+ g_assert_not_reached();
+}
+
+uint8_t satp_mode_max_from_map(uint32_t map)
+{
+ /* map here has at least one bit set, so no problem with clz */
+ return 31 - __builtin_clz(map);
+}
+
+const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit)
+{
+ if (is_32_bit) {
+ switch (satp_mode) {
+ case VM_1_10_SV32:
+ return "sv32";
+ case VM_1_10_MBARE:
+ return "none";
+ }
+ } else {
+ switch (satp_mode) {
+ case VM_1_10_SV64:
+ return "sv64";
+ case VM_1_10_SV57:
+ return "sv57";
+ case VM_1_10_SV48:
+ return "sv48";
+ case VM_1_10_SV39:
+ return "sv39";
+ case VM_1_10_MBARE:
+ return "none";
+ }
+ }
+
+ g_assert_not_reached();
+}
+
+static void set_satp_mode_max_supported(RISCVCPU *cpu,
+ uint8_t satp_mode)
+{
+ bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32;
+ const bool *valid_vm = rv32 ? valid_vm_1_10_32 : valid_vm_1_10_64;
+
+ for (int i = 0; i <= satp_mode; ++i) {
+ if (valid_vm[i]) {
+ cpu->cfg.satp_mode.supported |= (1 << i);
+ }
+ }
+}
+
+/* Set the satp mode to the max supported */
+static void set_satp_mode_default_map(RISCVCPU *cpu)
+{
+ cpu->cfg.satp_mode.map = cpu->cfg.satp_mode.supported;
+}
+#endif
+
static void riscv_any_cpu_init(Object *obj)
{
CPURISCVState *env = &RISCV_CPU(obj)->env;
@@ -255,8 +341,15 @@ static void riscv_any_cpu_init(Object *obj)
#elif defined(TARGET_RISCV64)
set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVU);
#endif
+
+#ifndef CONFIG_USER_ONLY
+ set_satp_mode_max_supported(RISCV_CPU(obj),
+ riscv_cpu_mxl(&RISCV_CPU(obj)->env) == MXL_RV32 ?
+ VM_1_10_SV32 : VM_1_10_SV57);
+#endif
+
set_priv_version(env, PRIV_VERSION_1_12_0);
- register_cpu_props(DEVICE(obj));
+ register_cpu_props(obj);
}
#if defined(TARGET_RISCV64)
@@ -265,17 +358,23 @@ static void rv64_base_cpu_init(Object *obj)
CPURISCVState *env = &RISCV_CPU(obj)->env;
/* We set this in the realise function */
set_misa(env, MXL_RV64, 0);
- register_cpu_props(DEVICE(obj));
+ register_cpu_props(obj);
/* Set latest version of privileged specification */
set_priv_version(env, PRIV_VERSION_1_12_0);
+#ifndef CONFIG_USER_ONLY
+ set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57);
+#endif
}
static void rv64_sifive_u_cpu_init(Object *obj)
{
CPURISCVState *env = &RISCV_CPU(obj)->env;
set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
- register_cpu_props(DEVICE(obj));
+ register_cpu_props(obj);
set_priv_version(env, PRIV_VERSION_1_10_0);
+#ifndef CONFIG_USER_ONLY
+ set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV39);
+#endif
}
static void rv64_sifive_e_cpu_init(Object *obj)
@@ -284,9 +383,12 @@ static void rv64_sifive_e_cpu_init(Object *obj)
RISCVCPU *cpu = RISCV_CPU(obj);
set_misa(env, MXL_RV64, RVI | RVM | RVA | RVC | RVU);
- register_cpu_props(DEVICE(obj));
+ register_cpu_props(obj);
set_priv_version(env, PRIV_VERSION_1_10_0);
cpu->cfg.mmu = false;
+#ifndef CONFIG_USER_ONLY
+ set_satp_mode_max_supported(cpu, VM_1_10_MBARE);
+#endif
}
static void rv64_thead_c906_cpu_init(Object *obj)
@@ -316,6 +418,9 @@ static void rv64_thead_c906_cpu_init(Object *obj)
cpu->cfg.ext_xtheadsync = true;
cpu->cfg.mvendorid = THEAD_VENDOR_ID;
+#ifndef CONFIG_USER_ONLY
+ set_satp_mode_max_supported(cpu, VM_1_10_SV39);
+#endif
}
static void rv128_base_cpu_init(Object *obj)
@@ -329,9 +434,12 @@ static void rv128_base_cpu_init(Object *obj)
CPURISCVState *env = &RISCV_CPU(obj)->env;
/* We set this in the realise function */
set_misa(env, MXL_RV128, 0);
- register_cpu_props(DEVICE(obj));
+ register_cpu_props(obj);
/* Set latest version of privileged specification */
set_priv_version(env, PRIV_VERSION_1_12_0);
+#ifndef CONFIG_USER_ONLY
+ set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57);
+#endif
}
#else
static void rv32_base_cpu_init(Object *obj)
@@ -339,17 +447,23 @@ static void rv32_base_cpu_init(Object *obj)
CPURISCVState *env = &RISCV_CPU(obj)->env;
/* We set this in the realise function */
set_misa(env, MXL_RV32, 0);
- register_cpu_props(DEVICE(obj));
+ register_cpu_props(obj);
/* Set latest version of privileged specification */
set_priv_version(env, PRIV_VERSION_1_12_0);
+#ifndef CONFIG_USER_ONLY
+ set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32);
+#endif
}
static void rv32_sifive_u_cpu_init(Object *obj)
{
CPURISCVState *env = &RISCV_CPU(obj)->env;
set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
- register_cpu_props(DEVICE(obj));
+ register_cpu_props(obj);
set_priv_version(env, PRIV_VERSION_1_10_0);
+#ifndef CONFIG_USER_ONLY
+ set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32);
+#endif
}
static void rv32_sifive_e_cpu_init(Object *obj)
@@ -358,9 +472,12 @@ static void rv32_sifive_e_cpu_init(Object *obj)
RISCVCPU *cpu = RISCV_CPU(obj);
set_misa(env, MXL_RV32, RVI | RVM | RVA | RVC | RVU);
- register_cpu_props(DEVICE(obj));
+ register_cpu_props(obj);
set_priv_version(env, PRIV_VERSION_1_10_0);
cpu->cfg.mmu = false;
+#ifndef CONFIG_USER_ONLY
+ set_satp_mode_max_supported(cpu, VM_1_10_MBARE);
+#endif
}
static void rv32_ibex_cpu_init(Object *obj)
@@ -369,9 +486,12 @@ static void rv32_ibex_cpu_init(Object *obj)
RISCVCPU *cpu = RISCV_CPU(obj);
set_misa(env, MXL_RV32, RVI | RVM | RVC | RVU);
- register_cpu_props(DEVICE(obj));
+ register_cpu_props(obj);
set_priv_version(env, PRIV_VERSION_1_11_0);
cpu->cfg.mmu = false;
+#ifndef CONFIG_USER_ONLY
+ set_satp_mode_max_supported(cpu, VM_1_10_MBARE);
+#endif
cpu->cfg.epmp = true;
}
@@ -381,9 +501,12 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj)
RISCVCPU *cpu = RISCV_CPU(obj);
set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVC | RVU);
- register_cpu_props(DEVICE(obj));
+ register_cpu_props(obj);
set_priv_version(env, PRIV_VERSION_1_10_0);
cpu->cfg.mmu = false;
+#ifndef CONFIG_USER_ONLY
+ set_satp_mode_max_supported(cpu, VM_1_10_MBARE);
+#endif
}
#endif
@@ -396,7 +519,7 @@ static void riscv_host_cpu_init(Object *obj)
#elif defined(TARGET_RISCV64)
set_misa(env, MXL_RV64, 0);
#endif
- register_cpu_props(DEVICE(obj));
+ register_cpu_props(obj);
}
#endif
@@ -916,6 +1039,88 @@ static void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
set_misa(env, env->misa_mxl, ext);
}
+#ifndef CONFIG_USER_ONLY
+static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp)
+{
+ bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32;
+ uint8_t satp_mode_map_max;
+ uint8_t satp_mode_supported_max =
+ satp_mode_max_from_map(cpu->cfg.satp_mode.supported);
+
+ if (cpu->cfg.satp_mode.map == 0) {
+ if (cpu->cfg.satp_mode.init == 0) {
+ /* If unset by the user, we fallback to the default satp mode. */
+ set_satp_mode_default_map(cpu);
+ } else {
+ /*
+ * Find the lowest level that was disabled and then enable the
+ * first valid level below which can be found in
+ * valid_vm_1_10_32/64.
+ */
+ for (int i = 1; i < 16; ++i) {
+ if ((cpu->cfg.satp_mode.init & (1 << i)) &&
+ (cpu->cfg.satp_mode.supported & (1 << i))) {
+ for (int j = i - 1; j >= 0; --j) {
+ if (cpu->cfg.satp_mode.supported & (1 << j)) {
+ cpu->cfg.satp_mode.map |= (1 << j);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ satp_mode_map_max = satp_mode_max_from_map(cpu->cfg.satp_mode.map);
+
+ /* Make sure the user asked for a supported configuration (HW and qemu) */
+ if (satp_mode_map_max > satp_mode_supported_max) {
+ error_setg(errp, "satp_mode %s is higher than hw max capability %s",
+ satp_mode_str(satp_mode_map_max, rv32),
+ satp_mode_str(satp_mode_supported_max, rv32));
+ return;
+ }
+
+ /*
+ * Make sure the user did not ask for an invalid configuration as per
+ * the specification.
+ */
+ if (!rv32) {
+ for (int i = satp_mode_map_max - 1; i >= 0; --i) {
+ if (!(cpu->cfg.satp_mode.map & (1 << i)) &&
+ (cpu->cfg.satp_mode.init & (1 << i)) &&
+ (cpu->cfg.satp_mode.supported & (1 << i))) {
+ error_setg(errp, "cannot disable %s satp mode if %s "
+ "is enabled", satp_mode_str(i, false),
+ satp_mode_str(satp_mode_map_max, false));
+ return;
+ }
+ }
+ }
+
+ /* Finally expand the map so that all valid modes are set */
+ for (int i = satp_mode_map_max - 1; i >= 0; --i) {
+ if (cpu->cfg.satp_mode.supported & (1 << i)) {
+ cpu->cfg.satp_mode.map |= (1 << i);
+ }
+ }
+}
+#endif
+
+static void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp)
+{
+#ifndef CONFIG_USER_ONLY
+ Error *local_err = NULL;
+
+ riscv_cpu_satp_mode_finalize(cpu, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+#endif
+}
+
static void riscv_cpu_realize(DeviceState *dev, Error **errp)
{
CPUState *cs = CPU(dev);
@@ -1015,6 +1220,12 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
}
#endif
+ riscv_cpu_finalize_features(cpu, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
riscv_cpu_register_gdb_regs_for_features(cs);
qemu_init_vcpu(cs);
@@ -1024,6 +1235,52 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
}
#ifndef CONFIG_USER_ONLY
+static void cpu_riscv_get_satp(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ RISCVSATPMap *satp_map = opaque;
+ uint8_t satp = satp_mode_from_str(name);
+ bool value;
+
+ value = satp_map->map & (1 << satp);
+
+ visit_type_bool(v, name, &value, errp);
+}
+
+static void cpu_riscv_set_satp(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ RISCVSATPMap *satp_map = opaque;
+ uint8_t satp = satp_mode_from_str(name);
+ bool value;
+
+ if (!visit_type_bool(v, name, &value, errp)) {
+ return;
+ }
+
+ satp_map->map = deposit32(satp_map->map, satp, 1, value);
+ satp_map->init |= 1 << satp;
+}
+
+static void riscv_add_satp_mode_properties(Object *obj)
+{
+ RISCVCPU *cpu = RISCV_CPU(obj);
+
+ if (cpu->env.misa_mxl == MXL_RV32) {
+ object_property_add(obj, "sv32", "bool", cpu_riscv_get_satp,
+ cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
+ } else {
+ object_property_add(obj, "sv39", "bool", cpu_riscv_get_satp,
+ cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
+ object_property_add(obj, "sv48", "bool", cpu_riscv_get_satp,
+ cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
+ object_property_add(obj, "sv57", "bool", cpu_riscv_get_satp,
+ cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
+ object_property_add(obj, "sv64", "bool", cpu_riscv_get_satp,
+ cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode);
+ }
+}
+
static void riscv_cpu_set_irq(void *opaque, int irq, int level)
{
RISCVCPU *cpu = RISCV_CPU(opaque);
@@ -1167,6 +1424,11 @@ static Property riscv_cpu_extensions[] = {
DEFINE_PROP_BOOL("zhinx", RISCVCPU, cfg.ext_zhinx, false),
DEFINE_PROP_BOOL("zhinxmin", RISCVCPU, cfg.ext_zhinxmin, false),
+ DEFINE_PROP_BOOL("zicbom", RISCVCPU, cfg.ext_icbom, true),
+ DEFINE_PROP_UINT16("cbom_blocksize", RISCVCPU, cfg.cbom_blocksize, 64),
+ DEFINE_PROP_BOOL("zicboz", RISCVCPU, cfg.ext_icboz, true),
+ DEFINE_PROP_UINT16("cboz_blocksize", RISCVCPU, cfg.cboz_blocksize, 64),
+
DEFINE_PROP_BOOL("zmmul", RISCVCPU, cfg.ext_zmmul, false),
/* Vendor-specific custom extensions */
@@ -1203,11 +1465,12 @@ static Property riscv_cpu_extensions[] = {
* properties and leave. env.misa_ext = 0 means that we want
* all the default properties to be registered.
*/
-static void register_cpu_props(DeviceState *dev)
+static void register_cpu_props(Object *obj)
{
- RISCVCPU *cpu = RISCV_CPU(OBJECT(dev));
+ RISCVCPU *cpu = RISCV_CPU(obj);
uint32_t misa_ext = cpu->env.misa_ext;
Property *prop;
+ DeviceState *dev = DEVICE(obj);
/*
* If misa_ext is not zero, set cfg properties now to
@@ -1238,6 +1501,10 @@ static void register_cpu_props(DeviceState *dev)
for (prop = riscv_cpu_extensions; prop && prop->name; prop++) {
qdev_property_add_static(dev, prop);
}
+
+#ifndef CONFIG_USER_ONLY
+ riscv_add_satp_mode_properties(obj);
+#endif
}
static Property riscv_cpu_properties[] = {
@@ -1294,6 +1561,13 @@ static const char *riscv_gdb_get_dynamic_xml(CPUState *cs, const char *xmlname)
}
#ifndef CONFIG_USER_ONLY
+static int64_t riscv_get_arch_id(CPUState *cs)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+
+ return cpu->env.mhartid;
+}
+
#include "hw/core/sysemu-cpu-ops.h"
static const struct SysemuCPUOps riscv_sysemu_ops = {
@@ -1348,6 +1622,7 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data)
cc->disas_set_info = riscv_cpu_disas_set_info;
#ifndef CONFIG_USER_ONLY
cc->sysemu_ops = &riscv_sysemu_ops;
+ cc->get_arch_id = riscv_get_arch_id;
#endif
cc->gdb_arch_name = riscv_gdb_arch_name;
cc->gdb_get_dynamic_xml = riscv_gdb_get_dynamic_xml;
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 665b4c6..638e47c 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -27,6 +27,7 @@
#include "qom/object.h"
#include "qemu/int128.h"
#include "cpu_bits.h"
+#include "qapi/qapi-types-common.h"
#define TCG_GUEST_DEFAULT_MO 0
@@ -401,6 +402,21 @@ struct RISCVCPUClass {
ResettablePhases parent_phases;
};
+/*
+ * map is a 16-bit bitmap: the most significant set bit in map is the maximum
+ * satp mode that is supported. It may be chosen by the user and must respect
+ * what qemu implements (valid_1_10_32/64) and what the hw is capable of
+ * (supported bitmap below).
+ *
+ * init is a 16-bit bitmap used to make sure the user selected a correct
+ * configuration as per the specification.
+ *
+ * supported is a 16-bit bitmap used to reflect the hw capabilities.
+ */
+typedef struct {
+ uint16_t map, init, supported;
+} RISCVSATPMap;
+
struct RISCVCPUConfig {
bool ext_i;
bool ext_e;
@@ -434,6 +450,8 @@ struct RISCVCPUConfig {
bool ext_zkt;
bool ext_ifencei;
bool ext_icsr;
+ bool ext_icbom;
+ bool ext_icboz;
bool ext_zicond;
bool ext_zihintpause;
bool ext_smstateen;
@@ -486,6 +504,8 @@ struct RISCVCPUConfig {
char *vext_spec;
uint16_t vlen;
uint16_t elen;
+ uint16_t cbom_blocksize;
+ uint16_t cboz_blocksize;
bool mmu;
bool pmp;
bool epmp;
@@ -493,6 +513,10 @@ struct RISCVCPUConfig {
bool misa_w;
bool short_isa_string;
+
+#ifndef CONFIG_USER_ONLY
+ RISCVSATPMap satp_mode;
+#endif
};
typedef struct RISCVCPUConfig RISCVCPUConfig;
@@ -794,9 +818,14 @@ enum riscv_pmu_event_idx {
/* CSR function table */
extern riscv_csr_operations csr_ops[CSR_TABLE_SIZE];
+extern const bool valid_vm_1_10_32[], valid_vm_1_10_64[];
+
void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops);
void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops);
void riscv_cpu_register_gdb_regs_for_features(CPUState *cs);
+uint8_t satp_mode_max_from_map(uint32_t map);
+const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit);
+
#endif /* RISCV_CPU_H */
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 3106f96..ab56663 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -1141,16 +1141,16 @@ static const target_ulong hip_writable_mask = MIP_VSSIP;
static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP;
static const target_ulong vsip_writable_mask = MIP_VSSIP;
-static const char valid_vm_1_10_32[16] = {
- [VM_1_10_MBARE] = 1,
- [VM_1_10_SV32] = 1
+const bool valid_vm_1_10_32[16] = {
+ [VM_1_10_MBARE] = true,
+ [VM_1_10_SV32] = true
};
-static const char valid_vm_1_10_64[16] = {
- [VM_1_10_MBARE] = 1,
- [VM_1_10_SV39] = 1,
- [VM_1_10_SV48] = 1,
- [VM_1_10_SV57] = 1
+const bool valid_vm_1_10_64[16] = {
+ [VM_1_10_MBARE] = true,
+ [VM_1_10_SV39] = true,
+ [VM_1_10_SV48] = true,
+ [VM_1_10_SV57] = true
};
/* Machine Information Registers */
@@ -1230,13 +1230,11 @@ static RISCVException read_mstatus(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
-static int validate_vm(CPURISCVState *env, target_ulong vm)
+static bool validate_vm(CPURISCVState *env, target_ulong vm)
{
- if (riscv_cpu_mxl(env) == MXL_RV32) {
- return valid_vm_1_10_32[vm & 0xf];
- } else {
- return valid_vm_1_10_64[vm & 0xf];
- }
+ RISCVCPU *cpu = RISCV_CPU(env_cpu(env));
+
+ return (vm & 0xf) <= satp_mode_max_from_map(cpu->cfg.satp_mode.map);
}
static RISCVException write_mstatus(CPURISCVState *env, int csrno,
@@ -2669,7 +2667,8 @@ static RISCVException read_satp(CPURISCVState *env, int csrno,
static RISCVException write_satp(CPURISCVState *env, int csrno,
target_ulong val)
{
- target_ulong vm, mask;
+ target_ulong mask;
+ bool vm;
if (!riscv_cpu_cfg(env)->mmu) {
return RISCV_EXCP_NONE;
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index 0497370..37b54e0 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -97,6 +97,11 @@ DEF_HELPER_FLAGS_2(fcvt_h_l, TCG_CALL_NO_RWG, i64, env, tl)
DEF_HELPER_FLAGS_2(fcvt_h_lu, TCG_CALL_NO_RWG, i64, env, tl)
DEF_HELPER_FLAGS_2(fclass_h, TCG_CALL_NO_RWG_SE, tl, env, i64)
+/* Cache-block operations */
+DEF_HELPER_2(cbo_clean_flush, void, env, tl)
+DEF_HELPER_2(cbo_inval, void, env, tl)
+DEF_HELPER_2(cbo_zero, void, env, tl)
+
/* Special functions */
DEF_HELPER_2(csrr, tl, env, int)
DEF_HELPER_3(csrw, void, env, int, tl)
diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
index fb537e9..73d5d1b 100644
--- a/target/riscv/insn32.decode
+++ b/target/riscv/insn32.decode
@@ -134,6 +134,7 @@ addi ............ ..... 000 ..... 0010011 @i
slti ............ ..... 010 ..... 0010011 @i
sltiu ............ ..... 011 ..... 0010011 @i
xori ............ ..... 100 ..... 0010011 @i
+# cbo.prefetch_{i,r,m} instructions are ori with rd=x0 and not decoded.
ori ............ ..... 110 ..... 0010011 @i
andi ............ ..... 111 ..... 0010011 @i
slli 00000. ...... ..... 001 ..... 0010011 @sh
@@ -179,7 +180,20 @@ sraw 0100000 ..... ..... 101 ..... 0111011 @r
# *** RV128I Base Instruction Set (in addition to RV64I) ***
ldu ............ ..... 111 ..... 0000011 @i
-lq ............ ..... 010 ..... 0001111 @i
+{
+ [
+ # *** RV32 Zicbom Standard Extension ***
+ cbo_clean 0000000 00001 ..... 010 00000 0001111 @sfence_vm
+ cbo_flush 0000000 00010 ..... 010 00000 0001111 @sfence_vm
+ cbo_inval 0000000 00000 ..... 010 00000 0001111 @sfence_vm
+
+ # *** RV32 Zicboz Standard Extension ***
+ cbo_zero 0000000 00100 ..... 010 00000 0001111 @sfence_vm
+ ]
+
+ # *** RVI128 lq ***
+ lq ............ ..... 010 ..... 0001111 @i
+}
sq ............ ..... 100 ..... 0100011 @s
addid ............ ..... 000 ..... 1011011 @i
sllid 000000 ...... ..... 001 ..... 1011011 @sh6
diff --git a/target/riscv/insn_trans/trans_rvzicbo.c.inc b/target/riscv/insn_trans/trans_rvzicbo.c.inc
new file mode 100644
index 0000000..7df9c30
--- /dev/null
+++ b/target/riscv/insn_trans/trans_rvzicbo.c.inc
@@ -0,0 +1,57 @@
+/*
+ * RISC-V translation routines for the RISC-V CBO Extension.
+ *
+ * Copyright (c) 2021 Philipp Tomsich, philipp.tomsich@vrull.eu
+ *
+ * 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/>.
+ */
+
+#define REQUIRE_ZICBOM(ctx) do { \
+ if (!ctx->cfg_ptr->ext_icbom) { \
+ return false; \
+ } \
+} while (0)
+
+#define REQUIRE_ZICBOZ(ctx) do { \
+ if (!ctx->cfg_ptr->ext_icboz) { \
+ return false; \
+ } \
+} while (0)
+
+static bool trans_cbo_clean(DisasContext *ctx, arg_cbo_clean *a)
+{
+ REQUIRE_ZICBOM(ctx);
+ gen_helper_cbo_clean_flush(cpu_env, cpu_gpr[a->rs1]);
+ return true;
+}
+
+static bool trans_cbo_flush(DisasContext *ctx, arg_cbo_flush *a)
+{
+ REQUIRE_ZICBOM(ctx);
+ gen_helper_cbo_clean_flush(cpu_env, cpu_gpr[a->rs1]);
+ return true;
+}
+
+static bool trans_cbo_inval(DisasContext *ctx, arg_cbo_inval *a)
+{
+ REQUIRE_ZICBOM(ctx);
+ gen_helper_cbo_inval(cpu_env, cpu_gpr[a->rs1]);
+ return true;
+}
+
+static bool trans_cbo_zero(DisasContext *ctx, arg_cbo_zero *a)
+{
+ REQUIRE_ZICBOZ(ctx);
+ gen_helper_cbo_zero(cpu_env, cpu_gpr[a->rs1]);
+ return true;
+}
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 9c0b91c..84ee018 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -3,6 +3,7 @@
*
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
* Copyright (c) 2017-2018 SiFive, Inc.
+ * Copyright (c) 2022 VRULL GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -123,6 +124,140 @@ target_ulong helper_csrrw_i128(CPURISCVState *env, int csr,
return int128_getlo(rv);
}
+
+/*
+ * check_zicbo_envcfg
+ *
+ * Raise virtual exceptions and illegal instruction exceptions for
+ * Zicbo[mz] instructions based on the settings of [mhs]envcfg as
+ * specified in section 2.5.1 of the CMO specification.
+ */
+static void check_zicbo_envcfg(CPURISCVState *env, target_ulong envbits,
+ uintptr_t ra)
+{
+#ifndef CONFIG_USER_ONLY
+ if ((env->priv < PRV_M) && !get_field(env->menvcfg, envbits)) {
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra);
+ }
+
+ if (riscv_cpu_virt_enabled(env) &&
+ (((env->priv < PRV_H) && !get_field(env->henvcfg, envbits)) ||
+ ((env->priv < PRV_S) && !get_field(env->senvcfg, envbits)))) {
+ riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, ra);
+ }
+
+ if ((env->priv < PRV_S) && !get_field(env->senvcfg, envbits)) {
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra);
+ }
+#endif
+}
+
+void helper_cbo_zero(CPURISCVState *env, target_ulong address)
+{
+ RISCVCPU *cpu = env_archcpu(env);
+ uint16_t cbozlen = cpu->cfg.cboz_blocksize;
+ int mmu_idx = cpu_mmu_index(env, false);
+ uintptr_t ra = GETPC();
+ void *mem;
+
+ check_zicbo_envcfg(env, MENVCFG_CBZE, ra);
+
+ /* Mask off low-bits to align-down to the cache-block. */
+ address &= ~(cbozlen - 1);
+
+ /*
+ * cbo.zero requires MMU_DATA_STORE access. Do a probe_write()
+ * to raise any exceptions, including PMP.
+ */
+ mem = probe_write(env, address, cbozlen, mmu_idx, ra);
+
+ if (likely(mem)) {
+ memset(mem, 0, cbozlen);
+ } else {
+ /*
+ * This means that we're dealing with an I/O page. Section 4.2
+ * of cmobase v1.0.1 says:
+ *
+ * "Cache-block zero instructions store zeros independently
+ * of whether data from the underlying memory locations are
+ * cacheable."
+ *
+ * Write zeros in address + cbozlen regardless of not being
+ * a RAM page.
+ */
+ for (int i = 0; i < cbozlen; i++) {
+ cpu_stb_mmuidx_ra(env, address + i, 0, mmu_idx, ra);
+ }
+ }
+}
+
+/*
+ * check_zicbom_access
+ *
+ * Check access permissions (LOAD, STORE or FETCH as specified in
+ * section 2.5.2 of the CMO specification) for Zicbom, raising
+ * either store page-fault (non-virtualized) or store guest-page
+ * fault (virtualized).
+ */
+static void check_zicbom_access(CPURISCVState *env,
+ target_ulong address,
+ uintptr_t ra)
+{
+ RISCVCPU *cpu = env_archcpu(env);
+ int mmu_idx = cpu_mmu_index(env, false);
+ uint16_t cbomlen = cpu->cfg.cbom_blocksize;
+ void *phost;
+ int ret;
+
+ /* Mask off low-bits to align-down to the cache-block. */
+ address &= ~(cbomlen - 1);
+
+ /*
+ * Section 2.5.2 of cmobase v1.0.1:
+ *
+ * "A cache-block management instruction is permitted to
+ * access the specified cache block whenever a load instruction
+ * or store instruction is permitted to access the corresponding
+ * physical addresses. If neither a load instruction nor store
+ * instruction is permitted to access the physical addresses,
+ * but an instruction fetch is permitted to access the physical
+ * addresses, whether a cache-block management instruction is
+ * permitted to access the cache block is UNSPECIFIED."
+ */
+ ret = probe_access_flags(env, address, cbomlen, MMU_DATA_LOAD,
+ mmu_idx, true, &phost, ra);
+ if (ret != TLB_INVALID_MASK) {
+ /* Success: readable */
+ return;
+ }
+
+ /*
+ * Since not readable, must be writable. On failure, store
+ * fault/store guest amo fault will be raised by
+ * riscv_cpu_tlb_fill(). PMP exceptions will be caught
+ * there as well.
+ */
+ probe_write(env, address, cbomlen, mmu_idx, ra);
+}
+
+void helper_cbo_clean_flush(CPURISCVState *env, target_ulong address)
+{
+ uintptr_t ra = GETPC();
+ check_zicbo_envcfg(env, MENVCFG_CBCFE, ra);
+ check_zicbom_access(env, address, ra);
+
+ /* We don't emulate the cache-hierarchy, so we're done. */
+}
+
+void helper_cbo_inval(CPURISCVState *env, target_ulong address)
+{
+ uintptr_t ra = GETPC();
+ check_zicbo_envcfg(env, MENVCFG_CBIE, ra);
+ check_zicbom_access(env, address, ra);
+
+ /* We don't emulate the cache-hierarchy, so we're done. */
+}
+
#ifndef CONFIG_USER_ONLY
target_ulong helper_sret(CPURISCVState *env)
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index 9390920..0ee8ee1 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -1080,6 +1080,7 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc)
#include "insn_trans/trans_rvb.c.inc"
#include "insn_trans/trans_rvzicond.c.inc"
#include "insn_trans/trans_rvzawrs.c.inc"
+#include "insn_trans/trans_rvzicbo.c.inc"
#include "insn_trans/trans_rvzfh.c.inc"
#include "insn_trans/trans_rvk.c.inc"
#include "insn_trans/trans_privileged.c.inc"