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 struct basl_table_checksum {
25 STAILQ_ENTRY(basl_table_checksum) chain;
31 struct basl_table_length {
32 STAILQ_ENTRY(basl_table_length) chain;
37 struct basl_table_pointer {
38 STAILQ_ENTRY(basl_table_pointer) chain;
39 uint8_t src_signature[ACPI_NAMESEG_SIZE];
45 STAILQ_ENTRY(basl_table) chain;
47 uint8_t fwcfg_name[QEMU_FWCFG_MAX_NAME];
52 STAILQ_HEAD(basl_table_checksum_list, basl_table_checksum) checksums;
53 STAILQ_HEAD(basl_table_length_list, basl_table_length) lengths;
54 STAILQ_HEAD(basl_table_pointer_list, basl_table_pointer) pointers;
56 static STAILQ_HEAD(basl_table_list, basl_table) basl_tables = STAILQ_HEAD_INITIALIZER(
59 static __inline uint64_t
60 basl_le_dec(void *pp, size_t len)
66 return ((uint8_t *)pp)[0];
79 basl_le_enc(void *pp, uint64_t val, size_t len)
90 basl_dump_table(const struct basl_table *const table, const bool mem)
92 const ACPI_TABLE_HEADER *const header = table->data;
98 data = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off,
105 printf("%.4s @ %8x (%s)\n", header->Signature,
106 BHYVE_ACPI_BASE + table->off, mem ? "Memory" : "FwCfg");
107 hexdump(data, table->len, NULL, 0);
113 basl_dump(const bool mem)
115 struct basl_table *table;
117 STAILQ_FOREACH(table, &basl_tables, chain) {
118 BASL_EXEC(basl_dump_table(table, mem));
125 basl_fill_gas(ACPI_GENERIC_ADDRESS *const gas, const uint8_t space_id,
126 const uint8_t bit_width, const uint8_t bit_offset,
127 const uint8_t access_width, const uint64_t address)
131 gas->SpaceId = space_id;
132 gas->BitWidth = bit_width;
133 gas->BitOffset = bit_offset;
134 gas->AccessWidth = access_width;
135 gas->Address = htole64(address);
139 basl_finish_install_guest_tables(struct basl_table *const table, uint32_t *const off)
143 table->off = roundup2(*off, table->alignment);
144 *off = table->off + table->len;
145 if (*off <= table->off) {
146 warnx("%s: invalid table length 0x%8x @ offset 0x%8x", __func__,
147 table->len, table->off);
152 * Install ACPI tables directly in guest memory for use by guests which
153 * do not boot via EFI. EFI ROMs provide a pointer to the firmware
154 * generated ACPI tables instead, but it doesn't hurt to install the
157 gva = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off, table->len);
159 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]", __func__,
160 (uint64_t)BHYVE_ACPI_BASE + table->off,
161 (uint64_t)BHYVE_ACPI_BASE + table->off + table->len);
164 memcpy(gva, table->data, table->len);
170 basl_finish_patch_checksums(struct basl_table *const table)
172 struct basl_table_checksum *checksum;
174 STAILQ_FOREACH(checksum, &table->checksums, chain) {
175 uint8_t *gva, *checksum_gva;
181 if (len == BASL_TABLE_CHECKSUM_LEN_FULL_TABLE) {
185 assert(checksum->off < table->len);
186 assert(checksum->start < table->len);
187 assert(checksum->start + len <= table->len);
190 * Install ACPI tables directly in guest memory for use by
191 * guests which do not boot via EFI. EFI ROMs provide a pointer
192 * to the firmware generated ACPI tables instead, but it doesn't
193 * hurt to install the tables always.
195 gpa = BHYVE_ACPI_BASE + table->off + checksum->start;
196 if ((gpa < BHYVE_ACPI_BASE) ||
197 (gpa < BHYVE_ACPI_BASE + table->off)) {
198 warnx("%s: invalid gpa (off 0x%8x start 0x%8x)",
199 __func__, table->off, checksum->start);
203 gva = vm_map_gpa(table->ctx, gpa, len);
205 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
206 __func__, gpa, gpa + len);
210 checksum_gva = gva + checksum->off;
211 if (checksum_gva < gva) {
212 warnx("%s: invalid checksum offset 0x%8x", __func__,
218 for (uint32_t i = 0; i < len; ++i) {
221 *checksum_gva = -sum;
227 static struct basl_table *
228 basl_get_table_by_signature(const uint8_t signature[ACPI_NAMESEG_SIZE])
230 struct basl_table *table;
232 STAILQ_FOREACH(table, &basl_tables, chain) {
233 const ACPI_TABLE_HEADER *const header =
234 (const ACPI_TABLE_HEADER *)table->data;
236 if (strncmp(header->Signature, signature,
237 sizeof(header->Signature)) == 0) {
242 warnx("%s: %.4s not found", __func__, signature);
247 basl_finish_patch_pointers(struct basl_table *const table)
249 struct basl_table_pointer *pointer;
251 STAILQ_FOREACH(pointer, &table->pointers, chain) {
252 const struct basl_table *src_table;
256 assert(pointer->off < table->len);
257 assert(pointer->off + pointer->size <= table->len);
259 src_table = basl_get_table_by_signature(pointer->src_signature);
260 if (src_table == NULL) {
261 warnx("%s: could not find ACPI table %.4s", __func__,
262 pointer->src_signature);
267 * Install ACPI tables directly in guest memory for use by
268 * guests which do not boot via EFI. EFI ROMs provide a pointer
269 * to the firmware generated ACPI tables instead, but it doesn't
270 * hurt to install the tables always.
272 gpa = BHYVE_ACPI_BASE + table->off;
273 if (gpa < BHYVE_ACPI_BASE) {
274 warnx("%s: table offset of 0x%8x is too large",
275 __func__, table->off);
279 gva = vm_map_gpa(table->ctx, gpa, table->len);
281 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
282 __func__, gpa, gpa + table->len);
286 val = basl_le_dec(gva + pointer->off, pointer->size);
287 val += BHYVE_ACPI_BASE + src_table->off;
288 basl_le_enc(gva + pointer->off, val, pointer->size);
295 basl_finish_set_length(struct basl_table *const table)
297 struct basl_table_length *length;
299 STAILQ_FOREACH(length, &table->lengths, chain) {
300 assert(length->off < table->len);
301 assert(length->off + length->size <= table->len);
303 basl_le_enc((uint8_t *)table->data + length->off, table->len,
313 struct basl_table *table;
316 if (STAILQ_EMPTY(&basl_tables)) {
317 warnx("%s: no ACPI tables found", __func__);
322 * We have to install all tables before we can patch them. Therefore,
323 * use two loops. The first one installs all tables and the second one
326 STAILQ_FOREACH(table, &basl_tables, chain) {
327 BASL_EXEC(basl_finish_set_length(table));
328 BASL_EXEC(basl_finish_install_guest_tables(table, &off));
330 STAILQ_FOREACH(table, &basl_tables, chain) {
331 BASL_EXEC(basl_finish_patch_pointers(table));
334 * Calculate the checksum as last step!
336 BASL_EXEC(basl_finish_patch_checksums(table));
349 basl_table_add_checksum(struct basl_table *const table, const uint32_t off,
350 const uint32_t start, const uint32_t len)
352 struct basl_table_checksum *checksum;
354 assert(table != NULL);
356 checksum = calloc(1, sizeof(struct basl_table_checksum));
357 if (checksum == NULL) {
358 warnx("%s: failed to allocate checksum", __func__);
363 checksum->start = start;
366 STAILQ_INSERT_TAIL(&table->checksums, checksum, chain);
372 basl_table_add_length(struct basl_table *const table, const uint32_t off,
375 struct basl_table_length *length;
377 assert(table != NULL);
378 assert(size == 4 || size == 8);
380 length = calloc(1, sizeof(struct basl_table_length));
381 if (length == NULL) {
382 warnx("%s: failed to allocate length", __func__);
389 STAILQ_INSERT_TAIL(&table->lengths, length, chain);
395 basl_table_add_pointer(struct basl_table *const table,
396 const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint32_t off,
399 struct basl_table_pointer *pointer;
401 assert(table != NULL);
402 assert(size == 4 || size == 8);
404 pointer = calloc(1, sizeof(struct basl_table_pointer));
405 if (pointer == NULL) {
406 warnx("%s: failed to allocate pointer", __func__);
410 memcpy(pointer->src_signature, src_signature,
411 sizeof(pointer->src_signature));
413 pointer->size = size;
415 STAILQ_INSERT_TAIL(&table->pointers, pointer, chain);
421 basl_table_append_bytes(struct basl_table *const table, const void *const bytes,
426 assert(table != NULL);
427 assert(bytes != NULL);
429 if (table->len + len <= table->len) {
430 warnx("%s: table too large (table->len 0x%8x len 0x%8x)",
431 __func__, table->len, len);
435 table->data = reallocf(table->data, table->len + len);
436 if (table->data == NULL) {
437 warnx("%s: failed to realloc table to length 0x%8x", __func__,
443 end = (uint8_t *)table->data + table->len;
446 memcpy(end, bytes, len);
452 basl_table_append_checksum(struct basl_table *const table, const uint32_t start,
455 assert(table != NULL);
457 BASL_EXEC(basl_table_add_checksum(table, table->len, start, len));
458 BASL_EXEC(basl_table_append_int(table, 0, 1));
464 basl_table_append_content(struct basl_table *table, void *data, uint32_t len)
466 assert(data != NULL);
467 assert(len >= sizeof(ACPI_TABLE_HEADER));
469 return (basl_table_append_bytes(table,
470 (void *)((uintptr_t)(data) + sizeof(ACPI_TABLE_HEADER)),
471 len - sizeof(ACPI_TABLE_HEADER)));
475 basl_table_append_gas(struct basl_table *const table, const uint8_t space_id,
476 const uint8_t bit_width, const uint8_t bit_offset,
477 const uint8_t access_width, const uint64_t address)
479 ACPI_GENERIC_ADDRESS gas_le = {
481 .BitWidth = bit_width,
482 .BitOffset = bit_offset,
483 .AccessWidth = access_width,
484 .Address = htole64(address),
487 return (basl_table_append_bytes(table, &gas_le, sizeof(gas_le)));
491 basl_table_append_header(struct basl_table *const table,
492 const uint8_t signature[ACPI_NAMESEG_SIZE], const uint8_t revision,
493 const uint32_t oem_revision)
495 ACPI_TABLE_HEADER header_le;
496 /* + 1 is required for the null terminator */
497 char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1];
499 assert(table != NULL);
500 assert(table->len == 0);
502 memcpy(header_le.Signature, signature, ACPI_NAMESEG_SIZE);
503 header_le.Length = 0; /* patched by basl_finish */
504 header_le.Revision = revision;
505 header_le.Checksum = 0; /* patched by basl_finish */
506 memcpy(header_le.OemId, "BHYVE ", ACPI_OEM_ID_SIZE);
507 snprintf(oem_table_id, ACPI_OEM_TABLE_ID_SIZE, "BV%.4s ", signature);
508 memcpy(header_le.OemTableId, oem_table_id,
509 sizeof(header_le.OemTableId));
510 header_le.OemRevision = htole32(oem_revision);
511 memcpy(header_le.AslCompilerId, "BASL", ACPI_NAMESEG_SIZE);
512 header_le.AslCompilerRevision = htole32(0x20220504);
515 basl_table_append_bytes(table, &header_le, sizeof(header_le)));
517 BASL_EXEC(basl_table_add_length(table,
518 offsetof(ACPI_TABLE_HEADER, Length), sizeof(header_le.Length)));
519 BASL_EXEC(basl_table_add_checksum(table,
520 offsetof(ACPI_TABLE_HEADER, Checksum), 0,
521 BASL_TABLE_CHECKSUM_LEN_FULL_TABLE));
527 basl_table_append_int(struct basl_table *const table, const uint64_t val,
532 assert(size <= sizeof(val));
534 basl_le_enc(buf, val, size);
535 return (basl_table_append_bytes(table, buf, size));
539 basl_table_append_length(struct basl_table *const table, const uint8_t size)
541 assert(table != NULL);
542 assert(size <= sizeof(table->len));
544 BASL_EXEC(basl_table_add_length(table, table->len, size));
545 BASL_EXEC(basl_table_append_int(table, 0, size));
551 basl_table_append_pointer(struct basl_table *const table,
552 const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint8_t size)
554 assert(table != NULL);
555 assert(size == 4 || size == 8);
557 BASL_EXEC(basl_table_add_pointer(table, src_signature, table->len, size));
558 BASL_EXEC(basl_table_append_int(table, 0, size));
564 basl_table_create(struct basl_table **const table, struct vmctx *ctx,
565 const uint8_t *const name, const uint32_t alignment)
567 struct basl_table *new_table;
569 assert(table != NULL);
571 new_table = calloc(1, sizeof(struct basl_table));
572 if (new_table == NULL) {
573 warnx("%s: failed to allocate table", __func__);
577 new_table->ctx = ctx;
579 snprintf(new_table->fwcfg_name, sizeof(new_table->fwcfg_name),
580 "etc/acpi/%s", name);
582 new_table->alignment = alignment;
584 STAILQ_INIT(&new_table->checksums);
585 STAILQ_INIT(&new_table->lengths);
586 STAILQ_INIT(&new_table->pointers);
588 STAILQ_INSERT_TAIL(&basl_tables, new_table, chain);