qemu/hw/riscv/sifive_e.c
Daniel Henrique Barboza 62c5bc348e
hw/riscv: handle 32 bit CPUs kernel_entry in riscv_load_kernel()
Next patch will move all calls to riscv_load_initrd() to
riscv_load_kernel(). Machines that want to load initrd will be able to
do via an extra flag to riscv_load_kernel().

This change will expose a sign-extend behavior that is happening in
load_elf_ram_sym() when running 32 bit guests [1]. This is currently
obscured by the fact that riscv_load_initrd() is using the return of
riscv_load_kernel(), defined as target_ulong, and this return type will
crop the higher 32 bits that would be padded with 1s by the sign
extension when running in 32 bit targets. The changes to be done will
force riscv_load_initrd() to use an uint64_t instead, exposing it to the
padding when dealing with 32 bit CPUs.

There is a discussion about whether load_elf_ram_sym() should or should
not sign extend the value returned by 'lowaddr'. What we can do is to
prevent the behavior change that the next patch will end up doing.
riscv_load_initrd() wasn't dealing with 64 bit kernel entries when
running 32 bit CPUs, and we want to keep it that way.

One way of doing it is to use target_ulong in 'kernel_entry' in
riscv_load_kernel() and rely on the fact that this var will not be sign
extended for 32 bit targets. Another way is to explictly clear the
higher 32 bits when running 32 bit CPUs for all possibilities of
kernel_entry.

We opted for the later. This will allow us to be clear about the design
choices made in the function, while also allowing us to add a small
comment about what load_elf_ram_sym() is doing. With this change, the
consolation patch can do its job without worrying about unintended
behavioral changes.

[1] https://lists.gnu.org/archive/html/qemu-devel/2023-01/msg02281.html

Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-Id: <20230206140022.2748401-2-dbarboza@ventanamicro.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2023-02-16 07:55:16 -08:00

295 lines
11 KiB
C

