2 * QEMU RISC-V VirtIO Board
4 * Copyright (c) 2017 SiFive, Inc.
6 * RISC-V machine with 16550a UART and VirtIO MMIO
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2 or later, as published by the Free Software Foundation.
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "qemu/osdep.h"
23 #include "qemu/error-report.h"
24 #include "qapi/error.h"
26 #include "hw/boards.h"
27 #include "hw/loader.h"
28 #include "hw/sysbus.h"
29 #include "hw/char/serial.h"
30 #include "target/riscv/cpu.h"
31 #include "hw/riscv/riscv_htif.h"
32 #include "hw/riscv/riscv_hart.h"
33 #include "hw/riscv/sifive_plic.h"
34 #include "hw/riscv/sifive_clint.h"
35 #include "hw/riscv/sifive_test.h"
36 #include "hw/riscv/virt.h"
37 #include "chardev/char.h"
38 #include "sysemu/arch_init.h"
39 #include "sysemu/device_tree.h"
40 #include "exec/address-spaces.h"
43 static const struct MemmapEntry
{
47 [VIRT_DEBUG
] = { 0x0, 0x100 },
48 [VIRT_MROM
] = { 0x1000, 0x2000 },
49 [VIRT_TEST
] = { 0x4000, 0x1000 },
50 [VIRT_CLINT
] = { 0x2000000, 0x10000 },
51 [VIRT_PLIC
] = { 0xc000000, 0x4000000 },
52 [VIRT_UART0
] = { 0x10000000, 0x100 },
53 [VIRT_VIRTIO
] = { 0x10001000, 0x1000 },
54 [VIRT_DRAM
] = { 0x80000000, 0x0 },
57 static void copy_le32_to_phys(hwaddr pa
, uint32_t *rom
, size_t len
)
60 for (i
= 0; i
< (len
>> 2); i
++) {
61 stl_phys(&address_space_memory
, pa
+ (i
<< 2), rom
[i
]);
65 static uint64_t identity_translate(void *opaque
, uint64_t addr
)
70 static uint64_t load_kernel(const char *kernel_filename
)
72 uint64_t kernel_entry
, kernel_high
;
74 if (load_elf(kernel_filename
, identity_translate
, NULL
,
75 &kernel_entry
, NULL
, &kernel_high
,
76 0, ELF_MACHINE
, 1, 0) < 0) {
77 error_report("qemu: could not load kernel '%s'", kernel_filename
);
83 static hwaddr
load_initrd(const char *filename
, uint64_t mem_size
,
84 uint64_t kernel_entry
, hwaddr
*start
)
88 /* We want to put the initrd far enough into RAM that when the
89 * kernel is uncompressed it will not clobber the initrd. However
90 * on boards without much RAM we must ensure that we still leave
91 * enough room for a decent sized initrd, and on boards with large
92 * amounts of RAM we must avoid the initrd being so far up in RAM
93 * that it is outside lowmem and inaccessible to the kernel.
94 * So for boards with less than 256MB of RAM we put the initrd
95 * halfway into RAM, and for boards with 256MB of RAM or more we put
96 * the initrd at 128MB.
98 *start
= kernel_entry
+ MIN(mem_size
/ 2, 128 * 1024 * 1024);
100 size
= load_ramdisk(filename
, *start
, mem_size
- *start
);
102 size
= load_image_targphys(filename
, *start
, mem_size
- *start
);
104 error_report("qemu: could not load ramdisk '%s'", filename
);
108 return *start
+ size
;
111 static void *create_fdt(RISCVVirtState
*s
, const struct MemmapEntry
*memmap
,
112 uint64_t mem_size
, const char *cmdline
)
118 uint32_t plic_phandle
, phandle
= 1;
121 fdt
= s
->fdt
= create_device_tree(&s
->fdt_size
);
123 error_report("create_device_tree() failed");
127 qemu_fdt_setprop_string(fdt
, "/", "model", "riscv-virtio,qemu");
128 qemu_fdt_setprop_string(fdt
, "/", "compatible", "riscv-virtio");
129 qemu_fdt_setprop_cell(fdt
, "/", "#size-cells", 0x2);
130 qemu_fdt_setprop_cell(fdt
, "/", "#address-cells", 0x2);
132 qemu_fdt_add_subnode(fdt
, "/soc");
133 qemu_fdt_setprop(fdt
, "/soc", "ranges", NULL
, 0);
134 qemu_fdt_setprop_string(fdt
, "/soc", "compatible", "riscv-virtio-soc");
135 qemu_fdt_setprop_cell(fdt
, "/soc", "#size-cells", 0x2);
136 qemu_fdt_setprop_cell(fdt
, "/soc", "#address-cells", 0x2);
138 nodename
= g_strdup_printf("/memory@%lx",
139 (long)memmap
[VIRT_DRAM
].base
);
140 qemu_fdt_add_subnode(fdt
, nodename
);
141 qemu_fdt_setprop_cells(fdt
, nodename
, "reg",
142 memmap
[VIRT_DRAM
].base
>> 32, memmap
[VIRT_DRAM
].base
,
143 mem_size
>> 32, mem_size
);
144 qemu_fdt_setprop_string(fdt
, nodename
, "device_type", "memory");
147 qemu_fdt_add_subnode(fdt
, "/cpus");
148 qemu_fdt_setprop_cell(fdt
, "/cpus", "timebase-frequency", 10000000);
149 qemu_fdt_setprop_cell(fdt
, "/cpus", "#size-cells", 0x0);
150 qemu_fdt_setprop_cell(fdt
, "/cpus", "#address-cells", 0x1);
152 for (cpu
= s
->soc
.num_harts
- 1; cpu
>= 0; cpu
--) {
153 int cpu_phandle
= phandle
++;
154 nodename
= g_strdup_printf("/cpus/cpu@%d", cpu
);
155 char *intc
= g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu
);
156 char *isa
= riscv_isa_string(&s
->soc
.harts
[cpu
]);
157 qemu_fdt_add_subnode(fdt
, nodename
);
158 qemu_fdt_setprop_cell(fdt
, nodename
, "clock-frequency", 1000000000);
159 qemu_fdt_setprop_string(fdt
, nodename
, "mmu-type", "riscv,sv48");
160 qemu_fdt_setprop_string(fdt
, nodename
, "riscv,isa", isa
);
161 qemu_fdt_setprop_string(fdt
, nodename
, "compatible", "riscv");
162 qemu_fdt_setprop_string(fdt
, nodename
, "status", "okay");
163 qemu_fdt_setprop_cell(fdt
, nodename
, "reg", cpu
);
164 qemu_fdt_setprop_string(fdt
, nodename
, "device_type", "cpu");
165 qemu_fdt_add_subnode(fdt
, intc
);
166 qemu_fdt_setprop_cell(fdt
, intc
, "phandle", cpu_phandle
);
167 qemu_fdt_setprop_cell(fdt
, intc
, "linux,phandle", cpu_phandle
);
168 qemu_fdt_setprop_string(fdt
, intc
, "compatible", "riscv,cpu-intc");
169 qemu_fdt_setprop(fdt
, intc
, "interrupt-controller", NULL
, 0);
170 qemu_fdt_setprop_cell(fdt
, intc
, "#interrupt-cells", 1);
176 cells
= g_new0(uint32_t, s
->soc
.num_harts
* 4);
177 for (cpu
= 0; cpu
< s
->soc
.num_harts
; cpu
++) {
179 g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu
);
180 uint32_t intc_phandle
= qemu_fdt_get_phandle(fdt
, nodename
);
181 cells
[cpu
* 4 + 0] = cpu_to_be32(intc_phandle
);
182 cells
[cpu
* 4 + 1] = cpu_to_be32(IRQ_M_SOFT
);
183 cells
[cpu
* 4 + 2] = cpu_to_be32(intc_phandle
);
184 cells
[cpu
* 4 + 3] = cpu_to_be32(IRQ_M_TIMER
);
187 nodename
= g_strdup_printf("/soc/clint@%lx",
188 (long)memmap
[VIRT_CLINT
].base
);
189 qemu_fdt_add_subnode(fdt
, nodename
);
190 qemu_fdt_setprop_string(fdt
, nodename
, "compatible", "riscv,clint0");
191 qemu_fdt_setprop_cells(fdt
, nodename
, "reg",
192 0x0, memmap
[VIRT_CLINT
].base
,
193 0x0, memmap
[VIRT_CLINT
].size
);
194 qemu_fdt_setprop(fdt
, nodename
, "interrupts-extended",
195 cells
, s
->soc
.num_harts
* sizeof(uint32_t) * 4);
199 plic_phandle
= phandle
++;
200 cells
= g_new0(uint32_t, s
->soc
.num_harts
* 4);
201 for (cpu
= 0; cpu
< s
->soc
.num_harts
; cpu
++) {
203 g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu
);
204 uint32_t intc_phandle
= qemu_fdt_get_phandle(fdt
, nodename
);
205 cells
[cpu
* 4 + 0] = cpu_to_be32(intc_phandle
);
206 cells
[cpu
* 4 + 1] = cpu_to_be32(IRQ_M_EXT
);
207 cells
[cpu
* 4 + 2] = cpu_to_be32(intc_phandle
);
208 cells
[cpu
* 4 + 3] = cpu_to_be32(IRQ_S_EXT
);
211 nodename
= g_strdup_printf("/soc/interrupt-controller@%lx",
212 (long)memmap
[VIRT_PLIC
].base
);
213 qemu_fdt_add_subnode(fdt
, nodename
);
214 qemu_fdt_setprop_cell(fdt
, nodename
, "#interrupt-cells", 1);
215 qemu_fdt_setprop_string(fdt
, nodename
, "compatible", "riscv,plic0");
216 qemu_fdt_setprop(fdt
, nodename
, "interrupt-controller", NULL
, 0);
217 qemu_fdt_setprop(fdt
, nodename
, "interrupts-extended",
218 cells
, s
->soc
.num_harts
* sizeof(uint32_t) * 4);
219 qemu_fdt_setprop_cells(fdt
, nodename
, "reg",
220 0x0, memmap
[VIRT_PLIC
].base
,
221 0x0, memmap
[VIRT_PLIC
].size
);
222 qemu_fdt_setprop_string(fdt
, nodename
, "reg-names", "control");
223 qemu_fdt_setprop_cell(fdt
, nodename
, "riscv,max-priority", 7);
224 qemu_fdt_setprop_cell(fdt
, nodename
, "riscv,ndev", VIRTIO_NDEV
);
225 qemu_fdt_setprop_cells(fdt
, nodename
, "phandle", plic_phandle
);
226 qemu_fdt_setprop_cells(fdt
, nodename
, "linux,phandle", plic_phandle
);
227 plic_phandle
= qemu_fdt_get_phandle(fdt
, nodename
);
231 for (i
= 0; i
< VIRTIO_COUNT
; i
++) {
232 nodename
= g_strdup_printf("/virtio_mmio@%lx",
233 (long)(memmap
[VIRT_VIRTIO
].base
+ i
* memmap
[VIRT_VIRTIO
].size
));
234 qemu_fdt_add_subnode(fdt
, nodename
);
235 qemu_fdt_setprop_string(fdt
, nodename
, "compatible", "virtio,mmio");
236 qemu_fdt_setprop_cells(fdt
, nodename
, "reg",
237 0x0, memmap
[VIRT_VIRTIO
].base
+ i
* memmap
[VIRT_VIRTIO
].size
,
238 0x0, memmap
[VIRT_VIRTIO
].size
);
239 qemu_fdt_setprop_cells(fdt
, nodename
, "interrupt-parent", plic_phandle
);
240 qemu_fdt_setprop_cells(fdt
, nodename
, "interrupts", VIRTIO_IRQ
+ i
);
244 nodename
= g_strdup_printf("/test@%lx",
245 (long)memmap
[VIRT_TEST
].base
);
246 qemu_fdt_add_subnode(fdt
, nodename
);
247 qemu_fdt_setprop_string(fdt
, nodename
, "compatible", "sifive,test0");
248 qemu_fdt_setprop_cells(fdt
, nodename
, "reg",
249 0x0, memmap
[VIRT_TEST
].base
,
250 0x0, memmap
[VIRT_TEST
].size
);
252 nodename
= g_strdup_printf("/uart@%lx",
253 (long)memmap
[VIRT_UART0
].base
);
254 qemu_fdt_add_subnode(fdt
, nodename
);
255 qemu_fdt_setprop_string(fdt
, nodename
, "compatible", "ns16550a");
256 qemu_fdt_setprop_cells(fdt
, nodename
, "reg",
257 0x0, memmap
[VIRT_UART0
].base
,
258 0x0, memmap
[VIRT_UART0
].size
);
259 qemu_fdt_setprop_cell(fdt
, nodename
, "clock-frequency", 3686400);
260 qemu_fdt_setprop_cells(fdt
, nodename
, "interrupt-parent", plic_phandle
);
261 qemu_fdt_setprop_cells(fdt
, nodename
, "interrupts", UART0_IRQ
);
263 qemu_fdt_add_subnode(fdt
, "/chosen");
264 qemu_fdt_setprop_string(fdt
, "/chosen", "stdout-path", nodename
);
265 qemu_fdt_setprop_string(fdt
, "/chosen", "bootargs", cmdline
);
271 static void riscv_virt_board_init(MachineState
*machine
)
273 const struct MemmapEntry
*memmap
= virt_memmap
;
275 RISCVVirtState
*s
= g_new0(RISCVVirtState
, 1);
276 MemoryRegion
*system_memory
= get_system_memory();
277 MemoryRegion
*main_mem
= g_new(MemoryRegion
, 1);
278 MemoryRegion
*boot_rom
= g_new(MemoryRegion
, 1);
279 char *plic_hart_config
;
280 size_t plic_hart_config_len
;
285 object_initialize(&s
->soc
, sizeof(s
->soc
), TYPE_RISCV_HART_ARRAY
);
286 object_property_add_child(OBJECT(machine
), "soc", OBJECT(&s
->soc
),
288 object_property_set_str(OBJECT(&s
->soc
), VIRT_CPU
, "cpu-type",
290 object_property_set_int(OBJECT(&s
->soc
), smp_cpus
, "num-harts",
292 object_property_set_bool(OBJECT(&s
->soc
), true, "realized",
295 /* register system main memory (actual RAM) */
296 memory_region_init_ram(main_mem
, NULL
, "riscv_virt_board.ram",
297 machine
->ram_size
, &error_fatal
);
298 memory_region_add_subregion(system_memory
, memmap
[VIRT_DRAM
].base
,
301 /* create device tree */
302 fdt
= create_fdt(s
, memmap
, machine
->ram_size
, machine
->kernel_cmdline
);
305 memory_region_init_ram(boot_rom
, NULL
, "riscv_virt_board.bootrom",
306 s
->fdt_size
+ 0x2000, &error_fatal
);
307 memory_region_add_subregion(system_memory
, 0x0, boot_rom
);
309 if (machine
->kernel_filename
) {
310 uint64_t kernel_entry
= load_kernel(machine
->kernel_filename
);
312 if (machine
->initrd_filename
) {
314 hwaddr end
= load_initrd(machine
->initrd_filename
,
315 machine
->ram_size
, kernel_entry
,
317 qemu_fdt_setprop_cell(fdt
, "/chosen",
318 "linux,initrd-start", start
);
319 qemu_fdt_setprop_cell(fdt
, "/chosen", "linux,initrd-end",
325 uint32_t reset_vec
[8] = {
326 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */
327 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */
328 0xf1402573, /* csrr a0, mhartid */
329 #if defined(TARGET_RISCV32)
330 0x0182a283, /* lw t0, 24(t0) */
331 #elif defined(TARGET_RISCV64)
332 0x0182b283, /* ld t0, 24(t0) */
334 0x00028067, /* jr t0 */
336 memmap
[VIRT_DRAM
].base
, /* start: .dword memmap[VIRT_DRAM].base */
341 /* copy in the reset vector */
342 copy_le32_to_phys(ROM_BASE
, reset_vec
, sizeof(reset_vec
));
344 /* copy in the device tree */
345 qemu_fdt_dumpdtb(s
->fdt
, s
->fdt_size
);
346 cpu_physical_memory_write(ROM_BASE
+ sizeof(reset_vec
),
347 s
->fdt
, s
->fdt_size
);
349 /* create PLIC hart topology configuration string */
350 plic_hart_config_len
= (strlen(VIRT_PLIC_HART_CONFIG
) + 1) * smp_cpus
;
351 plic_hart_config
= g_malloc0(plic_hart_config_len
);
352 for (i
= 0; i
< smp_cpus
; i
++) {
354 strncat(plic_hart_config
, ",", plic_hart_config_len
);
356 strncat(plic_hart_config
, VIRT_PLIC_HART_CONFIG
, plic_hart_config_len
);
357 plic_hart_config_len
-= (strlen(VIRT_PLIC_HART_CONFIG
) + 1);
361 s
->plic
= sifive_plic_create(memmap
[VIRT_PLIC
].base
,
363 VIRT_PLIC_NUM_SOURCES
,
364 VIRT_PLIC_NUM_PRIORITIES
,
365 VIRT_PLIC_PRIORITY_BASE
,
366 VIRT_PLIC_PENDING_BASE
,
367 VIRT_PLIC_ENABLE_BASE
,
368 VIRT_PLIC_ENABLE_STRIDE
,
369 VIRT_PLIC_CONTEXT_BASE
,
370 VIRT_PLIC_CONTEXT_STRIDE
,
371 memmap
[VIRT_PLIC
].size
);
372 sifive_clint_create(memmap
[VIRT_CLINT
].base
,
373 memmap
[VIRT_CLINT
].size
, smp_cpus
,
374 SIFIVE_SIP_BASE
, SIFIVE_TIMECMP_BASE
, SIFIVE_TIME_BASE
);
375 sifive_test_create(memmap
[VIRT_TEST
].base
);
377 for (i
= 0; i
< VIRTIO_COUNT
; i
++) {
378 sysbus_create_simple("virtio-mmio",
379 memmap
[VIRT_VIRTIO
].base
+ i
* memmap
[VIRT_VIRTIO
].size
,
380 SIFIVE_PLIC(s
->plic
)->irqs
[VIRTIO_IRQ
+ i
]);
383 serial_mm_init(system_memory
, memmap
[VIRT_UART0
].base
,
384 0, SIFIVE_PLIC(s
->plic
)->irqs
[UART0_IRQ
], 399193,
385 serial_hds
[0], DEVICE_LITTLE_ENDIAN
);
388 static int riscv_virt_board_sysbus_device_init(SysBusDevice
*sysbusdev
)
393 static void riscv_virt_board_class_init(ObjectClass
*klass
, void *data
)
395 SysBusDeviceClass
*k
= SYS_BUS_DEVICE_CLASS(klass
);
396 k
->init
= riscv_virt_board_sysbus_device_init
;
399 static const TypeInfo riscv_virt_board_device
= {
400 .name
= TYPE_RISCV_VIRT_BOARD
,
401 .parent
= TYPE_SYS_BUS_DEVICE
,
402 .instance_size
= sizeof(RISCVVirtState
),
403 .class_init
= riscv_virt_board_class_init
,
406 static void riscv_virt_board_machine_init(MachineClass
*mc
)
408 mc
->desc
= "RISC-V VirtIO Board (Privileged spec v1.10)";
409 mc
->init
= riscv_virt_board_init
;
410 mc
->max_cpus
= 8; /* hardcoded limit in BBL */
413 DEFINE_MACHINE("virt", riscv_virt_board_machine_init
)
415 static void riscv_virt_board_register_types(void)
417 type_register_static(&riscv_virt_board_device
);
420 type_init(riscv_virt_board_register_types
);