RISC-V Patches for the 4.1 Soft Freeze, Part 2 v3
This pull request contains a handful of patches that I'd like to target for the 4.1 soft freeze. There are a handful of new features: * Support for the 1.11.0, the latest privileged specification. * Support for reading and writing the PRCI registers. * Better control over the ISA of the target machine. * Support for the cpu-topology device tree node. Additionally, there are a handful of bug fixes including: * Load reservations are now broken by both store conditional and by scheduling, which fixes issues with parallel applications. * Various fixes to the PMP implementation. * Fixes to the 32-bit linux-user syscall ABI. * Various fixes for instruction decodeing. * A fix to the PCI device tree "bus-range" property. This boots 32-bit and 64-bit OpenEmbedded. Changes since v2 [riscv-for-master-4.1-sf1-v2]: * Dropped OpenSBI. Changes since v1 [riscv-for-master-4.1-sf1]: * Contains a fix to the sifive_u OpenSBI integration. -----BEGIN PGP SIGNATURE----- iQJHBAABCAAxFiEEAM520YNJYN/OiG3470yhUCzLq0EFAl0caa0THHBhbG1lckBk YWJiZWx0LmNvbQAKCRDvTKFQLMurQRTNEACwLeajFWIdeltz9PpzlmbABoY9p8rT amMVjEcisMmIzV7UjLcmqYAyPUFb2KvNy5EA15op0nQKGv7Mm7c4P7V5/eQyz9wE 9/81s3OoOuRa79mwgF3aekA6iQXb0ID+kMaIFSrbTTyECOULb2kb4/xxOH+N13wh 8VfYBdgxsvC37DDShXuzGOdtWpu1wZDgYwYOHAIns/r90FYA1w2vJhjn72AiDfnY QKwm6FHiplraMfbURRxbgU5oUIXsCelYNB3Fu9dFOuVwgmwrwFHJIG1Mi5pvMkbo vRnSDX5vKKpeyFj6U23RnCJpxoORe0Bp6GnD5MbByUr9oroBbD3dkNryXU0da5jN e5Jje3F8v/g3KdRUm6AQJb6JBBOZu4uSUn9jUfqlNLFDvUDLTq74kfJHLzsWr4Ds k/rP3fmyked27KnEwCOzAEEDI4Z3exbwnoPiP5ik92ZkSL7PVwLpWvB/8tn8HQab ldq8w/piUkbC9ug2p07TFg6aST8YjzuGHtOW4M8TpX5cuAiHPbjS54dj88+EL5i7 shBWFKEuKV0ESVgIBwJu9qlX5QvQWJA4LSo35PyrGJb6xV+lQ3j0pdcOojL6ZSkk JDbQtC1nsxDf/Qt4vHc4VbOZQChO+FYs2Smin8Wl5gN+hReY6lB/ZtHjYl7uw2bC 9Ijy2OkCjtz4mw== =lliO -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/palmer/tags/riscv-for-master-4.1-sf1-v3' into staging RISC-V Patches for the 4.1 Soft Freeze, Part 2 v3 This pull request contains a handful of patches that I'd like to target for the 4.1 soft freeze. There are a handful of new features: * Support for the 1.11.0, the latest privileged specification. * Support for reading and writing the PRCI registers. * Better control over the ISA of the target machine. * Support for the cpu-topology device tree node. Additionally, there are a handful of bug fixes including: * Load reservations are now broken by both store conditional and by scheduling, which fixes issues with parallel applications. * Various fixes to the PMP implementation. * Fixes to the 32-bit linux-user syscall ABI. * Various fixes for instruction decodeing. * A fix to the PCI device tree "bus-range" property. This boots 32-bit and 64-bit OpenEmbedded. Changes since v2 [riscv-for-master-4.1-sf1-v2]: * Dropped OpenSBI. Changes since v1 [riscv-for-master-4.1-sf1]: * Contains a fix to the sifive_u OpenSBI integration. # gpg: Signature made Wed 03 Jul 2019 09:39:09 BST # gpg: using RSA key 00CE76D1834960DFCE886DF8EF4CA1502CCBAB41 # gpg: issuer "palmer@dabbelt.com" # gpg: Good signature from "Palmer Dabbelt <palmer@dabbelt.com>" [unknown] # gpg: aka "Palmer Dabbelt <palmer@sifive.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 * remotes/palmer/tags/riscv-for-master-4.1-sf1-v3: (32 commits) hw/riscv: Extend the kernel loading support hw/riscv: Add support for loading a firmware hw/riscv: Split out the boot functions riscv: sifive_u: Update the plic hart config to support multicore riscv: sifive_u: Do not create hard-coded phandles in DT disas/riscv: Fix `rdinstreth` constraint disas/riscv: Disassemble reserved compressed encodings as illegal riscv: virt: Add cpu-topology DT node. RISC-V: Update syscall list for 32-bit support. RISC-V: Clear load reservations on context switch and SC RISC-V: Add support for the Zicsr extension RISC-V: Add support for the Zifencei extension target/riscv: Add support for disabling/enabling Counters target/riscv: Remove user version information target/riscv: Require either I or E base extension qemu-deprecated.texi: Deprecate the RISC-V privledge spec 1.09.1 target/riscv: Set privledge spec 1.11.0 as default target/riscv: Add the mcountinhibit CSR target/riscv: Add the privledge spec version 1.11.0 target/riscv: Restructure deprecatd CPUs ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
aff8cee805
@ -504,14 +504,19 @@ typedef struct {
|
||||
const rvc_constraint *constraints;
|
||||
} rv_comp_data;
|
||||
|
||||
enum {
|
||||
rvcd_imm_nz = 0x1
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char * const name;
|
||||
const rv_codec codec;
|
||||
const char * const format;
|
||||
const rv_comp_data *pseudo;
|
||||
const int decomp_rv32;
|
||||
const int decomp_rv64;
|
||||
const int decomp_rv128;
|
||||
const short decomp_rv32;
|
||||
const short decomp_rv64;
|
||||
const short decomp_rv128;
|
||||
const short decomp_data;
|
||||
} rv_opcode_data;
|
||||
|
||||
/* register names */
|
||||
@ -609,7 +614,8 @@ static const rvc_constraint rvcc_rdtime[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc01, r
|
||||
static const rvc_constraint rvcc_rdinstret[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc02, rvc_end };
|
||||
static const rvc_constraint rvcc_rdcycleh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end };
|
||||
static const rvc_constraint rvcc_rdtimeh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc81, rvc_end };
|
||||
static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end };
|
||||
static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0,
|
||||
rvc_csr_eq_0xc82, rvc_end };
|
||||
static const rvc_constraint rvcc_frcsr[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x003, rvc_end };
|
||||
static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, rvc_end };
|
||||
static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, rvc_end };
|
||||
@ -1011,7 +1017,8 @@ const rv_opcode_data opcode_data[] = {
|
||||
{ "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
|
||||
{ "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
|
||||
{ "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 },
|
||||
{ "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
|
||||
{ "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi,
|
||||
rv_op_addi, rv_op_addi, rvcd_imm_nz },
|
||||
{ "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, 0 },
|
||||
{ "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw },
|
||||
{ "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 },
|
||||
@ -1019,14 +1026,20 @@ const rv_opcode_data opcode_data[] = {
|
||||
{ "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw },
|
||||
{ "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 },
|
||||
{ "c.nop", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
|
||||
{ "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
|
||||
{ "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi,
|
||||
rv_op_addi, rvcd_imm_nz },
|
||||
{ "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, NULL, rv_op_jal, 0, 0 },
|
||||
{ "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
|
||||
{ "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
|
||||
{ "c.lui", rv_codec_ci_lui, rv_fmt_rd_imm, NULL, rv_op_lui, rv_op_lui, rv_op_lui },
|
||||
{ "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, rv_op_srli, rv_op_srli },
|
||||
{ "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, rv_op_srai, rv_op_srai },
|
||||
{ "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, rv_op_andi, rv_op_andi },
|
||||
{ "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi,
|
||||
rv_op_addi, rv_op_addi, rvcd_imm_nz },
|
||||
{ "c.lui", rv_codec_ci_lui, rv_fmt_rd_imm, NULL, rv_op_lui, rv_op_lui,
|
||||
rv_op_lui, rvcd_imm_nz },
|
||||
{ "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli,
|
||||
rv_op_srli, rv_op_srli, rvcd_imm_nz },
|
||||
{ "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai,
|
||||
rv_op_srai, rv_op_srai, rvcd_imm_nz },
|
||||
{ "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi,
|
||||
rv_op_andi, rv_op_andi },
|
||||
{ "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, rv_op_sub },
|
||||
{ "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, rv_op_xor },
|
||||
{ "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, rv_op_or },
|
||||
@ -1036,7 +1049,8 @@ const rv_opcode_data opcode_data[] = {
|
||||
{ "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal, rv_op_jal, rv_op_jal },
|
||||
{ "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq, rv_op_beq, rv_op_beq },
|
||||
{ "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, rv_op_bne },
|
||||
{ "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, rv_op_slli, rv_op_slli },
|
||||
{ "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli,
|
||||
rv_op_slli, rv_op_slli, rvcd_imm_nz },
|
||||
{ "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, rv_op_fld },
|
||||
{ "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw },
|
||||
{ "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 },
|
||||
@ -2795,27 +2809,42 @@ static void decode_inst_decompress_rv32(rv_decode *dec)
|
||||
{
|
||||
int decomp_op = opcode_data[dec->op].decomp_rv32;
|
||||
if (decomp_op != rv_op_illegal) {
|
||||
if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz)
|
||||
&& dec->imm == 0) {
|
||||
dec->op = rv_op_illegal;
|
||||
} else {
|
||||
dec->op = decomp_op;
|
||||
dec->codec = opcode_data[decomp_op].codec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_inst_decompress_rv64(rv_decode *dec)
|
||||
{
|
||||
int decomp_op = opcode_data[dec->op].decomp_rv64;
|
||||
if (decomp_op != rv_op_illegal) {
|
||||
if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz)
|
||||
&& dec->imm == 0) {
|
||||
dec->op = rv_op_illegal;
|
||||
} else {
|
||||
dec->op = decomp_op;
|
||||
dec->codec = opcode_data[decomp_op].codec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_inst_decompress_rv128(rv_decode *dec)
|
||||
{
|
||||
int decomp_op = opcode_data[dec->op].decomp_rv128;
|
||||
if (decomp_op != rv_op_illegal) {
|
||||
if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz)
|
||||
&& dec->imm == 0) {
|
||||
dec->op = rv_op_illegal;
|
||||
} else {
|
||||
dec->op = decomp_op;
|
||||
dec->codec = opcode_data[decomp_op].codec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_inst_decompress(rv_decode *dec, rv_isa isa)
|
||||
|
@ -1,3 +1,4 @@
|
||||
obj-y += boot.o
|
||||
obj-$(CONFIG_SPIKE) += riscv_htif.o
|
||||
obj-$(CONFIG_HART) += riscv_hart.o
|
||||
obj-$(CONFIG_SIFIVE_E) += sifive_e.o
|
||||
|
105
hw/riscv/boot.c
Normal file
105
hw/riscv/boot.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* QEMU RISC-V Boot Helper
|
||||
*
|
||||
* Copyright (c) 2017 SiFive, Inc.
|
||||
* Copyright (c) 2019 Alistair Francis <alistair.francis@wdc.com>
|
||||
*
|
||||
* 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 "qemu/units.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "exec/cpu-defs.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/riscv/boot.h"
|
||||
#include "hw/boards.h"
|
||||
#include "elf.h"
|
||||
|
||||
#if defined(TARGET_RISCV32)
|
||||
# define KERNEL_BOOT_ADDRESS 0x80400000
|
||||
#else
|
||||
# define KERNEL_BOOT_ADDRESS 0x80200000
|
||||
#endif
|
||||
|
||||
target_ulong riscv_load_firmware(const char *firmware_filename,
|
||||
hwaddr firmware_load_addr)
|
||||
{
|
||||
uint64_t firmware_entry, firmware_start, firmware_end;
|
||||
|
||||
if (load_elf(firmware_filename, NULL, NULL, NULL, &firmware_entry,
|
||||
&firmware_start, &firmware_end, 0, EM_RISCV, 1, 0) > 0) {
|
||||
return firmware_entry;
|
||||
}
|
||||
|
||||
if (load_image_targphys_as(firmware_filename, firmware_load_addr,
|
||||
ram_size, NULL) > 0) {
|
||||
return firmware_load_addr;
|
||||
}
|
||||
|
||||
error_report("could not load firmware '%s'", firmware_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
target_ulong riscv_load_kernel(const char *kernel_filename)
|
||||
{
|
||||
uint64_t kernel_entry, kernel_high;
|
||||
|
||||
if (load_elf(kernel_filename, NULL, NULL, NULL,
|
||||
&kernel_entry, NULL, &kernel_high, 0, EM_RISCV, 1, 0) > 0) {
|
||||
return kernel_entry;
|
||||
}
|
||||
|
||||
if (load_uimage_as(kernel_filename, &kernel_entry, NULL, NULL,
|
||||
NULL, NULL, NULL) > 0) {
|
||||
return kernel_entry;
|
||||
}
|
||||
|
||||
if (load_image_targphys_as(kernel_filename, KERNEL_BOOT_ADDRESS,
|
||||
ram_size, NULL) > 0) {
|
||||
return KERNEL_BOOT_ADDRESS;
|
||||
}
|
||||
|
||||
error_report("could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size,
|
||||
uint64_t kernel_entry, hwaddr *start)
|
||||
{
|
||||
int size;
|
||||
|
||||
/*
|
||||
* We want to put the initrd far enough into RAM that when the
|
||||
* kernel is uncompressed it will not clobber the initrd. However
|
||||
* on boards without much RAM we must ensure that we still leave
|
||||
* enough room for a decent sized initrd, and on boards with large
|
||||
* amounts of RAM we must avoid the initrd being so far up in RAM
|
||||
* that it is outside lowmem and inaccessible to the kernel.
|
||||
* So for boards with less than 256MB of RAM we put the initrd
|
||||
* halfway into RAM, and for boards with 256MB of RAM or more we put
|
||||
* the initrd at 128MB.
|
||||
*/
|
||||
*start = kernel_entry + MIN(mem_size / 2, 128 * MiB);
|
||||
|
||||
size = load_ramdisk(filename, *start, mem_size - *start);
|
||||
if (size == -1) {
|
||||
size = load_image_targphys(filename, *start, mem_size - *start);
|
||||
if (size == -1) {
|
||||
error_report("could not load ramdisk '%s'", filename);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return *start + size;
|
||||
}
|
@ -44,10 +44,10 @@
|
||||
#include "hw/riscv/sifive_prci.h"
|
||||
#include "hw/riscv/sifive_uart.h"
|
||||
#include "hw/riscv/sifive_e.h"
|
||||
#include "hw/riscv/boot.h"
|
||||
#include "chardev/char.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "elf.h"
|
||||
|
||||
static const struct MemmapEntry {
|
||||
hwaddr base;
|
||||
@ -74,19 +74,6 @@ static const struct MemmapEntry {
|
||||
[SIFIVE_E_DTIM] = { 0x80000000, 0x4000 }
|
||||
};
|
||||
|
||||
static target_ulong load_kernel(const char *kernel_filename)
|
||||
{
|
||||
uint64_t kernel_entry, kernel_high;
|
||||
|
||||
if (load_elf(kernel_filename, NULL, NULL, NULL,
|
||||
&kernel_entry, NULL, &kernel_high,
|
||||
0, EM_RISCV, 1, 0) < 0) {
|
||||
error_report("could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
return kernel_entry;
|
||||
}
|
||||
|
||||
static void sifive_mmio_emulate(MemoryRegion *parent, const char *name,
|
||||
uintptr_t offset, uintptr_t length)
|
||||
{
|
||||
@ -131,7 +118,7 @@ static void riscv_sifive_e_init(MachineState *machine)
|
||||
memmap[SIFIVE_E_MROM].base, &address_space_memory);
|
||||
|
||||
if (machine->kernel_filename) {
|
||||
load_kernel(machine->kernel_filename);
|
||||
riscv_load_kernel(machine->kernel_filename);
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,17 +145,15 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
SiFiveESoCState *s = RISCV_E_SOC(dev);
|
||||
MemoryRegion *sys_mem = get_system_memory();
|
||||
MemoryRegion *xip_mem = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->cpus), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
/* Mask ROM */
|
||||
memory_region_init_rom(mask_rom, NULL, "riscv.sifive.e.mrom",
|
||||
memory_region_init_rom(&s->mask_rom, NULL, "riscv.sifive.e.mrom",
|
||||
memmap[SIFIVE_E_MROM].size, &error_fatal);
|
||||
memory_region_add_subregion(sys_mem,
|
||||
memmap[SIFIVE_E_MROM].base, mask_rom);
|
||||
memmap[SIFIVE_E_MROM].base, &s->mask_rom);
|
||||
|
||||
/* MMIO */
|
||||
s->plic = sifive_plic_create(memmap[SIFIVE_E_PLIC].base,
|
||||
@ -228,10 +213,11 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp)
|
||||
memmap[SIFIVE_E_PWM2].base, memmap[SIFIVE_E_PWM2].size);
|
||||
|
||||
/* Flash memory */
|
||||
memory_region_init_ram(xip_mem, NULL, "riscv.sifive.e.xip",
|
||||
memory_region_init_ram(&s->xip_mem, NULL, "riscv.sifive.e.xip",
|
||||
memmap[SIFIVE_E_XIP].size, &error_fatal);
|
||||
memory_region_set_readonly(xip_mem, true);
|
||||
memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base, xip_mem);
|
||||
memory_region_set_readonly(&s->xip_mem, true);
|
||||
memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base,
|
||||
&s->xip_mem);
|
||||
}
|
||||
|
||||
static void riscv_sifive_e_machine_init(MachineClass *mc)
|
||||
|
@ -24,15 +24,18 @@
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/riscv/sifive_prci.h"
|
||||
|
||||
/* currently implements enough to mock freedom-e-sdk BSP clock programming */
|
||||
|
||||
static uint64_t sifive_prci_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
if (addr == 0 /* PRCI_HFROSCCFG */) {
|
||||
return 1 << 31; /* ROSC_RDY */
|
||||
}
|
||||
if (addr == 8 /* PRCI_PLLCFG */) {
|
||||
return 1 << 31; /* PLL_LOCK */
|
||||
SiFivePRCIState *s = opaque;
|
||||
switch (addr) {
|
||||
case SIFIVE_PRCI_HFROSCCFG:
|
||||
return s->hfrosccfg;
|
||||
case SIFIVE_PRCI_HFXOSCCFG:
|
||||
return s->hfxosccfg;
|
||||
case SIFIVE_PRCI_PLLCFG:
|
||||
return s->pllcfg;
|
||||
case SIFIVE_PRCI_PLLOUTDIV:
|
||||
return s->plloutdiv;
|
||||
}
|
||||
hw_error("%s: read: addr=0x%x\n", __func__, (int)addr);
|
||||
return 0;
|
||||
@ -41,7 +44,30 @@ static uint64_t sifive_prci_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
static void sifive_prci_write(void *opaque, hwaddr addr,
|
||||
uint64_t val64, unsigned int size)
|
||||
{
|
||||
/* discard writes */
|
||||
SiFivePRCIState *s = opaque;
|
||||
switch (addr) {
|
||||
case SIFIVE_PRCI_HFROSCCFG:
|
||||
s->hfrosccfg = (uint32_t) val64;
|
||||
/* OSC stays ready */
|
||||
s->hfrosccfg |= SIFIVE_PRCI_HFROSCCFG_RDY;
|
||||
break;
|
||||
case SIFIVE_PRCI_HFXOSCCFG:
|
||||
s->hfxosccfg = (uint32_t) val64;
|
||||
/* OSC stays ready */
|
||||
s->hfxosccfg |= SIFIVE_PRCI_HFXOSCCFG_RDY;
|
||||
break;
|
||||
case SIFIVE_PRCI_PLLCFG:
|
||||
s->pllcfg = (uint32_t) val64;
|
||||
/* PLL stays locked */
|
||||
s->pllcfg |= SIFIVE_PRCI_PLLCFG_LOCK;
|
||||
break;
|
||||
case SIFIVE_PRCI_PLLOUTDIV:
|
||||
s->plloutdiv = (uint32_t) val64;
|
||||
break;
|
||||
default:
|
||||
hw_error("%s: bad write: addr=0x%x v=0x%x\n",
|
||||
__func__, (int)addr, (int)val64);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps sifive_prci_ops = {
|
||||
@ -61,6 +87,13 @@ static void sifive_prci_init(Object *obj)
|
||||
memory_region_init_io(&s->mmio, obj, &sifive_prci_ops, s,
|
||||
TYPE_SIFIVE_PRCI, 0x8000);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
|
||||
|
||||
s->hfrosccfg = (SIFIVE_PRCI_HFROSCCFG_RDY | SIFIVE_PRCI_HFROSCCFG_EN);
|
||||
s->hfxosccfg = (SIFIVE_PRCI_HFROSCCFG_RDY | SIFIVE_PRCI_HFROSCCFG_EN);
|
||||
s->pllcfg = (SIFIVE_PRCI_PLLCFG_REFSEL | SIFIVE_PRCI_PLLCFG_BYPASS |
|
||||
SIFIVE_PRCI_PLLCFG_LOCK);
|
||||
s->plloutdiv = SIFIVE_PRCI_PLLOUTDIV_DIV1;
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo sifive_prci_info = {
|
||||
|
@ -41,11 +41,11 @@
|
||||
#include "hw/riscv/sifive_uart.h"
|
||||
#include "hw/riscv/sifive_prci.h"
|
||||
#include "hw/riscv/sifive_u.h"
|
||||
#include "hw/riscv/boot.h"
|
||||
#include "chardev/char.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "elf.h"
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
@ -65,19 +65,6 @@ static const struct MemmapEntry {
|
||||
|
||||
#define GEM_REVISION 0x10070109
|
||||
|
||||
static target_ulong load_kernel(const char *kernel_filename)
|
||||
{
|
||||
uint64_t kernel_entry, kernel_high;
|
||||
|
||||
if (load_elf(kernel_filename, NULL, NULL, NULL,
|
||||
&kernel_entry, NULL, &kernel_high,
|
||||
0, EM_RISCV, 1, 0) < 0) {
|
||||
error_report("could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
return kernel_entry;
|
||||
}
|
||||
|
||||
static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
|
||||
uint64_t mem_size, const char *cmdline)
|
||||
{
|
||||
@ -86,7 +73,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
|
||||
uint32_t *cells;
|
||||
char *nodename;
|
||||
char ethclk_names[] = "pclk\0hclk\0tx_clk";
|
||||
uint32_t plic_phandle, ethclk_phandle;
|
||||
uint32_t plic_phandle, ethclk_phandle, phandle = 1;
|
||||
|
||||
fdt = s->fdt = create_device_tree(&s->fdt_size);
|
||||
if (!fdt) {
|
||||
@ -121,6 +108,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
|
||||
qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
|
||||
|
||||
for (cpu = s->soc.cpus.num_harts - 1; cpu >= 0; cpu--) {
|
||||
int cpu_phandle = phandle++;
|
||||
nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
|
||||
char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
|
||||
char *isa = riscv_isa_string(&s->soc.cpus.harts[cpu]);
|
||||
@ -134,8 +122,8 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
|
||||
qemu_fdt_add_subnode(fdt, intc);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "phandle", 1);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", 1);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "phandle", cpu_phandle);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", cpu_phandle);
|
||||
qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
|
||||
qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
|
||||
@ -167,6 +155,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
|
||||
g_free(cells);
|
||||
g_free(nodename);
|
||||
|
||||
plic_phandle = phandle++;
|
||||
cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4);
|
||||
for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) {
|
||||
nodename =
|
||||
@ -192,20 +181,21 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
|
||||
qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control");
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 0x35);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "phandle", 2);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", 2);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "phandle", plic_phandle);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", plic_phandle);
|
||||
plic_phandle = qemu_fdt_get_phandle(fdt, nodename);
|
||||
g_free(cells);
|
||||
g_free(nodename);
|
||||
|
||||
ethclk_phandle = phandle++;
|
||||
nodename = g_strdup_printf("/soc/ethclk");
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "fixed-clock");
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "#clock-cells", 0x0);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency",
|
||||
SIFIVE_U_GEM_CLOCK_FREQ);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "phandle", 3);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "linux,phandle", 3);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "phandle", ethclk_phandle);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "linux,phandle", ethclk_phandle);
|
||||
ethclk_phandle = qemu_fdt_get_phandle(fdt, nodename);
|
||||
g_free(nodename);
|
||||
|
||||
@ -279,8 +269,12 @@ static void riscv_sifive_u_init(MachineState *machine)
|
||||
/* create device tree */
|
||||
create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
|
||||
|
||||
if (machine->firmware) {
|
||||
riscv_load_firmware(machine->firmware, memmap[SIFIVE_U_DRAM].base);
|
||||
}
|
||||
|
||||
if (machine->kernel_filename) {
|
||||
load_kernel(machine->kernel_filename);
|
||||
riscv_load_kernel(machine->kernel_filename);
|
||||
}
|
||||
|
||||
/* reset vector */
|
||||
@ -341,6 +335,8 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp)
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
|
||||
qemu_irq plic_gpios[SIFIVE_U_PLIC_NUM_SOURCES];
|
||||
char *plic_hart_config;
|
||||
size_t plic_hart_config_len;
|
||||
int i;
|
||||
Error *err = NULL;
|
||||
NICInfo *nd = &nd_table[0];
|
||||
@ -354,9 +350,21 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp)
|
||||
memory_region_add_subregion(system_memory, memmap[SIFIVE_U_MROM].base,
|
||||
mask_rom);
|
||||
|
||||
/* create PLIC hart topology configuration string */
|
||||
plic_hart_config_len = (strlen(SIFIVE_U_PLIC_HART_CONFIG) + 1) * smp_cpus;
|
||||
plic_hart_config = g_malloc0(plic_hart_config_len);
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
if (i != 0) {
|
||||
strncat(plic_hart_config, ",", plic_hart_config_len);
|
||||
}
|
||||
strncat(plic_hart_config, SIFIVE_U_PLIC_HART_CONFIG,
|
||||
plic_hart_config_len);
|
||||
plic_hart_config_len -= (strlen(SIFIVE_U_PLIC_HART_CONFIG) + 1);
|
||||
}
|
||||
|
||||
/* MMIO */
|
||||
s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base,
|
||||
(char *)SIFIVE_U_PLIC_HART_CONFIG,
|
||||
plic_hart_config,
|
||||
SIFIVE_U_PLIC_NUM_SOURCES,
|
||||
SIFIVE_U_PLIC_NUM_PRIORITIES,
|
||||
SIFIVE_U_PLIC_PRIORITY_BASE,
|
||||
|
@ -36,12 +36,12 @@
|
||||
#include "hw/riscv/riscv_hart.h"
|
||||
#include "hw/riscv/sifive_clint.h"
|
||||
#include "hw/riscv/spike.h"
|
||||
#include "hw/riscv/boot.h"
|
||||
#include "chardev/char.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "elf.h"
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
@ -54,19 +54,6 @@ static const struct MemmapEntry {
|
||||
[SPIKE_DRAM] = { 0x80000000, 0x0 },
|
||||
};
|
||||
|
||||
static target_ulong load_kernel(const char *kernel_filename)
|
||||
{
|
||||
uint64_t kernel_entry, kernel_high;
|
||||
|
||||
if (load_elf_ram_sym(kernel_filename, NULL, NULL, NULL,
|
||||
&kernel_entry, NULL, &kernel_high, 0, EM_RISCV, 1, 0,
|
||||
NULL, true, htif_symbol_callback) < 0) {
|
||||
error_report("could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
return kernel_entry;
|
||||
}
|
||||
|
||||
static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
|
||||
uint64_t mem_size, const char *cmdline)
|
||||
{
|
||||
@ -199,7 +186,7 @@ static void spike_board_init(MachineState *machine)
|
||||
mask_rom);
|
||||
|
||||
if (machine->kernel_filename) {
|
||||
load_kernel(machine->kernel_filename);
|
||||
riscv_load_kernel(machine->kernel_filename);
|
||||
}
|
||||
|
||||
/* reset vector */
|
||||
@ -287,7 +274,7 @@ static void spike_v1_10_0_board_init(MachineState *machine)
|
||||
mask_rom);
|
||||
|
||||
if (machine->kernel_filename) {
|
||||
load_kernel(machine->kernel_filename);
|
||||
riscv_load_kernel(machine->kernel_filename);
|
||||
}
|
||||
|
||||
/* reset vector */
|
||||
@ -372,7 +359,7 @@ static void spike_v1_09_1_board_init(MachineState *machine)
|
||||
mask_rom);
|
||||
|
||||
if (machine->kernel_filename) {
|
||||
load_kernel(machine->kernel_filename);
|
||||
riscv_load_kernel(machine->kernel_filename);
|
||||
}
|
||||
|
||||
/* reset vector */
|
||||
|
@ -34,13 +34,13 @@
|
||||
#include "hw/riscv/sifive_clint.h"
|
||||
#include "hw/riscv/sifive_test.h"
|
||||
#include "hw/riscv/virt.h"
|
||||
#include "hw/riscv/boot.h"
|
||||
#include "chardev/char.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci-host/gpex.h"
|
||||
#include "elf.h"
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
@ -61,47 +61,6 @@ static const struct MemmapEntry {
|
||||
[VIRT_PCIE_ECAM] = { 0x30000000, 0x10000000 },
|
||||
};
|
||||
|
||||
static target_ulong load_kernel(const char *kernel_filename)
|
||||
{
|
||||
uint64_t kernel_entry, kernel_high;
|
||||
|
||||
if (load_elf(kernel_filename, NULL, NULL, NULL,
|
||||
&kernel_entry, NULL, &kernel_high,
|
||||
0, EM_RISCV, 1, 0) < 0) {
|
||||
error_report("could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
return kernel_entry;
|
||||
}
|
||||
|
||||
static hwaddr load_initrd(const char *filename, uint64_t mem_size,
|
||||
uint64_t kernel_entry, hwaddr *start)
|
||||
{
|
||||
int size;
|
||||
|
||||
/* We want to put the initrd far enough into RAM that when the
|
||||
* kernel is uncompressed it will not clobber the initrd. However
|
||||
* on boards without much RAM we must ensure that we still leave
|
||||
* enough room for a decent sized initrd, and on boards with large
|
||||
* amounts of RAM we must avoid the initrd being so far up in RAM
|
||||
* that it is outside lowmem and inaccessible to the kernel.
|
||||
* So for boards with less than 256MB of RAM we put the initrd
|
||||
* halfway into RAM, and for boards with 256MB of RAM or more we put
|
||||
* the initrd at 128MB.
|
||||
*/
|
||||
*start = kernel_entry + MIN(mem_size / 2, 128 * MiB);
|
||||
|
||||
size = load_ramdisk(filename, *start, mem_size - *start);
|
||||
if (size == -1) {
|
||||
size = load_image_targphys(filename, *start, mem_size - *start);
|
||||
if (size == -1) {
|
||||
error_report("could not load ramdisk '%s'", filename);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return *start + size;
|
||||
}
|
||||
|
||||
static void create_pcie_irq_map(void *fdt, char *nodename,
|
||||
uint32_t plic_phandle)
|
||||
{
|
||||
@ -191,6 +150,7 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
|
||||
|
||||
for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
|
||||
int cpu_phandle = phandle++;
|
||||
int intc_phandle;
|
||||
nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
|
||||
char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
|
||||
char *isa = riscv_isa_string(&s->soc.harts[cpu]);
|
||||
@ -203,9 +163,12 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
|
||||
qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "phandle", cpu_phandle);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "linux,phandle", cpu_phandle);
|
||||
intc_phandle = phandle++;
|
||||
qemu_fdt_add_subnode(fdt, intc);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "phandle", cpu_phandle);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", cpu_phandle);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "phandle", intc_phandle);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", intc_phandle);
|
||||
qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
|
||||
qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
|
||||
@ -214,6 +177,20 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
|
||||
g_free(nodename);
|
||||
}
|
||||
|
||||
/* Add cpu-topology node */
|
||||
qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
|
||||
qemu_fdt_add_subnode(fdt, "/cpus/cpu-map/cluster0");
|
||||
for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
|
||||
char *core_nodename = g_strdup_printf("/cpus/cpu-map/cluster0/core%d",
|
||||
cpu);
|
||||
char *cpu_nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
|
||||
uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, cpu_nodename);
|
||||
qemu_fdt_add_subnode(fdt, core_nodename);
|
||||
qemu_fdt_setprop_cell(fdt, core_nodename, "cpu", intc_phandle);
|
||||
g_free(core_nodename);
|
||||
g_free(cpu_nodename);
|
||||
}
|
||||
|
||||
cells = g_new0(uint32_t, s->soc.num_harts * 4);
|
||||
for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
|
||||
nodename =
|
||||
@ -298,7 +275,7 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
|
||||
qemu_fdt_setprop_string(fdt, nodename, "device_type", "pci");
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "linux,pci-domain", 0);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "bus-range", 0,
|
||||
memmap[VIRT_PCIE_ECAM].base /
|
||||
memmap[VIRT_PCIE_ECAM].size /
|
||||
PCIE_MMCFG_SIZE_MIN - 1);
|
||||
qemu_fdt_setprop(fdt, nodename, "dma-coherent", NULL, 0);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg", 0, memmap[VIRT_PCIE_ECAM].base,
|
||||
@ -421,12 +398,16 @@ static void riscv_virt_board_init(MachineState *machine)
|
||||
memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base,
|
||||
mask_rom);
|
||||
|
||||
if (machine->firmware) {
|
||||
riscv_load_firmware(machine->firmware, memmap[VIRT_DRAM].base);
|
||||
}
|
||||
|
||||
if (machine->kernel_filename) {
|
||||
uint64_t kernel_entry = load_kernel(machine->kernel_filename);
|
||||
uint64_t kernel_entry = riscv_load_kernel(machine->kernel_filename);
|
||||
|
||||
if (machine->initrd_filename) {
|
||||
hwaddr start;
|
||||
hwaddr end = load_initrd(machine->initrd_filename,
|
||||
hwaddr end = riscv_load_initrd(machine->initrd_filename,
|
||||
machine->ram_size, kernel_entry,
|
||||
&start);
|
||||
qemu_fdt_setprop_cell(fdt, "/chosen",
|
||||
|
29
include/hw/riscv/boot.h
Normal file
29
include/hw/riscv/boot.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* QEMU RISC-V Boot Helper
|
||||
*
|
||||
* Copyright (c) 2017 SiFive, Inc.
|
||||
* Copyright (c) 2019 Alistair Francis <alistair.francis@wdc.com>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef RISCV_BOOT_H
|
||||
#define RISCV_BOOT_H
|
||||
|
||||
target_ulong riscv_load_firmware(const char *firmware_filename,
|
||||
hwaddr firmware_load_addr);
|
||||
target_ulong riscv_load_kernel(const char *kernel_filename);
|
||||
hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size,
|
||||
uint64_t kernel_entry, hwaddr *start);
|
||||
|
||||
#endif /* RISCV_BOOT_H */
|
@ -33,6 +33,8 @@ typedef struct SiFiveESoCState {
|
||||
RISCVHartArrayState cpus;
|
||||
DeviceState *plic;
|
||||
SIFIVEGPIOState gpio;
|
||||
MemoryRegion xip_mem;
|
||||
MemoryRegion mask_rom;
|
||||
} SiFiveESoCState;
|
||||
|
||||
typedef struct SiFiveEState {
|
||||
|
@ -19,6 +19,34 @@
|
||||
#ifndef HW_SIFIVE_PRCI_H
|
||||
#define HW_SIFIVE_PRCI_H
|
||||
|
||||
enum {
|
||||
SIFIVE_PRCI_HFROSCCFG = 0x0,
|
||||
SIFIVE_PRCI_HFXOSCCFG = 0x4,
|
||||
SIFIVE_PRCI_PLLCFG = 0x8,
|
||||
SIFIVE_PRCI_PLLOUTDIV = 0xC
|
||||
};
|
||||
|
||||
enum {
|
||||
SIFIVE_PRCI_HFROSCCFG_RDY = (1 << 31),
|
||||
SIFIVE_PRCI_HFROSCCFG_EN = (1 << 30)
|
||||
};
|
||||
|
||||
enum {
|
||||
SIFIVE_PRCI_HFXOSCCFG_RDY = (1 << 31),
|
||||
SIFIVE_PRCI_HFXOSCCFG_EN = (1 << 30)
|
||||
};
|
||||
|
||||
enum {
|
||||
SIFIVE_PRCI_PLLCFG_PLLSEL = (1 << 16),
|
||||
SIFIVE_PRCI_PLLCFG_REFSEL = (1 << 17),
|
||||
SIFIVE_PRCI_PLLCFG_BYPASS = (1 << 18),
|
||||
SIFIVE_PRCI_PLLCFG_LOCK = (1 << 31)
|
||||
};
|
||||
|
||||
enum {
|
||||
SIFIVE_PRCI_PLLOUTDIV_DIV1 = (1 << 8)
|
||||
};
|
||||
|
||||
#define TYPE_SIFIVE_PRCI "riscv.sifive.prci"
|
||||
|
||||
#define SIFIVE_PRCI(obj) \
|
||||
@ -30,6 +58,10 @@ typedef struct SiFivePRCIState {
|
||||
|
||||
/*< public >*/
|
||||
MemoryRegion mmio;
|
||||
uint32_t hfrosccfg;
|
||||
uint32_t hfxosccfg;
|
||||
uint32_t pllcfg;
|
||||
uint32_t plloutdiv;
|
||||
} SiFivePRCIState;
|
||||
|
||||
DeviceState *sifive_prci_create(hwaddr addr);
|
||||
|
@ -72,7 +72,11 @@
|
||||
#define TARGET_NR_pipe2 59
|
||||
#define TARGET_NR_quotactl 60
|
||||
#define TARGET_NR_getdents64 61
|
||||
#ifdef TARGET_RISCV32
|
||||
#define TARGET_NR__llseek 62
|
||||
#else
|
||||
#define TARGET_NR_lseek 62
|
||||
#endif
|
||||
#define TARGET_NR_read 63
|
||||
#define TARGET_NR_write 64
|
||||
#define TARGET_NR_readv 65
|
||||
@ -286,7 +290,16 @@
|
||||
#define TARGET_NR_membarrier 283
|
||||
#define TARGET_NR_mlock2 284
|
||||
#define TARGET_NR_copy_file_range 285
|
||||
#define TARGET_NR_preadv2 286
|
||||
#define TARGET_NR_pwritev2 287
|
||||
#define TARGET_NR_pkey_mprotect 288
|
||||
#define TARGET_NR_pkey_alloc 289
|
||||
#define TARGET_NR_pkey_free 290
|
||||
#define TARGET_NR_statx 291
|
||||
#define TARGET_NR_io_pgetevents 292
|
||||
#define TARGET_NR_rseq 293
|
||||
#define TARGET_NR_kexec_file_load 294
|
||||
|
||||
#define TARGET_NR_syscalls (TARGET_NR_copy_file_range + 1)
|
||||
#define TARGET_NR_syscalls (TARGET_NR_kexec_file_load + 1)
|
||||
|
||||
#endif
|
||||
|
@ -144,6 +144,14 @@ The ``acl_show'', ``acl_reset'', ``acl_policy'', ``acl_add'', and
|
||||
``acl_remove'' commands are deprecated with no replacement. Authorization
|
||||
for VNC should be performed using the pluggable QAuthZ objects.
|
||||
|
||||
@section Guest Emulator ISAs
|
||||
|
||||
@subsection RISC-V ISA privledge specification version 1.09.1 (since 4.1)
|
||||
|
||||
The RISC-V ISA privledge specification version 1.09.1 has been deprecated.
|
||||
QEMU supports both the newer version 1.10.0 and the ratified version 1.11.0, these
|
||||
should be used instead of the 1.09.1 version.
|
||||
|
||||
@section System emulator CPUS
|
||||
|
||||
@subsection RISC-V ISA CPUs (since 4.1)
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
@ -88,9 +89,8 @@ static void set_misa(CPURISCVState *env, target_ulong misa)
|
||||
env->misa_mask = env->misa = misa;
|
||||
}
|
||||
|
||||
static void set_versions(CPURISCVState *env, int user_ver, int priv_ver)
|
||||
static void set_priv_version(CPURISCVState *env, int priv_ver)
|
||||
{
|
||||
env->user_ver = user_ver;
|
||||
env->priv_ver = priv_ver;
|
||||
}
|
||||
|
||||
@ -110,7 +110,7 @@ static void riscv_any_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
|
||||
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
||||
set_priv_version(env, PRIV_VERSION_1_11_0);
|
||||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
}
|
||||
|
||||
@ -119,14 +119,15 @@ static void riscv_any_cpu_init(Object *obj)
|
||||
static void riscv_base32_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
||||
/* We set this in the realise function */
|
||||
set_misa(env, 0);
|
||||
}
|
||||
|
||||
static void rv32gcsu_priv1_09_1_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
||||
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
|
||||
set_priv_version(env, PRIV_VERSION_1_09_1);
|
||||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
set_feature(env, RISCV_FEATURE_MMU);
|
||||
set_feature(env, RISCV_FEATURE_PMP);
|
||||
@ -136,7 +137,7 @@ static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
||||
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
||||
set_priv_version(env, PRIV_VERSION_1_10_0);
|
||||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
set_feature(env, RISCV_FEATURE_MMU);
|
||||
set_feature(env, RISCV_FEATURE_PMP);
|
||||
@ -146,7 +147,7 @@ static void rv32imacu_nommu_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU);
|
||||
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
||||
set_priv_version(env, PRIV_VERSION_1_10_0);
|
||||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
set_feature(env, RISCV_FEATURE_PMP);
|
||||
}
|
||||
@ -156,14 +157,15 @@ static void rv32imacu_nommu_cpu_init(Object *obj)
|
||||
static void riscv_base64_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
||||
/* We set this in the realise function */
|
||||
set_misa(env, 0);
|
||||
}
|
||||
|
||||
static void rv64gcsu_priv1_09_1_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
||||
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
|
||||
set_priv_version(env, PRIV_VERSION_1_09_1);
|
||||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
set_feature(env, RISCV_FEATURE_MMU);
|
||||
set_feature(env, RISCV_FEATURE_PMP);
|
||||
@ -173,7 +175,7 @@ static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
||||
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
||||
set_priv_version(env, PRIV_VERSION_1_10_0);
|
||||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
set_feature(env, RISCV_FEATURE_MMU);
|
||||
set_feature(env, RISCV_FEATURE_PMP);
|
||||
@ -183,7 +185,7 @@ static void rv64imacu_nommu_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU);
|
||||
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
||||
set_priv_version(env, PRIV_VERSION_1_10_0);
|
||||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
set_feature(env, RISCV_FEATURE_PMP);
|
||||
}
|
||||
@ -295,6 +297,7 @@ static void riscv_cpu_reset(CPUState *cs)
|
||||
env->pc = env->resetvec;
|
||||
#endif
|
||||
cs->exception_index = EXCP_NONE;
|
||||
env->load_res = -1;
|
||||
set_default_nan_mode(1, &env->fp_status);
|
||||
}
|
||||
|
||||
@ -313,8 +316,8 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
||||
RISCVCPU *cpu = RISCV_CPU(dev);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
|
||||
int priv_version = PRIV_VERSION_1_10_0;
|
||||
int user_version = USER_VERSION_2_02_0;
|
||||
int priv_version = PRIV_VERSION_1_11_0;
|
||||
target_ulong target_misa = 0;
|
||||
Error *local_err = NULL;
|
||||
|
||||
cpu_exec_realizefn(cs, &local_err);
|
||||
@ -324,7 +327,9 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
if (cpu->cfg.priv_spec) {
|
||||
if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) {
|
||||
if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) {
|
||||
priv_version = PRIV_VERSION_1_11_0;
|
||||
} else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) {
|
||||
priv_version = PRIV_VERSION_1_10_0;
|
||||
} else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.9.1")) {
|
||||
priv_version = PRIV_VERSION_1_09_1;
|
||||
@ -336,18 +341,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu->cfg.user_spec) {
|
||||
if (!g_strcmp0(cpu->cfg.user_spec, "v2.02.0")) {
|
||||
user_version = USER_VERSION_2_02_0;
|
||||
} else {
|
||||
error_setg(errp,
|
||||
"Unsupported user spec version '%s'",
|
||||
cpu->cfg.user_spec);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
set_versions(env, user_version, priv_version);
|
||||
set_priv_version(env, priv_version);
|
||||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
|
||||
if (cpu->cfg.mmu) {
|
||||
@ -358,6 +352,64 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
||||
set_feature(env, RISCV_FEATURE_PMP);
|
||||
}
|
||||
|
||||
/* If misa isn't set (rv32 and rv64 machines) set it here */
|
||||
if (!env->misa) {
|
||||
/* Do some ISA extension error checking */
|
||||
if (cpu->cfg.ext_i && cpu->cfg.ext_e) {
|
||||
error_setg(errp,
|
||||
"I and E extensions are incompatible");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cpu->cfg.ext_i && !cpu->cfg.ext_e) {
|
||||
error_setg(errp,
|
||||
"Either I or E extension must be set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_g && !(cpu->cfg.ext_i & cpu->cfg.ext_m &
|
||||
cpu->cfg.ext_a & cpu->cfg.ext_f &
|
||||
cpu->cfg.ext_d)) {
|
||||
warn_report("Setting G will also set IMAFD");
|
||||
cpu->cfg.ext_i = true;
|
||||
cpu->cfg.ext_m = true;
|
||||
cpu->cfg.ext_a = true;
|
||||
cpu->cfg.ext_f = true;
|
||||
cpu->cfg.ext_d = true;
|
||||
}
|
||||
|
||||
/* Set the ISA extensions, checks should have happened above */
|
||||
if (cpu->cfg.ext_i) {
|
||||
target_misa |= RVI;
|
||||
}
|
||||
if (cpu->cfg.ext_e) {
|
||||
target_misa |= RVE;
|
||||
}
|
||||
if (cpu->cfg.ext_m) {
|
||||
target_misa |= RVM;
|
||||
}
|
||||
if (cpu->cfg.ext_a) {
|
||||
target_misa |= RVA;
|
||||
}
|
||||
if (cpu->cfg.ext_f) {
|
||||
target_misa |= RVF;
|
||||
}
|
||||
if (cpu->cfg.ext_d) {
|
||||
target_misa |= RVD;
|
||||
}
|
||||
if (cpu->cfg.ext_c) {
|
||||
target_misa |= RVC;
|
||||
}
|
||||
if (cpu->cfg.ext_s) {
|
||||
target_misa |= RVS;
|
||||
}
|
||||
if (cpu->cfg.ext_u) {
|
||||
target_misa |= RVU;
|
||||
}
|
||||
|
||||
set_misa(env, RVXLEN | target_misa);
|
||||
}
|
||||
|
||||
riscv_cpu_register_gdb_regs_for_features(cs);
|
||||
|
||||
qemu_init_vcpu(cs);
|
||||
@ -379,8 +431,20 @@ static const VMStateDescription vmstate_riscv_cpu = {
|
||||
};
|
||||
|
||||
static Property riscv_cpu_properties[] = {
|
||||
DEFINE_PROP_BOOL("i", RISCVCPU, cfg.ext_i, true),
|
||||
DEFINE_PROP_BOOL("e", RISCVCPU, cfg.ext_e, false),
|
||||
DEFINE_PROP_BOOL("g", RISCVCPU, cfg.ext_g, true),
|
||||
DEFINE_PROP_BOOL("m", RISCVCPU, cfg.ext_m, true),
|
||||
DEFINE_PROP_BOOL("a", RISCVCPU, cfg.ext_a, true),
|
||||
DEFINE_PROP_BOOL("f", RISCVCPU, cfg.ext_f, true),
|
||||
DEFINE_PROP_BOOL("d", RISCVCPU, cfg.ext_d, true),
|
||||
DEFINE_PROP_BOOL("c", RISCVCPU, cfg.ext_c, true),
|
||||
DEFINE_PROP_BOOL("s", RISCVCPU, cfg.ext_s, true),
|
||||
DEFINE_PROP_BOOL("u", RISCVCPU, cfg.ext_u, true),
|
||||
DEFINE_PROP_BOOL("Counters", RISCVCPU, cfg.ext_counters, true),
|
||||
DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true),
|
||||
DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true),
|
||||
DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec),
|
||||
DEFINE_PROP_STRING("user_spec", RISCVCPU, cfg.user_spec),
|
||||
DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
|
||||
DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
@ -416,6 +480,7 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data)
|
||||
cc->gdb_stop_before_watchpoint = true;
|
||||
cc->disas_set_info = riscv_cpu_disas_set_info;
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
cc->do_unassigned_access = riscv_cpu_unassigned_access;
|
||||
cc->do_unaligned_access = riscv_cpu_do_unaligned_access;
|
||||
cc->get_phys_page_debug = riscv_cpu_get_phys_page_debug;
|
||||
#endif
|
||||
@ -492,18 +557,20 @@ static const TypeInfo riscv_cpu_type_infos[] = {
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_ANY, riscv_any_cpu_init),
|
||||
#if defined(TARGET_RISCV32)
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_BASE32, riscv_base32_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_RV32IMACU_NOMMU, rv32imacu_nommu_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rv32imacu_nommu_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init)
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init),
|
||||
/* Depreacted */
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_RV32IMACU_NOMMU, rv32imacu_nommu_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init)
|
||||
#elif defined(TARGET_RISCV64)
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_BASE64, riscv_base64_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_RV64IMACU_NOMMU, rv64imacu_nommu_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51, rv64imacu_nommu_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rv64gcsu_priv1_10_0_cpu_init)
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rv64gcsu_priv1_10_0_cpu_init),
|
||||
/* Deprecated */
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_RV64IMACU_NOMMU, rv64imacu_nommu_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init)
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -35,16 +35,17 @@
|
||||
#define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any")
|
||||
#define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32")
|
||||
#define TYPE_RISCV_CPU_BASE64 RISCV_CPU_TYPE_NAME("rv64")
|
||||
#define TYPE_RISCV_CPU_RV32GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.9.1")
|
||||
#define TYPE_RISCV_CPU_RV32GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.10.0")
|
||||
#define TYPE_RISCV_CPU_RV32IMACU_NOMMU RISCV_CPU_TYPE_NAME("rv32imacu-nommu")
|
||||
#define TYPE_RISCV_CPU_RV64GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.9.1")
|
||||
#define TYPE_RISCV_CPU_RV64GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.10.0")
|
||||
#define TYPE_RISCV_CPU_RV64IMACU_NOMMU RISCV_CPU_TYPE_NAME("rv64imacu-nommu")
|
||||
#define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31")
|
||||
#define TYPE_RISCV_CPU_SIFIVE_E51 RISCV_CPU_TYPE_NAME("sifive-e51")
|
||||
#define TYPE_RISCV_CPU_SIFIVE_U34 RISCV_CPU_TYPE_NAME("sifive-u34")
|
||||
#define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54")
|
||||
/* Deprecated */
|
||||
#define TYPE_RISCV_CPU_RV32IMACU_NOMMU RISCV_CPU_TYPE_NAME("rv32imacu-nommu")
|
||||
#define TYPE_RISCV_CPU_RV32GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.9.1")
|
||||
#define TYPE_RISCV_CPU_RV32GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.10.0")
|
||||
#define TYPE_RISCV_CPU_RV64IMACU_NOMMU RISCV_CPU_TYPE_NAME("rv64imacu-nommu")
|
||||
#define TYPE_RISCV_CPU_RV64GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.9.1")
|
||||
#define TYPE_RISCV_CPU_RV64GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.10.0")
|
||||
|
||||
#define RV32 ((target_ulong)1 << (TARGET_LONG_BITS - 2))
|
||||
#define RV64 ((target_ulong)2 << (TARGET_LONG_BITS - 2))
|
||||
@ -77,10 +78,11 @@ enum {
|
||||
RISCV_FEATURE_MISA
|
||||
};
|
||||
|
||||
#define USER_VERSION_2_02_0 0x00020200
|
||||
#define PRIV_VERSION_1_09_1 0x00010901
|
||||
#define PRIV_VERSION_1_10_0 0x00011000
|
||||
#define PRIV_VERSION_1_11_0 0x00011100
|
||||
|
||||
#define TRANSLATE_PMP_FAIL 2
|
||||
#define TRANSLATE_FAIL 1
|
||||
#define TRANSLATE_SUCCESS 0
|
||||
#define MMU_USER_IDX 3
|
||||
@ -102,7 +104,6 @@ struct CPURISCVState {
|
||||
|
||||
target_ulong badaddr;
|
||||
|
||||
target_ulong user_ver;
|
||||
target_ulong priv_ver;
|
||||
target_ulong misa;
|
||||
target_ulong misa_mask;
|
||||
@ -211,6 +212,20 @@ typedef struct RISCVCPU {
|
||||
|
||||
/* Configuration Settings */
|
||||
struct {
|
||||
bool ext_i;
|
||||
bool ext_e;
|
||||
bool ext_g;
|
||||
bool ext_m;
|
||||
bool ext_a;
|
||||
bool ext_f;
|
||||
bool ext_d;
|
||||
bool ext_c;
|
||||
bool ext_s;
|
||||
bool ext_u;
|
||||
bool ext_counters;
|
||||
bool ext_ifencei;
|
||||
bool ext_icsr;
|
||||
|
||||
char *priv_spec;
|
||||
char *user_spec;
|
||||
bool mmu;
|
||||
@ -248,6 +263,8 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
|
||||
bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
||||
MMUAccessType access_type, int mmu_idx,
|
||||
bool probe, uintptr_t retaddr);
|
||||
void riscv_cpu_unassigned_access(CPUState *cpu, hwaddr addr, bool is_write,
|
||||
bool is_exec, int unused, unsigned size);
|
||||
char *riscv_isa_string(RISCVCPU *cpu);
|
||||
void riscv_cpu_list(void);
|
||||
|
||||
|
@ -136,6 +136,7 @@
|
||||
#define CSR_MCOUNTEREN 0x306
|
||||
|
||||
/* Legacy Counter Setup (priv v1.9.1) */
|
||||
/* Update to #define CSR_MCOUNTINHIBIT 0x320 for 1.11.0 */
|
||||
#define CSR_MUCOUNTEREN 0x320
|
||||
#define CSR_MSCOUNTEREN 0x321
|
||||
#define CSR_MHCOUNTEREN 0x322
|
||||
|
@ -132,6 +132,16 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
|
||||
}
|
||||
/* tlb_flush is unnecessary as mode is contained in mmu_idx */
|
||||
env->priv = newpriv;
|
||||
|
||||
/*
|
||||
* Clear the load reservation - otherwise a reservation placed in one
|
||||
* context/process can be used by another, resulting in an SC succeeding
|
||||
* incorrectly. Version 2.2 of the ISA specification explicitly requires
|
||||
* this behaviour, while later revisions say that the kernel "should" use
|
||||
* an SC instruction to force the yielding of a load reservation on a
|
||||
* preemptive context switch. As a result, do both.
|
||||
*/
|
||||
env->load_res = -1;
|
||||
}
|
||||
|
||||
/* get_physical_address - get the physical address for this virtual address
|
||||
@ -230,6 +240,12 @@ restart:
|
||||
|
||||
/* check that physical address of PTE is legal */
|
||||
target_ulong pte_addr = base + idx * ptesize;
|
||||
|
||||
if (riscv_feature(env, RISCV_FEATURE_PMP) &&
|
||||
!pmp_hart_has_privs(env, pte_addr, sizeof(target_ulong),
|
||||
1 << MMU_DATA_LOAD, PRV_S)) {
|
||||
return TRANSLATE_PMP_FAIL;
|
||||
}
|
||||
#if defined(TARGET_RISCV32)
|
||||
target_ulong pte = ldl_phys(cs->as, pte_addr);
|
||||
#elif defined(TARGET_RISCV64)
|
||||
@ -337,12 +353,13 @@ restart:
|
||||
}
|
||||
|
||||
static void raise_mmu_exception(CPURISCVState *env, target_ulong address,
|
||||
MMUAccessType access_type)
|
||||
MMUAccessType access_type, bool pmp_violation)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
int page_fault_exceptions =
|
||||
(env->priv_ver >= PRIV_VERSION_1_10_0) &&
|
||||
get_field(env->satp, SATP_MODE) != VM_1_10_MBARE;
|
||||
get_field(env->satp, SATP_MODE) != VM_1_10_MBARE &&
|
||||
!pmp_violation;
|
||||
switch (access_type) {
|
||||
case MMU_INST_FETCH:
|
||||
cs->exception_index = page_fault_exceptions ?
|
||||
@ -375,6 +392,22 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
|
||||
return phys_addr;
|
||||
}
|
||||
|
||||
void riscv_cpu_unassigned_access(CPUState *cs, hwaddr addr, bool is_write,
|
||||
bool is_exec, int unused, unsigned size)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
|
||||
if (is_write) {
|
||||
cs->exception_index = RISCV_EXCP_STORE_AMO_ACCESS_FAULT;
|
||||
} else {
|
||||
cs->exception_index = RISCV_EXCP_LOAD_ACCESS_FAULT;
|
||||
}
|
||||
|
||||
env->badaddr = addr;
|
||||
riscv_raise_exception(&cpu->env, cs->exception_index, GETPC());
|
||||
}
|
||||
|
||||
void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
|
||||
MMUAccessType access_type, int mmu_idx,
|
||||
uintptr_t retaddr)
|
||||
@ -408,20 +441,32 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
||||
CPURISCVState *env = &cpu->env;
|
||||
hwaddr pa = 0;
|
||||
int prot;
|
||||
bool pmp_violation = false;
|
||||
int ret = TRANSLATE_FAIL;
|
||||
int mode = mmu_idx;
|
||||
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s ad %" VADDR_PRIx " rw %d mmu_idx %d\n",
|
||||
__func__, address, access_type, mmu_idx);
|
||||
|
||||
ret = get_physical_address(env, &pa, &prot, address, access_type, mmu_idx);
|
||||
|
||||
if (mode == PRV_M && access_type != MMU_INST_FETCH) {
|
||||
if (get_field(env->mstatus, MSTATUS_MPRV)) {
|
||||
mode = get_field(env->mstatus, MSTATUS_MPP);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_log_mask(CPU_LOG_MMU,
|
||||
"%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx
|
||||
" prot %d\n", __func__, address, ret, pa, prot);
|
||||
|
||||
if (riscv_feature(env, RISCV_FEATURE_PMP) &&
|
||||
!pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << access_type)) {
|
||||
ret = TRANSLATE_FAIL;
|
||||
(ret == TRANSLATE_SUCCESS) &&
|
||||
!pmp_hart_has_privs(env, pa, size, 1 << access_type, mode)) {
|
||||
ret = TRANSLATE_PMP_FAIL;
|
||||
}
|
||||
if (ret == TRANSLATE_PMP_FAIL) {
|
||||
pmp_violation = true;
|
||||
}
|
||||
if (ret == TRANSLATE_SUCCESS) {
|
||||
tlb_set_page(cs, address & TARGET_PAGE_MASK, pa & TARGET_PAGE_MASK,
|
||||
@ -430,7 +475,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
||||
} else if (probe) {
|
||||
return false;
|
||||
} else {
|
||||
raise_mmu_exception(env, address, access_type);
|
||||
raise_mmu_exception(env, address, access_type, pmp_violation);
|
||||
riscv_raise_exception(env, cs->exception_index, retaddr);
|
||||
}
|
||||
#else
|
||||
|
@ -56,8 +56,24 @@ static int fs(CPURISCVState *env, int csrno)
|
||||
static int ctr(CPURISCVState *env, int csrno)
|
||||
{
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
CPUState *cs = env_cpu(env);
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
uint32_t ctr_en = ~0u;
|
||||
|
||||
if (!cpu->cfg.ext_counters) {
|
||||
/* The Counters extensions is not enabled */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The counters are always enabled at run time on newer priv specs, as the
|
||||
* CSR has changed from controlling that the counters can be read to
|
||||
* controlling that the counters increment.
|
||||
*/
|
||||
if (env->priv_ver > PRIV_VERSION_1_09_1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (env->priv < PRV_M) {
|
||||
ctr_en &= env->mcounteren;
|
||||
}
|
||||
@ -461,18 +477,22 @@ static int write_mcounteren(CPURISCVState *env, int csrno, target_ulong val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This regiser is replaced with CSR_MCOUNTINHIBIT in 1.11.0 */
|
||||
static int read_mscounteren(CPURISCVState *env, int csrno, target_ulong *val)
|
||||
{
|
||||
if (env->priv_ver > PRIV_VERSION_1_09_1) {
|
||||
if (env->priv_ver > PRIV_VERSION_1_09_1
|
||||
&& env->priv_ver < PRIV_VERSION_1_11_0) {
|
||||
return -1;
|
||||
}
|
||||
*val = env->mcounteren;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This regiser is replaced with CSR_MCOUNTINHIBIT in 1.11.0 */
|
||||
static int write_mscounteren(CPURISCVState *env, int csrno, target_ulong val)
|
||||
{
|
||||
if (env->priv_ver > PRIV_VERSION_1_09_1) {
|
||||
if (env->priv_ver > PRIV_VERSION_1_09_1
|
||||
&& env->priv_ver < PRIV_VERSION_1_11_0) {
|
||||
return -1;
|
||||
}
|
||||
env->mcounteren = val;
|
||||
@ -773,6 +793,7 @@ int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value,
|
||||
{
|
||||
int ret;
|
||||
target_ulong old_value;
|
||||
RISCVCPU *cpu = env_archcpu(env);
|
||||
|
||||
/* check privileges and return -1 if check fails */
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
@ -783,6 +804,11 @@ int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value,
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ensure the CSR extension is enabled. */
|
||||
if (!cpu->cfg.ext_icsr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check predicate */
|
||||
if (!csr_ops[csrno].predicate || csr_ops[csrno].predicate(env, csrno) < 0) {
|
||||
return -1;
|
||||
|
@ -90,7 +90,7 @@ static bool trans_wfi(DisasContext *ctx, arg_wfi *a)
|
||||
static bool trans_sfence_vma(DisasContext *ctx, arg_sfence_vma *a)
|
||||
{
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (ctx->priv_ver == PRIV_VERSION_1_10_0) {
|
||||
if (ctx->priv_ver >= PRIV_VERSION_1_10_0) {
|
||||
gen_helper_tlb_flush(cpu_env);
|
||||
return true;
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ static inline bool gen_sc(DisasContext *ctx, arg_atomic *a, TCGMemOp mop)
|
||||
|
||||
gen_set_label(l1);
|
||||
/*
|
||||
* Address comparion failure. However, we still need to
|
||||
* Address comparison failure. However, we still need to
|
||||
* provide the memory barrier implied by AQ/RL.
|
||||
*/
|
||||
tcg_gen_mb(TCG_MO_ALL + a->aq * TCG_BAR_LDAQ + a->rl * TCG_BAR_STRL);
|
||||
@ -69,6 +69,12 @@ static inline bool gen_sc(DisasContext *ctx, arg_atomic *a, TCGMemOp mop)
|
||||
gen_set_gpr(a->rd, dat);
|
||||
|
||||
gen_set_label(l2);
|
||||
/*
|
||||
* Clear the load reservation, since an SC must fail if there is
|
||||
* an SC to any address, in between an LR and SC pair.
|
||||
*/
|
||||
tcg_gen_movi_tl(load_res, -1);
|
||||
|
||||
tcg_temp_free(dat);
|
||||
tcg_temp_free(src1);
|
||||
tcg_temp_free(src2);
|
||||
|
@ -484,6 +484,10 @@ static bool trans_fence(DisasContext *ctx, arg_fence *a)
|
||||
|
||||
static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a)
|
||||
{
|
||||
if (!ctx->ext_ifencei) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* FENCE_I is a no-op in QEMU,
|
||||
* however we need to end the translation block
|
||||
|
@ -228,7 +228,7 @@ static int pmp_is_in_range(CPURISCVState *env, int pmp_index, target_ulong addr)
|
||||
* Check if the address has required RWX privs to complete desired operation
|
||||
*/
|
||||
bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
|
||||
target_ulong size, pmp_priv_t privs)
|
||||
target_ulong size, pmp_priv_t privs, target_ulong mode)
|
||||
{
|
||||
int i = 0;
|
||||
int ret = -1;
|
||||
@ -245,7 +245,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
|
||||
from low to high */
|
||||
for (i = 0; i < MAX_RISCV_PMPS; i++) {
|
||||
s = pmp_is_in_range(env, i, addr);
|
||||
e = pmp_is_in_range(env, i, addr + size);
|
||||
e = pmp_is_in_range(env, i, addr + size - 1);
|
||||
|
||||
/* partially inside */
|
||||
if ((s + e) == 1) {
|
||||
@ -258,13 +258,14 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
|
||||
/* fully inside */
|
||||
const uint8_t a_field =
|
||||
pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg);
|
||||
if ((s + e) == 2) {
|
||||
if (PMP_AMATCH_OFF == a_field) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the PMP entry is not off and the address is in range, do the priv
|
||||
* check
|
||||
*/
|
||||
if (((s + e) == 2) && (PMP_AMATCH_OFF != a_field)) {
|
||||
allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC;
|
||||
if ((env->priv != PRV_M) || pmp_is_locked(env, i)) {
|
||||
if ((mode != PRV_M) || pmp_is_locked(env, i)) {
|
||||
allowed_privs &= env->pmp_state.pmp[i].cfg_reg;
|
||||
}
|
||||
|
||||
@ -280,7 +281,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
|
||||
|
||||
/* No rule matched */
|
||||
if (ret == -1) {
|
||||
if (env->priv == PRV_M) {
|
||||
if (mode == PRV_M) {
|
||||
ret = 1; /* Privileged spec v1.10 states if no PMP entry matches an
|
||||
* M-Mode access, the access succeeds */
|
||||
} else {
|
||||
|
@ -59,6 +59,6 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
|
||||
target_ulong val);
|
||||
target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index);
|
||||
bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
|
||||
target_ulong size, pmp_priv_t priv);
|
||||
target_ulong size, pmp_priv_t priv, target_ulong mode);
|
||||
|
||||
#endif
|
||||
|
@ -54,6 +54,7 @@ typedef struct DisasContext {
|
||||
to any system register, which includes CSR_FRM, so we do not have
|
||||
to reset this known value. */
|
||||
int frm;
|
||||
bool ext_ifencei;
|
||||
} DisasContext;
|
||||
|
||||
#ifdef TARGET_RISCV64
|
||||
@ -752,6 +753,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
|
||||
{
|
||||
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
||||
CPURISCVState *env = cs->env_ptr;
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
|
||||
ctx->pc_succ_insn = ctx->base.pc_first;
|
||||
ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK;
|
||||
@ -759,6 +761,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
|
||||
ctx->priv_ver = env->priv_ver;
|
||||
ctx->misa = env->misa;
|
||||
ctx->frm = -1; /* unknown rounding mode */
|
||||
ctx->ext_ifencei = cpu->cfg.ext_ifencei;
|
||||
}
|
||||
|
||||
static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu)
|
||||
|
Loading…
Reference in New Issue
Block a user