/*
* QEMU RISC-V Board Compatible with SiFive Freedom E SDK
*
* Copyright (c) 2017 SiFive, Inc.
*
* Provides a board compatible with the SiFive Freedom E SDK:
*
* 0) UART
* 1) CLINT (Core Level Interruptor)
* 2) PLIC (Platform Level Interrupt Controller)
* 3) PRCI (Power, Reset, Clock, Interrupt)
* 4) Registers emulated as RAM: AON, GPIO, QSPI, PWM
* 5) Flash memory emulated as RAM
*
* The Mask ROM reset vector jumps to the flash payload at 0x2040_0000.
* The OTP ROM and Flash boot code will be emulated in a future version.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2 or later, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "hw/boards.h"
#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_e.h"
#include "hw/riscv/boot.h"
#include "hw/char/sifive_uart.h"
#include "hw/intc/riscv_aclint.h"
#include "hw/intc/sifive_plic.h"
#include "hw/misc/sifive_e_prci.h"
#include "chardev/char.h"
#include "sysemu/sysemu.h"
static const MemMapEntry sifive_e_memmap[] = {
[SIFIVE_E_DEV_DEBUG] = { 0x0, 0x1000 },
[SIFIVE_E_DEV_MROM] = { 0x1000, 0x2000 },
[SIFIVE_E_DEV_OTP] = { 0x20000, 0x2000 },
[SIFIVE_E_DEV_CLINT] = { 0x2000000, 0x10000 },
[SIFIVE_E_DEV_PLIC] = { 0xc000000, 0x4000000 },
[SIFIVE_E_DEV_AON] = { 0x10000000, 0x8000 },
[SIFIVE_E_DEV_PRCI] = { 0x10008000, 0x8000 },
[SIFIVE_E_DEV_OTP_CTRL] = { 0x10010000, 0x1000 },
[SIFIVE_E_DEV_GPIO0] = { 0x10012000, 0x1000 },
[SIFIVE_E_DEV_UART0] = { 0x10013000, 0x1000 },
[SIFIVE_E_DEV_QSPI0] = { 0x10014000, 0x1000 },
[SIFIVE_E_DEV_PWM0] = { 0x10015000, 0x1000 },
[SIFIVE_E_DEV_UART1] = { 0x10023000, 0x1000 },
[SIFIVE_E_DEV_QSPI1] = { 0x10024000, 0x1000 },
[SIFIVE_E_DEV_PWM1] = { 0x10025000, 0x1000 },
[SIFIVE_E_DEV_QSPI2] = { 0x10034000, 0x1000 },
[SIFIVE_E_DEV_PWM2] = { 0x10035000, 0x1000 },
[SIFIVE_E_DEV_XIP] = { 0x20000000, 0x20000000 },
[SIFIVE_E_DEV_DTIM] = { 0x80000000, 0x4000 }
};
static void sifive_e_machine_init(MachineState *machine)
{
MachineClass *mc = MACHINE_GET_CLASS(machine);
const MemMapEntry *memmap = sifive_e_memmap;
SiFiveEState *s = RISCV_E_MACHINE(machine);
MemoryRegion *sys_mem = get_system_memory();
int i;
if (machine->ram_size != mc->default_ram_size) {
char *sz = size_to_str(mc->default_ram_size);
error_report("Invalid RAM size, should be %s", sz);
g_free(sz);
exit(EXIT_FAILURE);
}
/* Initialize SoC */
object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RISCV_E_SOC);
qdev_realize(DEVICE(&s->soc), NULL, &error_fatal);
/* Data Tightly Integrated Memory */
memory_region_add_subregion(sys_mem,
memmap[SIFIVE_E_DEV_DTIM].base, machine->ram);
/* Mask ROM reset vector */
uint32_t reset_vec[4];
if (s->revb) {
reset_vec[1] = 0x200102b7; /* 0x1004: lui t0,0x20010 */
} else {
reset_vec[1] = 0x204002b7; /* 0x1004: lui t0,0x20400 */
}
reset_vec[2] = 0x00028067; /* 0x1008: jr t0 */
reset_vec[0] = reset_vec[3] = 0;
/* copy in the reset vector in little_endian byte order */
for (i = 0; i < sizeof(reset_vec) >> 2; i++) {
reset_vec[i] = cpu_to_le32(reset_vec[i]);
}
rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),
memmap[SIFIVE_E_DEV_MROM].base, &address_space_memory);
if (machine->kernel_filename) {
riscv_load_kernel(machine, &s->soc.cpus,
memmap[SIFIVE_E_DEV_DTIM].base, NULL);
}
}
static bool sifive_e_machine_get_revb(Object *obj, Error **errp)
{
SiFiveEState *s = RISCV_E_MACHINE(obj);
return s->revb;
}
static void sifive_e_machine_set_revb(Object *obj, bool value, Error **errp)
{
SiFiveEState *s = RISCV_E_MACHINE(obj);
s->revb = value;
}
static void sifive_e_machine_instance_init(Object *obj)
{
SiFiveEState *s = RISCV_E_MACHINE(obj);
s->revb = false;
}
static void sifive_e_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
mc->desc = "RISC-V Board compatible with SiFive E SDK";
mc->init = sifive_e_machine_init;
mc->max_cpus = 1;
mc->default_cpu_type = SIFIVE_E_CPU;
mc->default_ram_id = "riscv.sifive.e.ram";
mc->default_ram_size = sifive_e_memmap[SIFIVE_E_DEV_DTIM].size;
object_class_property_add_bool(oc, "revb", sifive_e_machine_get_revb,
sifive_e_machine_set_revb);
object_class_property_set_description(oc, "revb",
"Set on to tell QEMU that it should model "
"the revB HiFive1 board");
}
static const TypeInfo sifive_e_machine_typeinfo = {
.name = MACHINE_TYPE_NAME("sifive_e"),
.parent = TYPE_MACHINE,
.class_init = sifive_e_machine_class_init,
.instance_init = sifive_e_machine_instance_init,
.instance_size = sizeof(SiFiveEState),
};
static void sifive_e_machine_init_register_types(void)
{
type_register_static(&sifive_e_machine_typeinfo);
}
type_init(sifive_e_machine_init_register_types)
static void sifive_e_soc_init(Object *obj)
{
MachineState *ms = MACHINE(qdev_get_machine());
SiFiveESoCState *s = RISCV_E_SOC(obj);
object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);
object_property_set_int(OBJECT(&s->cpus), "num-harts", ms->smp.cpus,
&error_abort);
object_property_set_int(OBJECT(&s->cpus), "resetvec", 0x1004, &error_abort);
object_initialize_child(obj, "riscv.sifive.e.gpio0", &s->gpio,
TYPE_SIFIVE_GPIO);
}
static void sifive_e_soc_realize(DeviceState *dev, Error **errp)
{
MachineState *ms = MACHINE(qdev_get_machine());
const MemMapEntry *memmap = sifive_e_memmap;
SiFiveESoCState *s = RISCV_E_SOC(dev);
MemoryRegion *sys_mem = get_system_memory();
object_property_set_str(OBJECT(&s->cpus), "cpu-type", ms->cpu_type,
&error_abort);
sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_fatal);
/* Mask ROM */
memory_region_init_rom(&s->mask_rom, OBJECT(dev), "riscv.sifive.e.mrom",
memmap[SIFIVE_E_DEV_MROM].size, &error_fatal);
memory_region_add_subregion(sys_mem,
memmap[SIFIVE_E_DEV_MROM].base, &s->mask_rom);
/* MMIO */
s->plic = sifive_plic_create(memmap[SIFIVE_E_DEV_PLIC].base,
(char *)SIFIVE_E_PLIC_HART_CONFIG, ms->smp.cpus, 0,
SIFIVE_E_PLIC_NUM_SOURCES,
SIFIVE_E_PLIC_NUM_PRIORITIES,
SIFIVE_E_PLIC_PRIORITY_BASE,
SIFIVE_E_PLIC_PENDING_BASE,
SIFIVE_E_PLIC_ENABLE_BASE,
SIFIVE_E_PLIC_ENABLE_STRIDE,
SIFIVE_E_PLIC_CONTEXT_BASE,
SIFIVE_E_PLIC_CONTEXT_STRIDE,
memmap[SIFIVE_E_DEV_PLIC].size);
riscv_aclint_swi_create(memmap[SIFIVE_E_DEV_CLINT].base,
0, ms->smp.cpus, false);
riscv_aclint_mtimer_create(memmap[SIFIVE_E_DEV_CLINT].base +
RISCV_ACLINT_SWI_SIZE,
RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus,
RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false);
create_unimplemented_device("riscv.sifive.e.aon",
memmap[SIFIVE_E_DEV_AON].base, memmap[SIFIVE_E_DEV_AON].size);
sifive_e_prci_create(memmap[SIFIVE_E_DEV_PRCI].base);
/* GPIO */
if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) {
return;
}
/* Map GPIO registers */
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, memmap[SIFIVE_E_DEV_GPIO0].base);
/* Pass all GPIOs to the SOC layer so they are available to the board */
qdev_pass_gpios(DEVICE(&s->gpio), dev, NULL);
/* Connect GPIO interrupts to the PLIC */
for (int i = 0; i < 32; i++) {
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), i,
qdev_get_gpio_in(DEVICE(s->plic),
SIFIVE_E_GPIO0_IRQ0 + i));
}
sifive_uart_create(sys_mem, memmap[SIFIVE_E_DEV_UART0].base,
serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_UART0_IRQ));
create_unimplemented_device("riscv.sifive.e.qspi0",
memmap[SIFIVE_E_DEV_QSPI0].base, memmap[SIFIVE_E_DEV_QSPI0].size);
create_unimplemented_device("riscv.sifive.e.pwm0",
memmap[SIFIVE_E_DEV_PWM0].base, memmap[SIFIVE_E_DEV_PWM0].size);
sifive_uart_create(sys_mem, memmap[SIFIVE_E_DEV_UART1].base,
serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_UART1_IRQ));
create_unimplemented_device("riscv.sifive.e.qspi1",
memmap[SIFIVE_E_DEV_QSPI1].base, memmap[SIFIVE_E_DEV_QSPI1].size);
create_unimplemented_device("riscv.sifive.e.pwm1",
memmap[SIFIVE_E_DEV_PWM1].base, memmap[SIFIVE_E_DEV_PWM1].size);
create_unimplemented_device("riscv.sifive.e.qspi2",
memmap[SIFIVE_E_DEV_QSPI2].base, memmap[SIFIVE_E_DEV_QSPI2].size);
create_unimplemented_device("riscv.sifive.e.pwm2",
memmap[SIFIVE_E_DEV_PWM2].base, memmap[SIFIVE_E_DEV_PWM2].size);
/* Flash memory */
memory_region_init_rom(&s->xip_mem, OBJECT(dev), "riscv.sifive.e.xip",
memmap[SIFIVE_E_DEV_XIP].size, &error_fatal);
memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_DEV_XIP].base,
&s->xip_mem);
}
static void sifive_e_soc_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = sifive_e_soc_realize;
/* Reason: Uses serial_hds in realize function, thus can't be used twice */
dc->user_creatable = false;
}
static const TypeInfo sifive_e_soc_type_info = {
.name = TYPE_RISCV_E_SOC,
.parent = TYPE_DEVICE,
.instance_size = sizeof(SiFiveESoCState),
.instance_init = sifive_e_soc_init,
.class_init = sifive_e_soc_class_init,
};
static void sifive_e_soc_register_types(void)
{
type_register_static(&sifive_e_soc_type_info);
}
type_init(sifive_e_soc_register_types)