root / hw / arm / virt.c @ 5a4348d1
History | View | Annotate | Download (15.5 kB)
1 |
/*
|
---|---|
2 |
* ARM mach-virt emulation
|
3 |
*
|
4 |
* Copyright (c) 2013 Linaro Limited
|
5 |
*
|
6 |
* This program is free software; you can redistribute it and/or modify it
|
7 |
* under the terms and conditions of the GNU General Public License,
|
8 |
* version 2 or later, as published by the Free Software Foundation.
|
9 |
*
|
10 |
* This program is distributed in the hope it will be useful, but WITHOUT
|
11 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
12 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
13 |
* more details.
|
14 |
*
|
15 |
* You should have received a copy of the GNU General Public License along with
|
16 |
* this program. If not, see <http://www.gnu.org/licenses/>.
|
17 |
*
|
18 |
* Emulate a virtual board which works by passing Linux all the information
|
19 |
* it needs about what devices are present via the device tree.
|
20 |
* There are some restrictions about what we can do here:
|
21 |
* + we can only present devices whose Linux drivers will work based
|
22 |
* purely on the device tree with no platform data at all
|
23 |
* + we want to present a very stripped-down minimalist platform,
|
24 |
* both because this reduces the security attack surface from the guest
|
25 |
* and also because it reduces our exposure to being broken when
|
26 |
* the kernel updates its device tree bindings and requires further
|
27 |
* information in a device binding that we aren't providing.
|
28 |
* This is essentially the same approach kvmtool uses.
|
29 |
*/
|
30 |
|
31 |
#include "hw/sysbus.h" |
32 |
#include "hw/arm/arm.h" |
33 |
#include "hw/arm/primecell.h" |
34 |
#include "hw/devices.h" |
35 |
#include "net/net.h" |
36 |
#include "sysemu/device_tree.h" |
37 |
#include "sysemu/sysemu.h" |
38 |
#include "sysemu/kvm.h" |
39 |
#include "hw/boards.h" |
40 |
#include "exec/address-spaces.h" |
41 |
#include "qemu/bitops.h" |
42 |
#include "qemu/error-report.h" |
43 |
|
44 |
#define NUM_VIRTIO_TRANSPORTS 32 |
45 |
|
46 |
/* Number of external interrupt lines to configure the GIC with */
|
47 |
#define NUM_IRQS 128 |
48 |
|
49 |
#define GIC_FDT_IRQ_TYPE_SPI 0 |
50 |
#define GIC_FDT_IRQ_TYPE_PPI 1 |
51 |
|
52 |
#define GIC_FDT_IRQ_FLAGS_EDGE_LO_HI 1 |
53 |
#define GIC_FDT_IRQ_FLAGS_EDGE_HI_LO 2 |
54 |
#define GIC_FDT_IRQ_FLAGS_LEVEL_HI 4 |
55 |
#define GIC_FDT_IRQ_FLAGS_LEVEL_LO 8 |
56 |
|
57 |
#define GIC_FDT_IRQ_PPI_CPU_START 8 |
58 |
#define GIC_FDT_IRQ_PPI_CPU_WIDTH 8 |
59 |
|
60 |
enum {
|
61 |
VIRT_FLASH, |
62 |
VIRT_MEM, |
63 |
VIRT_CPUPERIPHS, |
64 |
VIRT_GIC_DIST, |
65 |
VIRT_GIC_CPU, |
66 |
VIRT_UART, |
67 |
VIRT_MMIO, |
68 |
}; |
69 |
|
70 |
typedef struct MemMapEntry { |
71 |
hwaddr base; |
72 |
hwaddr size; |
73 |
} MemMapEntry; |
74 |
|
75 |
typedef struct VirtBoardInfo { |
76 |
struct arm_boot_info bootinfo;
|
77 |
const char *cpu_model; |
78 |
const char *qdevname; |
79 |
const char *gic_compatible; |
80 |
const MemMapEntry *memmap;
|
81 |
const int *irqmap; |
82 |
int smp_cpus;
|
83 |
void *fdt;
|
84 |
int fdt_size;
|
85 |
uint32_t clock_phandle; |
86 |
} VirtBoardInfo; |
87 |
|
88 |
/* Addresses and sizes of our components.
|
89 |
* 0..128MB is space for a flash device so we can run bootrom code such as UEFI.
|
90 |
* 128MB..256MB is used for miscellaneous device I/O.
|
91 |
* 256MB..1GB is reserved for possible future PCI support (ie where the
|
92 |
* PCI memory window will go if we add a PCI host controller).
|
93 |
* 1GB and up is RAM (which may happily spill over into the
|
94 |
* high memory region beyond 4GB).
|
95 |
* This represents a compromise between how much RAM can be given to
|
96 |
* a 32 bit VM and leaving space for expansion and in particular for PCI.
|
97 |
*/
|
98 |
static const MemMapEntry a15memmap[] = { |
99 |
/* Space up to 0x8000000 is reserved for a boot ROM */
|
100 |
[VIRT_FLASH] = { 0, 0x8000000 }, |
101 |
[VIRT_CPUPERIPHS] = { 0x8000000, 0x8000 }, |
102 |
/* GIC distributor and CPU interfaces sit inside the CPU peripheral space */
|
103 |
[VIRT_GIC_DIST] = { 0x8001000, 0x1000 }, |
104 |
[VIRT_GIC_CPU] = { 0x8002000, 0x1000 }, |
105 |
[VIRT_UART] = { 0x9000000, 0x1000 }, |
106 |
[VIRT_MMIO] = { 0xa000000, 0x200 }, |
107 |
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
|
108 |
/* 0x10000000 .. 0x40000000 reserved for PCI */
|
109 |
[VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 }, |
110 |
}; |
111 |
|
112 |
static const int a15irqmap[] = { |
113 |
[VIRT_UART] = 1,
|
114 |
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ |
115 |
}; |
116 |
|
117 |
static VirtBoardInfo machines[] = {
|
118 |
{ |
119 |
.cpu_model = "cortex-a15",
|
120 |
.qdevname = "a15mpcore_priv",
|
121 |
.gic_compatible = "arm,cortex-a15-gic",
|
122 |
.memmap = a15memmap, |
123 |
.irqmap = a15irqmap, |
124 |
}, |
125 |
{ |
126 |
.cpu_model = "host",
|
127 |
/* We use the A15 private peripheral model to get a V2 GIC */
|
128 |
.qdevname = "a15mpcore_priv",
|
129 |
.gic_compatible = "arm,cortex-a15-gic",
|
130 |
.memmap = a15memmap, |
131 |
.irqmap = a15irqmap, |
132 |
}, |
133 |
}; |
134 |
|
135 |
static VirtBoardInfo *find_machine_info(const char *cpu) |
136 |
{ |
137 |
int i;
|
138 |
|
139 |
for (i = 0; i < ARRAY_SIZE(machines); i++) { |
140 |
if (strcmp(cpu, machines[i].cpu_model) == 0) { |
141 |
return &machines[i];
|
142 |
} |
143 |
} |
144 |
return NULL; |
145 |
} |
146 |
|
147 |
static void create_fdt(VirtBoardInfo *vbi) |
148 |
{ |
149 |
void *fdt = create_device_tree(&vbi->fdt_size);
|
150 |
|
151 |
if (!fdt) {
|
152 |
error_report("create_device_tree() failed");
|
153 |
exit(1);
|
154 |
} |
155 |
|
156 |
vbi->fdt = fdt; |
157 |
|
158 |
/* Header */
|
159 |
qemu_fdt_setprop_string(fdt, "/", "compatible", "linux,dummy-virt"); |
160 |
qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); |
161 |
qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); |
162 |
|
163 |
/*
|
164 |
* /chosen and /memory nodes must exist for load_dtb
|
165 |
* to fill in necessary properties later
|
166 |
*/
|
167 |
qemu_fdt_add_subnode(fdt, "/chosen");
|
168 |
qemu_fdt_add_subnode(fdt, "/memory");
|
169 |
qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory"); |
170 |
|
171 |
/* Clock node, for the benefit of the UART. The kernel device tree
|
172 |
* binding documentation claims the PL011 node clock properties are
|
173 |
* optional but in practice if you omit them the kernel refuses to
|
174 |
* probe for the device.
|
175 |
*/
|
176 |
vbi->clock_phandle = qemu_fdt_alloc_phandle(fdt); |
177 |
qemu_fdt_add_subnode(fdt, "/apb-pclk");
|
178 |
qemu_fdt_setprop_string(fdt, "/apb-pclk", "compatible", "fixed-clock"); |
179 |
qemu_fdt_setprop_cell(fdt, "/apb-pclk", "#clock-cells", 0x0); |
180 |
qemu_fdt_setprop_cell(fdt, "/apb-pclk", "clock-frequency", 24000000); |
181 |
qemu_fdt_setprop_string(fdt, "/apb-pclk", "clock-output-names", |
182 |
"clk24mhz");
|
183 |
qemu_fdt_setprop_cell(fdt, "/apb-pclk", "phandle", vbi->clock_phandle); |
184 |
|
185 |
/* No PSCI for TCG yet */
|
186 |
if (kvm_enabled()) {
|
187 |
qemu_fdt_add_subnode(fdt, "/psci");
|
188 |
qemu_fdt_setprop_string(fdt, "/psci", "compatible", "arm,psci"); |
189 |
qemu_fdt_setprop_string(fdt, "/psci", "method", "hvc"); |
190 |
qemu_fdt_setprop_cell(fdt, "/psci", "cpu_suspend", |
191 |
PSCI_FN_CPU_SUSPEND); |
192 |
qemu_fdt_setprop_cell(fdt, "/psci", "cpu_off", PSCI_FN_CPU_OFF); |
193 |
qemu_fdt_setprop_cell(fdt, "/psci", "cpu_on", PSCI_FN_CPU_ON); |
194 |
qemu_fdt_setprop_cell(fdt, "/psci", "migrate", PSCI_FN_MIGRATE); |
195 |
} |
196 |
} |
197 |
|
198 |
static void fdt_add_timer_nodes(const VirtBoardInfo *vbi) |
199 |
{ |
200 |
/* Note that on A15 h/w these interrupts are level-triggered,
|
201 |
* but for the GIC implementation provided by both QEMU and KVM
|
202 |
* they are edge-triggered.
|
203 |
*/
|
204 |
uint32_t irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI; |
205 |
|
206 |
irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, |
207 |
GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vbi->smp_cpus) - 1); |
208 |
|
209 |
qemu_fdt_add_subnode(vbi->fdt, "/timer");
|
210 |
qemu_fdt_setprop_string(vbi->fdt, "/timer",
|
211 |
"compatible", "arm,armv7-timer"); |
212 |
qemu_fdt_setprop_cells(vbi->fdt, "/timer", "interrupts", |
213 |
GIC_FDT_IRQ_TYPE_PPI, 13, irqflags,
|
214 |
GIC_FDT_IRQ_TYPE_PPI, 14, irqflags,
|
215 |
GIC_FDT_IRQ_TYPE_PPI, 11, irqflags,
|
216 |
GIC_FDT_IRQ_TYPE_PPI, 10, irqflags);
|
217 |
} |
218 |
|
219 |
static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) |
220 |
{ |
221 |
int cpu;
|
222 |
|
223 |
qemu_fdt_add_subnode(vbi->fdt, "/cpus");
|
224 |
qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#address-cells", 0x1); |
225 |
qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#size-cells", 0x0); |
226 |
|
227 |
for (cpu = vbi->smp_cpus - 1; cpu >= 0; cpu--) { |
228 |
char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu); |
229 |
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); |
230 |
|
231 |
qemu_fdt_add_subnode(vbi->fdt, nodename); |
232 |
qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "cpu"); |
233 |
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible",
|
234 |
armcpu->dtb_compatible); |
235 |
|
236 |
if (vbi->smp_cpus > 1) { |
237 |
qemu_fdt_setprop_string(vbi->fdt, nodename, |
238 |
"enable-method", "psci"); |
239 |
} |
240 |
|
241 |
qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", cpu);
|
242 |
g_free(nodename); |
243 |
} |
244 |
} |
245 |
|
246 |
static void fdt_add_gic_node(const VirtBoardInfo *vbi) |
247 |
{ |
248 |
uint32_t gic_phandle; |
249 |
|
250 |
gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt); |
251 |
qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", gic_phandle); |
252 |
|
253 |
qemu_fdt_add_subnode(vbi->fdt, "/intc");
|
254 |
qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible", |
255 |
vbi->gic_compatible); |
256 |
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3); |
257 |
qemu_fdt_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0); |
258 |
qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg", |
259 |
2, vbi->memmap[VIRT_GIC_DIST].base,
|
260 |
2, vbi->memmap[VIRT_GIC_DIST].size,
|
261 |
2, vbi->memmap[VIRT_GIC_CPU].base,
|
262 |
2, vbi->memmap[VIRT_GIC_CPU].size);
|
263 |
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle); |
264 |
} |
265 |
|
266 |
static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic) |
267 |
{ |
268 |
char *nodename;
|
269 |
hwaddr base = vbi->memmap[VIRT_UART].base; |
270 |
hwaddr size = vbi->memmap[VIRT_UART].size; |
271 |
int irq = vbi->irqmap[VIRT_UART];
|
272 |
const char compat[] = "arm,pl011\0arm,primecell"; |
273 |
const char clocknames[] = "uartclk\0apb_pclk"; |
274 |
|
275 |
sysbus_create_simple("pl011", base, pic[irq]);
|
276 |
|
277 |
nodename = g_strdup_printf("/pl011@%" PRIx64, base);
|
278 |
qemu_fdt_add_subnode(vbi->fdt, nodename); |
279 |
/* Note that we can't use setprop_string because of the embedded NUL */
|
280 |
qemu_fdt_setprop(vbi->fdt, nodename, "compatible",
|
281 |
compat, sizeof(compat));
|
282 |
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
|
283 |
2, base, 2, size); |
284 |
qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts",
|
285 |
GIC_FDT_IRQ_TYPE_SPI, irq, |
286 |
GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); |
287 |
qemu_fdt_setprop_cells(vbi->fdt, nodename, "clocks",
|
288 |
vbi->clock_phandle, vbi->clock_phandle); |
289 |
qemu_fdt_setprop(vbi->fdt, nodename, "clock-names",
|
290 |
clocknames, sizeof(clocknames));
|
291 |
g_free(nodename); |
292 |
} |
293 |
|
294 |
static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) |
295 |
{ |
296 |
int i;
|
297 |
hwaddr size = vbi->memmap[VIRT_MMIO].size; |
298 |
|
299 |
/* Note that we have to create the transports in forwards order
|
300 |
* so that command line devices are inserted lowest address first,
|
301 |
* and then add dtb nodes in reverse order so that they appear in
|
302 |
* the finished device tree lowest address first.
|
303 |
*/
|
304 |
for (i = 0; i < NUM_VIRTIO_TRANSPORTS; i++) { |
305 |
int irq = vbi->irqmap[VIRT_MMIO] + i;
|
306 |
hwaddr base = vbi->memmap[VIRT_MMIO].base + i * size; |
307 |
|
308 |
sysbus_create_simple("virtio-mmio", base, pic[irq]);
|
309 |
} |
310 |
|
311 |
for (i = NUM_VIRTIO_TRANSPORTS - 1; i >= 0; i--) { |
312 |
char *nodename;
|
313 |
int irq = vbi->irqmap[VIRT_MMIO] + i;
|
314 |
hwaddr base = vbi->memmap[VIRT_MMIO].base + i * size; |
315 |
|
316 |
nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base);
|
317 |
qemu_fdt_add_subnode(vbi->fdt, nodename); |
318 |
qemu_fdt_setprop_string(vbi->fdt, nodename, |
319 |
"compatible", "virtio,mmio"); |
320 |
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
|
321 |
2, base, 2, size); |
322 |
qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts",
|
323 |
GIC_FDT_IRQ_TYPE_SPI, irq, |
324 |
GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); |
325 |
g_free(nodename); |
326 |
} |
327 |
} |
328 |
|
329 |
static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) |
330 |
{ |
331 |
const VirtBoardInfo *board = (const VirtBoardInfo *)binfo; |
332 |
|
333 |
*fdt_size = board->fdt_size; |
334 |
return board->fdt;
|
335 |
} |
336 |
|
337 |
static void machvirt_init(QEMUMachineInitArgs *args) |
338 |
{ |
339 |
qemu_irq pic[NUM_IRQS]; |
340 |
MemoryRegion *sysmem = get_system_memory(); |
341 |
int n;
|
342 |
MemoryRegion *ram = g_new(MemoryRegion, 1);
|
343 |
DeviceState *dev; |
344 |
SysBusDevice *busdev; |
345 |
const char *cpu_model = args->cpu_model; |
346 |
VirtBoardInfo *vbi; |
347 |
|
348 |
if (!cpu_model) {
|
349 |
cpu_model = "cortex-a15";
|
350 |
} |
351 |
|
352 |
vbi = find_machine_info(cpu_model); |
353 |
|
354 |
if (!vbi) {
|
355 |
error_report("mach-virt: CPU %s not supported", cpu_model);
|
356 |
exit(1);
|
357 |
} |
358 |
|
359 |
vbi->smp_cpus = smp_cpus; |
360 |
|
361 |
/*
|
362 |
* Only supported method of starting secondary CPUs is PSCI and
|
363 |
* PSCI is not yet supported with TCG, so limit smp_cpus to 1
|
364 |
* if we're not using KVM.
|
365 |
*/
|
366 |
if (!kvm_enabled() && smp_cpus > 1) { |
367 |
error_report("mach-virt: must enable KVM to use multiple CPUs");
|
368 |
exit(1);
|
369 |
} |
370 |
|
371 |
if (args->ram_size > vbi->memmap[VIRT_MEM].size) {
|
372 |
error_report("mach-virt: cannot model more than 30GB RAM");
|
373 |
exit(1);
|
374 |
} |
375 |
|
376 |
create_fdt(vbi); |
377 |
fdt_add_timer_nodes(vbi); |
378 |
|
379 |
for (n = 0; n < smp_cpus; n++) { |
380 |
ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); |
381 |
Object *cpuobj; |
382 |
|
383 |
if (!oc) {
|
384 |
fprintf(stderr, "Unable to find CPU definition\n");
|
385 |
exit(1);
|
386 |
} |
387 |
cpuobj = object_new(object_class_get_name(oc)); |
388 |
|
389 |
/* Secondary CPUs start in PSCI powered-down state */
|
390 |
if (n > 0) { |
391 |
object_property_set_bool(cpuobj, true, "start-powered-off", NULL); |
392 |
} |
393 |
object_property_set_bool(cpuobj, true, "realized", NULL); |
394 |
} |
395 |
fdt_add_cpu_nodes(vbi); |
396 |
|
397 |
memory_region_init_ram(ram, NULL, "mach-virt.ram", args->ram_size); |
398 |
vmstate_register_ram_global(ram); |
399 |
memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram); |
400 |
|
401 |
dev = qdev_create(NULL, vbi->qdevname);
|
402 |
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
|
403 |
/* Note that the num-irq property counts both internal and external
|
404 |
* interrupts; there are always 32 of the former (mandated by GIC spec).
|
405 |
*/
|
406 |
qdev_prop_set_uint32(dev, "num-irq", NUM_IRQS + 32); |
407 |
qdev_init_nofail(dev); |
408 |
busdev = SYS_BUS_DEVICE(dev); |
409 |
sysbus_mmio_map(busdev, 0, vbi->memmap[VIRT_CPUPERIPHS].base);
|
410 |
fdt_add_gic_node(vbi); |
411 |
for (n = 0; n < smp_cpus; n++) { |
412 |
DeviceState *cpudev = DEVICE(qemu_get_cpu(n)); |
413 |
|
414 |
sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); |
415 |
} |
416 |
|
417 |
for (n = 0; n < NUM_IRQS; n++) { |
418 |
pic[n] = qdev_get_gpio_in(dev, n); |
419 |
} |
420 |
|
421 |
create_uart(vbi, pic); |
422 |
|
423 |
/* Create mmio transports, so the user can create virtio backends
|
424 |
* (which will be automatically plugged in to the transports). If
|
425 |
* no backend is created the transport will just sit harmlessly idle.
|
426 |
*/
|
427 |
create_virtio_devices(vbi, pic); |
428 |
|
429 |
vbi->bootinfo.ram_size = args->ram_size; |
430 |
vbi->bootinfo.kernel_filename = args->kernel_filename; |
431 |
vbi->bootinfo.kernel_cmdline = args->kernel_cmdline; |
432 |
vbi->bootinfo.initrd_filename = args->initrd_filename; |
433 |
vbi->bootinfo.nb_cpus = smp_cpus; |
434 |
vbi->bootinfo.board_id = -1;
|
435 |
vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base; |
436 |
vbi->bootinfo.get_dtb = machvirt_dtb; |
437 |
arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo); |
438 |
} |
439 |
|
440 |
static QEMUMachine machvirt_a15_machine = {
|
441 |
.name = "virt",
|
442 |
.desc = "ARM Virtual Machine",
|
443 |
.init = machvirt_init, |
444 |
.max_cpus = 4,
|
445 |
}; |
446 |
|
447 |
static void machvirt_machine_init(void) |
448 |
{ |
449 |
qemu_register_machine(&machvirt_a15_machine); |
450 |
} |
451 |
|
452 |
machine_init(machvirt_machine_init); |