121bbc284SCorvin Köhne /*-
221bbc284SCorvin Köhne * SPDX-License-Identifier: BSD-2-Clause
321bbc284SCorvin Köhne *
421bbc284SCorvin Köhne * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
521bbc284SCorvin Köhne */
621bbc284SCorvin Köhne
721bbc284SCorvin Köhne #include <sys/param.h>
821bbc284SCorvin Köhne #include <sys/endian.h>
921bbc284SCorvin Köhne #include <sys/errno.h>
1021bbc284SCorvin Köhne #include <sys/queue.h>
1121bbc284SCorvin Köhne #include <sys/stat.h>
1221bbc284SCorvin Köhne
1321bbc284SCorvin Köhne #include <machine/vmm.h>
1421bbc284SCorvin Köhne
1521bbc284SCorvin Köhne #include <assert.h>
1621bbc284SCorvin Köhne #include <err.h>
17ac3c2b3eSCorvin Köhne #include <libutil.h>
1821bbc284SCorvin Köhne #include <stddef.h>
1921bbc284SCorvin Köhne #include <stdio.h>
2021bbc284SCorvin Köhne #include <vmmapi.h>
2121bbc284SCorvin Köhne
2221bbc284SCorvin Köhne #include "basl.h"
23*6f7e9779SCorvin Köhne #include "config.h"
247959d80dSCorvin Köhne #include "qemu_loader.h"
2521bbc284SCorvin Köhne
2629578470SCorvin Köhne struct basl_table_checksum {
2729578470SCorvin Köhne STAILQ_ENTRY(basl_table_checksum) chain;
2829578470SCorvin Köhne uint32_t off;
2929578470SCorvin Köhne uint32_t start;
3029578470SCorvin Köhne uint32_t len;
3129578470SCorvin Köhne };
3229578470SCorvin Köhne
333a766cd0SCorvin Köhne struct basl_table_length {
343a766cd0SCorvin Köhne STAILQ_ENTRY(basl_table_length) chain;
353a766cd0SCorvin Köhne uint32_t off;
363a766cd0SCorvin Köhne uint8_t size;
373a766cd0SCorvin Köhne };
383a766cd0SCorvin Köhne
3949b947c0SCorvin Köhne struct basl_table_pointer {
4049b947c0SCorvin Köhne STAILQ_ENTRY(basl_table_pointer) chain;
4149b947c0SCorvin Köhne uint8_t src_signature[ACPI_NAMESEG_SIZE];
4249b947c0SCorvin Köhne uint32_t off;
4349b947c0SCorvin Köhne uint8_t size;
4449b947c0SCorvin Köhne };
4549b947c0SCorvin Köhne
4621bbc284SCorvin Köhne struct basl_table {
4721bbc284SCorvin Köhne STAILQ_ENTRY(basl_table) chain;
4821bbc284SCorvin Köhne struct vmctx *ctx;
4921bbc284SCorvin Köhne uint8_t fwcfg_name[QEMU_FWCFG_MAX_NAME];
5021bbc284SCorvin Köhne void *data;
5121bbc284SCorvin Köhne uint32_t len;
5221bbc284SCorvin Köhne uint32_t off;
5321bbc284SCorvin Köhne uint32_t alignment;
54c127c61eSMark Johnston STAILQ_HEAD(basl_table_checksum_list, basl_table_checksum) checksums;
553a766cd0SCorvin Köhne STAILQ_HEAD(basl_table_length_list, basl_table_length) lengths;
5649b947c0SCorvin Köhne STAILQ_HEAD(basl_table_pointer_list, basl_table_pointer) pointers;
5721bbc284SCorvin Köhne };
5821bbc284SCorvin Köhne static STAILQ_HEAD(basl_table_list, basl_table) basl_tables = STAILQ_HEAD_INITIALIZER(
5921bbc284SCorvin Köhne basl_tables);
6021bbc284SCorvin Köhne
617959d80dSCorvin Köhne static struct qemu_loader *basl_loader;
6224a0fef9SCorvin Köhne static struct basl_table *rsdt;
6324a0fef9SCorvin Köhne static struct basl_table *xsdt;
64*6f7e9779SCorvin Köhne static bool load_into_memory;
657959d80dSCorvin Köhne
6649b947c0SCorvin Köhne static __inline uint64_t
basl_le_dec(void * pp,size_t len)6749b947c0SCorvin Köhne basl_le_dec(void *pp, size_t len)
6849b947c0SCorvin Köhne {
6949b947c0SCorvin Köhne assert(len <= 8);
7049b947c0SCorvin Köhne
7149b947c0SCorvin Köhne switch (len) {
7249b947c0SCorvin Köhne case 1:
7349b947c0SCorvin Köhne return ((uint8_t *)pp)[0];
7449b947c0SCorvin Köhne case 2:
7549b947c0SCorvin Köhne return le16dec(pp);
7649b947c0SCorvin Köhne case 4:
7749b947c0SCorvin Köhne return le32dec(pp);
7849b947c0SCorvin Köhne case 8:
7949b947c0SCorvin Köhne return le64dec(pp);
8049b947c0SCorvin Köhne }
8149b947c0SCorvin Köhne
8249b947c0SCorvin Köhne return 0;
8349b947c0SCorvin Köhne }
8449b947c0SCorvin Köhne
85e22f5ce2SCorvin Köhne static __inline void
basl_le_enc(void * pp,uint64_t val,size_t len)86e22f5ce2SCorvin Köhne basl_le_enc(void *pp, uint64_t val, size_t len)
87e22f5ce2SCorvin Köhne {
88e22f5ce2SCorvin Köhne char buf[8];
89e22f5ce2SCorvin Köhne
90e22f5ce2SCorvin Köhne assert(len <= 8);
91e22f5ce2SCorvin Köhne
92e22f5ce2SCorvin Köhne le64enc(buf, val);
93e22f5ce2SCorvin Köhne memcpy(pp, buf, len);
94e22f5ce2SCorvin Köhne }
95e22f5ce2SCorvin Köhne
9621bbc284SCorvin Köhne static int
basl_dump_table(const struct basl_table * const table,const bool mem)97ac3c2b3eSCorvin Köhne basl_dump_table(const struct basl_table *const table, const bool mem)
98ac3c2b3eSCorvin Köhne {
99ac3c2b3eSCorvin Köhne const ACPI_TABLE_HEADER *const header = table->data;
100ac3c2b3eSCorvin Köhne const uint8_t *data;
101ac3c2b3eSCorvin Köhne
102ac3c2b3eSCorvin Köhne if (!mem) {
103ac3c2b3eSCorvin Köhne data = table->data;
104ac3c2b3eSCorvin Köhne } else {
105ac3c2b3eSCorvin Köhne data = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off,
106ac3c2b3eSCorvin Köhne table->len);
107ac3c2b3eSCorvin Köhne if (data == NULL) {
108ac3c2b3eSCorvin Köhne return (ENOMEM);
109ac3c2b3eSCorvin Köhne }
110ac3c2b3eSCorvin Köhne }
111ac3c2b3eSCorvin Köhne
112ac3c2b3eSCorvin Köhne printf("%.4s @ %8x (%s)\n", header->Signature,
113ac3c2b3eSCorvin Köhne BHYVE_ACPI_BASE + table->off, mem ? "Memory" : "FwCfg");
114ac3c2b3eSCorvin Köhne hexdump(data, table->len, NULL, 0);
115ac3c2b3eSCorvin Köhne
116ac3c2b3eSCorvin Köhne return (0);
117ac3c2b3eSCorvin Köhne }
118ac3c2b3eSCorvin Köhne
119c127c61eSMark Johnston static int __unused
basl_dump(const bool mem)120ac3c2b3eSCorvin Köhne basl_dump(const bool mem)
121ac3c2b3eSCorvin Köhne {
122ac3c2b3eSCorvin Köhne struct basl_table *table;
123ac3c2b3eSCorvin Köhne
124ac3c2b3eSCorvin Köhne STAILQ_FOREACH(table, &basl_tables, chain) {
125ac3c2b3eSCorvin Köhne BASL_EXEC(basl_dump_table(table, mem));
126ac3c2b3eSCorvin Köhne }
127ac3c2b3eSCorvin Köhne
128ac3c2b3eSCorvin Köhne return (0);
129ac3c2b3eSCorvin Köhne }
130ac3c2b3eSCorvin Köhne
13160277ad7SCorvin Köhne void
basl_fill_gas(ACPI_GENERIC_ADDRESS * const gas,const uint8_t space_id,const uint8_t bit_width,const uint8_t bit_offset,const uint8_t access_width,const uint64_t address)13260277ad7SCorvin Köhne basl_fill_gas(ACPI_GENERIC_ADDRESS *const gas, const uint8_t space_id,
13360277ad7SCorvin Köhne const uint8_t bit_width, const uint8_t bit_offset,
13460277ad7SCorvin Köhne const uint8_t access_width, const uint64_t address)
13560277ad7SCorvin Köhne {
13660277ad7SCorvin Köhne assert(gas != NULL);
13760277ad7SCorvin Köhne
13860277ad7SCorvin Köhne gas->SpaceId = space_id;
13960277ad7SCorvin Köhne gas->BitWidth = bit_width;
14060277ad7SCorvin Köhne gas->BitOffset = bit_offset;
14160277ad7SCorvin Köhne gas->AccessWidth = access_width;
14260277ad7SCorvin Köhne gas->Address = htole64(address);
14360277ad7SCorvin Köhne }
14460277ad7SCorvin Köhne
145ac3c2b3eSCorvin Köhne static int
basl_finish_install_guest_tables(struct basl_table * const table,uint32_t * const off)14667654ffdSCorvin Köhne basl_finish_install_guest_tables(struct basl_table *const table, uint32_t *const off)
14721bbc284SCorvin Köhne {
14821bbc284SCorvin Köhne void *gva;
14921bbc284SCorvin Köhne
15067654ffdSCorvin Köhne table->off = roundup2(*off, table->alignment);
15167654ffdSCorvin Köhne *off = table->off + table->len;
15267654ffdSCorvin Köhne if (*off <= table->off) {
15367654ffdSCorvin Köhne warnx("%s: invalid table length 0x%8x @ offset 0x%8x", __func__,
15467654ffdSCorvin Köhne table->len, table->off);
15567654ffdSCorvin Köhne return (EFAULT);
15667654ffdSCorvin Köhne }
15767654ffdSCorvin Köhne
158*6f7e9779SCorvin Köhne /* Cause guest BIOS to copy the ACPI table into guest memory. */
159*6f7e9779SCorvin Köhne BASL_EXEC(
160*6f7e9779SCorvin Köhne qemu_fwcfg_add_file(table->fwcfg_name, table->len, table->data));
161*6f7e9779SCorvin Köhne BASL_EXEC(qemu_loader_alloc(basl_loader, table->fwcfg_name,
162*6f7e9779SCorvin Köhne table->alignment, QEMU_LOADER_ALLOC_HIGH));
163*6f7e9779SCorvin Köhne
164*6f7e9779SCorvin Köhne if (!load_into_memory) {
165*6f7e9779SCorvin Köhne return (0);
166*6f7e9779SCorvin Köhne }
167*6f7e9779SCorvin Köhne
16821bbc284SCorvin Köhne /*
16921bbc284SCorvin Köhne * Install ACPI tables directly in guest memory for use by guests which
17021bbc284SCorvin Köhne * do not boot via EFI. EFI ROMs provide a pointer to the firmware
17121bbc284SCorvin Köhne * generated ACPI tables instead, but it doesn't hurt to install the
17221bbc284SCorvin Köhne * tables always.
17321bbc284SCorvin Köhne */
17421bbc284SCorvin Köhne gva = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off, table->len);
17521bbc284SCorvin Köhne if (gva == NULL) {
17621bbc284SCorvin Köhne warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]", __func__,
17721bbc284SCorvin Köhne (uint64_t)BHYVE_ACPI_BASE + table->off,
17821bbc284SCorvin Köhne (uint64_t)BHYVE_ACPI_BASE + table->off + table->len);
17921bbc284SCorvin Köhne return (ENOMEM);
18021bbc284SCorvin Köhne }
18121bbc284SCorvin Köhne memcpy(gva, table->data, table->len);
18221bbc284SCorvin Köhne
18321bbc284SCorvin Köhne return (0);
18421bbc284SCorvin Köhne }
18521bbc284SCorvin Köhne
1863a766cd0SCorvin Köhne static int
basl_finish_patch_checksums(struct basl_table * const table)18729578470SCorvin Köhne basl_finish_patch_checksums(struct basl_table *const table)
18829578470SCorvin Köhne {
18929578470SCorvin Köhne struct basl_table_checksum *checksum;
19029578470SCorvin Köhne
19129578470SCorvin Köhne STAILQ_FOREACH(checksum, &table->checksums, chain) {
19229578470SCorvin Köhne uint8_t *gva, *checksum_gva;
19329578470SCorvin Köhne uint64_t gpa;
19429578470SCorvin Köhne uint32_t len;
19529578470SCorvin Köhne uint8_t sum;
19629578470SCorvin Köhne
19729578470SCorvin Köhne len = checksum->len;
19829578470SCorvin Köhne if (len == BASL_TABLE_CHECKSUM_LEN_FULL_TABLE) {
19929578470SCorvin Köhne len = table->len;
20029578470SCorvin Köhne }
20129578470SCorvin Köhne
20229578470SCorvin Köhne assert(checksum->off < table->len);
20329578470SCorvin Köhne assert(checksum->start < table->len);
20429578470SCorvin Köhne assert(checksum->start + len <= table->len);
20529578470SCorvin Köhne
206*6f7e9779SCorvin Köhne /* Cause guest BIOS to patch the checksum. */
207*6f7e9779SCorvin Köhne BASL_EXEC(qemu_loader_add_checksum(basl_loader,
208*6f7e9779SCorvin Köhne table->fwcfg_name, checksum->off, checksum->start, len));
209*6f7e9779SCorvin Köhne
210*6f7e9779SCorvin Köhne if (!load_into_memory) {
211*6f7e9779SCorvin Köhne continue;
212*6f7e9779SCorvin Köhne }
213*6f7e9779SCorvin Köhne
21429578470SCorvin Köhne /*
21529578470SCorvin Köhne * Install ACPI tables directly in guest memory for use by
21629578470SCorvin Köhne * guests which do not boot via EFI. EFI ROMs provide a pointer
21729578470SCorvin Köhne * to the firmware generated ACPI tables instead, but it doesn't
21829578470SCorvin Köhne * hurt to install the tables always.
21929578470SCorvin Köhne */
22029578470SCorvin Köhne gpa = BHYVE_ACPI_BASE + table->off + checksum->start;
22129578470SCorvin Köhne if ((gpa < BHYVE_ACPI_BASE) ||
22229578470SCorvin Köhne (gpa < BHYVE_ACPI_BASE + table->off)) {
22329578470SCorvin Köhne warnx("%s: invalid gpa (off 0x%8x start 0x%8x)",
22429578470SCorvin Köhne __func__, table->off, checksum->start);
22529578470SCorvin Köhne return (EFAULT);
22629578470SCorvin Köhne }
22729578470SCorvin Köhne
22829578470SCorvin Köhne gva = vm_map_gpa(table->ctx, gpa, len);
22929578470SCorvin Köhne if (gva == NULL) {
23029578470SCorvin Köhne warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
23129578470SCorvin Köhne __func__, gpa, gpa + len);
23229578470SCorvin Köhne return (ENOMEM);
23329578470SCorvin Köhne }
23429578470SCorvin Köhne
23529578470SCorvin Köhne checksum_gva = gva + checksum->off;
23629578470SCorvin Köhne if (checksum_gva < gva) {
23729578470SCorvin Köhne warnx("%s: invalid checksum offset 0x%8x", __func__,
23829578470SCorvin Köhne checksum->off);
23929578470SCorvin Köhne return (EFAULT);
24029578470SCorvin Köhne }
24129578470SCorvin Köhne
24229578470SCorvin Köhne sum = 0;
24329578470SCorvin Köhne for (uint32_t i = 0; i < len; ++i) {
24429578470SCorvin Köhne sum += *(gva + i);
24529578470SCorvin Köhne }
24629578470SCorvin Köhne *checksum_gva = -sum;
24729578470SCorvin Köhne }
24829578470SCorvin Köhne
24929578470SCorvin Köhne return (0);
25029578470SCorvin Köhne }
25129578470SCorvin Köhne
25249b947c0SCorvin Köhne static struct basl_table *
basl_get_table_by_signature(const uint8_t signature[ACPI_NAMESEG_SIZE])25349b947c0SCorvin Köhne basl_get_table_by_signature(const uint8_t signature[ACPI_NAMESEG_SIZE])
25449b947c0SCorvin Köhne {
25549b947c0SCorvin Köhne struct basl_table *table;
25649b947c0SCorvin Köhne
25749b947c0SCorvin Köhne STAILQ_FOREACH(table, &basl_tables, chain) {
25849b947c0SCorvin Köhne const ACPI_TABLE_HEADER *const header =
25949b947c0SCorvin Köhne (const ACPI_TABLE_HEADER *)table->data;
26049b947c0SCorvin Köhne
26149b947c0SCorvin Köhne if (strncmp(header->Signature, signature,
26249b947c0SCorvin Köhne sizeof(header->Signature)) == 0) {
26349b947c0SCorvin Köhne return (table);
26449b947c0SCorvin Köhne }
26549b947c0SCorvin Köhne }
26649b947c0SCorvin Köhne
26749b947c0SCorvin Köhne warnx("%s: %.4s not found", __func__, signature);
26849b947c0SCorvin Köhne return (NULL);
26949b947c0SCorvin Köhne }
27049b947c0SCorvin Köhne
27149b947c0SCorvin Köhne static int
basl_finish_patch_pointers(struct basl_table * const table)27249b947c0SCorvin Köhne basl_finish_patch_pointers(struct basl_table *const table)
27349b947c0SCorvin Köhne {
27449b947c0SCorvin Köhne struct basl_table_pointer *pointer;
27549b947c0SCorvin Köhne
27649b947c0SCorvin Köhne STAILQ_FOREACH(pointer, &table->pointers, chain) {
27749b947c0SCorvin Köhne const struct basl_table *src_table;
27849b947c0SCorvin Köhne uint8_t *gva;
27949b947c0SCorvin Köhne uint64_t gpa, val;
28049b947c0SCorvin Köhne
28149b947c0SCorvin Köhne assert(pointer->off < table->len);
28249b947c0SCorvin Köhne assert(pointer->off + pointer->size <= table->len);
28349b947c0SCorvin Köhne
28449b947c0SCorvin Köhne src_table = basl_get_table_by_signature(pointer->src_signature);
28549b947c0SCorvin Köhne if (src_table == NULL) {
28649b947c0SCorvin Köhne warnx("%s: could not find ACPI table %.4s", __func__,
28749b947c0SCorvin Köhne pointer->src_signature);
28849b947c0SCorvin Köhne return (EFAULT);
28949b947c0SCorvin Köhne }
29049b947c0SCorvin Köhne
291*6f7e9779SCorvin Köhne /* Cause guest BIOS to patch the pointer. */
292*6f7e9779SCorvin Köhne BASL_EXEC(
293*6f7e9779SCorvin Köhne qemu_loader_add_pointer(basl_loader, table->fwcfg_name,
294*6f7e9779SCorvin Köhne src_table->fwcfg_name, pointer->off, pointer->size));
295*6f7e9779SCorvin Köhne
296*6f7e9779SCorvin Köhne if (!load_into_memory) {
297*6f7e9779SCorvin Köhne continue;
298*6f7e9779SCorvin Köhne }
299*6f7e9779SCorvin Köhne
30049b947c0SCorvin Köhne /*
30149b947c0SCorvin Köhne * Install ACPI tables directly in guest memory for use by
30249b947c0SCorvin Köhne * guests which do not boot via EFI. EFI ROMs provide a pointer
30349b947c0SCorvin Köhne * to the firmware generated ACPI tables instead, but it doesn't
30449b947c0SCorvin Köhne * hurt to install the tables always.
30549b947c0SCorvin Köhne */
30649b947c0SCorvin Köhne gpa = BHYVE_ACPI_BASE + table->off;
30749b947c0SCorvin Köhne if (gpa < BHYVE_ACPI_BASE) {
30849b947c0SCorvin Köhne warnx("%s: table offset of 0x%8x is too large",
30949b947c0SCorvin Köhne __func__, table->off);
31049b947c0SCorvin Köhne return (EFAULT);
31149b947c0SCorvin Köhne }
31249b947c0SCorvin Köhne
31349b947c0SCorvin Köhne gva = vm_map_gpa(table->ctx, gpa, table->len);
31449b947c0SCorvin Köhne if (gva == NULL) {
31549b947c0SCorvin Köhne warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
31649b947c0SCorvin Köhne __func__, gpa, gpa + table->len);
31749b947c0SCorvin Köhne return (ENOMEM);
31849b947c0SCorvin Köhne }
31949b947c0SCorvin Köhne
32049b947c0SCorvin Köhne val = basl_le_dec(gva + pointer->off, pointer->size);
32149b947c0SCorvin Köhne val += BHYVE_ACPI_BASE + src_table->off;
32249b947c0SCorvin Köhne basl_le_enc(gva + pointer->off, val, pointer->size);
32349b947c0SCorvin Köhne }
32449b947c0SCorvin Köhne
32549b947c0SCorvin Köhne return (0);
32649b947c0SCorvin Köhne }
32749b947c0SCorvin Köhne
32829578470SCorvin Köhne static int
basl_finish_set_length(struct basl_table * const table)3293a766cd0SCorvin Köhne basl_finish_set_length(struct basl_table *const table)
3303a766cd0SCorvin Köhne {
3313a766cd0SCorvin Köhne struct basl_table_length *length;
3323a766cd0SCorvin Köhne
3333a766cd0SCorvin Köhne STAILQ_FOREACH(length, &table->lengths, chain) {
3343a766cd0SCorvin Köhne assert(length->off < table->len);
3353a766cd0SCorvin Köhne assert(length->off + length->size <= table->len);
3363a766cd0SCorvin Köhne
337c127c61eSMark Johnston basl_le_enc((uint8_t *)table->data + length->off, table->len,
3383a766cd0SCorvin Köhne length->size);
3393a766cd0SCorvin Köhne }
3403a766cd0SCorvin Köhne
3413a766cd0SCorvin Köhne return (0);
3423a766cd0SCorvin Köhne }
3433a766cd0SCorvin Köhne
34421bbc284SCorvin Köhne int
basl_finish(void)34521bbc284SCorvin Köhne basl_finish(void)
34621bbc284SCorvin Köhne {
34721bbc284SCorvin Köhne struct basl_table *table;
34867654ffdSCorvin Köhne uint32_t off = 0;
34921bbc284SCorvin Köhne
35021bbc284SCorvin Köhne if (STAILQ_EMPTY(&basl_tables)) {
35121bbc284SCorvin Köhne warnx("%s: no ACPI tables found", __func__);
35221bbc284SCorvin Köhne return (EINVAL);
35321bbc284SCorvin Köhne }
35421bbc284SCorvin Köhne
35529578470SCorvin Köhne /*
356*6f7e9779SCorvin Köhne * If we install ACPI tables by FwCfg and by memory, Windows will use
357*6f7e9779SCorvin Köhne * the tables from memory. This can cause issues when using advanced
358*6f7e9779SCorvin Köhne * features like a TPM log because we aren't able to patch the memory
359*6f7e9779SCorvin Köhne * tables accordingly.
360*6f7e9779SCorvin Köhne */
361*6f7e9779SCorvin Köhne load_into_memory = get_config_bool_default("acpi_tables_in_memory",
362*6f7e9779SCorvin Köhne true);
363*6f7e9779SCorvin Köhne
364*6f7e9779SCorvin Köhne /*
36529578470SCorvin Köhne * We have to install all tables before we can patch them. Therefore,
36629578470SCorvin Köhne * use two loops. The first one installs all tables and the second one
36729578470SCorvin Köhne * patches them.
36829578470SCorvin Köhne */
36921bbc284SCorvin Köhne STAILQ_FOREACH(table, &basl_tables, chain) {
3703a766cd0SCorvin Köhne BASL_EXEC(basl_finish_set_length(table));
37167654ffdSCorvin Köhne BASL_EXEC(basl_finish_install_guest_tables(table, &off));
37221bbc284SCorvin Köhne }
37329578470SCorvin Köhne STAILQ_FOREACH(table, &basl_tables, chain) {
37449b947c0SCorvin Köhne BASL_EXEC(basl_finish_patch_pointers(table));
37549b947c0SCorvin Köhne
37629578470SCorvin Köhne /*
37729578470SCorvin Köhne * Calculate the checksum as last step!
37829578470SCorvin Köhne */
37929578470SCorvin Köhne BASL_EXEC(basl_finish_patch_checksums(table));
38029578470SCorvin Köhne }
3817959d80dSCorvin Köhne BASL_EXEC(qemu_loader_finish(basl_loader));
38221bbc284SCorvin Köhne
38321bbc284SCorvin Köhne return (0);
38421bbc284SCorvin Köhne }
38521bbc284SCorvin Köhne
38624a0fef9SCorvin Köhne static int
basl_init_rsdt(struct vmctx * const ctx)38724a0fef9SCorvin Köhne basl_init_rsdt(struct vmctx *const ctx)
38821bbc284SCorvin Köhne {
38924a0fef9SCorvin Köhne BASL_EXEC(
39024a0fef9SCorvin Köhne basl_table_create(&rsdt, ctx, ACPI_SIG_RSDT, BASL_TABLE_ALIGNMENT));
39124a0fef9SCorvin Köhne
39224a0fef9SCorvin Köhne /* Header */
39324a0fef9SCorvin Köhne BASL_EXEC(basl_table_append_header(rsdt, ACPI_SIG_RSDT, 1, 1));
39424a0fef9SCorvin Köhne /* Pointers (added by basl_table_register_to_rsdt) */
39524a0fef9SCorvin Köhne
39624a0fef9SCorvin Köhne return (0);
39724a0fef9SCorvin Köhne }
39824a0fef9SCorvin Köhne
39924a0fef9SCorvin Köhne static int
basl_init_xsdt(struct vmctx * const ctx)40024a0fef9SCorvin Köhne basl_init_xsdt(struct vmctx *const ctx)
40124a0fef9SCorvin Köhne {
40224a0fef9SCorvin Köhne BASL_EXEC(
40324a0fef9SCorvin Köhne basl_table_create(&xsdt, ctx, ACPI_SIG_XSDT, BASL_TABLE_ALIGNMENT));
40424a0fef9SCorvin Köhne
40524a0fef9SCorvin Köhne /* Header */
40624a0fef9SCorvin Köhne BASL_EXEC(basl_table_append_header(xsdt, ACPI_SIG_XSDT, 1, 1));
40724a0fef9SCorvin Köhne /* Pointers (added by basl_table_register_to_rsdt) */
40824a0fef9SCorvin Köhne
40924a0fef9SCorvin Köhne return (0);
41024a0fef9SCorvin Köhne }
41124a0fef9SCorvin Köhne
41224a0fef9SCorvin Köhne int
basl_init(struct vmctx * const ctx)41324a0fef9SCorvin Köhne basl_init(struct vmctx *const ctx)
41424a0fef9SCorvin Köhne {
41524a0fef9SCorvin Köhne BASL_EXEC(basl_init_rsdt(ctx));
41624a0fef9SCorvin Köhne BASL_EXEC(basl_init_xsdt(ctx));
41724a0fef9SCorvin Köhne BASL_EXEC(
41824a0fef9SCorvin Köhne qemu_loader_create(&basl_loader, QEMU_FWCFG_FILE_TABLE_LOADER));
41924a0fef9SCorvin Köhne
42024a0fef9SCorvin Köhne return (0);
42121bbc284SCorvin Köhne }
42221bbc284SCorvin Köhne
4237263419fSCorvin Köhne int
basl_table_add_checksum(struct basl_table * const table,const uint32_t off,const uint32_t start,const uint32_t len)42429578470SCorvin Köhne basl_table_add_checksum(struct basl_table *const table, const uint32_t off,
42529578470SCorvin Köhne const uint32_t start, const uint32_t len)
42629578470SCorvin Köhne {
42729578470SCorvin Köhne struct basl_table_checksum *checksum;
42829578470SCorvin Köhne
4297263419fSCorvin Köhne assert(table != NULL);
4307263419fSCorvin Köhne
43129578470SCorvin Köhne checksum = calloc(1, sizeof(struct basl_table_checksum));
43229578470SCorvin Köhne if (checksum == NULL) {
43329578470SCorvin Köhne warnx("%s: failed to allocate checksum", __func__);
43429578470SCorvin Köhne return (ENOMEM);
43529578470SCorvin Köhne }
43629578470SCorvin Köhne
43729578470SCorvin Köhne checksum->off = off;
43829578470SCorvin Köhne checksum->start = start;
43929578470SCorvin Köhne checksum->len = len;
44029578470SCorvin Köhne
44129578470SCorvin Köhne STAILQ_INSERT_TAIL(&table->checksums, checksum, chain);
44229578470SCorvin Köhne
44329578470SCorvin Köhne return (0);
44429578470SCorvin Köhne }
44529578470SCorvin Köhne
4467263419fSCorvin Köhne int
basl_table_add_length(struct basl_table * const table,const uint32_t off,const uint8_t size)4473a766cd0SCorvin Köhne basl_table_add_length(struct basl_table *const table, const uint32_t off,
4483a766cd0SCorvin Köhne const uint8_t size)
4493a766cd0SCorvin Köhne {
4503a766cd0SCorvin Köhne struct basl_table_length *length;
4513a766cd0SCorvin Köhne
4527263419fSCorvin Köhne assert(table != NULL);
4537263419fSCorvin Köhne assert(size == 4 || size == 8);
4547263419fSCorvin Köhne
4553a766cd0SCorvin Köhne length = calloc(1, sizeof(struct basl_table_length));
4563a766cd0SCorvin Köhne if (length == NULL) {
4573a766cd0SCorvin Köhne warnx("%s: failed to allocate length", __func__);
4583a766cd0SCorvin Köhne return (ENOMEM);
4593a766cd0SCorvin Köhne }
4603a766cd0SCorvin Köhne
4613a766cd0SCorvin Köhne length->off = off;
4623a766cd0SCorvin Köhne length->size = size;
4633a766cd0SCorvin Köhne
4643a766cd0SCorvin Köhne STAILQ_INSERT_TAIL(&table->lengths, length, chain);
4653a766cd0SCorvin Köhne
4663a766cd0SCorvin Köhne return (0);
4673a766cd0SCorvin Köhne }
4683a766cd0SCorvin Köhne
4697263419fSCorvin Köhne int
basl_table_add_pointer(struct basl_table * const table,const uint8_t src_signature[ACPI_NAMESEG_SIZE],const uint32_t off,const uint8_t size)47049b947c0SCorvin Köhne basl_table_add_pointer(struct basl_table *const table,
47149b947c0SCorvin Köhne const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint32_t off,
47249b947c0SCorvin Köhne const uint8_t size)
47349b947c0SCorvin Köhne {
47449b947c0SCorvin Köhne struct basl_table_pointer *pointer;
47549b947c0SCorvin Köhne
4767263419fSCorvin Köhne assert(table != NULL);
4777263419fSCorvin Köhne assert(size == 4 || size == 8);
4787263419fSCorvin Köhne
47949b947c0SCorvin Köhne pointer = calloc(1, sizeof(struct basl_table_pointer));
48049b947c0SCorvin Köhne if (pointer == NULL) {
48149b947c0SCorvin Köhne warnx("%s: failed to allocate pointer", __func__);
48249b947c0SCorvin Köhne return (ENOMEM);
48349b947c0SCorvin Köhne }
48449b947c0SCorvin Köhne
48549b947c0SCorvin Köhne memcpy(pointer->src_signature, src_signature,
48649b947c0SCorvin Köhne sizeof(pointer->src_signature));
48749b947c0SCorvin Köhne pointer->off = off;
48849b947c0SCorvin Köhne pointer->size = size;
48949b947c0SCorvin Köhne
49049b947c0SCorvin Köhne STAILQ_INSERT_TAIL(&table->pointers, pointer, chain);
49149b947c0SCorvin Köhne
49249b947c0SCorvin Köhne return (0);
49349b947c0SCorvin Köhne }
49449b947c0SCorvin Köhne
49521bbc284SCorvin Köhne int
basl_table_append_bytes(struct basl_table * const table,const void * const bytes,const uint32_t len)49621bbc284SCorvin Köhne basl_table_append_bytes(struct basl_table *const table, const void *const bytes,
49721bbc284SCorvin Köhne const uint32_t len)
49821bbc284SCorvin Köhne {
49921bbc284SCorvin Köhne void *end;
50021bbc284SCorvin Köhne
50121bbc284SCorvin Köhne assert(table != NULL);
50221bbc284SCorvin Köhne assert(bytes != NULL);
50321bbc284SCorvin Köhne
50421bbc284SCorvin Köhne if (table->len + len <= table->len) {
50521bbc284SCorvin Köhne warnx("%s: table too large (table->len 0x%8x len 0x%8x)",
50621bbc284SCorvin Köhne __func__, table->len, len);
50721bbc284SCorvin Köhne return (EFAULT);
50821bbc284SCorvin Köhne }
50921bbc284SCorvin Köhne
51021bbc284SCorvin Köhne table->data = reallocf(table->data, table->len + len);
51121bbc284SCorvin Köhne if (table->data == NULL) {
51221bbc284SCorvin Köhne warnx("%s: failed to realloc table to length 0x%8x", __func__,
51321bbc284SCorvin Köhne table->len + len);
51421bbc284SCorvin Köhne table->len = 0;
51521bbc284SCorvin Köhne return (ENOMEM);
51621bbc284SCorvin Köhne }
51721bbc284SCorvin Köhne
51821bbc284SCorvin Köhne end = (uint8_t *)table->data + table->len;
51921bbc284SCorvin Köhne table->len += len;
52021bbc284SCorvin Köhne
52121bbc284SCorvin Köhne memcpy(end, bytes, len);
52221bbc284SCorvin Köhne
52321bbc284SCorvin Köhne return (0);
52421bbc284SCorvin Köhne }
52521bbc284SCorvin Köhne
52621bbc284SCorvin Köhne int
basl_table_append_checksum(struct basl_table * const table,const uint32_t start,const uint32_t len)52729578470SCorvin Köhne basl_table_append_checksum(struct basl_table *const table, const uint32_t start,
52829578470SCorvin Köhne const uint32_t len)
52929578470SCorvin Köhne {
53029578470SCorvin Köhne assert(table != NULL);
53129578470SCorvin Köhne
53229578470SCorvin Köhne BASL_EXEC(basl_table_add_checksum(table, table->len, start, len));
53329578470SCorvin Köhne BASL_EXEC(basl_table_append_int(table, 0, 1));
53429578470SCorvin Köhne
53529578470SCorvin Köhne return (0);
53629578470SCorvin Köhne }
53729578470SCorvin Köhne
53829578470SCorvin Köhne int
basl_table_append_content(struct basl_table * table,void * data,uint32_t len)5398897b562SCorvin Köhne basl_table_append_content(struct basl_table *table, void *data, uint32_t len)
5408897b562SCorvin Köhne {
5418897b562SCorvin Köhne assert(data != NULL);
5428897b562SCorvin Köhne assert(len >= sizeof(ACPI_TABLE_HEADER));
5438897b562SCorvin Köhne
5448897b562SCorvin Köhne return (basl_table_append_bytes(table,
5458897b562SCorvin Köhne (void *)((uintptr_t)(data) + sizeof(ACPI_TABLE_HEADER)),
5468897b562SCorvin Köhne len - sizeof(ACPI_TABLE_HEADER)));
5478897b562SCorvin Köhne }
5488897b562SCorvin Köhne
5498897b562SCorvin Köhne int
basl_table_append_fwcfg(struct basl_table * const table,const uint8_t * fwcfg_name,const uint32_t alignment,const uint8_t size)5504e46ab0eSCorvin Köhne basl_table_append_fwcfg(struct basl_table *const table,
5514e46ab0eSCorvin Köhne const uint8_t *fwcfg_name, const uint32_t alignment, const uint8_t size)
5524e46ab0eSCorvin Köhne {
5534e46ab0eSCorvin Köhne assert(table != NULL);
5544e46ab0eSCorvin Köhne assert(fwcfg_name != NULL);
5554e46ab0eSCorvin Köhne assert(size <= sizeof(uint64_t));
5564e46ab0eSCorvin Köhne
5574e46ab0eSCorvin Köhne BASL_EXEC(qemu_loader_alloc(basl_loader, fwcfg_name, alignment,
5584e46ab0eSCorvin Köhne QEMU_LOADER_ALLOC_HIGH));
5594e46ab0eSCorvin Köhne BASL_EXEC(qemu_loader_add_pointer(basl_loader, table->fwcfg_name,
5604e46ab0eSCorvin Köhne fwcfg_name, table->len, size));
5614e46ab0eSCorvin Köhne BASL_EXEC(basl_table_append_int(table, 0, size));
5624e46ab0eSCorvin Köhne
5634e46ab0eSCorvin Köhne return (0);
5644e46ab0eSCorvin Köhne }
5654e46ab0eSCorvin Köhne
5664e46ab0eSCorvin Köhne int
basl_table_append_gas(struct basl_table * const table,const uint8_t space_id,const uint8_t bit_width,const uint8_t bit_offset,const uint8_t access_width,const uint64_t address)567995374a6SCorvin Köhne basl_table_append_gas(struct basl_table *const table, const uint8_t space_id,
568995374a6SCorvin Köhne const uint8_t bit_width, const uint8_t bit_offset,
569995374a6SCorvin Köhne const uint8_t access_width, const uint64_t address)
570995374a6SCorvin Köhne {
571995374a6SCorvin Köhne ACPI_GENERIC_ADDRESS gas_le = {
572995374a6SCorvin Köhne .SpaceId = space_id,
573995374a6SCorvin Köhne .BitWidth = bit_width,
574995374a6SCorvin Köhne .BitOffset = bit_offset,
575995374a6SCorvin Köhne .AccessWidth = access_width,
576995374a6SCorvin Köhne .Address = htole64(address),
577995374a6SCorvin Köhne };
578995374a6SCorvin Köhne
579995374a6SCorvin Köhne return (basl_table_append_bytes(table, &gas_le, sizeof(gas_le)));
580995374a6SCorvin Köhne }
581995374a6SCorvin Köhne
582995374a6SCorvin Köhne int
basl_table_append_header(struct basl_table * const table,const uint8_t signature[ACPI_NAMESEG_SIZE],const uint8_t revision,const uint32_t oem_revision)5832fb0f352SCorvin Köhne basl_table_append_header(struct basl_table *const table,
5842fb0f352SCorvin Köhne const uint8_t signature[ACPI_NAMESEG_SIZE], const uint8_t revision,
5852fb0f352SCorvin Köhne const uint32_t oem_revision)
5862fb0f352SCorvin Köhne {
5872fb0f352SCorvin Köhne ACPI_TABLE_HEADER header_le;
5882fb0f352SCorvin Köhne /* + 1 is required for the null terminator */
5892fb0f352SCorvin Köhne char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1];
5902fb0f352SCorvin Köhne
5912fb0f352SCorvin Köhne assert(table != NULL);
5922fb0f352SCorvin Köhne assert(table->len == 0);
5932fb0f352SCorvin Köhne
5942fb0f352SCorvin Köhne memcpy(header_le.Signature, signature, ACPI_NAMESEG_SIZE);
5952fb0f352SCorvin Köhne header_le.Length = 0; /* patched by basl_finish */
5962fb0f352SCorvin Köhne header_le.Revision = revision;
5972fb0f352SCorvin Köhne header_le.Checksum = 0; /* patched by basl_finish */
5982fb0f352SCorvin Köhne memcpy(header_le.OemId, "BHYVE ", ACPI_OEM_ID_SIZE);
5992fb0f352SCorvin Köhne snprintf(oem_table_id, ACPI_OEM_TABLE_ID_SIZE, "BV%.4s ", signature);
6002fb0f352SCorvin Köhne memcpy(header_le.OemTableId, oem_table_id,
6012fb0f352SCorvin Köhne sizeof(header_le.OemTableId));
6022fb0f352SCorvin Köhne header_le.OemRevision = htole32(oem_revision);
6032fb0f352SCorvin Köhne memcpy(header_le.AslCompilerId, "BASL", ACPI_NAMESEG_SIZE);
6042fb0f352SCorvin Köhne header_le.AslCompilerRevision = htole32(0x20220504);
6052fb0f352SCorvin Köhne
6062fb0f352SCorvin Köhne BASL_EXEC(
6072fb0f352SCorvin Köhne basl_table_append_bytes(table, &header_le, sizeof(header_le)));
6082fb0f352SCorvin Köhne
6092fb0f352SCorvin Köhne BASL_EXEC(basl_table_add_length(table,
6102fb0f352SCorvin Köhne offsetof(ACPI_TABLE_HEADER, Length), sizeof(header_le.Length)));
6112fb0f352SCorvin Köhne BASL_EXEC(basl_table_add_checksum(table,
6122fb0f352SCorvin Köhne offsetof(ACPI_TABLE_HEADER, Checksum), 0,
6132fb0f352SCorvin Köhne BASL_TABLE_CHECKSUM_LEN_FULL_TABLE));
6142fb0f352SCorvin Köhne
6152fb0f352SCorvin Köhne return (0);
6162fb0f352SCorvin Köhne }
6172fb0f352SCorvin Köhne
6182fb0f352SCorvin Köhne int
basl_table_append_int(struct basl_table * const table,const uint64_t val,const uint8_t size)619e22f5ce2SCorvin Köhne basl_table_append_int(struct basl_table *const table, const uint64_t val,
620e22f5ce2SCorvin Köhne const uint8_t size)
621e22f5ce2SCorvin Köhne {
622e22f5ce2SCorvin Köhne char buf[8];
623e22f5ce2SCorvin Köhne
624e22f5ce2SCorvin Köhne assert(size <= sizeof(val));
625e22f5ce2SCorvin Köhne
626e22f5ce2SCorvin Köhne basl_le_enc(buf, val, size);
627e22f5ce2SCorvin Köhne return (basl_table_append_bytes(table, buf, size));
628e22f5ce2SCorvin Köhne }
629e22f5ce2SCorvin Köhne
630e22f5ce2SCorvin Köhne int
basl_table_append_length(struct basl_table * const table,const uint8_t size)6313a766cd0SCorvin Köhne basl_table_append_length(struct basl_table *const table, const uint8_t size)
6323a766cd0SCorvin Köhne {
6333a766cd0SCorvin Köhne assert(table != NULL);
6343a766cd0SCorvin Köhne assert(size <= sizeof(table->len));
6353a766cd0SCorvin Köhne
6363a766cd0SCorvin Köhne BASL_EXEC(basl_table_add_length(table, table->len, size));
6373a766cd0SCorvin Köhne BASL_EXEC(basl_table_append_int(table, 0, size));
6383a766cd0SCorvin Köhne
6393a766cd0SCorvin Köhne return (0);
6403a766cd0SCorvin Köhne }
6413a766cd0SCorvin Köhne
6423a766cd0SCorvin Köhne int
basl_table_append_pointer(struct basl_table * const table,const uint8_t src_signature[ACPI_NAMESEG_SIZE],const uint8_t size)64349b947c0SCorvin Köhne basl_table_append_pointer(struct basl_table *const table,
64449b947c0SCorvin Köhne const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint8_t size)
64549b947c0SCorvin Köhne {
64649b947c0SCorvin Köhne assert(table != NULL);
64749b947c0SCorvin Köhne assert(size == 4 || size == 8);
64849b947c0SCorvin Köhne
64949b947c0SCorvin Köhne BASL_EXEC(basl_table_add_pointer(table, src_signature, table->len, size));
65049b947c0SCorvin Köhne BASL_EXEC(basl_table_append_int(table, 0, size));
65149b947c0SCorvin Köhne
65249b947c0SCorvin Köhne return (0);
65349b947c0SCorvin Köhne }
65449b947c0SCorvin Köhne
65549b947c0SCorvin Köhne int
basl_table_create(struct basl_table ** const table,struct vmctx * ctx,const uint8_t * const name,const uint32_t alignment)65621bbc284SCorvin Köhne basl_table_create(struct basl_table **const table, struct vmctx *ctx,
65767654ffdSCorvin Köhne const uint8_t *const name, const uint32_t alignment)
65821bbc284SCorvin Köhne {
65921bbc284SCorvin Köhne struct basl_table *new_table;
66021bbc284SCorvin Köhne
66121bbc284SCorvin Köhne assert(table != NULL);
66221bbc284SCorvin Köhne
66321bbc284SCorvin Köhne new_table = calloc(1, sizeof(struct basl_table));
66421bbc284SCorvin Köhne if (new_table == NULL) {
66521bbc284SCorvin Köhne warnx("%s: failed to allocate table", __func__);
66621bbc284SCorvin Köhne return (ENOMEM);
66721bbc284SCorvin Köhne }
66821bbc284SCorvin Köhne
66921bbc284SCorvin Köhne new_table->ctx = ctx;
67021bbc284SCorvin Köhne
67121bbc284SCorvin Köhne snprintf(new_table->fwcfg_name, sizeof(new_table->fwcfg_name),
67221bbc284SCorvin Köhne "etc/acpi/%s", name);
67321bbc284SCorvin Köhne
67421bbc284SCorvin Köhne new_table->alignment = alignment;
67521bbc284SCorvin Köhne
67629578470SCorvin Köhne STAILQ_INIT(&new_table->checksums);
6773a766cd0SCorvin Köhne STAILQ_INIT(&new_table->lengths);
67849b947c0SCorvin Köhne STAILQ_INIT(&new_table->pointers);
6793a766cd0SCorvin Köhne
68021bbc284SCorvin Köhne STAILQ_INSERT_TAIL(&basl_tables, new_table, chain);
68121bbc284SCorvin Köhne
68221bbc284SCorvin Köhne *table = new_table;
68321bbc284SCorvin Köhne
68421bbc284SCorvin Köhne return (0);
68521bbc284SCorvin Köhne }
68624a0fef9SCorvin Köhne
68724a0fef9SCorvin Köhne int
basl_table_register_to_rsdt(struct basl_table * table)68824a0fef9SCorvin Köhne basl_table_register_to_rsdt(struct basl_table *table)
68924a0fef9SCorvin Köhne {
69024a0fef9SCorvin Köhne const ACPI_TABLE_HEADER *header;
69124a0fef9SCorvin Köhne
69224a0fef9SCorvin Köhne assert(table != NULL);
69324a0fef9SCorvin Köhne
69424a0fef9SCorvin Köhne header = (const ACPI_TABLE_HEADER *)table->data;
69524a0fef9SCorvin Köhne
69624a0fef9SCorvin Köhne BASL_EXEC(basl_table_append_pointer(rsdt, header->Signature,
69724a0fef9SCorvin Köhne ACPI_RSDT_ENTRY_SIZE));
69824a0fef9SCorvin Köhne BASL_EXEC(basl_table_append_pointer(xsdt, header->Signature,
69924a0fef9SCorvin Köhne ACPI_XSDT_ENTRY_SIZE));
70024a0fef9SCorvin Köhne
70124a0fef9SCorvin Köhne return (0);
70224a0fef9SCorvin Köhne }
703