aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2019-09-19 11:14:28 +0100
committerPeter Maydell <peter.maydell@linaro.org>2019-09-19 11:14:28 +0100
commit7cc0cdcd6a771010ca4a4857c4e4df966bb4e6c2 (patch)
tree827153abeccb777659902d1546fe3c22e52d7d92
parentf39641125996550b76f9ebbfe03430aecc6473c4 (diff)
parentb3e86929189c526d22ef49e18f2f5066535f6deb (diff)
Merge remote-tracking branch 'remotes/palmer/tags/riscv-for-master-4.2-sf1-v3' into staging
RISC-V Patches for the 4.2 Soft Freeze, Part 1, v3 This contains quite a few patches that I'd like to target for 4.2. They're mostly emulation fixes for the sifive_u board, which now much more closely matches the hardware and can therefor run the same fireware as what gets loaded onto the board. Additional user-visible improvements include: * support for loading initrd files from the command line into Linux, via /chosen/linux,initrd-{start,end} device tree nodes. * The conversion of LOG_TRACE to trace events. * The addition of clock DT nodes for our uart and ethernet. This also includes some preliminary work for the H extension patches, but does not include the H extension patches as I haven't had time to review them yet. This passes my OE boot test on 32-bit and 64-bit virt machines, as well as a 64-bit upstream Linux boot on the sifive_u machine. It has been fixed to actually pass "make check" this time. Changes since v2 (never made it to the list): * Sets the sifive_u machine default core count to 2 instead of 5. Changes since v1 <20190910190513.21160-1-palmer@sifive.com>: * Sets the sifive_u machine default core count to 5 instead of 1, as it's impossible to have a single core sifive_u machine. # gpg: Signature made Tue 17 Sep 2019 16:43:30 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.2-sf1-v3: (48 commits) gdbstub: riscv: fix the fflags registers target/riscv: Use TB_FLAGS_MSTATUS_FS for floating point target/riscv: Fix mstatus dirty mask target/riscv: Use both register name and ABI name riscv: sifive_u: Update model and compatible strings in device tree riscv: sifive_u: Remove handcrafted clock nodes for UART and ethernet riscv: sifive_u: Fix broken GEM support riscv: sifive_u: Instantiate OTP memory with a serial number riscv: sifive: Implement a model for SiFive FU540 OTP riscv: roms: Update default bios for sifive_u machine riscv: sifive_u: Change UART node name in device tree riscv: sifive_u: Update UART base addresses and IRQs riscv: sifive_u: Reference PRCI clocks in UART and ethernet nodes riscv: sifive_u: Add PRCI block to the SoC riscv: sifive_u: Generate hfclk and rtcclk nodes riscv: sifive: Implement PRCI model for FU540 riscv: sifive_u: Update PLIC hart topology configuration string riscv: sifive_u: Update hart configuration to reflect the real FU540 SoC riscv: sifive_u: Set the minimum number of cpus to 2 riscv: hart: Add a "hartid-base" property to RISC-V hart array ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--hmp-commands-info.hx2
-rw-r--r--hw/riscv/Kconfig2
-rw-r--r--hw/riscv/Makefile.objs4
-rw-r--r--hw/riscv/boot.c26
-rw-r--r--hw/riscv/riscv_hart.c34
-rw-r--r--hw/riscv/sifive_e.c27
-rw-r--r--hw/riscv/sifive_e_prci.c124
-rw-r--r--hw/riscv/sifive_plic.c12
-rw-r--r--hw/riscv/sifive_prci.c125
-rw-r--r--hw/riscv/sifive_test.c11
-rw-r--r--hw/riscv/sifive_u.c253
-rw-r--r--hw/riscv/sifive_u_otp.c191
-rw-r--r--hw/riscv/sifive_u_prci.c169
-rw-r--r--hw/riscv/sifive_uart.c10
-rw-r--r--hw/riscv/spike.c1
-rw-r--r--hw/riscv/virt.c40
-rw-r--r--include/hw/riscv/boot.h1
-rw-r--r--include/hw/riscv/riscv_hart.h1
-rw-r--r--include/hw/riscv/sifive_cpu.h31
-rw-r--r--include/hw/riscv/sifive_e.h7
-rw-r--r--include/hw/riscv/sifive_e_prci.h71
-rw-r--r--include/hw/riscv/sifive_plic.h3
-rw-r--r--include/hw/riscv/sifive_prci.h71
-rw-r--r--include/hw/riscv/sifive_test.h3
-rw-r--r--include/hw/riscv/sifive_u.h31
-rw-r--r--include/hw/riscv/sifive_u_otp.h80
-rw-r--r--include/hw/riscv/sifive_u_prci.h91
-rw-r--r--[-rwxr-xr-x]pc-bios/opensbi-riscv32-virt-fw_jump.binbin36888 -> 36888 bytes
-rw-r--r--[-rwxr-xr-x]pc-bios/opensbi-riscv64-sifive_u-fw_jump.binbin40968 -> 45064 bytes
-rw-r--r--[-rwxr-xr-x]pc-bios/opensbi-riscv64-virt-fw_jump.binbin40968 -> 40968 bytes
-rw-r--r--roms/Makefile4
-rw-r--r--target/riscv/Makefile.objs7
-rw-r--r--target/riscv/cpu.c19
-rw-r--r--target/riscv/cpu.h6
-rw-r--r--target/riscv/cpu_bits.h35
-rw-r--r--target/riscv/cpu_helper.c20
-rw-r--r--target/riscv/csr.c22
-rw-r--r--target/riscv/gdbstub.c6
-rw-r--r--target/riscv/monitor.c229
-rw-r--r--target/riscv/pmp.c35
-rw-r--r--target/riscv/trace-events6
41 files changed, 1366 insertions, 444 deletions
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index c59444c461..257ee7d7a3 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -249,7 +249,7 @@ STEXI
Show virtual to physical memory mappings.
ETEXI
-#if defined(TARGET_I386)
+#if defined(TARGET_I386) || defined(TARGET_RISCV)
{
.name = "mem",
.args_type = "",
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 8674211085..fb19b2df3a 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -12,12 +12,14 @@ config SIFIVE_E
bool
select HART
select SIFIVE
+ select UNIMP
config SIFIVE_U
bool
select CADENCE
select HART
select SIFIVE
+ select UNIMP
config SPIKE
bool
diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs
index eb9d4f9ffc..fc3c6dd7c8 100644
--- a/hw/riscv/Makefile.objs
+++ b/hw/riscv/Makefile.objs
@@ -2,12 +2,14 @@ obj-y += boot.o
obj-$(CONFIG_SPIKE) += riscv_htif.o
obj-$(CONFIG_HART) += riscv_hart.o
obj-$(CONFIG_SIFIVE_E) += sifive_e.o
+obj-$(CONFIG_SIFIVE_E) += sifive_e_prci.o
obj-$(CONFIG_SIFIVE) += sifive_clint.o
obj-$(CONFIG_SIFIVE) += sifive_gpio.o
-obj-$(CONFIG_SIFIVE) += sifive_prci.o
obj-$(CONFIG_SIFIVE) += sifive_plic.o
obj-$(CONFIG_SIFIVE) += sifive_test.o
obj-$(CONFIG_SIFIVE_U) += sifive_u.o
+obj-$(CONFIG_SIFIVE_U) += sifive_u_otp.o
+obj-$(CONFIG_SIFIVE_U) += sifive_u_prci.o
obj-$(CONFIG_SIFIVE) += sifive_uart.o
obj-$(CONFIG_SPIKE) += spike.o
obj-$(CONFIG_RISCV_VIRT) += virt.o
diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
index 4c63b5cf8a..2e92fb0680 100644
--- a/hw/riscv/boot.c
+++ b/hw/riscv/boot.c
@@ -69,25 +69,33 @@ void riscv_find_and_load_firmware(MachineState *machine,
* so then in the future we can make "-bios default" the default option
* if no -bios option is set without breaking anything.
*/
- firmware_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
- default_machine_firmware);
- if (firmware_filename == NULL) {
- error_report("Unable to load the default RISC-V firmware \"%s\"",
- default_machine_firmware);
- exit(1);
- }
+ firmware_filename = riscv_find_firmware(default_machine_firmware);
} else {
firmware_filename = machine->firmware;
+ if (strcmp(firmware_filename, "none")) {
+ firmware_filename = riscv_find_firmware(firmware_filename);
+ }
}
if (strcmp(firmware_filename, "none")) {
/* If not "none" load the firmware */
riscv_load_firmware(firmware_filename, firmware_load_addr);
+ g_free(firmware_filename);
}
+}
- if (!strcmp(machine->firmware, "default")) {
- g_free(firmware_filename);
+char *riscv_find_firmware(const char *firmware_filename)
+{
+ char *filename;
+
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, firmware_filename);
+ if (filename == NULL) {
+ error_report("Unable to load the RISC-V firmware \"%s\"",
+ firmware_filename);
+ exit(1);
}
+
+ return filename;
}
target_ulong riscv_load_firmware(const char *firmware_filename,
diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c
index d0f3199968..5b98227db6 100644
--- a/hw/riscv/riscv_hart.c
+++ b/hw/riscv/riscv_hart.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2017 SiFive, Inc.
*
- * Holds the state of a heterogenous array of RISC-V harts
+ * Holds the state of a homogeneous array of RISC-V harts
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -29,6 +29,7 @@
static Property riscv_harts_props[] = {
DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1),
+ DEFINE_PROP_UINT32("hartid-base", RISCVHartArrayState, hartid_base, 0),
DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
DEFINE_PROP_END_OF_LIST(),
};
@@ -39,26 +40,33 @@ static void riscv_harts_cpu_reset(void *opaque)
cpu_reset(CPU(cpu));
}
+static void riscv_hart_realize(RISCVHartArrayState *s, int idx,
+ char *cpu_type, Error **errp)
+{
+ Error *err = NULL;
+
+ object_initialize_child(OBJECT(s), "harts[*]", &s->harts[idx],
+ sizeof(RISCVCPU), cpu_type,
+ &error_abort, NULL);
+ s->harts[idx].env.mhartid = s->hartid_base + idx;
+ qemu_register_reset(riscv_harts_cpu_reset, &s->harts[idx]);
+ object_property_set_bool(OBJECT(&s->harts[idx]), true,
+ "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+}
+
static void riscv_harts_realize(DeviceState *dev, Error **errp)
{
RISCVHartArrayState *s = RISCV_HART_ARRAY(dev);
- Error *err = NULL;
int n;
s->harts = g_new0(RISCVCPU, s->num_harts);
for (n = 0; n < s->num_harts; n++) {
- object_initialize_child(OBJECT(s), "harts[*]", &s->harts[n],
- sizeof(RISCVCPU), s->cpu_type,
- &error_abort, NULL);
- s->harts[n].env.mhartid = n;
- qemu_register_reset(riscv_harts_cpu_reset, &s->harts[n]);
- object_property_set_bool(OBJECT(&s->harts[n]), true,
- "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
+ riscv_hart_realize(s, n, s->cpu_type, errp);
}
}
diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c
index 792d75a1a3..0f9d641a0e 100644
--- a/hw/riscv/sifive_e.c
+++ b/hw/riscv/sifive_e.c
@@ -36,13 +36,14 @@
#include "hw/loader.h"
#include "hw/sysbus.h"
#include "hw/char/serial.h"
+#include "hw/misc/unimp.h"
#include "target/riscv/cpu.h"
#include "hw/riscv/riscv_hart.h"
#include "hw/riscv/sifive_plic.h"
#include "hw/riscv/sifive_clint.h"
-#include "hw/riscv/sifive_prci.h"
#include "hw/riscv/sifive_uart.h"
#include "hw/riscv/sifive_e.h"
+#include "hw/riscv/sifive_e_prci.h"
#include "hw/riscv/boot.h"
#include "chardev/char.h"
#include "sysemu/arch_init.h"
@@ -74,14 +75,6 @@ static const struct MemmapEntry {
[SIFIVE_E_DTIM] = { 0x80000000, 0x4000 }
};
-static void sifive_mmio_emulate(MemoryRegion *parent, const char *name,
- uintptr_t offset, uintptr_t length)
-{
- MemoryRegion *mock_mmio = g_new(MemoryRegion, 1);
- memory_region_init_ram(mock_mmio, NULL, name, length, &error_fatal);
- memory_region_add_subregion(parent, offset, mock_mmio);
-}
-
static void riscv_sifive_e_init(MachineState *machine)
{
const struct MemmapEntry *memmap = sifive_e_memmap;
@@ -172,9 +165,9 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp)
sifive_clint_create(memmap[SIFIVE_E_CLINT].base,
memmap[SIFIVE_E_CLINT].size, ms->smp.cpus,
SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
- sifive_mmio_emulate(sys_mem, "riscv.sifive.e.aon",
+ create_unimplemented_device("riscv.sifive.e.aon",
memmap[SIFIVE_E_AON].base, memmap[SIFIVE_E_AON].size);
- sifive_prci_create(memmap[SIFIVE_E_PRCI].base);
+ sifive_e_prci_create(memmap[SIFIVE_E_PRCI].base);
/* GPIO */
@@ -199,19 +192,19 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp)
sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART0].base,
serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_UART0_IRQ));
- sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi0",
+ create_unimplemented_device("riscv.sifive.e.qspi0",
memmap[SIFIVE_E_QSPI0].base, memmap[SIFIVE_E_QSPI0].size);
- sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm0",
+ create_unimplemented_device("riscv.sifive.e.pwm0",
memmap[SIFIVE_E_PWM0].base, memmap[SIFIVE_E_PWM0].size);
sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base,
serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_UART1_IRQ));
- sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi1",
+ create_unimplemented_device("riscv.sifive.e.qspi1",
memmap[SIFIVE_E_QSPI1].base, memmap[SIFIVE_E_QSPI1].size);
- sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm1",
+ create_unimplemented_device("riscv.sifive.e.pwm1",
memmap[SIFIVE_E_PWM1].base, memmap[SIFIVE_E_PWM1].size);
- sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi2",
+ create_unimplemented_device("riscv.sifive.e.qspi2",
memmap[SIFIVE_E_QSPI2].base, memmap[SIFIVE_E_QSPI2].size);
- sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm2",
+ create_unimplemented_device("riscv.sifive.e.pwm2",
memmap[SIFIVE_E_PWM2].base, memmap[SIFIVE_E_PWM2].size);
/* Flash memory */
diff --git a/hw/riscv/sifive_e_prci.c b/hw/riscv/sifive_e_prci.c
new file mode 100644
index 0000000000..a1c0d44f18
--- /dev/null
+++ b/hw/riscv/sifive_e_prci.c
@@ -0,0 +1,124 @@
+/*
+ * QEMU SiFive E PRCI (Power, Reset, Clock, Interrupt)
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Simple model of the PRCI to emulate register reads made by the SDK BSP
+ *
+ * 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 "hw/sysbus.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/hw.h"
+#include "hw/riscv/sifive_e_prci.h"
+
+static uint64_t sifive_e_prci_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ SiFiveEPRCIState *s = opaque;
+ switch (addr) {
+ case SIFIVE_E_PRCI_HFROSCCFG:
+ return s->hfrosccfg;
+ case SIFIVE_E_PRCI_HFXOSCCFG:
+ return s->hfxosccfg;
+ case SIFIVE_E_PRCI_PLLCFG:
+ return s->pllcfg;
+ case SIFIVE_E_PRCI_PLLOUTDIV:
+ return s->plloutdiv;
+ }
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%x\n",
+ __func__, (int)addr);
+ return 0;
+}
+
+static void sifive_e_prci_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ SiFiveEPRCIState *s = opaque;
+ switch (addr) {
+ case SIFIVE_E_PRCI_HFROSCCFG:
+ s->hfrosccfg = (uint32_t) val64;
+ /* OSC stays ready */
+ s->hfrosccfg |= SIFIVE_E_PRCI_HFROSCCFG_RDY;
+ break;
+ case SIFIVE_E_PRCI_HFXOSCCFG:
+ s->hfxosccfg = (uint32_t) val64;
+ /* OSC stays ready */
+ s->hfxosccfg |= SIFIVE_E_PRCI_HFXOSCCFG_RDY;
+ break;
+ case SIFIVE_E_PRCI_PLLCFG:
+ s->pllcfg = (uint32_t) val64;
+ /* PLL stays locked */
+ s->pllcfg |= SIFIVE_E_PRCI_PLLCFG_LOCK;
+ break;
+ case SIFIVE_E_PRCI_PLLOUTDIV:
+ s->plloutdiv = (uint32_t) val64;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n",
+ __func__, (int)addr, (int)val64);
+ }
+}
+
+static const MemoryRegionOps sifive_e_prci_ops = {
+ .read = sifive_e_prci_read,
+ .write = sifive_e_prci_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void sifive_e_prci_init(Object *obj)
+{
+ SiFiveEPRCIState *s = SIFIVE_E_PRCI(obj);
+
+ memory_region_init_io(&s->mmio, obj, &sifive_e_prci_ops, s,
+ TYPE_SIFIVE_E_PRCI, SIFIVE_E_PRCI_REG_SIZE);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+ s->hfrosccfg = (SIFIVE_E_PRCI_HFROSCCFG_RDY | SIFIVE_E_PRCI_HFROSCCFG_EN);
+ s->hfxosccfg = (SIFIVE_E_PRCI_HFXOSCCFG_RDY | SIFIVE_E_PRCI_HFXOSCCFG_EN);
+ s->pllcfg = (SIFIVE_E_PRCI_PLLCFG_REFSEL | SIFIVE_E_PRCI_PLLCFG_BYPASS |
+ SIFIVE_E_PRCI_PLLCFG_LOCK);
+ s->plloutdiv = SIFIVE_E_PRCI_PLLOUTDIV_DIV1;
+}
+
+static const TypeInfo sifive_e_prci_info = {
+ .name = TYPE_SIFIVE_E_PRCI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFiveEPRCIState),
+ .instance_init = sifive_e_prci_init,
+};
+
+static void sifive_e_prci_register_types(void)
+{
+ type_register_static(&sifive_e_prci_info);
+}
+
+type_init(sifive_e_prci_register_types)
+
+
+/*
+ * Create PRCI device.
+ */
+DeviceState *sifive_e_prci_create(hwaddr addr)
+{
+ DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_E_PRCI);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+ return dev;
+}
diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c
index 64a1a10380..98e4304b66 100644
--- a/hw/riscv/sifive_plic.c
+++ b/hw/riscv/sifive_plic.c
@@ -162,18 +162,6 @@ static void sifive_plic_update(SiFivePLICState *plic)
}
}
-void sifive_plic_raise_irq(SiFivePLICState *plic, uint32_t irq)
-{
- sifive_plic_set_pending(plic, irq, true);
- sifive_plic_update(plic);
-}
-
-void sifive_plic_lower_irq(SiFivePLICState *plic, uint32_t irq)
-{
- sifive_plic_set_pending(plic, irq, false);
- sifive_plic_update(plic);
-}
-
static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid)
{
int i, j;
diff --git a/hw/riscv/sifive_prci.c b/hw/riscv/sifive_prci.c
deleted file mode 100644
index 9837b6166f..0000000000
--- a/hw/riscv/sifive_prci.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * QEMU SiFive PRCI (Power, Reset, Clock, Interrupt)
- *
- * Copyright (c) 2017 SiFive, Inc.
- *
- * Simple model of the PRCI to emulate register reads made by the SDK BSP
- *
- * 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 "hw/hw.h"
-#include "hw/sysbus.h"
-#include "qemu/module.h"
-#include "target/riscv/cpu.h"
-#include "hw/hw.h"
-#include "hw/riscv/sifive_prci.h"
-
-static uint64_t sifive_prci_read(void *opaque, hwaddr addr, unsigned int size)
-{
- 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;
-}
-
-static void sifive_prci_write(void *opaque, hwaddr addr,
- uint64_t val64, unsigned int size)
-{
- 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 = {
- .read = sifive_prci_read,
- .write = sifive_prci_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4
- }
-};
-
-static void sifive_prci_init(Object *obj)
-{
- SiFivePRCIState *s = SIFIVE_PRCI(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 = {
- .name = TYPE_SIFIVE_PRCI,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(SiFivePRCIState),
- .instance_init = sifive_prci_init,
-};
-
-static void sifive_prci_register_types(void)
-{
- type_register_static(&sifive_prci_info);
-}
-
-type_init(sifive_prci_register_types)
-
-
-/*
- * Create PRCI device.
- */
-DeviceState *sifive_prci_create(hwaddr addr)
-{
- DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PRCI);
- qdev_init_nofail(dev);
- sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
- return dev;
-}
diff --git a/hw/riscv/sifive_test.c b/hw/riscv/sifive_test.c
index afbb3aaf34..339195c6ff 100644
--- a/hw/riscv/sifive_test.c
+++ b/hw/riscv/sifive_test.c
@@ -19,10 +19,10 @@
*/
#include "qemu/osdep.h"
-#include "hw/hw.h"
#include "hw/sysbus.h"
+#include "qemu/log.h"
#include "qemu/module.h"
-#include "target/riscv/cpu.h"
+#include "sysemu/runstate.h"
#include "hw/hw.h"
#include "hw/riscv/sifive_test.h"
@@ -42,12 +42,15 @@ static void sifive_test_write(void *opaque, hwaddr addr,
exit(code);
case FINISHER_PASS:
exit(0);
+ case FINISHER_RESET:
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ return;
default:
break;
}
}
- hw_error("%s: write: addr=0x%x val=0x%016" PRIx64 "\n",
- __func__, (int)addr, val64);
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: write: addr=0x%x val=0x%016" PRIx64 "\n",
+ __func__, (int)addr, val64);
}
static const MemoryRegionOps sifive_test_ops = {
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
index 9910fa6708..9f8e84bf2e 100644
--- a/hw/riscv/sifive_u.c
+++ b/hw/riscv/sifive_u.c
@@ -3,14 +3,19 @@
*
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
* Copyright (c) 2017 SiFive, Inc.
+ * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com>
*
* Provides a board compatible with the SiFive Freedom U SDK:
*
* 0) UART
* 1) CLINT (Core Level Interruptor)
* 2) PLIC (Platform Level Interrupt Controller)
+ * 3) PRCI (Power, Reset, Clock, Interrupt)
+ * 4) OTP (One-Time Programmable) memory with stored serial number
+ * 5) GEM (Gigabit Ethernet Controller) and management block
*
- * This board currently uses a hardcoded devicetree that indicates one hart.
+ * This board currently generates devicetree dynamically that indicates at least
+ * two harts and up to five harts.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -33,15 +38,17 @@
#include "hw/loader.h"
#include "hw/sysbus.h"
#include "hw/char/serial.h"
+#include "hw/cpu/cluster.h"
+#include "hw/misc/unimp.h"
#include "target/riscv/cpu.h"
#include "hw/riscv/riscv_hart.h"
#include "hw/riscv/sifive_plic.h"
#include "hw/riscv/sifive_clint.h"
#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 "net/eth.h"
#include "sysemu/arch_init.h"
#include "sysemu/device_tree.h"
#include "sysemu/sysemu.h"
@@ -59,23 +66,29 @@ static const struct MemmapEntry {
[SIFIVE_U_MROM] = { 0x1000, 0x11000 },
[SIFIVE_U_CLINT] = { 0x2000000, 0x10000 },
[SIFIVE_U_PLIC] = { 0xc000000, 0x4000000 },
- [SIFIVE_U_UART0] = { 0x10013000, 0x1000 },
- [SIFIVE_U_UART1] = { 0x10023000, 0x1000 },
+ [SIFIVE_U_PRCI] = { 0x10000000, 0x1000 },
+ [SIFIVE_U_UART0] = { 0x10010000, 0x1000 },
+ [SIFIVE_U_UART1] = { 0x10011000, 0x1000 },
+ [SIFIVE_U_OTP] = { 0x10070000, 0x1000 },
[SIFIVE_U_DRAM] = { 0x80000000, 0x0 },
- [SIFIVE_U_GEM] = { 0x100900FC, 0x2000 },
+ [SIFIVE_U_GEM] = { 0x10090000, 0x2000 },
+ [SIFIVE_U_GEM_MGMT] = { 0x100a0000, 0x1000 },
};
+#define OTP_SERIAL 1
#define GEM_REVISION 0x10070109
static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
uint64_t mem_size, const char *cmdline)
{
+ MachineState *ms = MACHINE(qdev_get_machine());
void *fdt;
int cpu;
uint32_t *cells;
char *nodename;
- char ethclk_names[] = "pclk\0hclk\0tx_clk";
- uint32_t plic_phandle, ethclk_phandle, phandle = 1;
+ char ethclk_names[] = "pclk\0hclk";
+ uint32_t plic_phandle, prci_phandle, phandle = 1;
+ uint32_t hfclk_phandle, rtcclk_phandle, phy_phandle;
fdt = s->fdt = create_device_tree(&s->fdt_size);
if (!fdt) {
@@ -83,8 +96,9 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
exit(1);
}
- qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu");
- qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev");
+ qemu_fdt_setprop_string(fdt, "/", "model", "SiFive HiFive Unleashed A00");
+ qemu_fdt_setprop_string(fdt, "/", "compatible",
+ "sifive,hifive-unleashed-a00");
qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
@@ -94,6 +108,28 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+ hfclk_phandle = phandle++;
+ nodename = g_strdup_printf("/hfclk");
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cell(fdt, nodename, "phandle", hfclk_phandle);
+ qemu_fdt_setprop_string(fdt, nodename, "clock-output-names", "hfclk");
+ qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency",
+ SIFIVE_U_HFCLK_FREQ);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "fixed-clock");
+ qemu_fdt_setprop_cell(fdt, nodename, "#clock-cells", 0x0);
+ g_free(nodename);
+
+ rtcclk_phandle = phandle++;
+ nodename = g_strdup_printf("/rtcclk");
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cell(fdt, nodename, "phandle", rtcclk_phandle);
+ qemu_fdt_setprop_string(fdt, nodename, "clock-output-names", "rtcclk");
+ qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency",
+ SIFIVE_U_RTCCLK_FREQ);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "fixed-clock");
+ qemu_fdt_setprop_cell(fdt, nodename, "#clock-cells", 0x0);
+ g_free(nodename);
+
nodename = g_strdup_printf("/memory@%lx",
(long)memmap[SIFIVE_U_DRAM].base);
qemu_fdt_add_subnode(fdt, nodename);
@@ -109,15 +145,21 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
- for (cpu = s->soc.cpus.num_harts - 1; cpu >= 0; cpu--) {
+ for (cpu = ms->smp.cpus - 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]);
+ char *isa;
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency",
SIFIVE_U_CLOCK_FREQ);
- qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
+ /* cpu 0 is the management hart that does not have mmu */
+ if (cpu != 0) {
+ qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
+ isa = riscv_isa_string(&s->soc.u_cpus.harts[cpu - 1]);
+ } else {
+ isa = riscv_isa_string(&s->soc.e_cpus.harts[0]);
+ }
qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
@@ -125,7 +167,6 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
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_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);
@@ -134,8 +175,8 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
g_free(nodename);
}
- cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4);
- for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) {
+ cells = g_new0(uint32_t, ms->smp.cpus * 4);
+ for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
nodename =
g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
@@ -153,20 +194,41 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
0x0, memmap[SIFIVE_U_CLINT].base,
0x0, memmap[SIFIVE_U_CLINT].size);
qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
- cells, s->soc.cpus.num_harts * sizeof(uint32_t) * 4);
+ cells, ms->smp.cpus * sizeof(uint32_t) * 4);
g_free(cells);
g_free(nodename);
+ prci_phandle = phandle++;
+ nodename = g_strdup_printf("/soc/clock-controller@%lx",
+ (long)memmap[SIFIVE_U_PRCI].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cell(fdt, nodename, "phandle", prci_phandle);
+ qemu_fdt_setprop_cell(fdt, nodename, "#clock-cells", 0x1);
+ qemu_fdt_setprop_cells(fdt, nodename, "clocks",
+ hfclk_phandle, rtcclk_phandle);
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ 0x0, memmap[SIFIVE_U_PRCI].base,
+ 0x0, memmap[SIFIVE_U_PRCI].size);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible",
+ "sifive,fu540-c000-prci");
+ 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++) {
+ cells = g_new0(uint32_t, ms->smp.cpus * 4 - 2);
+ for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
nodename =
g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
- cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
- cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
- cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
- cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
+ /* cpu 0 is the management hart that does not have S-mode */
+ if (cpu == 0) {
+ cells[0] = cpu_to_be32(intc_phandle);
+ cells[1] = cpu_to_be32(IRQ_M_EXT);
+ } else {
+ cells[cpu * 4 - 2] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 - 1] = cpu_to_be32(IRQ_M_EXT);
+ cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 + 1] = cpu_to_be32(IRQ_S_EXT);
+ }
g_free(nodename);
}
nodename = g_strdup_printf("/soc/interrupt-controller@%lx",
@@ -176,73 +238,70 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0");
qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
- cells, s->soc.cpus.num_harts * sizeof(uint32_t) * 4);
+ cells, (ms->smp.cpus * 4 - 2) * sizeof(uint32_t));
qemu_fdt_setprop_cells(fdt, nodename, "reg",
0x0, memmap[SIFIVE_U_PLIC].base,
0x0, memmap[SIFIVE_U_PLIC].size);
- 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", plic_phandle);
- qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", plic_phandle);
+ qemu_fdt_setprop_cell(fdt, nodename, "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", ethclk_phandle);
- qemu_fdt_setprop_cell(fdt, nodename, "linux,phandle", ethclk_phandle);
- ethclk_phandle = qemu_fdt_get_phandle(fdt, nodename);
- g_free(nodename);
-
+ phy_phandle = phandle++;
nodename = g_strdup_printf("/soc/ethernet@%lx",
(long)memmap[SIFIVE_U_GEM].base);
qemu_fdt_add_subnode(fdt, nodename);
- qemu_fdt_setprop_string(fdt, nodename, "compatible", "cdns,macb");
+ qemu_fdt_setprop_string(fdt, nodename, "compatible",
+ "sifive,fu540-c000-gem");
qemu_fdt_setprop_cells(fdt, nodename, "reg",
0x0, memmap[SIFIVE_U_GEM].base,
- 0x0, memmap[SIFIVE_U_GEM].size);
+ 0x0, memmap[SIFIVE_U_GEM].size,
+ 0x0, memmap[SIFIVE_U_GEM_MGMT].base,
+ 0x0, memmap[SIFIVE_U_GEM_MGMT].size);
qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control");
qemu_fdt_setprop_string(fdt, nodename, "phy-mode", "gmii");
- qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
- qemu_fdt_setprop_cells(fdt, nodename, "interrupts", SIFIVE_U_GEM_IRQ);
+ qemu_fdt_setprop_cell(fdt, nodename, "phy-handle", phy_phandle);
+ qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle);
+ qemu_fdt_setprop_cell(fdt, nodename, "interrupts", SIFIVE_U_GEM_IRQ);
qemu_fdt_setprop_cells(fdt, nodename, "clocks",
- ethclk_phandle, ethclk_phandle, ethclk_phandle);
- qemu_fdt_setprop(fdt, nodename, "clocks-names", ethclk_names,
+ prci_phandle, PRCI_CLK_GEMGXLPLL, prci_phandle, PRCI_CLK_GEMGXLPLL);
+ qemu_fdt_setprop(fdt, nodename, "clock-names", ethclk_names,
sizeof(ethclk_names));
- qemu_fdt_setprop_cells(fdt, nodename, "#address-cells", 1);
- qemu_fdt_setprop_cells(fdt, nodename, "#size-cells", 0);
+ qemu_fdt_setprop(fdt, nodename, "local-mac-address",
+ s->soc.gem.conf.macaddr.a, ETH_ALEN);
+ qemu_fdt_setprop_cell(fdt, nodename, "#address-cells", 1);
+ qemu_fdt_setprop_cell(fdt, nodename, "#size-cells", 0);
g_free(nodename);
nodename = g_strdup_printf("/soc/ethernet@%lx/ethernet-phy@0",
(long)memmap[SIFIVE_U_GEM].base);
qemu_fdt_add_subnode(fdt, nodename);
- qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0);
+ qemu_fdt_setprop_cell(fdt, nodename, "phandle", phy_phandle);
+ qemu_fdt_setprop_cell(fdt, nodename, "reg", 0x0);
g_free(nodename);
- nodename = g_strdup_printf("/soc/uart@%lx",
+ nodename = g_strdup_printf("/soc/serial@%lx",
(long)memmap[SIFIVE_U_UART0].base);
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,uart0");
qemu_fdt_setprop_cells(fdt, nodename, "reg",
0x0, memmap[SIFIVE_U_UART0].base,
0x0, memmap[SIFIVE_U_UART0].size);
- qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency",
- SIFIVE_U_CLOCK_FREQ / 2);
- qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
- qemu_fdt_setprop_cells(fdt, nodename, "interrupts", SIFIVE_U_UART0_IRQ);
+ qemu_fdt_setprop_cells(fdt, nodename, "clocks",
+ prci_phandle, PRCI_CLK_TLCLK);
+ qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle);
+ qemu_fdt_setprop_cell(fdt, nodename, "interrupts", SIFIVE_U_UART0_IRQ);
qemu_fdt_add_subnode(fdt, "/chosen");
qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename);
if (cmdline) {
qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
}
+
+ qemu_fdt_add_subnode(fdt, "/aliases");
+ qemu_fdt_setprop_string(fdt, "/aliases", "serial0", nodename);
+
g_free(nodename);
}
@@ -275,7 +334,18 @@ static void riscv_sifive_u_init(MachineState *machine)
memmap[SIFIVE_U_DRAM].base);
if (machine->kernel_filename) {
- riscv_load_kernel(machine->kernel_filename);
+ uint64_t kernel_entry = riscv_load_kernel(machine->kernel_filename);
+
+ if (machine->initrd_filename) {
+ hwaddr start;
+ hwaddr end = riscv_load_initrd(machine->initrd_filename,
+ machine->ram_size, kernel_entry,
+ &start);
+ qemu_fdt_setprop_cell(s->fdt, "/chosen",
+ "linux,initrd-start", start);
+ qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end",
+ end);
+ }
}
/* reset vector */
@@ -319,13 +389,37 @@ static void riscv_sifive_u_soc_init(Object *obj)
MachineState *ms = MACHINE(qdev_get_machine());
SiFiveUSoCState *s = RISCV_U_SOC(obj);
- object_initialize_child(obj, "cpus", &s->cpus, sizeof(s->cpus),
- TYPE_RISCV_HART_ARRAY, &error_abort, NULL);
- object_property_set_str(OBJECT(&s->cpus), SIFIVE_U_CPU, "cpu-type",
- &error_abort);
- object_property_set_int(OBJECT(&s->cpus), ms->smp.cpus, "num-harts",
- &error_abort);
-
+ object_initialize_child(obj, "e-cluster", &s->e_cluster,
+ sizeof(s->e_cluster), TYPE_CPU_CLUSTER,
+ &error_abort, NULL);
+ qdev_prop_set_uint32(DEVICE(&s->e_cluster), "cluster-id", 0);
+
+ object_initialize_child(OBJECT(&s->e_cluster), "e-cpus",
+ &s->e_cpus, sizeof(s->e_cpus),
+ TYPE_RISCV_HART_ARRAY, &error_abort,
+ NULL);
+ qdev_prop_set_uint32(DEVICE(&s->e_cpus), "num-harts", 1);
+ qdev_prop_set_uint32(DEVICE(&s->e_cpus), "hartid-base", 0);
+ qdev_prop_set_string(DEVICE(&s->e_cpus), "cpu-type", SIFIVE_E_CPU);
+
+ object_initialize_child(obj, "u-cluster", &s->u_cluster,
+ sizeof(s->u_cluster), TYPE_CPU_CLUSTER,
+ &error_abort, NULL);
+ qdev_prop_set_uint32(DEVICE(&s->u_cluster), "cluster-id", 1);
+
+ object_initialize_child(OBJECT(&s->u_cluster), "u-cpus",
+ &s->u_cpus, sizeof(s->u_cpus),
+ TYPE_RISCV_HART_ARRAY, &error_abort,
+ NULL);
+ qdev_prop_set_uint32(DEVICE(&s->u_cpus), "num-harts", ms->smp.cpus - 1);
+ qdev_prop_set_uint32(DEVICE(&s->u_cpus), "hartid-base", 1);
+ qdev_prop_set_string(DEVICE(&s->u_cpus), "cpu-type", SIFIVE_U_CPU);
+
+ sysbus_init_child_obj(obj, "prci", &s->prci, sizeof(s->prci),
+ TYPE_SIFIVE_U_PRCI);
+ sysbus_init_child_obj(obj, "otp", &s->otp, sizeof(s->otp),
+ TYPE_SIFIVE_U_OTP);
+ qdev_prop_set_uint32(DEVICE(&s->otp), "serial", OTP_SERIAL);
sysbus_init_child_obj(obj, "gem", &s->gem, sizeof(s->gem),
TYPE_CADENCE_GEM);
}
@@ -344,7 +438,19 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp)
Error *err = NULL;
NICInfo *nd = &nd_table[0];
- object_property_set_bool(OBJECT(&s->cpus), true, "realized",
+ object_property_set_bool(OBJECT(&s->e_cpus), true, "realized",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->u_cpus), true, "realized",
+ &error_abort);
+ /*
+ * The cluster must be realized after the RISC-V hart array container,
+ * as the container's CPU object is only created on realize, and the
+ * CPU must exist and have been parented into the cluster before the
+ * cluster is realized.
+ */
+ object_property_set_bool(OBJECT(&s->e_cluster), true, "realized",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->u_cluster), true, "realized",
&error_abort);
/* boot rom */
@@ -359,10 +465,11 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp)
plic_hart_config = g_malloc0(plic_hart_config_len);
for (i = 0; i < ms->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);
+ } else {
+ strncat(plic_hart_config, "M", 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);
}
@@ -386,6 +493,12 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp)
memmap[SIFIVE_U_CLINT].size, ms->smp.cpus,
SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+ object_property_set_bool(OBJECT(&s->prci), true, "realized", &err);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->prci), 0, memmap[SIFIVE_U_PRCI].base);
+
+ object_property_set_bool(OBJECT(&s->otp), true, "realized", &err);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->otp), 0, memmap[SIFIVE_U_OTP].base);
+
for (i = 0; i < SIFIVE_U_PLIC_NUM_SOURCES; i++) {
plic_gpios[i] = qdev_get_gpio_in(DEVICE(s->plic), i);
}
@@ -404,16 +517,18 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp)
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem), 0, memmap[SIFIVE_U_GEM].base);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem), 0,
plic_gpios[SIFIVE_U_GEM_IRQ]);
+
+ create_unimplemented_device("riscv.sifive.u.gem-mgmt",
+ memmap[SIFIVE_U_GEM_MGMT].base, memmap[SIFIVE_U_GEM_MGMT].size);
}
static void riscv_sifive_u_machine_init(MachineClass *mc)
{
mc->desc = "RISC-V Board compatible with SiFive U SDK";
mc->init = riscv_sifive_u_init;
- /* The real hardware has 5 CPUs, but one of them is a small embedded power
- * management CPU.
- */
- mc->max_cpus = 4;
+ mc->max_cpus = SIFIVE_U_MANAGEMENT_CPU_COUNT + SIFIVE_U_COMPUTE_CPU_COUNT;
+ mc->min_cpus = SIFIVE_U_MANAGEMENT_CPU_COUNT + 1;
+ mc->default_cpus = mc->min_cpus;
}
DEFINE_MACHINE("sifive_u", riscv_sifive_u_machine_init)
diff --git a/hw/riscv/sifive_u_otp.c b/hw/riscv/sifive_u_otp.c
new file mode 100644
index 0000000000..ea0eee5678
--- /dev/null
+++ b/hw/riscv/sifive_u_otp.c
@@ -0,0 +1,191 @@
+/*
+ * QEMU SiFive U OTP (One-Time Programmable) Memory interface
+ *
+ * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com>
+ *
+ * Simple model of the OTP to emulate register reads made by the SDK BSP
+ *
+ * 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 "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/riscv/sifive_u_otp.h"
+
+static uint64_t sifive_u_otp_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ SiFiveUOTPState *s = opaque;
+
+ switch (addr) {
+ case SIFIVE_U_OTP_PA:
+ return s->pa;
+ case SIFIVE_U_OTP_PAIO:
+ return s->paio;
+ case SIFIVE_U_OTP_PAS:
+ return s->pas;
+ case SIFIVE_U_OTP_PCE:
+ return s->pce;
+ case SIFIVE_U_OTP_PCLK:
+ return s->pclk;
+ case SIFIVE_U_OTP_PDIN:
+ return s->pdin;
+ case SIFIVE_U_OTP_PDOUT:
+ if ((s->pce & SIFIVE_U_OTP_PCE_EN) &&
+ (s->pdstb & SIFIVE_U_OTP_PDSTB_EN) &&
+ (s->ptrim & SIFIVE_U_OTP_PTRIM_EN)) {
+ return s->fuse[s->pa & SIFIVE_U_OTP_PA_MASK];
+ } else {
+ return 0xff;
+ }
+ case SIFIVE_U_OTP_PDSTB:
+ return s->pdstb;
+ case SIFIVE_U_OTP_PPROG:
+ return s->pprog;
+ case SIFIVE_U_OTP_PTC:
+ return s->ptc;
+ case SIFIVE_U_OTP_PTM:
+ return s->ptm;
+ case SIFIVE_U_OTP_PTM_REP:
+ return s->ptm_rep;
+ case SIFIVE_U_OTP_PTR:
+ return s->ptr;
+ case SIFIVE_U_OTP_PTRIM:
+ return s->ptrim;
+ case SIFIVE_U_OTP_PWE:
+ return s->pwe;
+ }
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ return 0;
+}
+
+static void sifive_u_otp_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ SiFiveUOTPState *s = opaque;
+ uint32_t val32 = (uint32_t)val64;
+
+ switch (addr) {
+ case SIFIVE_U_OTP_PA:
+ s->pa = val32 & SIFIVE_U_OTP_PA_MASK;
+ break;
+ case SIFIVE_U_OTP_PAIO:
+ s->paio = val32;
+ break;
+ case SIFIVE_U_OTP_PAS:
+ s->pas = val32;
+ break;
+ case SIFIVE_U_OTP_PCE:
+ s->pce = val32;
+ break;
+ case SIFIVE_U_OTP_PCLK:
+ s->pclk = val32;
+ break;
+ case SIFIVE_U_OTP_PDIN:
+ s->pdin = val32;
+ break;
+ case SIFIVE_U_OTP_PDOUT:
+ /* read-only */
+ break;
+ case SIFIVE_U_OTP_PDSTB:
+ s->pdstb = val32;
+ break;
+ case SIFIVE_U_OTP_PPROG:
+ s->pprog = val32;
+ break;
+ case SIFIVE_U_OTP_PTC:
+ s->ptc = val32;
+ break;
+ case SIFIVE_U_OTP_PTM:
+ s->ptm = val32;
+ break;
+ case SIFIVE_U_OTP_PTM_REP:
+ s->ptm_rep = val32;
+ break;
+ case SIFIVE_U_OTP_PTR:
+ s->ptr = val32;
+ break;
+ case SIFIVE_U_OTP_PTRIM:
+ s->ptrim = val32;
+ break;
+ case SIFIVE_U_OTP_PWE:
+ s->pwe = val32;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx
+ " v=0x%x\n", __func__, addr, val32);
+ }
+}
+
+static const MemoryRegionOps sifive_u_otp_ops = {
+ .read = sifive_u_otp_read,
+ .write = sifive_u_otp_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static Property sifive_u_otp_properties[] = {
+ DEFINE_PROP_UINT32("serial", SiFiveUOTPState, serial, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sifive_u_otp_realize(DeviceState *dev, Error **errp)
+{
+ SiFiveUOTPState *s = SIFIVE_U_OTP(dev);
+
+ memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_u_otp_ops, s,
+ TYPE_SIFIVE_U_OTP, SIFIVE_U_OTP_REG_SIZE);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
+}
+
+static void sifive_u_otp_reset(DeviceState *dev)
+{
+ SiFiveUOTPState *s = SIFIVE_U_OTP(dev);
+
+ /* Initialize all fuses' initial value to 0xFFs */
+ memset(s->fuse, 0xff, sizeof(s->fuse));
+
+ /* Make a valid content of serial number */
+ s->fuse[SIFIVE_U_OTP_SERIAL_ADDR] = s->serial;
+ s->fuse[SIFIVE_U_OTP_SERIAL_ADDR + 1] = ~(s->serial);
+}
+
+static void sifive_u_otp_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = sifive_u_otp_properties;
+ dc->realize = sifive_u_otp_realize;
+ dc->reset = sifive_u_otp_reset;
+}
+
+static const TypeInfo sifive_u_otp_info = {
+ .name = TYPE_SIFIVE_U_OTP,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFiveUOTPState),
+ .class_init = sifive_u_otp_class_init,
+};
+
+static void sifive_u_otp_register_types(void)
+{
+ type_register_static(&sifive_u_otp_info);
+}
+
+type_init(sifive_u_otp_register_types)
diff --git a/hw/riscv/sifive_u_prci.c b/hw/riscv/sifive_u_prci.c
new file mode 100644
index 0000000000..4fa590c064
--- /dev/null
+++ b/hw/riscv/sifive_u_prci.c
@@ -0,0 +1,169 @@
+/*
+ * QEMU SiFive U PRCI (Power, Reset, Clock, Interrupt)
+ *
+ * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com>
+ *
+ * Simple model of the PRCI to emulate register reads made by the SDK BSP
+ *
+ * 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 "hw/sysbus.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/riscv/sifive_u_prci.h"
+
+static uint64_t sifive_u_prci_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ SiFiveUPRCIState *s = opaque;
+
+ switch (addr) {
+ case SIFIVE_U_PRCI_HFXOSCCFG:
+ return s->hfxosccfg;
+ case SIFIVE_U_PRCI_COREPLLCFG0:
+ return s->corepllcfg0;
+ case SIFIVE_U_PRCI_DDRPLLCFG0:
+ return s->ddrpllcfg0;
+ case SIFIVE_U_PRCI_DDRPLLCFG1:
+ return s->ddrpllcfg1;
+ case SIFIVE_U_PRCI_GEMGXLPLLCFG0:
+ return s->gemgxlpllcfg0;
+ case SIFIVE_U_PRCI_GEMGXLPLLCFG1:
+ return s->gemgxlpllcfg1;
+ case SIFIVE_U_PRCI_CORECLKSEL:
+ return s->coreclksel;
+ case SIFIVE_U_PRCI_DEVICESRESET:
+ return s->devicesreset;
+ case SIFIVE_U_PRCI_CLKMUXSTATUS:
+ return s->clkmuxstatus;
+ }
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+
+ return 0;
+}
+
+static void sifive_u_prci_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ SiFiveUPRCIState *s = opaque;
+ uint32_t val32 = (uint32_t)val64;
+
+ switch (addr) {
+ case SIFIVE_U_PRCI_HFXOSCCFG:
+ s->hfxosccfg = val32;
+ /* OSC stays ready */
+ s->hfxosccfg |= SIFIVE_U_PRCI_HFXOSCCFG_RDY;
+ break;
+ case SIFIVE_U_PRCI_COREPLLCFG0:
+ s->corepllcfg0 = val32;
+ /* internal feedback */
+ s->corepllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE;
+ /* PLL stays locked */
+ s->corepllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK;
+ break;
+ case SIFIVE_U_PRCI_DDRPLLCFG0:
+ s->ddrpllcfg0 = val32;
+ /* internal feedback */
+ s->ddrpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE;
+ /* PLL stays locked */
+ s->ddrpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK;
+ break;
+ case SIFIVE_U_PRCI_DDRPLLCFG1:
+ s->ddrpllcfg1 = val32;
+ break;
+ case SIFIVE_U_PRCI_GEMGXLPLLCFG0:
+ s->gemgxlpllcfg0 = val32;
+ /* internal feedback */
+ s->gemgxlpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE;
+ /* PLL stays locked */
+ s->gemgxlpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK;
+ break;
+ case SIFIVE_U_PRCI_GEMGXLPLLCFG1:
+ s->gemgxlpllcfg1 = val32;
+ break;
+ case SIFIVE_U_PRCI_CORECLKSEL:
+ s->coreclksel = val32;
+ break;
+ case SIFIVE_U_PRCI_DEVICESRESET:
+ s->devicesreset = val32;
+ break;
+ case SIFIVE_U_PRCI_CLKMUXSTATUS:
+ s->clkmuxstatus = val32;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx
+ " v=0x%x\n", __func__, addr, val32);
+ }
+}
+
+static const MemoryRegionOps sifive_u_prci_ops = {
+ .read = sifive_u_prci_read,
+ .write = sifive_u_prci_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void sifive_u_prci_realize(DeviceState *dev, Error **errp)
+{
+ SiFiveUPRCIState *s = SIFIVE_U_PRCI(dev);
+
+ memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_u_prci_ops, s,
+ TYPE_SIFIVE_U_PRCI, SIFIVE_U_PRCI_REG_SIZE);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
+}
+
+static void sifive_u_prci_reset(DeviceState *dev)
+{
+ SiFiveUPRCIState *s = SIFIVE_U_PRCI(dev);
+
+ /* Initialize register to power-on-reset values */
+ s->hfxosccfg = SIFIVE_U_PRCI_HFXOSCCFG_RDY | SIFIVE_U_PRCI_HFXOSCCFG_EN;
+ s->corepllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF |
+ SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE |
+ SIFIVE_U_PRCI_PLLCFG0_LOCK;
+ s->ddrpllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF |
+ SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE |
+ SIFIVE_U_PRCI_PLLCFG0_LOCK;
+ s->gemgxlpllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF |
+ SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE |
+ SIFIVE_U_PRCI_PLLCFG0_LOCK;
+ s->coreclksel = SIFIVE_U_PRCI_CORECLKSEL_HFCLK;
+}
+
+static void sifive_u_prci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = sifive_u_prci_realize;
+ dc->reset = sifive_u_prci_reset;
+}
+
+static const TypeInfo sifive_u_prci_info = {
+ .name = TYPE_SIFIVE_U_PRCI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFiveUPRCIState),
+ .class_init = sifive_u_prci_class_init,
+};
+
+static void sifive_u_prci_register_types(void)
+{
+ type_register_static(&sifive_u_prci_info);
+}
+
+type_init(sifive_u_prci_register_types)
diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
index 9de42b1680..a403ae90f5 100644
--- a/hw/riscv/sifive_uart.c
+++ b/hw/riscv/sifive_uart.c
@@ -18,10 +18,10 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
+#include "qemu/log.h"
#include "hw/sysbus.h"
#include "chardev/char.h"
#include "chardev/char-fe.h"
-#include "target/riscv/cpu.h"
#include "hw/hw.h"
#include "hw/irq.h"
#include "hw/riscv/sifive_uart.h"
@@ -95,8 +95,8 @@ uart_read(void *opaque, hwaddr addr, unsigned int size)
return s->div;
}
- hw_error("%s: bad read: addr=0x%x\n",
- __func__, (int)addr);
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n",
+ __func__, (int)addr);
return 0;
}
@@ -127,8 +127,8 @@ uart_write(void *opaque, hwaddr addr,
s->div = val64;
return;
}
- hw_error("%s: bad write: addr=0x%x v=0x%x\n",
- __func__, (int)addr, (int)value);
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n",
+ __func__, (int)addr, (int)value);
}
static const MemoryRegionOps uart_ops = {
diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c
index 7c04bd554f..d60415d190 100644
--- a/hw/riscv/spike.c
+++ b/hw/riscv/spike.c
@@ -112,7 +112,6 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
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_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);
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 9bced28486..d36f5625ec 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -112,7 +112,7 @@ static void create_pcie_irq_map(void *fdt, char *nodename,
0x1800, 0, 0, 0x7);
}
-static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
+static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
uint64_t mem_size, const char *cmdline)
{
void *fdt;
@@ -170,11 +170,9 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
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", 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);
@@ -235,8 +233,8 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
nodename = g_strdup_printf("/soc/interrupt-controller@%lx",
(long)memmap[VIRT_PLIC].base);
qemu_fdt_add_subnode(fdt, nodename);
- qemu_fdt_setprop_cells(fdt, nodename, "#address-cells",
- FDT_PLIC_ADDR_CELLS);
+ qemu_fdt_setprop_cell(fdt, nodename, "#address-cells",
+ FDT_PLIC_ADDR_CELLS);
qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells",
FDT_PLIC_INT_CELLS);
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0");
@@ -246,11 +244,8 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
qemu_fdt_setprop_cells(fdt, nodename, "reg",
0x0, memmap[VIRT_PLIC].base,
0x0, memmap[VIRT_PLIC].size);
- 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", VIRTIO_NDEV);
- qemu_fdt_setprop_cells(fdt, nodename, "phandle", plic_phandle);
- qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", plic_phandle);
+ qemu_fdt_setprop_cell(fdt, nodename, "phandle", plic_phandle);
plic_phandle = qemu_fdt_get_phandle(fdt, nodename);
g_free(cells);
g_free(nodename);
@@ -263,19 +258,19 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
qemu_fdt_setprop_cells(fdt, nodename, "reg",
0x0, memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
0x0, memmap[VIRT_VIRTIO].size);
- qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
- qemu_fdt_setprop_cells(fdt, nodename, "interrupts", VIRTIO_IRQ + i);
+ qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle);
+ qemu_fdt_setprop_cell(fdt, nodename, "interrupts", VIRTIO_IRQ + i);
g_free(nodename);
}
nodename = g_strdup_printf("/soc/pci@%lx",
(long) memmap[VIRT_PCIE_ECAM].base);
qemu_fdt_add_subnode(fdt, nodename);
- qemu_fdt_setprop_cells(fdt, nodename, "#address-cells",
- FDT_PCI_ADDR_CELLS);
- qemu_fdt_setprop_cells(fdt, nodename, "#interrupt-cells",
- FDT_PCI_INT_CELLS);
- qemu_fdt_setprop_cells(fdt, nodename, "#size-cells", 0x2);
+ qemu_fdt_setprop_cell(fdt, nodename, "#address-cells",
+ FDT_PCI_ADDR_CELLS);
+ qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells",
+ FDT_PCI_INT_CELLS);
+ qemu_fdt_setprop_cell(fdt, nodename, "#size-cells", 0x2);
qemu_fdt_setprop_string(fdt, nodename, "compatible",
"pci-host-ecam-generic");
qemu_fdt_setprop_string(fdt, nodename, "device_type", "pci");
@@ -312,8 +307,8 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
0x0, memmap[VIRT_UART0].base,
0x0, memmap[VIRT_UART0].size);
qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 3686400);
- qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
- qemu_fdt_setprop_cells(fdt, nodename, "interrupts", UART0_IRQ);
+ qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle);
+ qemu_fdt_setprop_cell(fdt, nodename, "interrupts", UART0_IRQ);
qemu_fdt_add_subnode(fdt, "/chosen");
qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename);
@@ -321,8 +316,6 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
}
g_free(nodename);
-
- return fdt;
}
@@ -378,7 +371,6 @@ static void riscv_virt_board_init(MachineState *machine)
size_t plic_hart_config_len;
int i;
unsigned int smp_cpus = machine->smp.cpus;
- void *fdt;
/* Initialize SOC */
object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc),
@@ -397,7 +389,7 @@ static void riscv_virt_board_init(MachineState *machine)
main_mem);
/* create device tree */
- fdt = create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
+ create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
/* boot rom */
memory_region_init_rom(mask_rom, NULL, "riscv_virt_board.mrom",
@@ -416,9 +408,9 @@ static void riscv_virt_board_init(MachineState *machine)
hwaddr end = riscv_load_initrd(machine->initrd_filename,
machine->ram_size, kernel_entry,
&start);
- qemu_fdt_setprop_cell(fdt, "/chosen",
+ qemu_fdt_setprop_cell(s->fdt, "/chosen",
"linux,initrd-start", start);
- qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+ qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end",
end);
}
}
diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h
index 1f21c2bef1..66075d0e57 100644
--- a/include/hw/riscv/boot.h
+++ b/include/hw/riscv/boot.h
@@ -25,6 +25,7 @@
void riscv_find_and_load_firmware(MachineState *machine,
const char *default_machine_firmware,
hwaddr firmware_load_addr);
+char *riscv_find_firmware(const char *firmware_filename);
target_ulong riscv_load_firmware(const char *firmware_filename,
hwaddr firmware_load_addr);
target_ulong riscv_load_kernel(const char *kernel_filename);
diff --git a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h
index 3b52b50571..c75856fa73 100644
--- a/include/hw/riscv/riscv_hart.h
+++ b/include/hw/riscv/riscv_hart.h
@@ -35,6 +35,7 @@ typedef struct RISCVHartArrayState {
/*< public >*/
uint32_t num_harts;
+ uint32_t hartid_base;
char *cpu_type;
RISCVCPU *harts;
} RISCVHartArrayState;
diff --git a/include/hw/riscv/sifive_cpu.h b/include/hw/riscv/sifive_cpu.h
new file mode 100644
index 0000000000..136799633a
--- /dev/null
+++ b/include/hw/riscv/sifive_cpu.h
@@ -0,0 +1,31 @@
+/*
+ * SiFive CPU types
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.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 HW_SIFIVE_CPU_H
+#define HW_SIFIVE_CPU_H
+
+#if defined(TARGET_RISCV32)
+#define SIFIVE_E_CPU TYPE_RISCV_CPU_SIFIVE_E31
+#define SIFIVE_U_CPU TYPE_RISCV_CPU_SIFIVE_U34
+#elif defined(TARGET_RISCV64)
+#define SIFIVE_E_CPU TYPE_RISCV_CPU_SIFIVE_E51
+#define SIFIVE_U_CPU TYPE_RISCV_CPU_SIFIVE_U54
+#endif
+
+#endif /* HW_SIFIVE_CPU_H */
diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h
index 9c868dd7f9..25ce7aa9d5 100644
--- a/include/hw/riscv/sifive_e.h
+++ b/include/hw/riscv/sifive_e.h
@@ -20,6 +20,7 @@
#define HW_SIFIVE_E_H
#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_cpu.h"
#include "hw/riscv/sifive_gpio.h"
#define TYPE_RISCV_E_SOC "riscv.sifive.e.soc"
@@ -84,10 +85,4 @@ enum {
#define SIFIVE_E_PLIC_CONTEXT_BASE 0x200000
#define SIFIVE_E_PLIC_CONTEXT_STRIDE 0x1000
-#if defined(TARGET_RISCV32)
-#define SIFIVE_E_CPU TYPE_RISCV_CPU_SIFIVE_E31
-#elif defined(TARGET_RISCV64)
-#define SIFIVE_E_CPU TYPE_RISCV_CPU_SIFIVE_E51
-#endif
-
#endif
diff --git a/include/hw/riscv/sifive_e_prci.h b/include/hw/riscv/sifive_e_prci.h
new file mode 100644
index 0000000000..698b0b451c
--- /dev/null
+++ b/include/hw/riscv/sifive_e_prci.h
@@ -0,0 +1,71 @@
+/*
+ * QEMU SiFive E PRCI (Power, Reset, Clock, Interrupt) interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 HW_SIFIVE_E_PRCI_H
+#define HW_SIFIVE_E_PRCI_H
+
+enum {
+ SIFIVE_E_PRCI_HFROSCCFG = 0x0,
+ SIFIVE_E_PRCI_HFXOSCCFG = 0x4,
+ SIFIVE_E_PRCI_PLLCFG = 0x8,
+ SIFIVE_E_PRCI_PLLOUTDIV = 0xC
+};
+
+enum {
+ SIFIVE_E_PRCI_HFROSCCFG_RDY = (1 << 31),
+ SIFIVE_E_PRCI_HFROSCCFG_EN = (1 << 30)
+};
+
+enum {
+ SIFIVE_E_PRCI_HFXOSCCFG_RDY = (1 << 31),
+ SIFIVE_E_PRCI_HFXOSCCFG_EN = (1 << 30)
+};
+
+enum {
+ SIFIVE_E_PRCI_PLLCFG_PLLSEL = (1 << 16),
+ SIFIVE_E_PRCI_PLLCFG_REFSEL = (1 << 17),
+ SIFIVE_E_PRCI_PLLCFG_BYPASS = (1 << 18),
+ SIFIVE_E_PRCI_PLLCFG_LOCK = (1 << 31)
+};
+
+enum {
+ SIFIVE_E_PRCI_PLLOUTDIV_DIV1 = (1 << 8)
+};
+
+#define SIFIVE_E_PRCI_REG_SIZE 0x1000
+
+#define TYPE_SIFIVE_E_PRCI "riscv.sifive.e.prci"
+
+#define SIFIVE_E_PRCI(obj) \
+ OBJECT_CHECK(SiFiveEPRCIState, (obj), TYPE_SIFIVE_E_PRCI)
+
+typedef struct SiFiveEPRCIState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ MemoryRegion mmio;
+ uint32_t hfrosccfg;
+ uint32_t hfxosccfg;
+ uint32_t pllcfg;
+ uint32_t plloutdiv;
+} SiFiveEPRCIState;
+
+DeviceState *sifive_e_prci_create(hwaddr addr);
+
+#endif
diff --git a/include/hw/riscv/sifive_plic.h b/include/hw/riscv/sifive_plic.h
index b0edba2884..4421e81249 100644
--- a/include/hw/riscv/sifive_plic.h
+++ b/include/hw/riscv/sifive_plic.h
@@ -69,9 +69,6 @@ typedef struct SiFivePLICState {
uint32_t aperture_size;
} SiFivePLICState;
-void sifive_plic_raise_irq(SiFivePLICState *plic, uint32_t irq);
-void sifive_plic_lower_irq(SiFivePLICState *plic, uint32_t irq);
-
DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
uint32_t num_sources, uint32_t num_priorities,
uint32_t priority_base, uint32_t pending_base,
diff --git a/include/hw/riscv/sifive_prci.h b/include/hw/riscv/sifive_prci.h
deleted file mode 100644
index 8b7de134f8..0000000000
--- a/include/hw/riscv/sifive_prci.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * QEMU SiFive PRCI (Power, Reset, Clock, Interrupt) interface
- *
- * Copyright (c) 2017 SiFive, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2 or later, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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 HW_SIFIVE_PRCI_H
-#define HW_SIFIVE_PRCI_H
-
-#include "hw/sysbus.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) \
- OBJECT_CHECK(SiFivePRCIState, (obj), TYPE_SIFIVE_PRCI)
-
-typedef struct SiFivePRCIState {
- /*< private >*/
- SysBusDevice parent_obj;
-
- /*< public >*/
- MemoryRegion mmio;
- uint32_t hfrosccfg;
- uint32_t hfxosccfg;
- uint32_t pllcfg;
- uint32_t plloutdiv;
-} SiFivePRCIState;
-
-DeviceState *sifive_prci_create(hwaddr addr);
-
-#endif
diff --git a/include/hw/riscv/sifive_test.h b/include/hw/riscv/sifive_test.h
index 3a603a6ead..1ec416ac1b 100644
--- a/include/hw/riscv/sifive_test.h
+++ b/include/hw/riscv/sifive_test.h
@@ -36,7 +36,8 @@ typedef struct SiFiveTestState {
enum {
FINISHER_FAIL = 0x3333,
- FINISHER_PASS = 0x5555
+ FINISHER_PASS = 0x5555,
+ FINISHER_RESET = 0x7777
};
DeviceState *sifive_test_create(hwaddr addr);
diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h
index be021ce256..e4df298c23 100644
--- a/include/hw/riscv/sifive_u.h
+++ b/include/hw/riscv/sifive_u.h
@@ -21,6 +21,9 @@
#include "hw/net/cadence_gem.h"
#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_cpu.h"
+#include "hw/riscv/sifive_u_prci.h"
+#include "hw/riscv/sifive_u_otp.h"
#define TYPE_RISCV_U_SOC "riscv.sifive.u.soc"
#define RISCV_U_SOC(obj) \
@@ -31,8 +34,13 @@ typedef struct SiFiveUSoCState {
SysBusDevice parent_obj;
/*< public >*/
- RISCVHartArrayState cpus;
+ CPUClusterState e_cluster;
+ CPUClusterState u_cluster;
+ RISCVHartArrayState e_cpus;
+ RISCVHartArrayState u_cpus;
DeviceState *plic;
+ SiFiveUPRCIState prci;
+ SiFiveUOTPState otp;
CadenceGEMState gem;
} SiFiveUSoCState;
@@ -51,23 +59,30 @@ enum {
SIFIVE_U_MROM,
SIFIVE_U_CLINT,
SIFIVE_U_PLIC,
+ SIFIVE_U_PRCI,
SIFIVE_U_UART0,
SIFIVE_U_UART1,
+ SIFIVE_U_OTP,
SIFIVE_U_DRAM,
- SIFIVE_U_GEM
+ SIFIVE_U_GEM,
+ SIFIVE_U_GEM_MGMT
};
enum {
- SIFIVE_U_UART0_IRQ = 3,
- SIFIVE_U_UART1_IRQ = 4,
+ SIFIVE_U_UART0_IRQ = 4,
+ SIFIVE_U_UART1_IRQ = 5,
SIFIVE_U_GEM_IRQ = 0x35
};
enum {
SIFIVE_U_CLOCK_FREQ = 1000000000,
- SIFIVE_U_GEM_CLOCK_FREQ = 125000000
+ SIFIVE_U_HFCLK_FREQ = 33333333,
+ SIFIVE_U_RTCCLK_FREQ = 1000000
};
+#define SIFIVE_U_MANAGEMENT_CPU_COUNT 1
+#define SIFIVE_U_COMPUTE_CPU_COUNT 4
+
#define SIFIVE_U_PLIC_HART_CONFIG "MS"
#define SIFIVE_U_PLIC_NUM_SOURCES 54
#define SIFIVE_U_PLIC_NUM_PRIORITIES 7
@@ -78,10 +93,4 @@ enum {
#define SIFIVE_U_PLIC_CONTEXT_BASE 0x200000
#define SIFIVE_U_PLIC_CONTEXT_STRIDE 0x1000
-#if defined(TARGET_RISCV32)
-#define SIFIVE_U_CPU TYPE_RISCV_CPU_SIFIVE_U34
-#elif defined(TARGET_RISCV64)
-#define SIFIVE_U_CPU TYPE_RISCV_CPU_SIFIVE_U54
-#endif
-
#endif
diff --git a/include/hw/riscv/sifive_u_otp.h b/include/hw/riscv/sifive_u_otp.h
new file mode 100644
index 0000000000..639297564a
--- /dev/null
+++ b/include/hw/riscv/sifive_u_otp.h
@@ -0,0 +1,80 @@
+/*
+ * QEMU SiFive U OTP (One-Time Programmable) Memory interface
+ *
+ * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.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 HW_SIFIVE_U_OTP_H
+#define HW_SIFIVE_U_OTP_H
+
+#define SIFIVE_U_OTP_PA 0x00
+#define SIFIVE_U_OTP_PAIO 0x04
+#define SIFIVE_U_OTP_PAS 0x08
+#define SIFIVE_U_OTP_PCE 0x0C
+#define SIFIVE_U_OTP_PCLK 0x10
+#define SIFIVE_U_OTP_PDIN 0x14
+#define SIFIVE_U_OTP_PDOUT 0x18
+#define SIFIVE_U_OTP_PDSTB 0x1C
+#define SIFIVE_U_OTP_PPROG 0x20
+#define SIFIVE_U_OTP_PTC 0x24
+#define SIFIVE_U_OTP_PTM 0x28
+#define SIFIVE_U_OTP_PTM_REP 0x2C
+#define SIFIVE_U_OTP_PTR 0x30
+#define SIFIVE_U_OTP_PTRIM 0x34
+#define SIFIVE_U_OTP_PWE 0x38
+
+#define SIFIVE_U_OTP_PCE_EN (1 << 0)
+
+#define SIFIVE_U_OTP_PDSTB_EN (1 << 0)
+
+#define SIFIVE_U_OTP_PTRIM_EN (1 << 0)
+
+#define SIFIVE_U_OTP_PA_MASK 0xfff
+#define SIFIVE_U_OTP_NUM_FUSES 0x1000
+#define SIFIVE_U_OTP_SERIAL_ADDR 0xfc
+
+#define SIFIVE_U_OTP_REG_SIZE 0x1000
+
+#define TYPE_SIFIVE_U_OTP "riscv.sifive.u.otp"
+
+#define SIFIVE_U_OTP(obj) \
+ OBJECT_CHECK(SiFiveUOTPState, (obj), TYPE_SIFIVE_U_OTP)
+
+typedef struct SiFiveUOTPState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ MemoryRegion mmio;
+ uint32_t pa;
+ uint32_t paio;
+ uint32_t pas;
+ uint32_t pce;
+ uint32_t pclk;
+ uint32_t pdin;
+ uint32_t pdstb;
+ uint32_t pprog;
+ uint32_t ptc;
+ uint32_t ptm;
+ uint32_t ptm_rep;
+ uint32_t ptr;
+ uint32_t ptrim;
+ uint32_t pwe;
+ uint32_t fuse[SIFIVE_U_OTP_NUM_FUSES];
+ /* config */
+ uint32_t serial;
+} SiFiveUOTPState;
+
+#endif /* HW_SIFIVE_U_OTP_H */
diff --git a/include/hw/riscv/sifive_u_prci.h b/include/hw/riscv/sifive_u_prci.h
new file mode 100644
index 0000000000..0a531fdadc
--- /dev/null
+++ b/include/hw/riscv/sifive_u_prci.h
@@ -0,0 +1,91 @@
+/*
+ * QEMU SiFive U PRCI (Power, Reset, Clock, Interrupt) interface
+ *
+ * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.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 HW_SIFIVE_U_PRCI_H
+#define HW_SIFIVE_U_PRCI_H
+
+#define SIFIVE_U_PRCI_HFXOSCCFG 0x00
+#define SIFIVE_U_PRCI_COREPLLCFG0 0x04
+#define SIFIVE_U_PRCI_DDRPLLCFG0 0x0C
+#define SIFIVE_U_PRCI_DDRPLLCFG1 0x10
+#define SIFIVE_U_PRCI_GEMGXLPLLCFG0 0x1C
+#define SIFIVE_U_PRCI_GEMGXLPLLCFG1 0x20
+#define SIFIVE_U_PRCI_CORECLKSEL 0x24
+#define SIFIVE_U_PRCI_DEVICESRESET 0x28
+#define SIFIVE_U_PRCI_CLKMUXSTATUS 0x2C
+
+/*
+ * Current FU540-C000 manual says ready bit is at bit 29, but
+ * freedom-u540-c000-bootloader codes (ux00prci.h) says it is at bit 31.
+ * We have to trust the actual code that works.
+ *
+ * see https://github.com/sifive/freedom-u540-c000-bootloader
+ */
+
+#define SIFIVE_U_PRCI_HFXOSCCFG_EN (1 << 30)
+#define SIFIVE_U_PRCI_HFXOSCCFG_RDY (1 << 31)
+
+/* xxxPLLCFG0 register bits */
+#define SIFIVE_U_PRCI_PLLCFG0_DIVR (1 << 0)
+#define SIFIVE_U_PRCI_PLLCFG0_DIVF (31 << 6)
+#define SIFIVE_U_PRCI_PLLCFG0_DIVQ (3 << 15)
+#define SIFIVE_U_PRCI_PLLCFG0_FSE (1 << 25)
+#define SIFIVE_U_PRCI_PLLCFG0_LOCK (1 << 31)
+
+/* xxxPLLCFG1 register bits */
+#define SIFIVE_U_PRCI_PLLCFG1_CKE (1 << 24)
+
+/* coreclksel register bits */
+#define SIFIVE_U_PRCI_CORECLKSEL_HFCLK (1 << 0)
+
+
+#define SIFIVE_U_PRCI_REG_SIZE 0x1000
+
+#define TYPE_SIFIVE_U_PRCI "riscv.sifive.u.prci"
+
+#define SIFIVE_U_PRCI(obj) \
+ OBJECT_CHECK(SiFiveUPRCIState, (obj), TYPE_SIFIVE_U_PRCI)
+
+typedef struct SiFiveUPRCIState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ MemoryRegion mmio;
+ uint32_t hfxosccfg;
+ uint32_t corepllcfg0;
+ uint32_t ddrpllcfg0;
+ uint32_t ddrpllcfg1;
+ uint32_t gemgxlpllcfg0;
+ uint32_t gemgxlpllcfg1;
+ uint32_t coreclksel;
+ uint32_t devicesreset;
+ uint32_t clkmuxstatus;
+} SiFiveUPRCIState;
+
+/*
+ * Clock indexes for use by Device Tree data and the PRCI driver.
+ *
+ * These values are from sifive-fu540-prci.h in the Linux kernel.
+ */
+#define PRCI_CLK_COREPLL 0
+#define PRCI_CLK_DDRPLL 1
+#define PRCI_CLK_GEMGXLPLL 2
+#define PRCI_CLK_TLCLK 3
+
+#endif /* HW_SIFIVE_U_PRCI_H */
diff --git a/pc-bios/opensbi-riscv32-virt-fw_jump.bin b/pc-bios/opensbi-riscv32-virt-fw_jump.bin
index f5bcaa5695..f5bcaa5695 100755..100644
--- a/pc-bios/opensbi-riscv32-virt-fw_jump.bin
+++ b/pc-bios/opensbi-riscv32-virt-fw_jump.bin
Binary files differ
diff --git a/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin b/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
index 5d7a1ef681..eb22aefdfb 100755..100644
--- a/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
+++ b/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
Binary files differ
diff --git a/pc-bios/opensbi-riscv64-virt-fw_jump.bin b/pc-bios/opensbi-riscv64-virt-fw_jump.bin
index 4cec6f0210..4cec6f0210 100755..100644
--- a/pc-bios/opensbi-riscv64-virt-fw_jump.bin
+++ b/pc-bios/opensbi-riscv64-virt-fw_jump.bin
Binary files differ
diff --git a/roms/Makefile b/roms/Makefile
index 775c963f9d..6cf07d3b44 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -182,8 +182,8 @@ opensbi64-virt:
opensbi64-sifive_u:
$(MAKE) -C opensbi \
CROSS_COMPILE=$(riscv64_cross_prefix) \
- PLATFORM="qemu/sifive_u"
- cp opensbi/build/platform/qemu/sifive_u/firmware/fw_jump.bin ../pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
+ PLATFORM="sifive/fu540"
+ cp opensbi/build/platform/sifive/fu540/firmware/fw_jump.bin ../pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
clean:
rm -rf seabios/.config seabios/out seabios/builds
diff --git a/target/riscv/Makefile.objs b/target/riscv/Makefile.objs
index b1c79bc1d1..ff651f69f6 100644
--- a/target/riscv/Makefile.objs
+++ b/target/riscv/Makefile.objs
@@ -1,4 +1,9 @@
-obj-y += translate.o op_helper.o cpu_helper.o cpu.o csr.o fpu_helper.o gdbstub.o pmp.o
+obj-y += translate.o op_helper.o cpu_helper.o cpu.o csr.o fpu_helper.o gdbstub.o
+obj-$(CONFIG_SOFTMMU) += pmp.o
+
+ifeq ($(CONFIG_SOFTMMU),y)
+obj-y += monitor.o
+endif
DECODETREE = $(SRC_PATH)/scripts/decodetree.py
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 6d52f97d7c..f13e298a36 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -34,17 +34,20 @@
static const char riscv_exts[26] = "IEMAFDQCLBJTPVNSUHKORWXYZG";
const char * const riscv_int_regnames[] = {
- "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
- "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
- "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7",
- "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"
+ "x0/zero", "x1/ra", "x2/sp", "x3/gp", "x4/tp", "x5/t0", "x6/t1",
+ "x7/t2", "x8/s0", "x9/s1", "x10/a0", "x11/a1", "x12/a2", "x13/a3",
+ "x14/a4", "x15/a5", "x16/a6", "x17/a7", "x18/s2", "x19/s3", "x20/s4",
+ "x21/s5", "x22/s6", "x23/s7", "x24/s8", "x25/s9", "x26/s10", "x27/s11",
+ "x28/t3", "x29/t4", "x30/t5", "x31/t6"
};
const char * const riscv_fpr_regnames[] = {
- "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7",
- "fs0", "fs1", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5",
- "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7",
- "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11"
+ "f0/ft0", "f1/ft1", "f2/ft2", "f3/ft3", "f4/ft4", "f5/ft5",
+ "f6/ft6", "f7/ft7", "f8/fs0", "f9/fs1", "f10/fa0", "f11/fa1",
+ "f12/fa2", "f13/fa3", "f14/fa4", "f15/fa5", "f16/fa6", "f17/fa7",
+ "f18/fs2", "f19/fs3", "f20/fs4", "f21/fs5", "f22/fs6", "f23/fs7",
+ "f24/fs8", "f25/fs9", "f26/fs10", "f27/fs11", "f28/ft8", "f29/ft9",
+ "f30/ft10", "f31/ft11"
};
const char * const riscv_excp_names[] = {
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 18d91d0920..124ed33ee4 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -255,6 +255,7 @@ void riscv_cpu_do_interrupt(CPUState *cpu);
int riscv_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request);
+bool riscv_cpu_fp_enabled(CPURISCVState *env);
int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch);
hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
@@ -298,7 +299,10 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
#ifdef CONFIG_USER_ONLY
*flags = TB_FLAGS_MSTATUS_FS;
#else
- *flags = cpu_mmu_index(env, 0) | (env->mstatus & MSTATUS_FS);
+ *flags = cpu_mmu_index(env, 0);
+ if (riscv_cpu_fp_enabled(env)) {
+ *flags |= TB_FLAGS_MSTATUS_FS;
+ }
#endif
}
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 11f971ad5d..e99834856c 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -173,6 +173,24 @@
#define CSR_SPTBR 0x180
#define CSR_SATP 0x180
+/* Hpervisor CSRs */
+#define CSR_HSTATUS 0x600
+#define CSR_HEDELEG 0x602
+#define CSR_HIDELEG 0x603
+#define CSR_HCOUNTERNEN 0x606
+#define CSR_HGATP 0x680
+
+#if defined(TARGET_RISCV32)
+#define HGATP_MODE SATP32_MODE
+#define HGATP_VMID SATP32_ASID
+#define HGATP_PPN SATP32_PPN
+#endif
+#if defined(TARGET_RISCV64)
+#define HGATP_MODE SATP64_MODE
+#define HGATP_VMID SATP64_ASID
+#define HGATP_PPN SATP64_PPN
+#endif
+
/* Physical Memory Protection */
#define CSR_PMPCFG0 0x3a0
#define CSR_PMPCFG1 0x3a1
@@ -206,23 +224,6 @@
#define CSR_DPC 0x7b1
#define CSR_DSCRATCH 0x7b2
-/* Hpervisor CSRs */
-#define CSR_HSTATUS 0xa00
-#define CSR_HEDELEG 0xa02
-#define CSR_HIDELEG 0xa03
-#define CSR_HGATP 0xa80
-
-#if defined(TARGET_RISCV32)
-#define HGATP_MODE SATP32_MODE
-#define HGATP_ASID SATP32_ASID
-#define HGATP_PPN SATP32_PPN
-#endif
-#if defined(TARGET_RISCV64)
-#define HGATP_MODE SATP64_MODE
-#define HGATP_ASID SATP64_ASID
-#define HGATP_PPN SATP64_PPN
-#endif
-
/* Performance Counters */
#define CSR_MHPMCOUNTER3 0xb03
#define CSR_MHPMCOUNTER4 0xb04
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index e32b6126af..87dd6a6ece 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -71,6 +71,16 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
#if !defined(CONFIG_USER_ONLY)
+/* Return true is floating point support is currently enabled */
+bool riscv_cpu_fp_enabled(CPURISCVState *env)
+{
+ if (env->mstatus & MSTATUS_FS) {
+ return true;
+ }
+
+ return false;
+}
+
int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts)
{
CPURISCVState *env = &cpu->env;
@@ -176,12 +186,12 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
*prot = 0;
- target_ulong base;
+ hwaddr base;
int levels, ptidxbits, ptesize, vm, sum;
int mxr = get_field(env->mstatus, MSTATUS_MXR);
if (env->priv_ver >= PRIV_VERSION_1_10_0) {
- base = get_field(env->satp, SATP_PPN) << PGSHIFT;
+ base = (hwaddr)get_field(env->satp, SATP_PPN) << PGSHIFT;
sum = get_field(env->mstatus, MSTATUS_SUM);
vm = get_field(env->satp, SATP_MODE);
switch (vm) {
@@ -201,7 +211,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
g_assert_not_reached();
}
} else {
- base = env->sptbr << PGSHIFT;
+ base = (hwaddr)(env->sptbr) << PGSHIFT;
sum = !get_field(env->mstatus, MSTATUS_PUM);
vm = get_field(env->mstatus, MSTATUS_VM);
switch (vm) {
@@ -239,7 +249,7 @@ restart:
((1 << ptidxbits) - 1);
/* check that physical address of PTE is legal */
- target_ulong pte_addr = base + idx * ptesize;
+ hwaddr pte_addr = base + idx * ptesize;
if (riscv_feature(env, RISCV_FEATURE_PMP) &&
!pmp_hart_has_privs(env, pte_addr, sizeof(target_ulong),
@@ -251,7 +261,7 @@ restart:
#elif defined(TARGET_RISCV64)
target_ulong pte = ldq_phys(cs->as, pte_addr);
#endif
- target_ulong ppn = pte >> PTE_PPN_SHIFT;
+ hwaddr ppn = pte >> PTE_PPN_SHIFT;
if (!(pte & PTE_V)) {
/* Invalid PTE */
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index e0d4586760..f767ad24be 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -46,7 +46,7 @@ void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops)
static int fs(CPURISCVState *env, int csrno)
{
#if !defined(CONFIG_USER_ONLY)
- if (!env->debugger && !(env->mstatus & MSTATUS_FS)) {
+ if (!env->debugger && !riscv_cpu_fp_enabled(env)) {
return -1;
}
#endif
@@ -108,7 +108,7 @@ static int pmp(CPURISCVState *env, int csrno)
static int read_fflags(CPURISCVState *env, int csrno, target_ulong *val)
{
#if !defined(CONFIG_USER_ONLY)
- if (!env->debugger && !(env->mstatus & MSTATUS_FS)) {
+ if (!env->debugger && !riscv_cpu_fp_enabled(env)) {
return -1;
}
#endif
@@ -119,7 +119,7 @@ static int read_fflags(CPURISCVState *env, int csrno, target_ulong *val)
static int write_fflags(CPURISCVState *env, int csrno, target_ulong val)
{
#if !defined(CONFIG_USER_ONLY)
- if (!env->debugger && !(env->mstatus & MSTATUS_FS)) {
+ if (!env->debugger && !riscv_cpu_fp_enabled(env)) {
return -1;
}
env->mstatus |= MSTATUS_FS;
@@ -131,7 +131,7 @@ static int write_fflags(CPURISCVState *env, int csrno, target_ulong val)
static int read_frm(CPURISCVState *env, int csrno, target_ulong *val)
{
#if !defined(CONFIG_USER_ONLY)
- if (!env->debugger && !(env->mstatus & MSTATUS_FS)) {
+ if (!env->debugger && !riscv_cpu_fp_enabled(env)) {
return -1;
}
#endif
@@ -142,7 +142,7 @@ static int read_frm(CPURISCVState *env, int csrno, target_ulong *val)
static int write_frm(CPURISCVState *env, int csrno, target_ulong val)
{
#if !defined(CONFIG_USER_ONLY)
- if (!env->debugger && !(env->mstatus & MSTATUS_FS)) {
+ if (!env->debugger && !riscv_cpu_fp_enabled(env)) {
return -1;
}
env->mstatus |= MSTATUS_FS;
@@ -154,7 +154,7 @@ static int write_frm(CPURISCVState *env, int csrno, target_ulong val)
static int read_fcsr(CPURISCVState *env, int csrno, target_ulong *val)
{
#if !defined(CONFIG_USER_ONLY)
- if (!env->debugger && !(env->mstatus & MSTATUS_FS)) {
+ if (!env->debugger && !riscv_cpu_fp_enabled(env)) {
return -1;
}
#endif
@@ -166,7 +166,7 @@ static int read_fcsr(CPURISCVState *env, int csrno, target_ulong *val)
static int write_fcsr(CPURISCVState *env, int csrno, target_ulong val)
{
#if !defined(CONFIG_USER_ONLY)
- if (!env->debugger && !(env->mstatus & MSTATUS_FS)) {
+ if (!env->debugger && !riscv_cpu_fp_enabled(env)) {
return -1;
}
env->mstatus |= MSTATUS_FS;
@@ -307,6 +307,7 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val)
{
target_ulong mstatus = env->mstatus;
target_ulong mask = 0;
+ int dirty;
/* flush tlb on mstatus fields that affect VM */
if (env->priv_ver <= PRIV_VERSION_1_09_1) {
@@ -334,14 +335,15 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val)
* RV32: MPV and MTL are not in mstatus. The current plan is to
* add them to mstatush. For now, we just don't support it.
*/
- mask |= MSTATUS_MPP | MSTATUS_MPV;
+ mask |= MSTATUS_MTL | MSTATUS_MPV;
#endif
}
mstatus = (mstatus & ~mask) | (val & mask);
- int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) |
- ((mstatus & MSTATUS_XS) == MSTATUS_XS);
+ dirty = (riscv_cpu_fp_enabled(env) &&
+ ((mstatus & MSTATUS_FS) == MSTATUS_FS)) |
+ ((mstatus & MSTATUS_XS) == MSTATUS_XS);
mstatus = set_field(mstatus, MSTATUS_SD, dirty);
env->mstatus = mstatus;
diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c
index 27be93279b..ded140e8d8 100644
--- a/target/riscv/gdbstub.c
+++ b/target/riscv/gdbstub.c
@@ -313,7 +313,8 @@ static int riscv_gdb_get_fpu(CPURISCVState *env, uint8_t *mem_buf, int n)
* register 33, so we recalculate the map index.
* This also works for CSR_FRM and CSR_FCSR.
*/
- result = riscv_csrrw_debug(env, n - 33 + 8, &val, 0, 0);
+ result = riscv_csrrw_debug(env, n - 33 + csr_register_map[8], &val,
+ 0, 0);
if (result == 0) {
return gdb_get_regl(mem_buf, val);
}
@@ -335,7 +336,8 @@ static int riscv_gdb_set_fpu(CPURISCVState *env, uint8_t *mem_buf, int n)
* register 33, so we recalculate the map index.
* This also works for CSR_FRM and CSR_FCSR.
*/
- result = riscv_csrrw_debug(env, n - 33 + 8, NULL, val, -1);
+ result = riscv_csrrw_debug(env, n - 33 + csr_register_map[8], NULL,
+ val, -1);
if (result == 0) {
return sizeof(target_ulong);
}
diff --git a/target/riscv/monitor.c b/target/riscv/monitor.c
new file mode 100644
index 0000000000..d725a7a36e
--- /dev/null
+++ b/target/riscv/monitor.c
@@ -0,0 +1,229 @@
+/*
+ * QEMU monitor for RISC-V
+ *
+ * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com>
+ *
+ * RISC-V specific monitor commands implementation
+ *
+ * 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 "cpu.h"
+#include "cpu_bits.h"
+#include "monitor/monitor.h"
+#include "monitor/hmp-target.h"
+
+#ifdef TARGET_RISCV64
+#define PTE_HEADER_FIELDS "vaddr paddr "\
+ "size attr\n"
+#define PTE_HEADER_DELIMITER "---------------- ---------------- "\
+ "---------------- -------\n"
+#else
+#define PTE_HEADER_FIELDS "vaddr paddr size attr\n"
+#define PTE_HEADER_DELIMITER "-------- ---------------- -------- -------\n"
+#endif
+
+/* Perform linear address sign extension */
+static target_ulong addr_canonical(int va_bits, target_ulong addr)
+{
+#ifdef TARGET_RISCV64
+ if (addr & (1UL << (va_bits - 1))) {
+ addr |= (hwaddr)-(1L << va_bits);
+ }
+#endif
+
+ return addr;
+}
+
+static void print_pte_header(Monitor *mon)
+{
+ monitor_printf(mon, PTE_HEADER_FIELDS);
+ monitor_printf(mon, PTE_HEADER_DELIMITER);
+}
+
+static void print_pte(Monitor *mon, int va_bits, target_ulong vaddr,
+ hwaddr paddr, target_ulong size, int attr)
+{
+ /* santity check on vaddr */
+ if (vaddr >= (1UL << va_bits)) {
+ return;
+ }
+
+ if (!size) {
+ return;
+ }
+
+ monitor_printf(mon, TARGET_FMT_lx " " TARGET_FMT_plx " " TARGET_FMT_lx
+ " %c%c%c%c%c%c%c\n",
+ addr_canonical(va_bits, vaddr),
+ paddr, size,
+ attr & PTE_R ? 'r' : '-',
+ attr & PTE_W ? 'w' : '-',
+ attr & PTE_X ? 'x' : '-',
+ attr & PTE_U ? 'u' : '-',
+ attr & PTE_G ? 'g' : '-',
+ attr & PTE_A ? 'a' : '-',
+ attr & PTE_D ? 'd' : '-');
+}
+
+static void walk_pte(Monitor *mon, hwaddr base, target_ulong start,
+ int level, int ptidxbits, int ptesize, int va_bits,
+ target_ulong *vbase, hwaddr *pbase, hwaddr *last_paddr,
+ target_ulong *last_size, int *last_attr)
+{
+ hwaddr pte_addr;
+ hwaddr paddr;
+ target_ulong pgsize;
+ target_ulong pte;
+ int ptshift;
+ int attr;
+ int idx;
+
+ if (level < 0) {
+ return;
+ }
+
+ ptshift = level * ptidxbits;
+ pgsize = 1UL << (PGSHIFT + ptshift);
+
+ for (idx = 0; idx < (1UL << ptidxbits); idx++) {
+ pte_addr = base + idx * ptesize;
+ cpu_physical_memory_read(pte_addr, &pte, ptesize);
+
+ paddr = (hwaddr)(pte >> PTE_PPN_SHIFT) << PGSHIFT;
+ attr = pte & 0xff;
+
+ /* PTE has to be valid */
+ if (attr & PTE_V) {
+ if (attr & (PTE_R | PTE_W | PTE_X)) {
+ /*
+ * A leaf PTE has been found
+ *
+ * If current PTE's permission bits differ from the last one,
+ * or current PTE's ppn does not make a contiguous physical
+ * address block together with the last one, print out the last
+ * contiguous mapped block details.
+ */
+ if ((*last_attr != attr) ||
+ (*last_paddr + *last_size != paddr)) {
+ print_pte(mon, va_bits, *vbase, *pbase,
+ *last_paddr + *last_size - *pbase, *last_attr);
+
+ *vbase = start;
+ *pbase = paddr;
+ *last_attr = attr;
+ }
+
+ *last_paddr = paddr;
+ *last_size = pgsize;
+ } else {
+ /* pointer to the next level of the page table */
+ walk_pte(mon, paddr, start, level - 1, ptidxbits, ptesize,
+ va_bits, vbase, pbase, last_paddr,
+ last_size, last_attr);
+ }
+ }
+
+ start += pgsize;
+ }
+
+}
+
+static void mem_info_svxx(Monitor *mon, CPUArchState *env)
+{
+ int levels, ptidxbits, ptesize, vm, va_bits;
+ hwaddr base;
+ target_ulong vbase;
+ hwaddr pbase;
+ hwaddr last_paddr;
+ target_ulong last_size;
+ int last_attr;
+
+ base = (hwaddr)get_field(env->satp, SATP_PPN) << PGSHIFT;
+
+ vm = get_field(env->satp, SATP_MODE);
+ switch (vm) {
+ case VM_1_10_SV32:
+ levels = 2;
+ ptidxbits = 10;
+ ptesize = 4;
+ break;
+ case VM_1_10_SV39:
+ levels = 3;
+ ptidxbits = 9;
+ ptesize = 8;
+ break;
+ case VM_1_10_SV48:
+ levels = 4;
+ ptidxbits = 9;
+ ptesize = 8;
+ break;
+ case VM_1_10_SV57:
+ levels = 5;
+ ptidxbits = 9;
+ ptesize = 8;
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ /* calculate virtual address bits */
+ va_bits = PGSHIFT + levels * ptidxbits;
+
+ /* print header */
+ print_pte_header(mon);
+
+ vbase = -1;
+ pbase = -1;
+ last_paddr = -1;
+ last_size = 0;
+ last_attr = 0;
+
+ /* walk page tables, starting from address 0 */
+ walk_pte(mon, base, 0, levels - 1, ptidxbits, ptesize, va_bits,
+ &vbase, &pbase, &last_paddr, &last_size, &last_attr);
+
+ /* don't forget the last one */
+ print_pte(mon, va_bits, vbase, pbase,
+ last_paddr + last_size - pbase, last_attr);
+}
+
+void hmp_info_mem(Monitor *mon, const QDict *qdict)
+{
+ CPUArchState *env;
+
+ env = mon_get_cpu_env();
+ if (!env) {
+ monitor_printf(mon, "No CPU available\n");
+ return;
+ }
+
+ if (!riscv_feature(env, RISCV_FEATURE_MMU)) {
+ monitor_printf(mon, "S-mode MMU unavailable\n");
+ return;
+ }
+
+ if (env->priv_ver < PRIV_VERSION_1_10_0) {
+ monitor_printf(mon, "Privileged mode < 1.10 unsupported\n");
+ return;
+ }
+
+ if (!(env->satp & SATP_MODE)) {
+ monitor_printf(mon, "No translation or protection\n");
+ return;
+ }
+
+ mem_info_svxx(mon, env);
+}
diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
index 958c7502a0..d4f1007109 100644
--- a/target/riscv/pmp.c
+++ b/target/riscv/pmp.c
@@ -27,16 +27,7 @@
#include "qemu/log.h"
#include "qapi/error.h"
#include "cpu.h"
-
-#ifndef CONFIG_USER_ONLY
-
-#define RISCV_DEBUG_PMP 0
-#define PMP_DEBUG(fmt, ...) \
- do { \
- if (RISCV_DEBUG_PMP) { \
- qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\
- } \
- } while (0)
+#include "trace.h"
static void pmp_write_cfg(CPURISCVState *env, uint32_t addr_index,
uint8_t val);
@@ -304,8 +295,7 @@ void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index,
int i;
uint8_t cfg_val;
- PMP_DEBUG("hart " TARGET_FMT_ld ": reg%d, val: 0x" TARGET_FMT_lx,
- env->mhartid, reg_index, val);
+ trace_pmpcfg_csr_write(env->mhartid, reg_index, val);
if ((reg_index & 1) && (sizeof(target_ulong) == 8)) {
qemu_log_mask(LOG_GUEST_ERROR,
@@ -334,9 +324,7 @@ target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index)
val = pmp_read_cfg(env, (reg_index * sizeof(target_ulong)) + i);
cfg_val |= (val << (i * 8));
}
-
- PMP_DEBUG("hart " TARGET_FMT_ld ": reg%d, val: 0x" TARGET_FMT_lx,
- env->mhartid, reg_index, cfg_val);
+ trace_pmpcfg_csr_read(env->mhartid, reg_index, cfg_val);
return cfg_val;
}
@@ -348,9 +336,7 @@ target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index)
void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
target_ulong val)
{
- PMP_DEBUG("hart " TARGET_FMT_ld ": addr%d, val: 0x" TARGET_FMT_lx,
- env->mhartid, addr_index, val);
-
+ trace_pmpaddr_csr_write(env->mhartid, addr_index, val);
if (addr_index < MAX_RISCV_PMPS) {
if (!pmp_is_locked(env, addr_index)) {
env->pmp_state.pmp[addr_index].addr_reg = val;
@@ -371,16 +357,15 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
*/
target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index)
{
- PMP_DEBUG("hart " TARGET_FMT_ld ": addr%d, val: 0x" TARGET_FMT_lx,
- env->mhartid, addr_index,
- env->pmp_state.pmp[addr_index].addr_reg);
+ target_ulong val = 0;
+
if (addr_index < MAX_RISCV_PMPS) {
- return env->pmp_state.pmp[addr_index].addr_reg;
+ val = env->pmp_state.pmp[addr_index].addr_reg;
+ trace_pmpaddr_csr_read(env->mhartid, addr_index, val);
} else {
qemu_log_mask(LOG_GUEST_ERROR,
"ignoring pmpaddr read - out of bounds\n");
- return 0;
}
-}
-#endif
+ return val;
+}
diff --git a/target/riscv/trace-events b/target/riscv/trace-events
index 48af0373df..4b6c652ae9 100644
--- a/target/riscv/trace-events
+++ b/target/riscv/trace-events
@@ -1,2 +1,8 @@
# target/riscv/cpu_helper.c
riscv_trap(uint64_t hartid, bool async, uint64_t cause, uint64_t epc, uint64_t tval, const char *desc) "hart:%"PRId64", async:%d, cause:%"PRId64", epc:0x%"PRIx64", tval:0x%"PRIx64", desc=%s"
+
+# pmp.c
+pmpcfg_csr_read(uint64_t mhartid, uint32_t reg_index, uint64_t val) "hart %" PRIu64 ": read reg%" PRIu32", val: 0x%" PRIx64
+pmpcfg_csr_write(uint64_t mhartid, uint32_t reg_index, uint64_t val) "hart %" PRIu64 ": write reg%" PRIu32", val: 0x%" PRIx64
+pmpaddr_csr_read(uint64_t mhartid, uint32_t addr_index, uint64_t val) "hart %" PRIu64 ": read addr%" PRIu32", val: 0x%" PRIx64
+pmpaddr_csr_write(uint64_t mhartid, uint32_t addr_index, uint64_t val) "hart %" PRIu64 ": write addr%" PRIu32", val: 0x%" PRIx64