1 /*
2 * ARM DomU ACPI generation
3 *
4 * Copyright (C) 2016 Linaro Ltd.
5 *
6 * Author: Shannon Zhao <shannon.zhao@linaro.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published
10 * by the Free Software Foundation; version 2.1 only. with the special
11 * exception on linking described in file LICENSE.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 */
18
19 #include "libxl_arm.h"
20
21 #include <stdint.h>
22
23 /* Below typedefs are useful for the headers under acpi/ */
24 typedef uint8_t u8;
25 typedef uint16_t u16;
26 typedef uint32_t u32;
27 typedef uint64_t u64;
28 typedef int64_t s64;
29
30 #include <acpi/acconfig.h>
31 #include <acpi/actbl.h>
32
33 #ifndef BITS_PER_LONG
34 #ifdef _LP64
35 #define BITS_PER_LONG 64
36 #else
37 #define BITS_PER_LONG 32
38 #endif
39 #endif
40 #define ACPI_MACHINE_WIDTH BITS_PER_LONG
41 #define COMPILER_DEPENDENT_INT64 int64_t
42 #define COMPILER_DEPENDENT_UINT64 uint64_t
43
44 #include <acpi/actypes.h>
45
46 _hidden
47 extern const unsigned char dsdt_anycpu_arm[];
48 _hidden
49 extern const int dsdt_anycpu_arm_len;
50
51 #define ACPI_OEM_ID "Xen\0\0"
52 #define ACPI_OEM_TABLE_ID "ARM\0\0\0\0"
53 #define ACPI_ASL_COMPILER_ID "XL\0"
54
55 enum {
56 RSDP,
57 XSDT,
58 GTDT,
59 MADT,
60 FADT,
61 DSDT,
62 MAX_TABLE_NUMS,
63 };
64
65 struct acpitable {
66 uint64_t addr;
67 size_t size;
68 };
69
libxl__estimate_madt_size(libxl__gc * gc,const libxl_domain_build_info * info,size_t * size)70 static int libxl__estimate_madt_size(libxl__gc *gc,
71 const libxl_domain_build_info *info,
72 size_t *size)
73 {
74 int rc = 0;
75
76 switch (info->arch_arm.gic_version) {
77 case LIBXL_GIC_VERSION_V2:
78 *size = sizeof(struct acpi_table_madt) +
79 ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus +
80 sizeof(struct acpi_madt_generic_distributor);
81 break;
82 case LIBXL_GIC_VERSION_V3:
83 *size = sizeof(struct acpi_table_madt) +
84 ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus +
85 sizeof(struct acpi_madt_generic_distributor) +
86 sizeof(struct acpi_madt_generic_redistributor);
87 break;
88 default:
89 LOG(ERROR, "Unknown GIC version");
90 *size = 0;
91 rc = ERROR_FAIL;
92 break;
93 }
94
95 return rc;
96 }
97
libxl__get_acpi_size(libxl__gc * gc,const libxl_domain_build_info * info,uint64_t * out)98 int libxl__get_acpi_size(libxl__gc *gc,
99 const libxl_domain_build_info *info,
100 uint64_t *out)
101 {
102 uint64_t size;
103 int rc = 0;
104
105
106 rc = libxl__estimate_madt_size(gc, info, &size);
107 if (rc < 0)
108 goto out;
109
110 *out = ROUNDUP(size, 3) +
111 ROUNDUP(sizeof(struct acpi_table_rsdp), 3) +
112 ROUNDUP(sizeof(struct acpi_table_xsdt), 3) +
113 ROUNDUP(sizeof(struct acpi_table_gtdt), 3) +
114 ROUNDUP(sizeof(struct acpi_table_fadt), 3) +
115 ROUNDUP(sizeof(dsdt_anycpu_arm_len), 3);
116
117 out:
118 return rc;
119 }
120
libxl__allocate_acpi_tables(libxl__gc * gc,libxl_domain_build_info * info,struct xc_dom_image * dom,struct acpitable acpitables[])121 static int libxl__allocate_acpi_tables(libxl__gc *gc,
122 libxl_domain_build_info *info,
123 struct xc_dom_image *dom,
124 struct acpitable acpitables[])
125 {
126 int rc;
127 size_t size;
128
129 acpitables[RSDP].addr = GUEST_ACPI_BASE;
130 acpitables[RSDP].size = sizeof(struct acpi_table_rsdp);
131 dom->acpi_modules[0].length += ROUNDUP(acpitables[RSDP].size, 3);
132
133 acpitables[XSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
134 /*
135 * Currently only 3 tables(GTDT, FADT, MADT) are pointed by XSDT. Alloc
136 * entries for them.
137 */
138 acpitables[XSDT].size = sizeof(struct acpi_table_xsdt) +
139 sizeof(uint64_t) * 2;
140 dom->acpi_modules[0].length += ROUNDUP(acpitables[XSDT].size, 3);
141
142 acpitables[GTDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
143 acpitables[GTDT].size = sizeof(struct acpi_table_gtdt);
144 dom->acpi_modules[0].length += ROUNDUP(acpitables[GTDT].size, 3);
145
146 acpitables[MADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
147
148 rc = libxl__estimate_madt_size(gc, info, &size);
149 if (rc < 0)
150 goto out;
151
152 acpitables[MADT].size = size;
153 dom->acpi_modules[0].length += ROUNDUP(acpitables[MADT].size, 3);
154
155 acpitables[FADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
156 acpitables[FADT].size = sizeof(struct acpi_table_fadt);
157 dom->acpi_modules[0].length += ROUNDUP(acpitables[FADT].size, 3);
158
159 acpitables[DSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length;
160 acpitables[DSDT].size = dsdt_anycpu_arm_len;
161 dom->acpi_modules[0].length += ROUNDUP(acpitables[DSDT].size, 3);
162
163 assert(dom->acpi_modules[0].length <= GUEST_ACPI_SIZE);
164 dom->acpi_modules[0].data = libxl__zalloc(gc, dom->acpi_modules[0].length);
165
166 rc = 0;
167 out:
168 return rc;
169 }
170
calculate_checksum(void * table,uint32_t checksum_offset,uint32_t length)171 static void calculate_checksum(void *table, uint32_t checksum_offset,
172 uint32_t length)
173 {
174 uint8_t *p, sum = 0;
175
176 p = table;
177 p[checksum_offset] = 0;
178
179 while (length--)
180 sum = sum + *p++;
181
182 p = table;
183 p[checksum_offset] = -sum;
184 }
185
make_acpi_rsdp(libxl__gc * gc,struct xc_dom_image * dom,struct acpitable acpitables[])186 static void make_acpi_rsdp(libxl__gc *gc, struct xc_dom_image *dom,
187 struct acpitable acpitables[])
188 {
189 uint64_t offset = acpitables[RSDP].addr - GUEST_ACPI_BASE;
190 struct acpi_table_rsdp *rsdp = (void *)dom->acpi_modules[0].data + offset;
191
192 memcpy(rsdp->signature, "RSD PTR ", sizeof(rsdp->signature));
193 BUILD_BUG_ON(sizeof(ACPI_OEM_ID) != sizeof(rsdp->oem_id));
194 memcpy(rsdp->oem_id, ACPI_OEM_ID, sizeof(rsdp->oem_id));
195 rsdp->length = acpitables[RSDP].size;
196 rsdp->revision = 0x02;
197 rsdp->xsdt_physical_address = acpitables[XSDT].addr;
198 calculate_checksum(rsdp,
199 offsetof(struct acpi_table_rsdp, extended_checksum),
200 acpitables[RSDP].size);
201 }
202
make_acpi_header(struct acpi_table_header * h,const char * sig,size_t len,uint8_t rev)203 static void make_acpi_header(struct acpi_table_header *h, const char *sig,
204 size_t len, uint8_t rev)
205 {
206 memcpy(h->signature, sig, 4);
207 h->length = len;
208 h->revision = rev;
209 BUILD_BUG_ON(sizeof(ACPI_OEM_ID) != sizeof(h->oem_id));
210 memcpy(h->oem_id, ACPI_OEM_ID, sizeof(h->oem_id));
211 BUILD_BUG_ON(sizeof(ACPI_OEM_TABLE_ID) != sizeof(h->oem_table_id));
212 memcpy(h->oem_table_id, ACPI_OEM_TABLE_ID, sizeof(h->oem_table_id));
213 h->oem_revision = 0;
214 BUILD_BUG_ON(sizeof(ACPI_ASL_COMPILER_ID) != sizeof(h->asl_compiler_id));
215 memcpy(h->asl_compiler_id, ACPI_ASL_COMPILER_ID,
216 sizeof(h->asl_compiler_id));
217 h->asl_compiler_revision = 0;
218 h->checksum = 0;
219 }
220
make_acpi_xsdt(libxl__gc * gc,struct xc_dom_image * dom,struct acpitable acpitables[])221 static void make_acpi_xsdt(libxl__gc *gc, struct xc_dom_image *dom,
222 struct acpitable acpitables[])
223 {
224 uint64_t offset = acpitables[XSDT].addr - GUEST_ACPI_BASE;
225 struct acpi_table_xsdt *xsdt = (void *)dom->acpi_modules[0].data + offset;
226
227 xsdt->table_offset_entry[0] = acpitables[MADT].addr;
228 xsdt->table_offset_entry[1] = acpitables[GTDT].addr;
229 xsdt->table_offset_entry[2] = acpitables[FADT].addr;
230 make_acpi_header(&xsdt->header, "XSDT", acpitables[XSDT].size, 1);
231 calculate_checksum(xsdt, offsetof(struct acpi_table_header, checksum),
232 acpitables[XSDT].size);
233 }
234
make_acpi_gtdt(libxl__gc * gc,struct xc_dom_image * dom,struct acpitable acpitables[])235 static void make_acpi_gtdt(libxl__gc *gc, struct xc_dom_image *dom,
236 struct acpitable acpitables[])
237 {
238 uint64_t offset = acpitables[GTDT].addr - GUEST_ACPI_BASE;
239 struct acpi_table_gtdt *gtdt = (void *)dom->acpi_modules[0].data + offset;
240
241 gtdt->non_secure_el1_interrupt = GUEST_TIMER_PHYS_NS_PPI;
242 gtdt->non_secure_el1_flags =
243 (ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE)
244 |(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY);
245 gtdt->virtual_timer_interrupt = GUEST_TIMER_VIRT_PPI;
246 gtdt->virtual_timer_flags =
247 (ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE)
248 |(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY);
249
250 gtdt->counter_block_addresss = ~((uint64_t)0);
251 gtdt->counter_read_block_address = ~((uint64_t)0);
252
253 make_acpi_header(>dt->header, "GTDT", acpitables[GTDT].size, 2);
254 calculate_checksum(gtdt, offsetof(struct acpi_table_header, checksum),
255 acpitables[GTDT].size);
256 }
257
make_acpi_madt_gicc(void * table,int nr_cpus,uint64_t gicc_base)258 static void make_acpi_madt_gicc(void *table, int nr_cpus, uint64_t gicc_base)
259 {
260 int i;
261 struct acpi_madt_generic_interrupt *gicc = table;
262
263 for (i = 0; i < nr_cpus; i++) {
264 gicc->header.type = ACPI_MADT_TYPE_GENERIC_INTERRUPT;
265 gicc->header.length = ACPI_MADT_GICC_SIZE_v5;
266 gicc->base_address = gicc_base;
267 gicc->cpu_interface_number = i;
268 gicc->arm_mpidr = libxl__compute_mpdir(i);
269 gicc->uid = i;
270 gicc->flags = ACPI_MADT_ENABLED;
271 gicc = table + ACPI_MADT_GICC_SIZE_v5;
272 }
273 }
274
make_acpi_madt_gicd(void * table,uint64_t gicd_base,uint8_t gic_version)275 static void make_acpi_madt_gicd(void *table, uint64_t gicd_base,
276 uint8_t gic_version)
277 {
278 struct acpi_madt_generic_distributor *gicd = table;
279
280 gicd->header.type = ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR;
281 gicd->header.length = sizeof(*gicd);
282 gicd->base_address = gicd_base;
283 /* This version field has no meaning before ACPI 5.1 errata. */
284 gicd->version = gic_version;
285 }
286
make_acpi_madt_gicr(void * table,uint64_t gicr_base,uint64_t gicr_size)287 static void make_acpi_madt_gicr(void *table, uint64_t gicr_base,
288 uint64_t gicr_size)
289 {
290 struct acpi_madt_generic_redistributor *gicr = table;
291
292 gicr->header.type = ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR;
293 gicr->header.length = sizeof(*gicr);
294 gicr->base_address = gicr_base;
295 gicr->length = gicr_size;
296 }
297
make_acpi_madt(libxl__gc * gc,struct xc_dom_image * dom,libxl_domain_build_info * info,struct acpitable acpitables[])298 static int make_acpi_madt(libxl__gc *gc, struct xc_dom_image *dom,
299 libxl_domain_build_info *info,
300 struct acpitable acpitables[])
301 {
302 uint64_t offset = acpitables[MADT].addr - GUEST_ACPI_BASE;
303 void *table = dom->acpi_modules[0].data + offset;
304 struct acpi_table_madt *madt = table;
305 int rc = 0;
306
307 switch (info->arch_arm.gic_version) {
308 case LIBXL_GIC_VERSION_V2:
309 table += sizeof(struct acpi_table_madt);
310 make_acpi_madt_gicc(table, info->max_vcpus, GUEST_GICC_BASE);
311
312 table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus;
313 make_acpi_madt_gicd(table, GUEST_GICD_BASE, ACPI_MADT_GIC_VERSION_V2);
314 break;
315 case LIBXL_GIC_VERSION_V3:
316 table += sizeof(struct acpi_table_madt);
317 make_acpi_madt_gicc(table, info->max_vcpus, 0);
318
319 table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus;
320 make_acpi_madt_gicd(table, GUEST_GICV3_GICD_BASE,
321 ACPI_MADT_GIC_VERSION_V3);
322
323 table += sizeof(struct acpi_madt_generic_distributor);
324 make_acpi_madt_gicr(table, GUEST_GICV3_GICR0_BASE,
325 GUEST_GICV3_GICR0_SIZE);
326 break;
327 default:
328 LOG(ERROR, "Unknown GIC version");
329 rc = ERROR_FAIL;
330 goto out;
331 }
332
333 make_acpi_header(&madt->header, "APIC", acpitables[MADT].size, 3);
334 calculate_checksum(madt, offsetof(struct acpi_table_header, checksum),
335 acpitables[MADT].size);
336
337 out:
338 return rc;
339 }
340
make_acpi_fadt(libxl__gc * gc,struct xc_dom_image * dom,struct acpitable acpitables[])341 static void make_acpi_fadt(libxl__gc *gc, struct xc_dom_image *dom,
342 struct acpitable acpitables[])
343 {
344 uint64_t offset = acpitables[FADT].addr - GUEST_ACPI_BASE;
345 struct acpi_table_fadt *fadt = (void *)dom->acpi_modules[0].data + offset;
346
347 /* Hardware Reduced = 1 and use PSCI 0.2+ and with HVC */
348 fadt->flags = ACPI_FADT_HW_REDUCED;
349 fadt->arm_boot_flags = ACPI_FADT_PSCI_COMPLIANT | ACPI_FADT_PSCI_USE_HVC;
350
351 /* ACPI v5.1 (fadt->revision.fadt->minor_revision) */
352 fadt->minor_revision = 0x1;
353 fadt->dsdt = acpitables[DSDT].addr;
354
355 make_acpi_header(&fadt->header, "FACP", acpitables[FADT].size, 5);
356 calculate_checksum(fadt, offsetof(struct acpi_table_header, checksum),
357 acpitables[FADT].size);
358 }
359
make_acpi_dsdt(libxl__gc * gc,struct xc_dom_image * dom,struct acpitable acpitables[])360 static void make_acpi_dsdt(libxl__gc *gc, struct xc_dom_image *dom,
361 struct acpitable acpitables[])
362 {
363 uint64_t offset = acpitables[DSDT].addr - GUEST_ACPI_BASE;
364 void *dsdt = dom->acpi_modules[0].data + offset;
365
366 memcpy(dsdt, dsdt_anycpu_arm, dsdt_anycpu_arm_len);
367 }
368
libxl__prepare_acpi(libxl__gc * gc,libxl_domain_build_info * info,struct xc_dom_image * dom)369 int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info,
370 struct xc_dom_image *dom)
371 {
372 const libxl_version_info *vers;
373 int rc = 0;
374 struct acpitable acpitables[MAX_TABLE_NUMS];
375
376 vers = libxl_get_version_info(CTX);
377 if (vers == NULL) {
378 rc = ERROR_FAIL;
379 goto out;
380 }
381
382 LOG(DEBUG, "constructing ACPI tables for Xen version %d.%d guest",
383 vers->xen_version_major, vers->xen_version_minor);
384
385 dom->acpi_modules[0].data = NULL;
386 dom->acpi_modules[0].length = 0;
387 dom->acpi_modules[0].guest_addr_out = GUEST_ACPI_BASE;
388
389 rc = libxl__allocate_acpi_tables(gc, info, dom, acpitables);
390 if (rc)
391 goto out;
392
393 make_acpi_rsdp(gc, dom, acpitables);
394 make_acpi_xsdt(gc, dom, acpitables);
395 make_acpi_gtdt(gc, dom, acpitables);
396 rc = make_acpi_madt(gc, dom, info, acpitables);
397 if (rc)
398 goto out;
399
400 make_acpi_fadt(gc, dom, acpitables);
401 make_acpi_dsdt(gc, dom, acpitables);
402
403 out:
404 return rc;
405 }
406
407 /*
408 * Local variables:
409 * mode: C
410 * c-basic-offset: 4
411 * indent-tabs-mode: nil
412 * End:
413 */
414