hw/riscv: virt: Add optional ACLINT support to virt machine
We extend virt machine to emulate ACLINT devices only when "aclint=on" parameter is passed along with machine name in QEMU command-line. Signed-off-by: Anup Patel <anup.patel@wdc.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Bin Meng <bmeng.cn@gmail.com> Message-id: 20210831110603.338681-5-anup.patel@wdc.com Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
parent
0ffc1a9522
commit
954886ea6d
@ -53,6 +53,16 @@ with the default OpenSBI firmware image as the -bios. It also supports
|
||||
the recommended RISC-V bootflow: U-Boot SPL (M-mode) loads OpenSBI fw_dynamic
|
||||
firmware and U-Boot proper (S-mode), using the standard -bios functionality.
|
||||
|
||||
Machine-specific options
|
||||
------------------------
|
||||
|
||||
The following machine-specific options are supported:
|
||||
|
||||
- aclint=[on|off]
|
||||
|
||||
When this option is "on", ACLINT devices will be emulated instead of
|
||||
SiFive CLINT. When not specified, this option is assumed to be "off".
|
||||
|
||||
Running Linux kernel
|
||||
--------------------
|
||||
|
||||
|
113
hw/riscv/virt.c
113
hw/riscv/virt.c
@ -48,6 +48,7 @@ static const MemMapEntry virt_memmap[] = {
|
||||
[VIRT_TEST] = { 0x100000, 0x1000 },
|
||||
[VIRT_RTC] = { 0x101000, 0x1000 },
|
||||
[VIRT_CLINT] = { 0x2000000, 0x10000 },
|
||||
[VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 },
|
||||
[VIRT_PCIE_PIO] = { 0x3000000, 0x10000 },
|
||||
[VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) },
|
||||
[VIRT_UART0] = { 0x10000000, 0x100 },
|
||||
@ -281,6 +282,82 @@ static void create_fdt_socket_clint(RISCVVirtState *s,
|
||||
g_free(clint_cells);
|
||||
}
|
||||
|
||||
static void create_fdt_socket_aclint(RISCVVirtState *s,
|
||||
const MemMapEntry *memmap, int socket,
|
||||
uint32_t *intc_phandles)
|
||||
{
|
||||
int cpu;
|
||||
char *name;
|
||||
unsigned long addr;
|
||||
uint32_t aclint_cells_size;
|
||||
uint32_t *aclint_mswi_cells;
|
||||
uint32_t *aclint_sswi_cells;
|
||||
uint32_t *aclint_mtimer_cells;
|
||||
MachineState *mc = MACHINE(s);
|
||||
|
||||
aclint_mswi_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
|
||||
aclint_mtimer_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
|
||||
aclint_sswi_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
|
||||
|
||||
for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
|
||||
aclint_mswi_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
|
||||
aclint_mswi_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_SOFT);
|
||||
aclint_mtimer_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
|
||||
aclint_mtimer_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
|
||||
aclint_sswi_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
|
||||
aclint_sswi_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_SOFT);
|
||||
}
|
||||
aclint_cells_size = s->soc[socket].num_harts * sizeof(uint32_t) * 2;
|
||||
|
||||
addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket);
|
||||
name = g_strdup_printf("/soc/mswi@%lx", addr);
|
||||
qemu_fdt_add_subnode(mc->fdt, name);
|
||||
qemu_fdt_setprop_string(mc->fdt, name, "compatible", "riscv,aclint-mswi");
|
||||
qemu_fdt_setprop_cells(mc->fdt, name, "reg",
|
||||
0x0, addr, 0x0, RISCV_ACLINT_SWI_SIZE);
|
||||
qemu_fdt_setprop(mc->fdt, name, "interrupts-extended",
|
||||
aclint_mswi_cells, aclint_cells_size);
|
||||
qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0);
|
||||
riscv_socket_fdt_write_id(mc, mc->fdt, name, socket);
|
||||
g_free(name);
|
||||
|
||||
addr = memmap[VIRT_CLINT].base + RISCV_ACLINT_SWI_SIZE +
|
||||
(memmap[VIRT_CLINT].size * socket);
|
||||
name = g_strdup_printf("/soc/mtimer@%lx", addr);
|
||||
qemu_fdt_add_subnode(mc->fdt, name);
|
||||
qemu_fdt_setprop_string(mc->fdt, name, "compatible",
|
||||
"riscv,aclint-mtimer");
|
||||
qemu_fdt_setprop_cells(mc->fdt, name, "reg",
|
||||
0x0, addr + RISCV_ACLINT_DEFAULT_MTIME,
|
||||
0x0, memmap[VIRT_CLINT].size - RISCV_ACLINT_SWI_SIZE -
|
||||
RISCV_ACLINT_DEFAULT_MTIME,
|
||||
0x0, addr + RISCV_ACLINT_DEFAULT_MTIMECMP,
|
||||
0x0, RISCV_ACLINT_DEFAULT_MTIME);
|
||||
qemu_fdt_setprop(mc->fdt, name, "interrupts-extended",
|
||||
aclint_mtimer_cells, aclint_cells_size);
|
||||
riscv_socket_fdt_write_id(mc, mc->fdt, name, socket);
|
||||
g_free(name);
|
||||
|
||||
addr = memmap[VIRT_ACLINT_SSWI].base +
|
||||
(memmap[VIRT_ACLINT_SSWI].size * socket);
|
||||
name = g_strdup_printf("/soc/sswi@%lx", addr);
|
||||
qemu_fdt_add_subnode(mc->fdt, name);
|
||||
qemu_fdt_setprop_string(mc->fdt, name, "compatible", "riscv,aclint-sswi");
|
||||
qemu_fdt_setprop_cells(mc->fdt, name, "reg",
|
||||
0x0, addr, 0x0, memmap[VIRT_ACLINT_SSWI].size);
|
||||
qemu_fdt_setprop(mc->fdt, name, "interrupts-extended",
|
||||
aclint_sswi_cells, aclint_cells_size);
|
||||
qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0);
|
||||
riscv_socket_fdt_write_id(mc, mc->fdt, name, socket);
|
||||
g_free(name);
|
||||
|
||||
g_free(aclint_mswi_cells);
|
||||
g_free(aclint_mtimer_cells);
|
||||
g_free(aclint_sswi_cells);
|
||||
}
|
||||
|
||||
static void create_fdt_socket_plic(RISCVVirtState *s,
|
||||
const MemMapEntry *memmap, int socket,
|
||||
uint32_t *phandle, uint32_t *intc_phandles,
|
||||
@ -359,7 +436,11 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap,
|
||||
|
||||
create_fdt_socket_memory(s, memmap, socket);
|
||||
|
||||
create_fdt_socket_clint(s, memmap, socket, intc_phandles);
|
||||
if (s->have_aclint) {
|
||||
create_fdt_socket_aclint(s, memmap, socket, intc_phandles);
|
||||
} else {
|
||||
create_fdt_socket_clint(s, memmap, socket, intc_phandles);
|
||||
}
|
||||
|
||||
create_fdt_socket_plic(s, memmap, socket, phandle,
|
||||
intc_phandles, xplic_phandles);
|
||||
@ -750,6 +831,14 @@ static void virt_machine_init(MachineState *machine)
|
||||
RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
|
||||
RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true);
|
||||
|
||||
/* Per-socket ACLINT SSWI */
|
||||
if (s->have_aclint) {
|
||||
riscv_aclint_swi_create(
|
||||
memmap[VIRT_ACLINT_SSWI].base +
|
||||
i * memmap[VIRT_ACLINT_SSWI].size,
|
||||
base_hartid, hart_count, true);
|
||||
}
|
||||
|
||||
/* Per-socket PLIC hart topology configuration string */
|
||||
plic_hart_config = plic_hart_config_string(hart_count);
|
||||
|
||||
@ -914,6 +1003,22 @@ static void virt_machine_instance_init(Object *obj)
|
||||
{
|
||||
}
|
||||
|
||||
static bool virt_get_aclint(Object *obj, Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(obj);
|
||||
RISCVVirtState *s = RISCV_VIRT_MACHINE(ms);
|
||||
|
||||
return s->have_aclint;
|
||||
}
|
||||
|
||||
static void virt_set_aclint(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(obj);
|
||||
RISCVVirtState *s = RISCV_VIRT_MACHINE(ms);
|
||||
|
||||
s->have_aclint = value;
|
||||
}
|
||||
|
||||
static void virt_machine_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
@ -929,6 +1034,12 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
|
||||
mc->numa_mem_supported = true;
|
||||
|
||||
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE);
|
||||
|
||||
object_class_property_add_bool(oc, "aclint", virt_get_aclint,
|
||||
virt_set_aclint);
|
||||
object_class_property_set_description(oc, "aclint",
|
||||
"Set on/off to enable/disable "
|
||||
"emulating ACLINT devices");
|
||||
}
|
||||
|
||||
static const TypeInfo virt_machine_typeinfo = {
|
||||
|
@ -43,6 +43,7 @@ struct RISCVVirtState {
|
||||
FWCfgState *fw_cfg;
|
||||
|
||||
int fdt_size;
|
||||
bool have_aclint;
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -51,6 +52,7 @@ enum {
|
||||
VIRT_TEST,
|
||||
VIRT_RTC,
|
||||
VIRT_CLINT,
|
||||
VIRT_ACLINT_SSWI,
|
||||
VIRT_PLIC,
|
||||
VIRT_UART0,
|
||||
VIRT_VIRTIO,
|
||||
|
Loading…
Reference in New Issue
Block a user