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