416 changes: 416 additions & 0 deletions hw/riscv/virt-acpi-build.c

Large diffs are not rendered by default.

70 changes: 62 additions & 8 deletions hw/riscv/virt.c
Expand Up @@ -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
Expand Down Expand Up @@ -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];
Expand All @@ -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",
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 = {
Expand Down
6 changes: 6 additions & 0 deletions include/hw/riscv/virt.h
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Binary file modified pc-bios/opensbi-riscv32-generic-fw_dynamic.bin
Binary file not shown.
Binary file modified pc-bios/opensbi-riscv64-generic-fw_dynamic.bin
Binary file not shown.
2 changes: 1 addition & 1 deletion roms/opensbi
Submodule opensbi updated from 448987 to 6b5188
303 changes: 289 additions & 14 deletions target/riscv/cpu.c

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions target/riscv/cpu.h
Expand Up @@ -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

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -486,13 +504,19 @@ 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;
bool debug;
bool misa_w;

bool short_isa_string;

#ifndef CONFIG_USER_ONLY
RISCVSATPMap satp_mode;
#endif
};

typedef struct RISCVCPUConfig RISCVCPUConfig;
Expand Down Expand Up @@ -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 */
29 changes: 14 additions & 15 deletions target/riscv/csr.c
Expand Up @@ -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 */
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions target/riscv/helper.h
Expand Up @@ -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)
Expand Down
16 changes: 15 additions & 1 deletion target/riscv/insn32.decode
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
57 changes: 57 additions & 0 deletions 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;
}
135 changes: 135 additions & 0 deletions target/riscv/op_helper.c
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions target/riscv/translate.c
Expand Up @@ -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"
Expand Down