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