1 #include "libxl_internal.h"
2 #include "libxl_arch.h"
3 #include "libxl_libfdt_compat.h"
4 #include "libxl_arm.h"
5
6 #include <xc_dom.h>
7 #include <stdbool.h>
8 #include <libfdt.h>
9 #include <assert.h>
10
11 /**
12 * IRQ line type.
13 * DT_IRQ_TYPE_NONE - default, unspecified type
14 * DT_IRQ_TYPE_EDGE_RISING - rising edge triggered
15 * DT_IRQ_TYPE_EDGE_FALLING - falling edge triggered
16 * DT_IRQ_TYPE_EDGE_BOTH - rising and falling edge triggered
17 * DT_IRQ_TYPE_LEVEL_HIGH - high level triggered
18 * DT_IRQ_TYPE_LEVEL_LOW - low level triggered
19 */
20 #define DT_IRQ_TYPE_NONE 0x00000000
21 #define DT_IRQ_TYPE_EDGE_RISING 0x00000001
22 #define DT_IRQ_TYPE_EDGE_FALLING 0x00000002
23 #define DT_IRQ_TYPE_EDGE_BOTH \
24 (DT_IRQ_TYPE_EDGE_FALLING | DT_IRQ_TYPE_EDGE_RISING)
25 #define DT_IRQ_TYPE_LEVEL_HIGH 0x00000004
26 #define DT_IRQ_TYPE_LEVEL_LOW 0x00000008
27
gicv_to_string(uint8_t gic_version)28 static const char *gicv_to_string(uint8_t gic_version)
29 {
30 switch (gic_version) {
31 case XEN_DOMCTL_CONFIG_GIC_V2:
32 return "V2";
33 case XEN_DOMCTL_CONFIG_GIC_V3:
34 return "V3";
35 default:
36 return "unknown";
37 }
38 }
39
libxl__arch_domain_prepare_config(libxl__gc * gc,libxl_domain_config * d_config,xc_domain_configuration_t * xc_config)40 int libxl__arch_domain_prepare_config(libxl__gc *gc,
41 libxl_domain_config *d_config,
42 xc_domain_configuration_t *xc_config)
43 {
44 uint32_t nr_spis = 0;
45 unsigned int i;
46 uint32_t vuart_irq;
47 bool vuart_enabled = false;
48
49 /*
50 * If pl011 vuart is enabled then increment the nr_spis to allow allocation
51 * of SPI VIRQ for pl011.
52 */
53 if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
54 nr_spis += (GUEST_VPL011_SPI - 32) + 1;
55 vuart_irq = GUEST_VPL011_SPI;
56 vuart_enabled = true;
57 }
58
59 for (i = 0; i < d_config->b_info.num_irqs; i++) {
60 uint32_t irq = d_config->b_info.irqs[i];
61 uint32_t spi;
62
63 /*
64 * This check ensures the if user has requested pass-through of a certain irq
65 * which conflicts with vpl011 irq then it flags an error to indicate to the
66 * user that the specific HW irq cannot be used as it is dedicated for vpl011.
67 *
68 * TODO:
69 * The vpl011 irq should be assigned such that it never conflicts with user
70 * specified irqs thereby preventing its pass-through. This TODO is for
71 * implementing that logic in future.
72 */
73 if (vuart_enabled && irq == vuart_irq) {
74 LOG(ERROR, "Physical IRQ %u conflicting with pl011 SPI\n", irq);
75 return ERROR_FAIL;
76 }
77
78 if (irq < 32)
79 continue;
80
81 spi = irq - 32;
82
83 if (nr_spis <= spi)
84 nr_spis = spi + 1;
85 }
86
87 LOG(DEBUG, "Configure the domain");
88
89 xc_config->nr_spis = nr_spis;
90 LOG(DEBUG, " - Allocate %u SPIs", nr_spis);
91
92 switch (d_config->b_info.arch_arm.gic_version) {
93 case LIBXL_GIC_VERSION_DEFAULT:
94 xc_config->gic_version = XEN_DOMCTL_CONFIG_GIC_NATIVE;
95 break;
96 case LIBXL_GIC_VERSION_V2:
97 xc_config->gic_version = XEN_DOMCTL_CONFIG_GIC_V2;
98 break;
99 case LIBXL_GIC_VERSION_V3:
100 xc_config->gic_version = XEN_DOMCTL_CONFIG_GIC_V3;
101 break;
102 default:
103 LOG(ERROR, "Unknown GIC version %d",
104 d_config->b_info.arch_arm.gic_version);
105 return ERROR_FAIL;
106 }
107
108 return 0;
109 }
110
libxl__arch_domain_save_config(libxl__gc * gc,libxl_domain_config * d_config,const xc_domain_configuration_t * xc_config)111 int libxl__arch_domain_save_config(libxl__gc *gc,
112 libxl_domain_config *d_config,
113 const xc_domain_configuration_t *xc_config)
114 {
115 switch (xc_config->gic_version) {
116 case XEN_DOMCTL_CONFIG_GIC_V2:
117 d_config->b_info.arch_arm.gic_version = LIBXL_GIC_VERSION_V2;
118 break;
119 case XEN_DOMCTL_CONFIG_GIC_V3:
120 d_config->b_info.arch_arm.gic_version = LIBXL_GIC_VERSION_V3;
121 break;
122 default:
123 LOG(ERROR, "Unexpected gic version %u", xc_config->gic_version);
124 return ERROR_FAIL;
125 }
126
127 return 0;
128 }
129
libxl__arch_domain_create(libxl__gc * gc,libxl_domain_config * d_config,uint32_t domid)130 int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,
131 uint32_t domid)
132 {
133 return 0;
134 }
135
libxl__arch_extra_memory(libxl__gc * gc,const libxl_domain_build_info * info,uint64_t * out)136 int libxl__arch_extra_memory(libxl__gc *gc,
137 const libxl_domain_build_info *info,
138 uint64_t *out)
139 {
140 int rc = 0;
141 uint64_t size = 0;
142
143 if (libxl_defbool_val(info->acpi)) {
144 rc = libxl__get_acpi_size(gc, info, &size);
145 if (rc < 0) {
146 rc = ERROR_FAIL;
147 goto out;
148 }
149 }
150
151 *out = LIBXL_MAXMEM_CONSTANT + DIV_ROUNDUP(size, 1024);
152 out:
153 return rc;
154 }
155
156 static struct arch_info {
157 const char *guest_type;
158 const char *timer_compat;
159 const char *cpu_compat;
160 } arch_info[] = {
161 {"xen-3.0-armv7l", "arm,armv7-timer", "arm,cortex-a15" },
162 {"xen-3.0-aarch64", "arm,armv8-timer", "arm,armv8" },
163 };
164
165 /*
166 * The device tree compiler (DTC) is allocating the phandle from 1 to
167 * onwards. Reserve a high value for the GIC phandle.
168 */
169 #define PHANDLE_GIC (65000)
170
171 typedef uint32_t be32;
172 typedef be32 gic_interrupt[3];
173
174 #define ROOT_ADDRESS_CELLS 2
175 #define ROOT_SIZE_CELLS 2
176
177 #define PROP_INITRD_START "linux,initrd-start"
178 #define PROP_INITRD_END "linux,initrd-end"
179
set_cell(be32 ** cellp,int size,uint64_t val)180 static void set_cell(be32 **cellp, int size, uint64_t val)
181 {
182 int cells = size;
183
184 while (size--) {
185 (*cellp)[size] = cpu_to_fdt32(val);
186 val >>= 32;
187 }
188
189 (*cellp) += cells;
190 }
191
set_interrupt(gic_interrupt interrupt,unsigned int irq,unsigned int cpumask,unsigned int level)192 static void set_interrupt(gic_interrupt interrupt, unsigned int irq,
193 unsigned int cpumask, unsigned int level)
194 {
195 be32 *cells = interrupt;
196 int is_ppi = (irq < 32);
197
198 /* SGIs are not describe in the device tree */
199 assert(irq >= 16);
200
201 irq -= (is_ppi) ? 16: 32; /* PPIs start at 16, SPIs at 32 */
202
203 /* See linux Documentation/devictree/bindings/arm/gic.txt */
204 set_cell(&cells, 1, is_ppi); /* is a PPI? */
205 set_cell(&cells, 1, irq);
206 set_cell(&cells, 1, (cpumask << 8) | level);
207 }
208
set_range(be32 ** cellp,int address_cells,int size_cells,uint64_t address,uint64_t size)209 static void set_range(be32 **cellp,
210 int address_cells, int size_cells,
211 uint64_t address, uint64_t size)
212 {
213 set_cell(cellp, address_cells, address);
214 set_cell(cellp, size_cells, size);
215 }
216
fdt_property_compat(libxl__gc * gc,void * fdt,unsigned nr_compat,...)217 static int fdt_property_compat(libxl__gc *gc, void *fdt, unsigned nr_compat, ...)
218 {
219 const char *compats[nr_compat];
220 int i;
221 size_t sz;
222 va_list ap;
223 char *compat, *p;
224
225 va_start(ap, nr_compat);
226 sz = 0;
227 for (i = 0; i < nr_compat; i++) {
228 const char *c = va_arg(ap, const char *);
229 compats[i] = c;
230 sz += strlen(compats[i]) + 1;
231 }
232 va_end(ap);
233
234 p = compat = libxl__zalloc(gc, sz);
235 for (i = 0; i < nr_compat; i++) {
236 strcpy(p, compats[i]);
237 p += strlen(compats[i]) + 1;
238 }
239
240 return fdt_property(fdt, "compatible", compat, sz);
241 }
242
fdt_property_interrupts(libxl__gc * gc,void * fdt,gic_interrupt * intr,unsigned num_irq)243 static int fdt_property_interrupts(libxl__gc *gc, void *fdt,
244 gic_interrupt *intr,
245 unsigned num_irq)
246 {
247 int res;
248
249 res = fdt_property(fdt, "interrupts", intr, sizeof (intr[0]) * num_irq);
250 if (res) return res;
251
252 res = fdt_property_cell(fdt, "interrupt-parent", PHANDLE_GIC);
253 if (res) return res;
254
255 return 0;
256 }
257
fdt_property_regs(libxl__gc * gc,void * fdt,unsigned addr_cells,unsigned size_cells,unsigned num_regs,...)258 static int fdt_property_regs(libxl__gc *gc, void *fdt,
259 unsigned addr_cells,
260 unsigned size_cells,
261 unsigned num_regs, ...)
262 {
263 uint32_t regs[num_regs*(addr_cells+size_cells)];
264 be32 *cells = ®s[0];
265 int i;
266 va_list ap;
267 uint64_t base, size;
268
269 va_start(ap, num_regs);
270 for (i = 0 ; i < num_regs; i++) {
271 base = addr_cells ? va_arg(ap, uint64_t) : 0;
272 size = size_cells ? va_arg(ap, uint64_t) : 0;
273 set_range(&cells, addr_cells, size_cells, base, size);
274 }
275 va_end(ap);
276
277 return fdt_property(fdt, "reg", regs, sizeof(regs));
278 }
279
make_root_properties(libxl__gc * gc,const libxl_version_info * vers,void * fdt)280 static int make_root_properties(libxl__gc *gc,
281 const libxl_version_info *vers,
282 void *fdt)
283 {
284 int res;
285
286 res = fdt_property_string(fdt, "model", GCSPRINTF("XENVM-%d.%d",
287 vers->xen_version_major,
288 vers->xen_version_minor));
289 if (res) return res;
290
291 res = fdt_property_compat(gc, fdt, 2,
292 GCSPRINTF("xen,xenvm-%d.%d",
293 vers->xen_version_major,
294 vers->xen_version_minor),
295 "xen,xenvm");
296 if (res) return res;
297
298 res = fdt_property_cell(fdt, "interrupt-parent", PHANDLE_GIC);
299 if (res) return res;
300
301 res = fdt_property_cell(fdt, "#address-cells", ROOT_ADDRESS_CELLS);
302 if (res) return res;
303
304 res = fdt_property_cell(fdt, "#size-cells", ROOT_SIZE_CELLS);
305 if (res) return res;
306
307 return 0;
308 }
309
make_chosen_node(libxl__gc * gc,void * fdt,bool ramdisk,libxl__domain_build_state * state,const libxl_domain_build_info * info)310 static int make_chosen_node(libxl__gc *gc, void *fdt, bool ramdisk,
311 libxl__domain_build_state *state,
312 const libxl_domain_build_info *info)
313 {
314 int res;
315
316 /* See linux Documentation/devicetree/... */
317 res = fdt_begin_node(fdt, "chosen");
318 if (res) return res;
319
320 if (state->pv_cmdline) {
321 LOG(DEBUG, "/chosen/bootargs = %s", state->pv_cmdline);
322 res = fdt_property_string(fdt, "bootargs", state->pv_cmdline);
323 if (res) return res;
324 }
325
326 if (ramdisk) {
327 uint64_t dummy = 0;
328 LOG(DEBUG, "/chosen adding placeholder linux,initrd properties");
329 res = fdt_property(fdt, PROP_INITRD_START, &dummy, sizeof(dummy));
330 if (res) return res;
331 res = fdt_property(fdt, PROP_INITRD_END, &dummy, sizeof(dummy));
332 if (res) return res;
333 }
334
335 if (libxl_defbool_val(info->acpi)) {
336 const uint64_t acpi_base = GUEST_ACPI_BASE;
337 const char *name = GCSPRINTF("module@%"PRIx64, acpi_base);
338
339 res = fdt_begin_node(fdt, name);
340 if (res) return res;
341
342 res = fdt_property_compat(gc, fdt, 2, "xen,guest-acpi",
343 "multiboot,module");
344 if (res) return res;
345
346 res = fdt_property_regs(gc, fdt, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS,
347 1, 0, 0);
348 if (res) return res;
349
350 res = fdt_end_node(fdt);
351 if (res) return res;
352 }
353
354 res = fdt_end_node(fdt);
355 if (res) return res;
356
357 return 0;
358 }
359
make_cpus_node(libxl__gc * gc,void * fdt,int nr_cpus,const struct arch_info * ainfo)360 static int make_cpus_node(libxl__gc *gc, void *fdt, int nr_cpus,
361 const struct arch_info *ainfo)
362 {
363 int res, i;
364 uint64_t mpidr_aff;
365
366 res = fdt_begin_node(fdt, "cpus");
367 if (res) return res;
368
369 res = fdt_property_cell(fdt, "#address-cells", 1);
370 if (res) return res;
371
372 res = fdt_property_cell(fdt, "#size-cells", 0);
373 if (res) return res;
374
375 for (i = 0; i < nr_cpus; i++) {
376 const char *name;
377
378 mpidr_aff = libxl__compute_mpdir(i);
379 name = GCSPRINTF("cpu@%"PRIx64, mpidr_aff);
380
381 res = fdt_begin_node(fdt, name);
382 if (res) return res;
383
384 res = fdt_property_string(fdt, "device_type", "cpu");
385 if (res) return res;
386
387 res = fdt_property_compat(gc, fdt, 1, ainfo->cpu_compat);
388 if (res) return res;
389
390 res = fdt_property_string(fdt, "enable-method", "psci");
391 if (res) return res;
392
393 res = fdt_property_regs(gc, fdt, 1, 0, 1, mpidr_aff);
394 if (res) return res;
395
396 res = fdt_end_node(fdt);
397 if (res) return res;
398 }
399
400 res = fdt_end_node(fdt);
401 if (res) return res;
402
403 return 0;
404 }
405
make_psci_node(libxl__gc * gc,void * fdt)406 static int make_psci_node(libxl__gc *gc, void *fdt)
407 {
408 int res;
409
410 res = fdt_begin_node(fdt, "psci");
411 if (res) return res;
412
413 res = fdt_property_compat(gc, fdt, 2, "arm,psci-0.2","arm,psci");
414 if (res) return res;
415
416 res = fdt_property_string(fdt, "method", "hvc");
417 if (res) return res;
418
419 res = fdt_property_cell(fdt, "cpu_off", PSCI_cpu_off);
420 if (res) return res;
421
422 res = fdt_property_cell(fdt, "cpu_on", PSCI_cpu_on);
423 if (res) return res;
424
425 res = fdt_end_node(fdt);
426 if (res) return res;
427
428 return 0;
429 }
430
make_memory_nodes(libxl__gc * gc,void * fdt,const struct xc_dom_image * dom)431 static int make_memory_nodes(libxl__gc *gc, void *fdt,
432 const struct xc_dom_image *dom)
433 {
434 int res, i;
435 const char *name;
436 const uint64_t bankbase[] = GUEST_RAM_BANK_BASES;
437
438 for (i = 0; i < GUEST_RAM_BANKS; i++) {
439 name = GCSPRINTF("memory@%"PRIx64, bankbase[i]);
440
441 LOG(DEBUG, "Creating placeholder node /%s", name);
442
443 res = fdt_begin_node(fdt, name);
444 if (res) return res;
445
446 res = fdt_property_string(fdt, "device_type", "memory");
447 if (res) return res;
448
449 res = fdt_property_regs(gc, fdt, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS,
450 1, 0, 0);
451 if (res) return res;
452
453 res = fdt_end_node(fdt);
454 if (res) return res;
455 }
456
457 return 0;
458 }
459
make_gicv2_node(libxl__gc * gc,void * fdt,uint64_t gicd_base,uint64_t gicd_size,uint64_t gicc_base,uint64_t gicc_size)460 static int make_gicv2_node(libxl__gc *gc, void *fdt,
461 uint64_t gicd_base, uint64_t gicd_size,
462 uint64_t gicc_base, uint64_t gicc_size)
463 {
464 int res;
465 const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base);
466
467 res = fdt_begin_node(fdt, name);
468 if (res) return res;
469
470 res = fdt_property_compat(gc, fdt, 2,
471 "arm,cortex-a15-gic",
472 "arm,cortex-a9-gic");
473 if (res) return res;
474
475
476 res = fdt_property_cell(fdt, "#interrupt-cells", 3);
477 if (res) return res;
478
479 res = fdt_property_cell(fdt, "#address-cells", 0);
480 if (res) return res;
481
482 res = fdt_property(fdt, "interrupt-controller", NULL, 0);
483 if (res) return res;
484
485 res = fdt_property_regs(gc, fdt, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS,
486 2,
487 gicd_base, gicd_size,
488 gicc_base, gicc_size);
489 if (res) return res;
490
491 res = fdt_property_cell(fdt, "linux,phandle", PHANDLE_GIC);
492 if (res) return res;
493
494 res = fdt_property_cell(fdt, "phandle", PHANDLE_GIC);
495 if (res) return res;
496
497 res = fdt_end_node(fdt);
498 if (res) return res;
499
500 return 0;
501 }
502
make_gicv3_node(libxl__gc * gc,void * fdt)503 static int make_gicv3_node(libxl__gc *gc, void *fdt)
504 {
505 int res;
506 const uint64_t gicd_base = GUEST_GICV3_GICD_BASE;
507 const uint64_t gicd_size = GUEST_GICV3_GICD_SIZE;
508 const uint64_t gicr0_base = GUEST_GICV3_GICR0_BASE;
509 const uint64_t gicr0_size = GUEST_GICV3_GICR0_SIZE;
510 const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base);
511
512 res = fdt_begin_node(fdt, name);
513 if (res) return res;
514
515 res = fdt_property_compat(gc, fdt, 1, "arm,gic-v3");
516 if (res) return res;
517
518 res = fdt_property_cell(fdt, "#interrupt-cells", 3);
519 if (res) return res;
520
521 res = fdt_property_cell(fdt, "#address-cells", 0);
522 if (res) return res;
523
524 res = fdt_property(fdt, "interrupt-controller", NULL, 0);
525 if (res) return res;
526
527 res = fdt_property_cell(fdt, "redistributor-stride",
528 GUEST_GICV3_RDIST_STRIDE);
529 if (res) return res;
530
531 res = fdt_property_cell(fdt, "#redistributor-regions",
532 GUEST_GICV3_RDIST_REGIONS);
533 if (res) return res;
534
535 res = fdt_property_regs(gc, fdt, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS,
536 2,
537 gicd_base, gicd_size,
538 gicr0_base, gicr0_size);
539 if (res) return res;
540
541 res = fdt_property_cell(fdt, "linux,phandle", PHANDLE_GIC);
542 if (res) return res;
543
544 res = fdt_property_cell(fdt, "phandle", PHANDLE_GIC);
545 if (res) return res;
546
547 res = fdt_end_node(fdt);
548 if (res) return res;
549
550 return 0;
551 }
552
make_timer_node(libxl__gc * gc,void * fdt,const struct arch_info * ainfo,uint32_t frequency)553 static int make_timer_node(libxl__gc *gc, void *fdt,
554 const struct arch_info *ainfo,
555 uint32_t frequency)
556 {
557 int res;
558 gic_interrupt ints[3];
559
560 res = fdt_begin_node(fdt, "timer");
561 if (res) return res;
562
563 res = fdt_property_compat(gc, fdt, 1, ainfo->timer_compat);
564 if (res) return res;
565
566 set_interrupt(ints[0], GUEST_TIMER_PHYS_S_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
567 set_interrupt(ints[1], GUEST_TIMER_PHYS_NS_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
568 set_interrupt(ints[2], GUEST_TIMER_VIRT_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
569
570 res = fdt_property_interrupts(gc, fdt, ints, 3);
571 if (res) return res;
572
573 if ( frequency )
574 fdt_property_u32(fdt, "clock-frequency", frequency);
575
576 res = fdt_end_node(fdt);
577 if (res) return res;
578
579 return 0;
580 }
581
make_hypervisor_node(libxl__gc * gc,void * fdt,const libxl_version_info * vers)582 static int make_hypervisor_node(libxl__gc *gc, void *fdt,
583 const libxl_version_info *vers)
584 {
585 int res;
586 gic_interrupt intr;
587
588 /* See linux Documentation/devicetree/bindings/arm/xen.txt */
589 res = fdt_begin_node(fdt, "hypervisor");
590 if (res) return res;
591
592 res = fdt_property_compat(gc, fdt, 2,
593 GCSPRINTF("xen,xen-%d.%d",
594 vers->xen_version_major,
595 vers->xen_version_minor),
596 "xen,xen");
597 if (res) return res;
598
599 /* reg 0 is grant table space */
600 res = fdt_property_regs(gc, fdt, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS,
601 1,GUEST_GNTTAB_BASE, GUEST_GNTTAB_SIZE);
602 if (res) return res;
603
604 /*
605 * interrupts is evtchn upcall:
606 * - Active-low level-sensitive
607 * - All cpus
608 */
609 set_interrupt(intr, GUEST_EVTCHN_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW);
610
611 res = fdt_property_interrupts(gc, fdt, &intr, 1);
612 if (res) return res;
613
614 res = fdt_end_node(fdt);
615 if (res) return res;
616
617 return 0;
618 }
619
make_vpl011_uart_node(libxl__gc * gc,void * fdt,const struct arch_info * ainfo,struct xc_dom_image * dom)620 static int make_vpl011_uart_node(libxl__gc *gc, void *fdt,
621 const struct arch_info *ainfo,
622 struct xc_dom_image *dom)
623 {
624 int res;
625 gic_interrupt intr;
626
627 res = fdt_begin_node(fdt, "sbsa-pl011");
628 if (res) return res;
629
630 res = fdt_property_compat(gc, fdt, 1, "arm,sbsa-uart");
631 if (res) return res;
632
633 res = fdt_property_regs(gc, fdt, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS,
634 1,
635 GUEST_PL011_BASE, GUEST_PL011_SIZE);
636 if (res) return res;
637
638 set_interrupt(intr, GUEST_VPL011_SPI, 0xf, DT_IRQ_TYPE_LEVEL_HIGH);
639
640 res = fdt_property_interrupts(gc, fdt, &intr, 1);
641 if (res) return res;
642
643 /* Use a default baud rate of 115200. */
644 fdt_property_u32(fdt, "current-speed", 115200);
645
646 res = fdt_end_node(fdt);
647 if (res) return res;
648
649 return 0;
650 }
651
get_arch_info(libxl__gc * gc,const struct xc_dom_image * dom)652 static const struct arch_info *get_arch_info(libxl__gc *gc,
653 const struct xc_dom_image *dom)
654 {
655 int i;
656
657 for (i=0; i < ARRAY_SIZE(arch_info); i++) {
658 const struct arch_info *info = &arch_info[i];
659 if (!strcmp(dom->guest_type, info->guest_type))
660 return info;
661 }
662 LOG(ERROR, "Unable to find arch FDT info for %s", dom->guest_type);
663 return NULL;
664 }
665
debug_dump_fdt(libxl__gc * gc,void * fdt)666 static void debug_dump_fdt(libxl__gc *gc, void *fdt)
667 {
668 int fd = -1, rc, r;
669
670 const char *dtb = getenv("LIBXL_DEBUG_DUMP_DTB");
671
672 if (!dtb) goto out;
673
674 fd = open(dtb, O_CREAT|O_TRUNC|O_WRONLY, 0666);
675 if (fd < 0) {
676 LOGE(DEBUG, "cannot open %s for LIBXL_DEBUG_DUMP_DTB", dtb);
677 goto out;
678 }
679
680 rc = libxl_write_exactly(CTX, fd, fdt, fdt_totalsize(fdt), dtb, "dtb");
681 if (rc < 0) goto out;
682
683 out:
684 if (fd >= 0) {
685 r = close(fd);
686 if (r < 0) LOGE(DEBUG, "failed to close DTB debug dump output");
687 }
688 }
689
690 #ifdef ENABLE_PARTIAL_DEVICE_TREE
691
check_partial_fdt(libxl__gc * gc,void * fdt,size_t size)692 static int check_partial_fdt(libxl__gc *gc, void *fdt, size_t size)
693 {
694 int r;
695
696 if (fdt_magic(fdt) != FDT_MAGIC) {
697 LOG(ERROR, "Partial FDT is not a valid Flat Device Tree");
698 return ERROR_FAIL;
699 }
700
701 r = fdt_check_header(fdt);
702 if (r) {
703 LOG(ERROR, "Failed to check the partial FDT (%d)", r);
704 return ERROR_FAIL;
705 }
706
707 if (fdt_totalsize(fdt) > size) {
708 LOG(ERROR, "Partial FDT totalsize is too big");
709 return ERROR_FAIL;
710 }
711
712 return 0;
713 }
714
copy_properties(libxl__gc * gc,void * fdt,void * pfdt,int nodeoff)715 static int copy_properties(libxl__gc *gc, void *fdt, void *pfdt,
716 int nodeoff)
717 {
718 int propoff, nameoff, r;
719 const struct fdt_property *prop;
720
721 for (propoff = fdt_first_property_offset(pfdt, nodeoff);
722 propoff >= 0;
723 propoff = fdt_next_property_offset(pfdt, propoff)) {
724
725 if (!(prop = fdt_get_property_by_offset(pfdt, propoff, NULL))) {
726 return -FDT_ERR_INTERNAL;
727 }
728
729 nameoff = fdt32_to_cpu(prop->nameoff);
730 r = fdt_property(fdt, fdt_string(pfdt, nameoff),
731 prop->data, fdt32_to_cpu(prop->len));
732 if (r) return r;
733 }
734
735 /* FDT_ERR_NOTFOUND => There is no more properties for this node */
736 return (propoff != -FDT_ERR_NOTFOUND)? propoff : 0;
737 }
738
739 /* Copy a node from the partial device tree to the guest device tree */
copy_node(libxl__gc * gc,void * fdt,void * pfdt,int nodeoff,int depth)740 static int copy_node(libxl__gc *gc, void *fdt, void *pfdt,
741 int nodeoff, int depth)
742 {
743 int r;
744
745 r = fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL));
746 if (r) return r;
747
748 r = copy_properties(gc, fdt, pfdt, nodeoff);
749 if (r) return r;
750
751 for (nodeoff = fdt_first_subnode(pfdt, nodeoff);
752 nodeoff >= 0;
753 nodeoff = fdt_next_subnode(pfdt, nodeoff)) {
754 r = copy_node(gc, fdt, pfdt, nodeoff, depth + 1);
755 if (r) return r;
756 }
757
758 if (nodeoff != -FDT_ERR_NOTFOUND)
759 return nodeoff;
760
761 r = fdt_end_node(fdt);
762 if (r) return r;
763
764 return 0;
765 }
766
copy_node_by_path(libxl__gc * gc,const char * path,void * fdt,void * pfdt)767 static int copy_node_by_path(libxl__gc *gc, const char *path,
768 void *fdt, void *pfdt)
769 {
770 int nodeoff, r;
771 const char *name = strrchr(path, '/');
772
773 if (!name)
774 return -FDT_ERR_INTERNAL;
775
776 name++;
777
778 /*
779 * The FDT function to look at a node doesn't take into account the
780 * unit (i.e anything after @) when search by name. Check if the
781 * name exactly matches.
782 */
783 nodeoff = fdt_path_offset(pfdt, path);
784 if (nodeoff < 0)
785 return nodeoff;
786
787 if (strcmp(fdt_get_name(pfdt, nodeoff, NULL), name))
788 return -FDT_ERR_NOTFOUND;
789
790 r = copy_node(gc, fdt, pfdt, nodeoff, 0);
791 if (r) return r;
792
793 return 0;
794 }
795
796 /*
797 * The partial device tree is not copied entirely. Only the relevant bits are
798 * copied to the guest device tree:
799 * - /passthrough node
800 * - /aliases node
801 */
copy_partial_fdt(libxl__gc * gc,void * fdt,void * pfdt)802 static int copy_partial_fdt(libxl__gc *gc, void *fdt, void *pfdt)
803 {
804 int r;
805
806 r = copy_node_by_path(gc, "/passthrough", fdt, pfdt);
807 if (r < 0) {
808 LOG(ERROR, "Can't copy the node \"/passthrough\" from the partial FDT");
809 return r;
810 }
811
812 r = copy_node_by_path(gc, "/aliases", fdt, pfdt);
813 if (r < 0 && r != -FDT_ERR_NOTFOUND) {
814 LOG(ERROR, "Can't copy the node \"/aliases\" from the partial FDT");
815 return r;
816 }
817
818 return 0;
819 }
820
821 #else
822
check_partial_fdt(libxl__gc * gc,void * fdt,size_t size)823 static int check_partial_fdt(libxl__gc *gc, void *fdt, size_t size)
824 {
825 LOG(ERROR, "partial device tree not supported");
826
827 return ERROR_FAIL;
828 }
829
copy_partial_fdt(libxl__gc * gc,void * fdt,void * pfdt)830 static int copy_partial_fdt(libxl__gc *gc, void *fdt, void *pfdt)
831 {
832 /*
833 * We should never be here when the partial device tree is not
834 * supported.
835 * */
836 return -FDT_ERR_INTERNAL;
837 }
838
839 #endif /* ENABLE_PARTIAL_DEVICE_TREE */
840
841 #define FDT_MAX_SIZE (1<<20)
842
libxl__prepare_dtb(libxl__gc * gc,libxl_domain_build_info * info,libxl__domain_build_state * state,struct xc_dom_image * dom)843 static int libxl__prepare_dtb(libxl__gc *gc, libxl_domain_build_info *info,
844 libxl__domain_build_state *state,
845 struct xc_dom_image *dom)
846 {
847 void *fdt = NULL;
848 void *pfdt = NULL;
849 int rc, res;
850 size_t fdt_size = 0;
851 int pfdt_size = 0;
852
853 const libxl_version_info *vers;
854 const struct arch_info *ainfo;
855
856 /* convenience aliases */
857 xc_domain_configuration_t *xc_config = &state->config;
858
859 vers = libxl_get_version_info(CTX);
860 if (vers == NULL) return ERROR_FAIL;
861
862 ainfo = get_arch_info(gc, dom);
863 if (ainfo == NULL) return ERROR_FAIL;
864
865 LOG(DEBUG, "constructing DTB for Xen version %d.%d guest",
866 vers->xen_version_major, vers->xen_version_minor);
867 LOG(DEBUG, " - vGIC version: %s", gicv_to_string(xc_config->gic_version));
868
869 if (info->device_tree) {
870 LOG(DEBUG, " - Partial device tree provided: %s", info->device_tree);
871
872 rc = libxl_read_file_contents(CTX, info->device_tree,
873 &pfdt, &pfdt_size);
874 if (rc) {
875 LOGEV(ERROR, rc, "failed to read the partial device file %s",
876 info->device_tree);
877 return ERROR_FAIL;
878 }
879 libxl__ptr_add(gc, pfdt);
880
881 if (check_partial_fdt(gc, pfdt, pfdt_size))
882 return ERROR_FAIL;
883 }
884
885 /*
886 * Call "call" handling FDT_ERR_*. Will either:
887 * - loop back to retry_resize
888 * - set rc and goto out
889 * - fall through successfully
890 *
891 * On FDT_ERR_NOSPACE we start again from scratch rather than
892 * realloc+libfdt_open_into because "call" may have failed half way
893 * through a series of steps leaving the partial tree in an
894 * inconsistent state, e.g. leaving a node open.
895 */
896 #define FDT( call ) do { \
897 int fdt_res = (call); \
898 if (fdt_res == -FDT_ERR_NOSPACE && fdt_size < FDT_MAX_SIZE) \
899 goto next_resize; \
900 else if (fdt_res < 0) { \
901 LOG(ERROR, "FDT: %s failed: %d = %s", \
902 #call, fdt_res, fdt_strerror(fdt_res)); \
903 rc = ERROR_FAIL; \
904 goto out; \
905 } \
906 } while(0)
907
908 for (;;) {
909 next_resize:
910 if (fdt_size) {
911 fdt_size <<= 1;
912 LOG(DEBUG, "Increasing FDT size to %zd and retrying", fdt_size);
913 } else {
914 fdt_size = 4096;
915 }
916
917 fdt = libxl__realloc(gc, fdt, fdt_size);
918
919 FDT( fdt_create(fdt, fdt_size) );
920
921 FDT( fdt_finish_reservemap(fdt) );
922
923 FDT( fdt_begin_node(fdt, "") );
924
925 FDT( make_root_properties(gc, vers, fdt) );
926 FDT( make_chosen_node(gc, fdt, !!dom->ramdisk_blob, state, info) );
927 FDT( make_cpus_node(gc, fdt, info->max_vcpus, ainfo) );
928 FDT( make_psci_node(gc, fdt) );
929
930 FDT( make_memory_nodes(gc, fdt, dom) );
931
932 switch (xc_config->gic_version) {
933 case XEN_DOMCTL_CONFIG_GIC_V2:
934 FDT( make_gicv2_node(gc, fdt,
935 GUEST_GICD_BASE, GUEST_GICD_SIZE,
936 GUEST_GICC_BASE, GUEST_GICC_SIZE) );
937 break;
938 case XEN_DOMCTL_CONFIG_GIC_V3:
939 FDT( make_gicv3_node(gc, fdt) );
940 break;
941 default:
942 LOG(ERROR, "Unknown GIC version %s",
943 gicv_to_string(xc_config->gic_version));
944 rc = ERROR_FAIL;
945 goto out;
946 }
947
948 FDT( make_timer_node(gc, fdt, ainfo, xc_config->clock_frequency) );
949 FDT( make_hypervisor_node(gc, fdt, vers) );
950
951 if (info->arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART)
952 FDT( make_vpl011_uart_node(gc, fdt, ainfo, dom) );
953
954 if (pfdt)
955 FDT( copy_partial_fdt(gc, fdt, pfdt) );
956
957 FDT( fdt_end_node(fdt) );
958
959 FDT( fdt_finish(fdt) );
960 break;
961 }
962 #undef FDT
963
964 LOG(DEBUG, "fdt total size %d", fdt_totalsize(fdt));
965
966 res = xc_dom_devicetree_mem(dom, fdt, fdt_totalsize(fdt));
967 if (res) {
968 LOGE(ERROR, "xc_dom_devicetree_mem failed");
969 rc = ERROR_FAIL;
970 goto out;
971 }
972
973 rc = 0;
974
975 out:
976 return rc;
977 }
978
libxl__arch_domain_init_hw_description(libxl__gc * gc,libxl_domain_build_info * info,libxl__domain_build_state * state,struct xc_dom_image * dom)979 int libxl__arch_domain_init_hw_description(libxl__gc *gc,
980 libxl_domain_build_info *info,
981 libxl__domain_build_state *state,
982 struct xc_dom_image *dom)
983 {
984 int rc;
985 uint64_t val;
986
987 assert(info->type == LIBXL_DOMAIN_TYPE_PV);
988
989 /* Set the value of domain param HVM_PARAM_CALLBACK_IRQ. */
990 val = MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI,
991 HVM_PARAM_CALLBACK_IRQ_TYPE_MASK);
992 /* Active-low level-sensitive */
993 val |= MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_LOW_LEVEL,
994 HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_MASK);
995 val |= GUEST_EVTCHN_PPI;
996 rc = xc_hvm_param_set(dom->xch, dom->guest_domid, HVM_PARAM_CALLBACK_IRQ,
997 val);
998 if (rc)
999 return rc;
1000
1001 rc = libxl__prepare_dtb(gc, info, state, dom);
1002 if (rc) goto out;
1003
1004 if (!libxl_defbool_val(info->acpi)) {
1005 LOG(DEBUG, "Generating ACPI tables is disabled by user.");
1006 rc = 0;
1007 goto out;
1008 }
1009
1010 if (strcmp(dom->guest_type, "xen-3.0-aarch64")) {
1011 /* ACPI is only supported for 64-bit guest currently. */
1012 LOG(ERROR, "Can not enable libxl option 'acpi' for %s", dom->guest_type);
1013 rc = ERROR_FAIL;
1014 goto out;
1015 }
1016
1017 rc = libxl__prepare_acpi(gc, info, dom);
1018
1019 out:
1020 return rc;
1021 }
1022
finalise_one_node(libxl__gc * gc,void * fdt,const char * uname,uint64_t base,uint64_t size)1023 static void finalise_one_node(libxl__gc *gc, void *fdt, const char *uname,
1024 uint64_t base, uint64_t size)
1025 {
1026 int node, res;
1027 const char *name = GCSPRINTF("%s@%"PRIx64, uname, base);
1028
1029 node = fdt_path_offset(fdt, name);
1030 assert(node > 0);
1031
1032 if (size == 0) {
1033 LOG(DEBUG, "Nopping out placeholder node %s", name);
1034 fdt_nop_node(fdt, node);
1035 } else {
1036 uint32_t regs[ROOT_ADDRESS_CELLS+ROOT_SIZE_CELLS];
1037 be32 *cells = ®s[0];
1038
1039 LOG(DEBUG, "Populating placeholder node %s", name);
1040
1041 set_range(&cells, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS, base, size);
1042
1043 res = fdt_setprop_inplace(fdt, node, "reg", regs, sizeof(regs));
1044 assert(!res);
1045 }
1046 }
1047
libxl__arch_domain_finalise_hw_description(libxl__gc * gc,libxl_domain_build_info * info,struct xc_dom_image * dom)1048 int libxl__arch_domain_finalise_hw_description(libxl__gc *gc,
1049 libxl_domain_build_info *info,
1050 struct xc_dom_image *dom)
1051 {
1052 void *fdt = dom->devicetree_blob;
1053 int i;
1054 const uint64_t bankbase[] = GUEST_RAM_BANK_BASES;
1055
1056 const struct xc_dom_seg *ramdisk = dom->ramdisk_blob ?
1057 &dom->ramdisk_seg : NULL;
1058
1059 if (ramdisk) {
1060 int chosen, res;
1061 uint64_t val;
1062
1063 /* Neither the fdt_path_offset() nor either of the
1064 * fdt_setprop_inplace() calls can fail. If they do then
1065 * make_chosen_node() (see above) has got something very
1066 * wrong.
1067 */
1068 chosen = fdt_path_offset(fdt, "/chosen");
1069 assert(chosen > 0);
1070
1071 LOG(DEBUG, "/chosen updating initrd properties to cover "
1072 "%"PRIx64"-%"PRIx64,
1073 ramdisk->vstart, ramdisk->vend);
1074
1075 val = cpu_to_fdt64(ramdisk->vstart);
1076 res = fdt_setprop_inplace(fdt, chosen, PROP_INITRD_START,
1077 &val, sizeof(val));
1078 assert(!res);
1079
1080 val = cpu_to_fdt64(ramdisk->vend);
1081 res = fdt_setprop_inplace(fdt, chosen, PROP_INITRD_END,
1082 &val, sizeof(val));
1083 assert(!res);
1084
1085 }
1086
1087 for (i = 0; i < GUEST_RAM_BANKS; i++) {
1088 const uint64_t size = (uint64_t)dom->rambank_size[i] << XC_PAGE_SHIFT;
1089
1090 finalise_one_node(gc, fdt, "/memory", bankbase[i], size);
1091 }
1092
1093 if (dom->acpi_modules[0].data) {
1094 finalise_one_node(gc, fdt, "/chosen/module", GUEST_ACPI_BASE,
1095 dom->acpi_modules[0].length);
1096 }
1097
1098 debug_dump_fdt(gc, fdt);
1099
1100 return 0;
1101 }
1102
libxl__arch_build_dom_finish(libxl__gc * gc,libxl_domain_build_info * info,struct xc_dom_image * dom,libxl__domain_build_state * state)1103 int libxl__arch_build_dom_finish(libxl__gc *gc,
1104 libxl_domain_build_info *info,
1105 struct xc_dom_image *dom,
1106 libxl__domain_build_state *state)
1107 {
1108 int rc = 0, ret;
1109
1110 if (info->arch_arm.vuart != LIBXL_VUART_TYPE_SBSA_UART) {
1111 rc = 0;
1112 goto out;
1113 }
1114
1115 ret = xc_dom_vuart_init(CTX->xch,
1116 XEN_DOMCTL_VUART_TYPE_VPL011,
1117 dom->guest_domid,
1118 dom->console_domid,
1119 dom->vuart_gfn,
1120 &state->vuart_port);
1121 if (ret < 0) {
1122 rc = ERROR_FAIL;
1123 LOG(ERROR, "xc_dom_vuart_init failed\n");
1124 }
1125
1126 out:
1127 return rc;
1128 }
1129
libxl__arch_vnuma_build_vmemrange(libxl__gc * gc,uint32_t domid,libxl_domain_build_info * info,libxl__domain_build_state * state)1130 int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc,
1131 uint32_t domid,
1132 libxl_domain_build_info *info,
1133 libxl__domain_build_state *state)
1134 {
1135 return libxl__vnuma_build_vmemrange_pv_generic(gc, domid, info, state);
1136 }
1137
libxl__arch_domain_map_irq(libxl__gc * gc,uint32_t domid,int irq)1138 int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq)
1139 {
1140 return xc_domain_bind_pt_spi_irq(CTX->xch, domid, irq, irq);
1141 }
1142
libxl__arch_domain_construct_memmap(libxl__gc * gc,libxl_domain_config * d_config,uint32_t domid,struct xc_dom_image * dom)1143 int libxl__arch_domain_construct_memmap(libxl__gc *gc,
1144 libxl_domain_config *d_config,
1145 uint32_t domid,
1146 struct xc_dom_image *dom)
1147 {
1148 return 0;
1149 }
1150
libxl__arch_domain_build_info_acpi_setdefault(libxl_domain_build_info * b_info)1151 void libxl__arch_domain_build_info_acpi_setdefault(
1152 libxl_domain_build_info *b_info)
1153 {
1154 libxl_defbool_setdefault(&b_info->acpi, false);
1155 }
1156
1157 /*
1158 * Local variables:
1159 * mode: C
1160 * c-basic-offset: 4
1161 * indent-tabs-mode: nil
1162 * End:
1163 */
1164