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.
 -----BEGIN PGP SIGNATURE-----
 
 iQJHBAABCAAxFiEEAM520YNJYN/OiG3470yhUCzLq0EFAl2A/yITHHBhbG1lckBk
 YWJiZWx0LmNvbQAKCRDvTKFQLMurQTr/D/9HUJ7GKIrZ5j++RO47fy/IkJtv3Irb
 leOCGQdNBlyqiq9bPIQpcCprhpTDr7s0YyPzzztxWxAfpN3Pku2YgYhkOwiwCDPU
 5M55Zu8ppEBmU4Zh9p7A7ARbKEymBJ2ZwFxoKgXQ6bCzlmFdyHiA5tTfAxGEyRhB
 lt3u472DWMnfNakJp13CyLOM1FTDD6LyT8PDjpefCoWZWDU8gC2ALQHRLkdYeRYE
 XM4XXSvud+DkRjI0Lh5gG8gmFpkk5/ekSb914Ry9G1MhSgkKPzoh/DtIhqtkHClT
 yRDl4ZW7P6AqKJcwVAgZXyCK/kFpSqDyw2cgysozWvklH6bKnTobkJYB5iLlQ9HW
 O6R4DmXpJj9SFLV+bbDSzlGw0cl2meDCIl2t9FJj3Y9etNWaX5kgZ8Aqc6ehiYKa
 N0FU0VbVqqyfwXUyZOU8N160YDnLyPiKnAK7AkCt8CJxWodyFmoHvZcEsHGZOlv3
 5gnsDUPA/+FGsN5mu9SkmG3UE6rGedxJ2PIpyvwqzb1bjOtxBY/5WK7Kr2EjaBX8
 iVod4bzKCqnEWabmZKzxpd3VcWCyoEtlzfe6Xuy5hL0MX6OC3I8S49/BBLnm9qcl
 PeKk01WTOgOX6GfudY0Lt+gILMMJ3IQF91/jmmA2H3LN8pyMKYcx7NSb7hPYNz0U
 bF3fUnZF2nyeHQ==
 =yDSj
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Peter Maydell 2019-09-19 11:14:28 +01:00
commit 7cc0cdcd6a
41 changed files with 1366 additions and 444 deletions

View File

@ -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 = "",

View File

@ -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

View File

@ -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

View File

@ -69,27 +69,35 @@ 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);
}
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,
hwaddr firmware_load_addr)
{

View File

@ -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);
}
}

View File

@ -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 */

124
hw/riscv/sifive_e_prci.c Normal file
View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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 = {

View File

@ -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)

191
hw/riscv/sifive_u_otp.c Normal file
View File

@ -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)

169
hw/riscv/sifive_u_prci.c Normal file
View File

@ -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)

View File

@ -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 = {

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);

View File

@ -35,6 +35,7 @@ typedef struct RISCVHartArrayState {
/*< public >*/
uint32_t num_harts;
uint32_t hartid_base;
char *cpu_type;
RISCVCPU *harts;
} RISCVHartArrayState;

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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 */

View File

@ -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 */

0
pc-bios/opensbi-riscv32-virt-fw_jump.bin Executable file → Normal file
View File

BIN
pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin Executable file → Normal file

Binary file not shown.

0
pc-bios/opensbi-riscv64-virt-fw_jump.bin Executable file → Normal file
View File

View File

@ -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

View File

@ -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

View File

@ -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[] = {

View File

@ -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
}

View File

@ -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

View File

@ -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 */

View File

@ -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;

View File

@ -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);
}

229
target/riscv/monitor.c Normal file
View File

@ -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);
}

View File

@ -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;
}

View File

@ -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