2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
8 #include <sys/endian.h>
10 #include <sys/queue.h>
13 #include <machine/vmm.h>
24 #include "qemu_loader.h"
26 struct basl_table_checksum {
27 STAILQ_ENTRY(basl_table_checksum) chain;
33 struct basl_table_length {
34 STAILQ_ENTRY(basl_table_length) chain;
39 struct basl_table_pointer {
40 STAILQ_ENTRY(basl_table_pointer) chain;
41 uint8_t src_signature[ACPI_NAMESEG_SIZE];
47 STAILQ_ENTRY(basl_table) chain;
49 uint8_t fwcfg_name[QEMU_FWCFG_MAX_NAME];
54 STAILQ_HEAD(basl_table_checksum_list, basl_table_checksum) checksums;
55 STAILQ_HEAD(basl_table_length_list, basl_table_length) lengths;
56 STAILQ_HEAD(basl_table_pointer_list, basl_table_pointer) pointers;
58 static STAILQ_HEAD(basl_table_list, basl_table) basl_tables = STAILQ_HEAD_INITIALIZER(
61 static struct qemu_loader *basl_loader;
62 static struct basl_table *rsdt;
63 static struct basl_table *xsdt;
64 static bool load_into_memory;
66 static __inline uint64_t
67 basl_le_dec(void *pp, size_t len)
73 return ((uint8_t *)pp)[0];
86 basl_le_enc(void *pp, uint64_t val, size_t len)
97 basl_dump_table(const struct basl_table *const table, const bool mem)
99 const ACPI_TABLE_HEADER *const header = table->data;
105 data = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off,
112 printf("%.4s @ %8x (%s)\n", header->Signature,
113 BHYVE_ACPI_BASE + table->off, mem ? "Memory" : "FwCfg");
114 hexdump(data, table->len, NULL, 0);
120 basl_dump(const bool mem)
122 struct basl_table *table;
124 STAILQ_FOREACH(table, &basl_tables, chain) {
125 BASL_EXEC(basl_dump_table(table, mem));
132 basl_fill_gas(ACPI_GENERIC_ADDRESS *const gas, const uint8_t space_id,
133 const uint8_t bit_width, const uint8_t bit_offset,
134 const uint8_t access_width, const uint64_t address)
138 gas->SpaceId = space_id;
139 gas->BitWidth = bit_width;
140 gas->BitOffset = bit_offset;
141 gas->AccessWidth = access_width;
142 gas->Address = htole64(address);
146 basl_finish_install_guest_tables(struct basl_table *const table, uint32_t *const off)
150 table->off = roundup2(*off, table->alignment);
151 *off = table->off + table->len;
152 if (*off <= table->off) {
153 warnx("%s: invalid table length 0x%8x @ offset 0x%8x", __func__,
154 table->len, table->off);
158 /* Cause guest BIOS to copy the ACPI table into guest memory. */
160 qemu_fwcfg_add_file(table->fwcfg_name, table->len, table->data));
161 BASL_EXEC(qemu_loader_alloc(basl_loader, table->fwcfg_name,
162 table->alignment, QEMU_LOADER_ALLOC_HIGH));
164 if (!load_into_memory) {
169 * Install ACPI tables directly in guest memory for use by guests which
170 * do not boot via EFI. EFI ROMs provide a pointer to the firmware
171 * generated ACPI tables instead, but it doesn't hurt to install the
174 gva = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off, table->len);
176 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]", __func__,
177 (uint64_t)BHYVE_ACPI_BASE + table->off,
178 (uint64_t)BHYVE_ACPI_BASE + table->off + table->len);
181 memcpy(gva, table->data, table->len);
187 basl_finish_patch_checksums(struct basl_table *const table)
189 struct basl_table_checksum *checksum;
191 STAILQ_FOREACH(checksum, &table->checksums, chain) {
192 uint8_t *gva, *checksum_gva;
198 if (len == BASL_TABLE_CHECKSUM_LEN_FULL_TABLE) {
202 assert(checksum->off < table->len);
203 assert(checksum->start < table->len);
204 assert(checksum->start + len <= table->len);
206 /* Cause guest BIOS to patch the checksum. */
207 BASL_EXEC(qemu_loader_add_checksum(basl_loader,
208 table->fwcfg_name, checksum->off, checksum->start, len));
210 if (!load_into_memory) {
215 * Install ACPI tables directly in guest memory for use by
216 * guests which do not boot via EFI. EFI ROMs provide a pointer
217 * to the firmware generated ACPI tables instead, but it doesn't
218 * hurt to install the tables always.
220 gpa = BHYVE_ACPI_BASE + table->off + checksum->start;
221 if ((gpa < BHYVE_ACPI_BASE) ||
222 (gpa < BHYVE_ACPI_BASE + table->off)) {
223 warnx("%s: invalid gpa (off 0x%8x start 0x%8x)",
224 __func__, table->off, checksum->start);
228 gva = vm_map_gpa(table->ctx, gpa, len);
230 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
231 __func__, gpa, gpa + len);
235 checksum_gva = gva + checksum->off;
236 if (checksum_gva < gva) {
237 warnx("%s: invalid checksum offset 0x%8x", __func__,
243 for (uint32_t i = 0; i < len; ++i) {
246 *checksum_gva = -sum;
252 static struct basl_table *
253 basl_get_table_by_signature(const uint8_t signature[ACPI_NAMESEG_SIZE])
255 struct basl_table *table;
257 STAILQ_FOREACH(table, &basl_tables, chain) {
258 const ACPI_TABLE_HEADER *const header =
259 (const ACPI_TABLE_HEADER *)table->data;
261 if (strncmp(header->Signature, signature,
262 sizeof(header->Signature)) == 0) {
267 warnx("%s: %.4s not found", __func__, signature);
272 basl_finish_patch_pointers(struct basl_table *const table)
274 struct basl_table_pointer *pointer;
276 STAILQ_FOREACH(pointer, &table->pointers, chain) {
277 const struct basl_table *src_table;
281 assert(pointer->off < table->len);
282 assert(pointer->off + pointer->size <= table->len);
284 src_table = basl_get_table_by_signature(pointer->src_signature);
285 if (src_table == NULL) {
286 warnx("%s: could not find ACPI table %.4s", __func__,
287 pointer->src_signature);
291 /* Cause guest BIOS to patch the pointer. */
293 qemu_loader_add_pointer(basl_loader, table->fwcfg_name,
294 src_table->fwcfg_name, pointer->off, pointer->size));
296 if (!load_into_memory) {
301 * Install ACPI tables directly in guest memory for use by
302 * guests which do not boot via EFI. EFI ROMs provide a pointer
303 * to the firmware generated ACPI tables instead, but it doesn't
304 * hurt to install the tables always.
306 gpa = BHYVE_ACPI_BASE + table->off;
307 if (gpa < BHYVE_ACPI_BASE) {
308 warnx("%s: table offset of 0x%8x is too large",
309 __func__, table->off);
313 gva = vm_map_gpa(table->ctx, gpa, table->len);
315 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
316 __func__, gpa, gpa + table->len);
320 val = basl_le_dec(gva + pointer->off, pointer->size);
321 val += BHYVE_ACPI_BASE + src_table->off;
322 basl_le_enc(gva + pointer->off, val, pointer->size);
329 basl_finish_set_length(struct basl_table *const table)
331 struct basl_table_length *length;
333 STAILQ_FOREACH(length, &table->lengths, chain) {
334 assert(length->off < table->len);
335 assert(length->off + length->size <= table->len);
337 basl_le_enc((uint8_t *)table->data + length->off, table->len,
347 struct basl_table *table;
350 if (STAILQ_EMPTY(&basl_tables)) {
351 warnx("%s: no ACPI tables found", __func__);
356 * If we install ACPI tables by FwCfg and by memory, Windows will use
357 * the tables from memory. This can cause issues when using advanced
358 * features like a TPM log because we aren't able to patch the memory
359 * tables accordingly.
361 load_into_memory = get_config_bool_default("acpi_tables_in_memory",
365 * We have to install all tables before we can patch them. Therefore,
366 * use two loops. The first one installs all tables and the second one
369 STAILQ_FOREACH(table, &basl_tables, chain) {
370 BASL_EXEC(basl_finish_set_length(table));
371 BASL_EXEC(basl_finish_install_guest_tables(table, &off));
373 STAILQ_FOREACH(table, &basl_tables, chain) {
374 BASL_EXEC(basl_finish_patch_pointers(table));
377 * Calculate the checksum as last step!
379 BASL_EXEC(basl_finish_patch_checksums(table));
381 BASL_EXEC(qemu_loader_finish(basl_loader));
387 basl_init_rsdt(struct vmctx *const ctx)
390 basl_table_create(&rsdt, ctx, ACPI_SIG_RSDT, BASL_TABLE_ALIGNMENT));
393 BASL_EXEC(basl_table_append_header(rsdt, ACPI_SIG_RSDT, 1, 1));
394 /* Pointers (added by basl_table_register_to_rsdt) */
400 basl_init_xsdt(struct vmctx *const ctx)
403 basl_table_create(&xsdt, ctx, ACPI_SIG_XSDT, BASL_TABLE_ALIGNMENT));
406 BASL_EXEC(basl_table_append_header(xsdt, ACPI_SIG_XSDT, 1, 1));
407 /* Pointers (added by basl_table_register_to_rsdt) */
413 basl_init(struct vmctx *const ctx)
415 BASL_EXEC(basl_init_rsdt(ctx));
416 BASL_EXEC(basl_init_xsdt(ctx));
418 qemu_loader_create(&basl_loader, QEMU_FWCFG_FILE_TABLE_LOADER));
424 basl_table_add_checksum(struct basl_table *const table, const uint32_t off,
425 const uint32_t start, const uint32_t len)
427 struct basl_table_checksum *checksum;
429 assert(table != NULL);
431 checksum = calloc(1, sizeof(struct basl_table_checksum));
432 if (checksum == NULL) {
433 warnx("%s: failed to allocate checksum", __func__);
438 checksum->start = start;
441 STAILQ_INSERT_TAIL(&table->checksums, checksum, chain);
447 basl_table_add_length(struct basl_table *const table, const uint32_t off,
450 struct basl_table_length *length;
452 assert(table != NULL);
453 assert(size == 4 || size == 8);
455 length = calloc(1, sizeof(struct basl_table_length));
456 if (length == NULL) {
457 warnx("%s: failed to allocate length", __func__);
464 STAILQ_INSERT_TAIL(&table->lengths, length, chain);
470 basl_table_add_pointer(struct basl_table *const table,
471 const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint32_t off,
474 struct basl_table_pointer *pointer;
476 assert(table != NULL);
477 assert(size == 4 || size == 8);
479 pointer = calloc(1, sizeof(struct basl_table_pointer));
480 if (pointer == NULL) {
481 warnx("%s: failed to allocate pointer", __func__);
485 memcpy(pointer->src_signature, src_signature,
486 sizeof(pointer->src_signature));
488 pointer->size = size;
490 STAILQ_INSERT_TAIL(&table->pointers, pointer, chain);
496 basl_table_append_bytes(struct basl_table *const table, const void *const bytes,
501 assert(table != NULL);
502 assert(bytes != NULL);
504 if (table->len + len <= table->len) {
505 warnx("%s: table too large (table->len 0x%8x len 0x%8x)",
506 __func__, table->len, len);
510 table->data = reallocf(table->data, table->len + len);
511 if (table->data == NULL) {
512 warnx("%s: failed to realloc table to length 0x%8x", __func__,
518 end = (uint8_t *)table->data + table->len;
521 memcpy(end, bytes, len);
527 basl_table_append_checksum(struct basl_table *const table, const uint32_t start,
530 assert(table != NULL);
532 BASL_EXEC(basl_table_add_checksum(table, table->len, start, len));
533 BASL_EXEC(basl_table_append_int(table, 0, 1));
539 basl_table_append_content(struct basl_table *table, void *data, uint32_t len)
541 assert(data != NULL);
542 assert(len >= sizeof(ACPI_TABLE_HEADER));
544 return (basl_table_append_bytes(table,
545 (void *)((uintptr_t)(data) + sizeof(ACPI_TABLE_HEADER)),
546 len - sizeof(ACPI_TABLE_HEADER)));
550 basl_table_append_fwcfg(struct basl_table *const table,
551 const uint8_t *fwcfg_name, const uint32_t alignment, const uint8_t size)
553 assert(table != NULL);
554 assert(fwcfg_name != NULL);
555 assert(size <= sizeof(uint64_t));
557 BASL_EXEC(qemu_loader_alloc(basl_loader, fwcfg_name, alignment,
558 QEMU_LOADER_ALLOC_HIGH));
559 BASL_EXEC(qemu_loader_add_pointer(basl_loader, table->fwcfg_name,
560 fwcfg_name, table->len, size));
561 BASL_EXEC(basl_table_append_int(table, 0, size));
567 basl_table_append_gas(struct basl_table *const table, const uint8_t space_id,
568 const uint8_t bit_width, const uint8_t bit_offset,
569 const uint8_t access_width, const uint64_t address)
571 ACPI_GENERIC_ADDRESS gas_le = {
573 .BitWidth = bit_width,
574 .BitOffset = bit_offset,
575 .AccessWidth = access_width,
576 .Address = htole64(address),
579 return (basl_table_append_bytes(table, &gas_le, sizeof(gas_le)));
583 basl_table_append_header(struct basl_table *const table,
584 const uint8_t signature[ACPI_NAMESEG_SIZE], const uint8_t revision,
585 const uint32_t oem_revision)
587 ACPI_TABLE_HEADER header_le;
588 /* + 1 is required for the null terminator */
589 char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1];
591 assert(table != NULL);
592 assert(table->len == 0);
594 memcpy(header_le.Signature, signature, ACPI_NAMESEG_SIZE);
595 header_le.Length = 0; /* patched by basl_finish */
596 header_le.Revision = revision;
597 header_le.Checksum = 0; /* patched by basl_finish */
598 memcpy(header_le.OemId, "BHYVE ", ACPI_OEM_ID_SIZE);
599 snprintf(oem_table_id, ACPI_OEM_TABLE_ID_SIZE, "BV%.4s ", signature);
600 memcpy(header_le.OemTableId, oem_table_id,
601 sizeof(header_le.OemTableId));
602 header_le.OemRevision = htole32(oem_revision);
603 memcpy(header_le.AslCompilerId, "BASL", ACPI_NAMESEG_SIZE);
604 header_le.AslCompilerRevision = htole32(0x20220504);
607 basl_table_append_bytes(table, &header_le, sizeof(header_le)));
609 BASL_EXEC(basl_table_add_length(table,
610 offsetof(ACPI_TABLE_HEADER, Length), sizeof(header_le.Length)));
611 BASL_EXEC(basl_table_add_checksum(table,
612 offsetof(ACPI_TABLE_HEADER, Checksum), 0,
613 BASL_TABLE_CHECKSUM_LEN_FULL_TABLE));
619 basl_table_append_int(struct basl_table *const table, const uint64_t val,
624 assert(size <= sizeof(val));
626 basl_le_enc(buf, val, size);
627 return (basl_table_append_bytes(table, buf, size));
631 basl_table_append_length(struct basl_table *const table, const uint8_t size)
633 assert(table != NULL);
634 assert(size <= sizeof(table->len));
636 BASL_EXEC(basl_table_add_length(table, table->len, size));
637 BASL_EXEC(basl_table_append_int(table, 0, size));
643 basl_table_append_pointer(struct basl_table *const table,
644 const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint8_t size)
646 assert(table != NULL);
647 assert(size == 4 || size == 8);
649 BASL_EXEC(basl_table_add_pointer(table, src_signature, table->len, size));
650 BASL_EXEC(basl_table_append_int(table, 0, size));
656 basl_table_create(struct basl_table **const table, struct vmctx *ctx,
657 const uint8_t *const name, const uint32_t alignment)
659 struct basl_table *new_table;
661 assert(table != NULL);
663 new_table = calloc(1, sizeof(struct basl_table));
664 if (new_table == NULL) {
665 warnx("%s: failed to allocate table", __func__);
669 new_table->ctx = ctx;
671 snprintf(new_table->fwcfg_name, sizeof(new_table->fwcfg_name),
672 "etc/acpi/%s", name);
674 new_table->alignment = alignment;
676 STAILQ_INIT(&new_table->checksums);
677 STAILQ_INIT(&new_table->lengths);
678 STAILQ_INIT(&new_table->pointers);
680 STAILQ_INSERT_TAIL(&basl_tables, new_table, chain);
688 basl_table_register_to_rsdt(struct basl_table *table)
690 const ACPI_TABLE_HEADER *header;
692 assert(table != NULL);
694 header = (const ACPI_TABLE_HEADER *)table->data;
696 BASL_EXEC(basl_table_append_pointer(rsdt, header->Signature,
697 ACPI_RSDT_ENTRY_SIZE));
698 BASL_EXEC(basl_table_append_pointer(xsdt, header->Signature,
699 ACPI_XSDT_ENTRY_SIZE));