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 = &regs[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 = &regs[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