xref: /illumos-gate/usr/src/cmd/bhyve/common/basl.c (revision 5c4a5fe16715fb423db76577a6883b5bbecdbe45)
1*5c4a5fe1SAndy Fiddaman /*-
2*5c4a5fe1SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
3*5c4a5fe1SAndy Fiddaman  *
4*5c4a5fe1SAndy Fiddaman  * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
5*5c4a5fe1SAndy Fiddaman  */
6*5c4a5fe1SAndy Fiddaman 
7*5c4a5fe1SAndy Fiddaman #include <sys/param.h>
8*5c4a5fe1SAndy Fiddaman #include <sys/endian.h>
9*5c4a5fe1SAndy Fiddaman #include <sys/errno.h>
10*5c4a5fe1SAndy Fiddaman #include <sys/queue.h>
11*5c4a5fe1SAndy Fiddaman #include <sys/stat.h>
12*5c4a5fe1SAndy Fiddaman 
13*5c4a5fe1SAndy Fiddaman #include <machine/vmm.h>
14*5c4a5fe1SAndy Fiddaman 
15*5c4a5fe1SAndy Fiddaman #include <assert.h>
16*5c4a5fe1SAndy Fiddaman #include <err.h>
17*5c4a5fe1SAndy Fiddaman #include <libutil.h>
18*5c4a5fe1SAndy Fiddaman #include <stddef.h>
19*5c4a5fe1SAndy Fiddaman #include <stdio.h>
20*5c4a5fe1SAndy Fiddaman #include <vmmapi.h>
21*5c4a5fe1SAndy Fiddaman 
22*5c4a5fe1SAndy Fiddaman #ifndef	__FreeBSD__
23*5c4a5fe1SAndy Fiddaman #include "hexdump.h"
24*5c4a5fe1SAndy Fiddaman #endif
25*5c4a5fe1SAndy Fiddaman 
26*5c4a5fe1SAndy Fiddaman #include "basl.h"
27*5c4a5fe1SAndy Fiddaman #include "config.h"
28*5c4a5fe1SAndy Fiddaman #include "qemu_loader.h"
29*5c4a5fe1SAndy Fiddaman 
30*5c4a5fe1SAndy Fiddaman struct basl_table_checksum {
31*5c4a5fe1SAndy Fiddaman 	STAILQ_ENTRY(basl_table_checksum) chain;
32*5c4a5fe1SAndy Fiddaman 	uint32_t off;
33*5c4a5fe1SAndy Fiddaman 	uint32_t start;
34*5c4a5fe1SAndy Fiddaman 	uint32_t len;
35*5c4a5fe1SAndy Fiddaman };
36*5c4a5fe1SAndy Fiddaman 
37*5c4a5fe1SAndy Fiddaman struct basl_table_length {
38*5c4a5fe1SAndy Fiddaman 	STAILQ_ENTRY(basl_table_length) chain;
39*5c4a5fe1SAndy Fiddaman 	uint32_t off;
40*5c4a5fe1SAndy Fiddaman 	uint8_t size;
41*5c4a5fe1SAndy Fiddaman };
42*5c4a5fe1SAndy Fiddaman 
43*5c4a5fe1SAndy Fiddaman struct basl_table_pointer {
44*5c4a5fe1SAndy Fiddaman 	STAILQ_ENTRY(basl_table_pointer) chain;
45*5c4a5fe1SAndy Fiddaman 	uint8_t src_signature[ACPI_NAMESEG_SIZE];
46*5c4a5fe1SAndy Fiddaman 	uint32_t off;
47*5c4a5fe1SAndy Fiddaman 	uint8_t size;
48*5c4a5fe1SAndy Fiddaman };
49*5c4a5fe1SAndy Fiddaman 
50*5c4a5fe1SAndy Fiddaman struct basl_table {
51*5c4a5fe1SAndy Fiddaman 	STAILQ_ENTRY(basl_table) chain;
52*5c4a5fe1SAndy Fiddaman 	struct vmctx *ctx;
53*5c4a5fe1SAndy Fiddaman 	uint8_t fwcfg_name[QEMU_FWCFG_MAX_NAME];
54*5c4a5fe1SAndy Fiddaman 	void *data;
55*5c4a5fe1SAndy Fiddaman 	uint32_t len;
56*5c4a5fe1SAndy Fiddaman 	uint32_t off;
57*5c4a5fe1SAndy Fiddaman 	uint32_t alignment;
58*5c4a5fe1SAndy Fiddaman 	STAILQ_HEAD(basl_table_checksum_list, basl_table_checksum) checksums;
59*5c4a5fe1SAndy Fiddaman 	STAILQ_HEAD(basl_table_length_list, basl_table_length) lengths;
60*5c4a5fe1SAndy Fiddaman 	STAILQ_HEAD(basl_table_pointer_list, basl_table_pointer) pointers;
61*5c4a5fe1SAndy Fiddaman };
62*5c4a5fe1SAndy Fiddaman static STAILQ_HEAD(basl_table_list, basl_table) basl_tables = STAILQ_HEAD_INITIALIZER(
63*5c4a5fe1SAndy Fiddaman     basl_tables);
64*5c4a5fe1SAndy Fiddaman 
65*5c4a5fe1SAndy Fiddaman static struct qemu_loader *basl_loader;
66*5c4a5fe1SAndy Fiddaman static struct basl_table *rsdt;
67*5c4a5fe1SAndy Fiddaman static struct basl_table *xsdt;
68*5c4a5fe1SAndy Fiddaman static bool load_into_memory;
69*5c4a5fe1SAndy Fiddaman 
70*5c4a5fe1SAndy Fiddaman static __inline uint64_t
basl_le_dec(void * pp,size_t len)71*5c4a5fe1SAndy Fiddaman basl_le_dec(void *pp, size_t len)
72*5c4a5fe1SAndy Fiddaman {
73*5c4a5fe1SAndy Fiddaman 	assert(len <= 8);
74*5c4a5fe1SAndy Fiddaman 
75*5c4a5fe1SAndy Fiddaman 	switch (len) {
76*5c4a5fe1SAndy Fiddaman 	case 1:
77*5c4a5fe1SAndy Fiddaman 		return ((uint8_t *)pp)[0];
78*5c4a5fe1SAndy Fiddaman 	case 2:
79*5c4a5fe1SAndy Fiddaman 		return le16dec(pp);
80*5c4a5fe1SAndy Fiddaman 	case 4:
81*5c4a5fe1SAndy Fiddaman 		return le32dec(pp);
82*5c4a5fe1SAndy Fiddaman 	case 8:
83*5c4a5fe1SAndy Fiddaman 		return le64dec(pp);
84*5c4a5fe1SAndy Fiddaman 	}
85*5c4a5fe1SAndy Fiddaman 
86*5c4a5fe1SAndy Fiddaman 	return 0;
87*5c4a5fe1SAndy Fiddaman }
88*5c4a5fe1SAndy Fiddaman 
89*5c4a5fe1SAndy Fiddaman static __inline void
basl_le_enc(void * pp,uint64_t val,size_t len)90*5c4a5fe1SAndy Fiddaman basl_le_enc(void *pp, uint64_t val, size_t len)
91*5c4a5fe1SAndy Fiddaman {
92*5c4a5fe1SAndy Fiddaman 	char buf[8];
93*5c4a5fe1SAndy Fiddaman 
94*5c4a5fe1SAndy Fiddaman 	assert(len <= 8);
95*5c4a5fe1SAndy Fiddaman 
96*5c4a5fe1SAndy Fiddaman 	le64enc(buf, val);
97*5c4a5fe1SAndy Fiddaman 	memcpy(pp, buf, len);
98*5c4a5fe1SAndy Fiddaman }
99*5c4a5fe1SAndy Fiddaman 
100*5c4a5fe1SAndy Fiddaman static int
basl_dump_table(const struct basl_table * const table,const bool mem)101*5c4a5fe1SAndy Fiddaman basl_dump_table(const struct basl_table *const table, const bool mem)
102*5c4a5fe1SAndy Fiddaman {
103*5c4a5fe1SAndy Fiddaman 	const ACPI_TABLE_HEADER *const header = table->data;
104*5c4a5fe1SAndy Fiddaman 	const uint8_t *data;
105*5c4a5fe1SAndy Fiddaman 
106*5c4a5fe1SAndy Fiddaman 	if (!mem) {
107*5c4a5fe1SAndy Fiddaman 		data = table->data;
108*5c4a5fe1SAndy Fiddaman 	} else {
109*5c4a5fe1SAndy Fiddaman 		data = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off,
110*5c4a5fe1SAndy Fiddaman 		    table->len);
111*5c4a5fe1SAndy Fiddaman 		if (data == NULL) {
112*5c4a5fe1SAndy Fiddaman 			return (ENOMEM);
113*5c4a5fe1SAndy Fiddaman 		}
114*5c4a5fe1SAndy Fiddaman 	}
115*5c4a5fe1SAndy Fiddaman 
116*5c4a5fe1SAndy Fiddaman 	printf("%.4s @ %8x (%s)\n", header->Signature,
117*5c4a5fe1SAndy Fiddaman 	    BHYVE_ACPI_BASE + table->off, mem ? "Memory" : "FwCfg");
118*5c4a5fe1SAndy Fiddaman 	hexdump(data, table->len, NULL, 0);
119*5c4a5fe1SAndy Fiddaman 
120*5c4a5fe1SAndy Fiddaman 	return (0);
121*5c4a5fe1SAndy Fiddaman }
122*5c4a5fe1SAndy Fiddaman 
123*5c4a5fe1SAndy Fiddaman static int __unused
basl_dump(const bool mem)124*5c4a5fe1SAndy Fiddaman basl_dump(const bool mem)
125*5c4a5fe1SAndy Fiddaman {
126*5c4a5fe1SAndy Fiddaman 	struct basl_table *table;
127*5c4a5fe1SAndy Fiddaman 
128*5c4a5fe1SAndy Fiddaman 	STAILQ_FOREACH(table, &basl_tables, chain) {
129*5c4a5fe1SAndy Fiddaman 		BASL_EXEC(basl_dump_table(table, mem));
130*5c4a5fe1SAndy Fiddaman 	}
131*5c4a5fe1SAndy Fiddaman 
132*5c4a5fe1SAndy Fiddaman 	return (0);
133*5c4a5fe1SAndy Fiddaman }
134*5c4a5fe1SAndy Fiddaman 
135*5c4a5fe1SAndy Fiddaman 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)136*5c4a5fe1SAndy Fiddaman basl_fill_gas(ACPI_GENERIC_ADDRESS *const gas, const uint8_t space_id,
137*5c4a5fe1SAndy Fiddaman     const uint8_t bit_width, const uint8_t bit_offset,
138*5c4a5fe1SAndy Fiddaman     const uint8_t access_width, const uint64_t address)
139*5c4a5fe1SAndy Fiddaman {
140*5c4a5fe1SAndy Fiddaman 	assert(gas != NULL);
141*5c4a5fe1SAndy Fiddaman 
142*5c4a5fe1SAndy Fiddaman 	gas->SpaceId = space_id;
143*5c4a5fe1SAndy Fiddaman 	gas->BitWidth = bit_width;
144*5c4a5fe1SAndy Fiddaman 	gas->BitOffset = bit_offset;
145*5c4a5fe1SAndy Fiddaman 	gas->AccessWidth = access_width;
146*5c4a5fe1SAndy Fiddaman 	gas->Address = htole64(address);
147*5c4a5fe1SAndy Fiddaman }
148*5c4a5fe1SAndy Fiddaman 
149*5c4a5fe1SAndy Fiddaman static int
basl_finish_install_guest_tables(struct basl_table * const table,uint32_t * const off)150*5c4a5fe1SAndy Fiddaman basl_finish_install_guest_tables(struct basl_table *const table, uint32_t *const off)
151*5c4a5fe1SAndy Fiddaman {
152*5c4a5fe1SAndy Fiddaman 	void *gva;
153*5c4a5fe1SAndy Fiddaman 
154*5c4a5fe1SAndy Fiddaman 	table->off = roundup2(*off, table->alignment);
155*5c4a5fe1SAndy Fiddaman 	*off = table->off + table->len;
156*5c4a5fe1SAndy Fiddaman 	if (*off <= table->off) {
157*5c4a5fe1SAndy Fiddaman 		warnx("%s: invalid table length 0x%8x @ offset 0x%8x", __func__,
158*5c4a5fe1SAndy Fiddaman 		    table->len, table->off);
159*5c4a5fe1SAndy Fiddaman 		return (EFAULT);
160*5c4a5fe1SAndy Fiddaman 	}
161*5c4a5fe1SAndy Fiddaman 
162*5c4a5fe1SAndy Fiddaman 	/* Cause guest BIOS to copy the ACPI table into guest memory. */
163*5c4a5fe1SAndy Fiddaman #ifdef	__FreeBSD__
164*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(
165*5c4a5fe1SAndy Fiddaman 	    qemu_fwcfg_add_file(table->fwcfg_name, table->len, table->data));
166*5c4a5fe1SAndy Fiddaman #else
167*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(
168*5c4a5fe1SAndy Fiddaman 	    qemu_fwcfg_add_file((const char *)table->fwcfg_name,
169*5c4a5fe1SAndy Fiddaman 	    table->len, table->data));
170*5c4a5fe1SAndy Fiddaman #endif
171*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(qemu_loader_alloc(basl_loader, table->fwcfg_name,
172*5c4a5fe1SAndy Fiddaman 	    table->alignment, QEMU_LOADER_ALLOC_HIGH));
173*5c4a5fe1SAndy Fiddaman 
174*5c4a5fe1SAndy Fiddaman 	if (!load_into_memory) {
175*5c4a5fe1SAndy Fiddaman 		return (0);
176*5c4a5fe1SAndy Fiddaman 	}
177*5c4a5fe1SAndy Fiddaman 
178*5c4a5fe1SAndy Fiddaman 	/*
179*5c4a5fe1SAndy Fiddaman 	 * Install ACPI tables directly in guest memory for use by guests which
180*5c4a5fe1SAndy Fiddaman 	 * do not boot via EFI. EFI ROMs provide a pointer to the firmware
181*5c4a5fe1SAndy Fiddaman 	 * generated ACPI tables instead, but it doesn't hurt to install the
182*5c4a5fe1SAndy Fiddaman 	 * tables always.
183*5c4a5fe1SAndy Fiddaman 	 */
184*5c4a5fe1SAndy Fiddaman 	gva = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off, table->len);
185*5c4a5fe1SAndy Fiddaman 	if (gva == NULL) {
186*5c4a5fe1SAndy Fiddaman 		warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]", __func__,
187*5c4a5fe1SAndy Fiddaman 		    (uint64_t)BHYVE_ACPI_BASE + table->off,
188*5c4a5fe1SAndy Fiddaman 		    (uint64_t)BHYVE_ACPI_BASE + table->off + table->len);
189*5c4a5fe1SAndy Fiddaman 		return (ENOMEM);
190*5c4a5fe1SAndy Fiddaman 	}
191*5c4a5fe1SAndy Fiddaman 	memcpy(gva, table->data, table->len);
192*5c4a5fe1SAndy Fiddaman 
193*5c4a5fe1SAndy Fiddaman 	return (0);
194*5c4a5fe1SAndy Fiddaman }
195*5c4a5fe1SAndy Fiddaman 
196*5c4a5fe1SAndy Fiddaman static int
basl_finish_patch_checksums(struct basl_table * const table)197*5c4a5fe1SAndy Fiddaman basl_finish_patch_checksums(struct basl_table *const table)
198*5c4a5fe1SAndy Fiddaman {
199*5c4a5fe1SAndy Fiddaman 	struct basl_table_checksum *checksum;
200*5c4a5fe1SAndy Fiddaman 
201*5c4a5fe1SAndy Fiddaman 	STAILQ_FOREACH(checksum, &table->checksums, chain) {
202*5c4a5fe1SAndy Fiddaman 		uint8_t *gva, *checksum_gva;
203*5c4a5fe1SAndy Fiddaman 		uint64_t gpa;
204*5c4a5fe1SAndy Fiddaman 		uint32_t len;
205*5c4a5fe1SAndy Fiddaman 		uint8_t sum;
206*5c4a5fe1SAndy Fiddaman 
207*5c4a5fe1SAndy Fiddaman 		len = checksum->len;
208*5c4a5fe1SAndy Fiddaman 		if (len == BASL_TABLE_CHECKSUM_LEN_FULL_TABLE) {
209*5c4a5fe1SAndy Fiddaman 			len = table->len;
210*5c4a5fe1SAndy Fiddaman 		}
211*5c4a5fe1SAndy Fiddaman 
212*5c4a5fe1SAndy Fiddaman 		assert(checksum->off < table->len);
213*5c4a5fe1SAndy Fiddaman 		assert(checksum->start < table->len);
214*5c4a5fe1SAndy Fiddaman 		assert(checksum->start + len <= table->len);
215*5c4a5fe1SAndy Fiddaman 
216*5c4a5fe1SAndy Fiddaman 		/* Cause guest BIOS to patch the checksum. */
217*5c4a5fe1SAndy Fiddaman 		BASL_EXEC(qemu_loader_add_checksum(basl_loader,
218*5c4a5fe1SAndy Fiddaman 		    table->fwcfg_name, checksum->off, checksum->start, len));
219*5c4a5fe1SAndy Fiddaman 
220*5c4a5fe1SAndy Fiddaman 		if (!load_into_memory) {
221*5c4a5fe1SAndy Fiddaman 			continue;
222*5c4a5fe1SAndy Fiddaman 		}
223*5c4a5fe1SAndy Fiddaman 
224*5c4a5fe1SAndy Fiddaman 		/*
225*5c4a5fe1SAndy Fiddaman 		 * Install ACPI tables directly in guest memory for use by
226*5c4a5fe1SAndy Fiddaman 		 * guests which do not boot via EFI. EFI ROMs provide a pointer
227*5c4a5fe1SAndy Fiddaman 		 * to the firmware generated ACPI tables instead, but it doesn't
228*5c4a5fe1SAndy Fiddaman 		 * hurt to install the tables always.
229*5c4a5fe1SAndy Fiddaman 		 */
230*5c4a5fe1SAndy Fiddaman 		gpa = BHYVE_ACPI_BASE + table->off + checksum->start;
231*5c4a5fe1SAndy Fiddaman 		if ((gpa < BHYVE_ACPI_BASE) ||
232*5c4a5fe1SAndy Fiddaman 		    (gpa < BHYVE_ACPI_BASE + table->off)) {
233*5c4a5fe1SAndy Fiddaman 			warnx("%s: invalid gpa (off 0x%8x start 0x%8x)",
234*5c4a5fe1SAndy Fiddaman 			    __func__, table->off, checksum->start);
235*5c4a5fe1SAndy Fiddaman 			return (EFAULT);
236*5c4a5fe1SAndy Fiddaman 		}
237*5c4a5fe1SAndy Fiddaman 
238*5c4a5fe1SAndy Fiddaman 		gva = vm_map_gpa(table->ctx, gpa, len);
239*5c4a5fe1SAndy Fiddaman 		if (gva == NULL) {
240*5c4a5fe1SAndy Fiddaman 			warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
241*5c4a5fe1SAndy Fiddaman 			    __func__, gpa, gpa + len);
242*5c4a5fe1SAndy Fiddaman 			return (ENOMEM);
243*5c4a5fe1SAndy Fiddaman 		}
244*5c4a5fe1SAndy Fiddaman 
245*5c4a5fe1SAndy Fiddaman 		checksum_gva = gva + checksum->off;
246*5c4a5fe1SAndy Fiddaman 		if (checksum_gva < gva) {
247*5c4a5fe1SAndy Fiddaman 			warnx("%s: invalid checksum offset 0x%8x", __func__,
248*5c4a5fe1SAndy Fiddaman 			    checksum->off);
249*5c4a5fe1SAndy Fiddaman 			return (EFAULT);
250*5c4a5fe1SAndy Fiddaman 		}
251*5c4a5fe1SAndy Fiddaman 
252*5c4a5fe1SAndy Fiddaman 		sum = 0;
253*5c4a5fe1SAndy Fiddaman 		for (uint32_t i = 0; i < len; ++i) {
254*5c4a5fe1SAndy Fiddaman 			sum += *(gva + i);
255*5c4a5fe1SAndy Fiddaman 		}
256*5c4a5fe1SAndy Fiddaman 		*checksum_gva = -sum;
257*5c4a5fe1SAndy Fiddaman 	}
258*5c4a5fe1SAndy Fiddaman 
259*5c4a5fe1SAndy Fiddaman 	return (0);
260*5c4a5fe1SAndy Fiddaman }
261*5c4a5fe1SAndy Fiddaman 
262*5c4a5fe1SAndy Fiddaman static struct basl_table *
basl_get_table_by_signature(const uint8_t signature[ACPI_NAMESEG_SIZE])263*5c4a5fe1SAndy Fiddaman basl_get_table_by_signature(const uint8_t signature[ACPI_NAMESEG_SIZE])
264*5c4a5fe1SAndy Fiddaman {
265*5c4a5fe1SAndy Fiddaman 	struct basl_table *table;
266*5c4a5fe1SAndy Fiddaman 
267*5c4a5fe1SAndy Fiddaman 	STAILQ_FOREACH(table, &basl_tables, chain) {
268*5c4a5fe1SAndy Fiddaman 		const ACPI_TABLE_HEADER *const header =
269*5c4a5fe1SAndy Fiddaman 		    (const ACPI_TABLE_HEADER *)table->data;
270*5c4a5fe1SAndy Fiddaman 
271*5c4a5fe1SAndy Fiddaman #ifdef	__FreeBSD__
272*5c4a5fe1SAndy Fiddaman 		if (strncmp(header->Signature, signature,
273*5c4a5fe1SAndy Fiddaman 			sizeof(header->Signature)) == 0) {
274*5c4a5fe1SAndy Fiddaman 			return (table);
275*5c4a5fe1SAndy Fiddaman #else
276*5c4a5fe1SAndy Fiddaman 		if (strncmp(header->Signature, (char *)signature,
277*5c4a5fe1SAndy Fiddaman 			sizeof(header->Signature)) == 0) {
278*5c4a5fe1SAndy Fiddaman 			return (table);
279*5c4a5fe1SAndy Fiddaman #endif
280*5c4a5fe1SAndy Fiddaman 		}
281*5c4a5fe1SAndy Fiddaman 	}
282*5c4a5fe1SAndy Fiddaman 
283*5c4a5fe1SAndy Fiddaman 	warnx("%s: %.4s not found", __func__, signature);
284*5c4a5fe1SAndy Fiddaman 	return (NULL);
285*5c4a5fe1SAndy Fiddaman }
286*5c4a5fe1SAndy Fiddaman 
287*5c4a5fe1SAndy Fiddaman static int
288*5c4a5fe1SAndy Fiddaman basl_finish_patch_pointers(struct basl_table *const table)
289*5c4a5fe1SAndy Fiddaman {
290*5c4a5fe1SAndy Fiddaman 	struct basl_table_pointer *pointer;
291*5c4a5fe1SAndy Fiddaman 
292*5c4a5fe1SAndy Fiddaman 	STAILQ_FOREACH(pointer, &table->pointers, chain) {
293*5c4a5fe1SAndy Fiddaman 		const struct basl_table *src_table;
294*5c4a5fe1SAndy Fiddaman 		uint8_t *gva;
295*5c4a5fe1SAndy Fiddaman 		uint64_t gpa, val;
296*5c4a5fe1SAndy Fiddaman 
297*5c4a5fe1SAndy Fiddaman 		assert(pointer->off < table->len);
298*5c4a5fe1SAndy Fiddaman 		assert(pointer->off + pointer->size <= table->len);
299*5c4a5fe1SAndy Fiddaman 
300*5c4a5fe1SAndy Fiddaman 		src_table = basl_get_table_by_signature(pointer->src_signature);
301*5c4a5fe1SAndy Fiddaman 		if (src_table == NULL) {
302*5c4a5fe1SAndy Fiddaman 			warnx("%s: could not find ACPI table %.4s", __func__,
303*5c4a5fe1SAndy Fiddaman 			    pointer->src_signature);
304*5c4a5fe1SAndy Fiddaman 			return (EFAULT);
305*5c4a5fe1SAndy Fiddaman 		}
306*5c4a5fe1SAndy Fiddaman 
307*5c4a5fe1SAndy Fiddaman 		/* Cause guest BIOS to patch the pointer. */
308*5c4a5fe1SAndy Fiddaman 		BASL_EXEC(
309*5c4a5fe1SAndy Fiddaman 		    qemu_loader_add_pointer(basl_loader, table->fwcfg_name,
310*5c4a5fe1SAndy Fiddaman 			src_table->fwcfg_name, pointer->off, pointer->size));
311*5c4a5fe1SAndy Fiddaman 
312*5c4a5fe1SAndy Fiddaman 		if (!load_into_memory) {
313*5c4a5fe1SAndy Fiddaman 			continue;
314*5c4a5fe1SAndy Fiddaman 		}
315*5c4a5fe1SAndy Fiddaman 
316*5c4a5fe1SAndy Fiddaman 		/*
317*5c4a5fe1SAndy Fiddaman 		 * Install ACPI tables directly in guest memory for use by
318*5c4a5fe1SAndy Fiddaman 		 * guests which do not boot via EFI. EFI ROMs provide a pointer
319*5c4a5fe1SAndy Fiddaman 		 * to the firmware generated ACPI tables instead, but it doesn't
320*5c4a5fe1SAndy Fiddaman 		 * hurt to install the tables always.
321*5c4a5fe1SAndy Fiddaman 		 */
322*5c4a5fe1SAndy Fiddaman 		gpa = BHYVE_ACPI_BASE + table->off;
323*5c4a5fe1SAndy Fiddaman 		if (gpa < BHYVE_ACPI_BASE) {
324*5c4a5fe1SAndy Fiddaman 			warnx("%s: table offset of 0x%8x is too large",
325*5c4a5fe1SAndy Fiddaman 			    __func__, table->off);
326*5c4a5fe1SAndy Fiddaman 			return (EFAULT);
327*5c4a5fe1SAndy Fiddaman 		}
328*5c4a5fe1SAndy Fiddaman 
329*5c4a5fe1SAndy Fiddaman 		gva = vm_map_gpa(table->ctx, gpa, table->len);
330*5c4a5fe1SAndy Fiddaman 		if (gva == NULL) {
331*5c4a5fe1SAndy Fiddaman 			warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
332*5c4a5fe1SAndy Fiddaman 			    __func__, gpa, gpa + table->len);
333*5c4a5fe1SAndy Fiddaman 			return (ENOMEM);
334*5c4a5fe1SAndy Fiddaman 		}
335*5c4a5fe1SAndy Fiddaman 
336*5c4a5fe1SAndy Fiddaman 		val = basl_le_dec(gva + pointer->off, pointer->size);
337*5c4a5fe1SAndy Fiddaman 		val += BHYVE_ACPI_BASE + src_table->off;
338*5c4a5fe1SAndy Fiddaman 		basl_le_enc(gva + pointer->off, val, pointer->size);
339*5c4a5fe1SAndy Fiddaman 	}
340*5c4a5fe1SAndy Fiddaman 
341*5c4a5fe1SAndy Fiddaman 	return (0);
342*5c4a5fe1SAndy Fiddaman }
343*5c4a5fe1SAndy Fiddaman 
344*5c4a5fe1SAndy Fiddaman static int
345*5c4a5fe1SAndy Fiddaman basl_finish_set_length(struct basl_table *const table)
346*5c4a5fe1SAndy Fiddaman {
347*5c4a5fe1SAndy Fiddaman 	struct basl_table_length *length;
348*5c4a5fe1SAndy Fiddaman 
349*5c4a5fe1SAndy Fiddaman 	STAILQ_FOREACH(length, &table->lengths, chain) {
350*5c4a5fe1SAndy Fiddaman 		assert(length->off < table->len);
351*5c4a5fe1SAndy Fiddaman 		assert(length->off + length->size <= table->len);
352*5c4a5fe1SAndy Fiddaman 
353*5c4a5fe1SAndy Fiddaman 		basl_le_enc((uint8_t *)table->data + length->off, table->len,
354*5c4a5fe1SAndy Fiddaman 		    length->size);
355*5c4a5fe1SAndy Fiddaman 	}
356*5c4a5fe1SAndy Fiddaman 
357*5c4a5fe1SAndy Fiddaman 	return (0);
358*5c4a5fe1SAndy Fiddaman }
359*5c4a5fe1SAndy Fiddaman 
360*5c4a5fe1SAndy Fiddaman int
361*5c4a5fe1SAndy Fiddaman basl_finish(void)
362*5c4a5fe1SAndy Fiddaman {
363*5c4a5fe1SAndy Fiddaman 	struct basl_table *table;
364*5c4a5fe1SAndy Fiddaman 	uint32_t off = 0;
365*5c4a5fe1SAndy Fiddaman 
366*5c4a5fe1SAndy Fiddaman 	if (STAILQ_EMPTY(&basl_tables)) {
367*5c4a5fe1SAndy Fiddaman 		warnx("%s: no ACPI tables found", __func__);
368*5c4a5fe1SAndy Fiddaman 		return (EINVAL);
369*5c4a5fe1SAndy Fiddaman 	}
370*5c4a5fe1SAndy Fiddaman 
371*5c4a5fe1SAndy Fiddaman 	/*
372*5c4a5fe1SAndy Fiddaman 	 * If we install ACPI tables by FwCfg and by memory, Windows will use
373*5c4a5fe1SAndy Fiddaman 	 * the tables from memory. This can cause issues when using advanced
374*5c4a5fe1SAndy Fiddaman 	 * features like a TPM log because we aren't able to patch the memory
375*5c4a5fe1SAndy Fiddaman 	 * tables accordingly.
376*5c4a5fe1SAndy Fiddaman 	 */
377*5c4a5fe1SAndy Fiddaman 	load_into_memory = get_config_bool_default("acpi_tables_in_memory",
378*5c4a5fe1SAndy Fiddaman 	    true);
379*5c4a5fe1SAndy Fiddaman 
380*5c4a5fe1SAndy Fiddaman 	/*
381*5c4a5fe1SAndy Fiddaman 	 * We have to install all tables before we can patch them. Therefore,
382*5c4a5fe1SAndy Fiddaman 	 * use two loops. The first one installs all tables and the second one
383*5c4a5fe1SAndy Fiddaman 	 * patches them.
384*5c4a5fe1SAndy Fiddaman 	 */
385*5c4a5fe1SAndy Fiddaman 	STAILQ_FOREACH(table, &basl_tables, chain) {
386*5c4a5fe1SAndy Fiddaman 		BASL_EXEC(basl_finish_set_length(table));
387*5c4a5fe1SAndy Fiddaman 		BASL_EXEC(basl_finish_install_guest_tables(table, &off));
388*5c4a5fe1SAndy Fiddaman 	}
389*5c4a5fe1SAndy Fiddaman 	STAILQ_FOREACH(table, &basl_tables, chain) {
390*5c4a5fe1SAndy Fiddaman 		BASL_EXEC(basl_finish_patch_pointers(table));
391*5c4a5fe1SAndy Fiddaman 
392*5c4a5fe1SAndy Fiddaman 		/*
393*5c4a5fe1SAndy Fiddaman 		 * Calculate the checksum as last step!
394*5c4a5fe1SAndy Fiddaman 		 */
395*5c4a5fe1SAndy Fiddaman 		BASL_EXEC(basl_finish_patch_checksums(table));
396*5c4a5fe1SAndy Fiddaman 	}
397*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(qemu_loader_finish(basl_loader));
398*5c4a5fe1SAndy Fiddaman 
399*5c4a5fe1SAndy Fiddaman 	return (0);
400*5c4a5fe1SAndy Fiddaman }
401*5c4a5fe1SAndy Fiddaman 
402*5c4a5fe1SAndy Fiddaman static int
403*5c4a5fe1SAndy Fiddaman basl_init_rsdt(struct vmctx *const ctx)
404*5c4a5fe1SAndy Fiddaman {
405*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(
406*5c4a5fe1SAndy Fiddaman 	    basl_table_create(&rsdt, ctx, ACPI_SIG_RSDT, BASL_TABLE_ALIGNMENT));
407*5c4a5fe1SAndy Fiddaman 
408*5c4a5fe1SAndy Fiddaman 	/* Header */
409*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_append_header(rsdt, ACPI_SIG_RSDT, 1, 1));
410*5c4a5fe1SAndy Fiddaman 	/* Pointers (added by basl_table_register_to_rsdt) */
411*5c4a5fe1SAndy Fiddaman 
412*5c4a5fe1SAndy Fiddaman 	return (0);
413*5c4a5fe1SAndy Fiddaman }
414*5c4a5fe1SAndy Fiddaman 
415*5c4a5fe1SAndy Fiddaman static int
416*5c4a5fe1SAndy Fiddaman basl_init_xsdt(struct vmctx *const ctx)
417*5c4a5fe1SAndy Fiddaman {
418*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(
419*5c4a5fe1SAndy Fiddaman 	    basl_table_create(&xsdt, ctx, ACPI_SIG_XSDT, BASL_TABLE_ALIGNMENT));
420*5c4a5fe1SAndy Fiddaman 
421*5c4a5fe1SAndy Fiddaman 	/* Header */
422*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_append_header(xsdt, ACPI_SIG_XSDT, 1, 1));
423*5c4a5fe1SAndy Fiddaman 	/* Pointers (added by basl_table_register_to_rsdt) */
424*5c4a5fe1SAndy Fiddaman 
425*5c4a5fe1SAndy Fiddaman 	return (0);
426*5c4a5fe1SAndy Fiddaman }
427*5c4a5fe1SAndy Fiddaman 
428*5c4a5fe1SAndy Fiddaman int
429*5c4a5fe1SAndy Fiddaman basl_init(struct vmctx *const ctx)
430*5c4a5fe1SAndy Fiddaman {
431*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_init_rsdt(ctx));
432*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_init_xsdt(ctx));
433*5c4a5fe1SAndy Fiddaman #ifdef	__FreeBSD__
434*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(
435*5c4a5fe1SAndy Fiddaman 	    qemu_loader_create(&basl_loader, QEMU_FWCFG_FILE_TABLE_LOADER));
436*5c4a5fe1SAndy Fiddaman #else
437*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(
438*5c4a5fe1SAndy Fiddaman 	    qemu_loader_create(&basl_loader,
439*5c4a5fe1SAndy Fiddaman 	    (uint8_t *)QEMU_FWCFG_FILE_TABLE_LOADER));
440*5c4a5fe1SAndy Fiddaman #endif
441*5c4a5fe1SAndy Fiddaman 
442*5c4a5fe1SAndy Fiddaman 	return (0);
443*5c4a5fe1SAndy Fiddaman }
444*5c4a5fe1SAndy Fiddaman 
445*5c4a5fe1SAndy Fiddaman int
446*5c4a5fe1SAndy Fiddaman basl_table_add_checksum(struct basl_table *const table, const uint32_t off,
447*5c4a5fe1SAndy Fiddaman     const uint32_t start, const uint32_t len)
448*5c4a5fe1SAndy Fiddaman {
449*5c4a5fe1SAndy Fiddaman 	struct basl_table_checksum *checksum;
450*5c4a5fe1SAndy Fiddaman 
451*5c4a5fe1SAndy Fiddaman 	assert(table != NULL);
452*5c4a5fe1SAndy Fiddaman 
453*5c4a5fe1SAndy Fiddaman 	checksum = calloc(1, sizeof(struct basl_table_checksum));
454*5c4a5fe1SAndy Fiddaman 	if (checksum == NULL) {
455*5c4a5fe1SAndy Fiddaman 		warnx("%s: failed to allocate checksum", __func__);
456*5c4a5fe1SAndy Fiddaman 		return (ENOMEM);
457*5c4a5fe1SAndy Fiddaman 	}
458*5c4a5fe1SAndy Fiddaman 
459*5c4a5fe1SAndy Fiddaman 	checksum->off = off;
460*5c4a5fe1SAndy Fiddaman 	checksum->start = start;
461*5c4a5fe1SAndy Fiddaman 	checksum->len = len;
462*5c4a5fe1SAndy Fiddaman 
463*5c4a5fe1SAndy Fiddaman 	STAILQ_INSERT_TAIL(&table->checksums, checksum, chain);
464*5c4a5fe1SAndy Fiddaman 
465*5c4a5fe1SAndy Fiddaman 	return (0);
466*5c4a5fe1SAndy Fiddaman }
467*5c4a5fe1SAndy Fiddaman 
468*5c4a5fe1SAndy Fiddaman int
469*5c4a5fe1SAndy Fiddaman basl_table_add_length(struct basl_table *const table, const uint32_t off,
470*5c4a5fe1SAndy Fiddaman     const uint8_t size)
471*5c4a5fe1SAndy Fiddaman {
472*5c4a5fe1SAndy Fiddaman 	struct basl_table_length *length;
473*5c4a5fe1SAndy Fiddaman 
474*5c4a5fe1SAndy Fiddaman 	assert(table != NULL);
475*5c4a5fe1SAndy Fiddaman 	assert(size == 4 || size == 8);
476*5c4a5fe1SAndy Fiddaman 
477*5c4a5fe1SAndy Fiddaman 	length = calloc(1, sizeof(struct basl_table_length));
478*5c4a5fe1SAndy Fiddaman 	if (length == NULL) {
479*5c4a5fe1SAndy Fiddaman 		warnx("%s: failed to allocate length", __func__);
480*5c4a5fe1SAndy Fiddaman 		return (ENOMEM);
481*5c4a5fe1SAndy Fiddaman 	}
482*5c4a5fe1SAndy Fiddaman 
483*5c4a5fe1SAndy Fiddaman 	length->off = off;
484*5c4a5fe1SAndy Fiddaman 	length->size = size;
485*5c4a5fe1SAndy Fiddaman 
486*5c4a5fe1SAndy Fiddaman 	STAILQ_INSERT_TAIL(&table->lengths, length, chain);
487*5c4a5fe1SAndy Fiddaman 
488*5c4a5fe1SAndy Fiddaman 	return (0);
489*5c4a5fe1SAndy Fiddaman }
490*5c4a5fe1SAndy Fiddaman 
491*5c4a5fe1SAndy Fiddaman int
492*5c4a5fe1SAndy Fiddaman basl_table_add_pointer(struct basl_table *const table,
493*5c4a5fe1SAndy Fiddaman     const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint32_t off,
494*5c4a5fe1SAndy Fiddaman     const uint8_t size)
495*5c4a5fe1SAndy Fiddaman {
496*5c4a5fe1SAndy Fiddaman 	struct basl_table_pointer *pointer;
497*5c4a5fe1SAndy Fiddaman 
498*5c4a5fe1SAndy Fiddaman 	assert(table != NULL);
499*5c4a5fe1SAndy Fiddaman 	assert(size == 4 || size == 8);
500*5c4a5fe1SAndy Fiddaman 
501*5c4a5fe1SAndy Fiddaman 	pointer = calloc(1, sizeof(struct basl_table_pointer));
502*5c4a5fe1SAndy Fiddaman 	if (pointer == NULL) {
503*5c4a5fe1SAndy Fiddaman 		warnx("%s: failed to allocate pointer", __func__);
504*5c4a5fe1SAndy Fiddaman 		return (ENOMEM);
505*5c4a5fe1SAndy Fiddaman 	}
506*5c4a5fe1SAndy Fiddaman 
507*5c4a5fe1SAndy Fiddaman 	memcpy(pointer->src_signature, src_signature,
508*5c4a5fe1SAndy Fiddaman 	    sizeof(pointer->src_signature));
509*5c4a5fe1SAndy Fiddaman 	pointer->off = off;
510*5c4a5fe1SAndy Fiddaman 	pointer->size = size;
511*5c4a5fe1SAndy Fiddaman 
512*5c4a5fe1SAndy Fiddaman 	STAILQ_INSERT_TAIL(&table->pointers, pointer, chain);
513*5c4a5fe1SAndy Fiddaman 
514*5c4a5fe1SAndy Fiddaman 	return (0);
515*5c4a5fe1SAndy Fiddaman }
516*5c4a5fe1SAndy Fiddaman 
517*5c4a5fe1SAndy Fiddaman int
518*5c4a5fe1SAndy Fiddaman basl_table_append_bytes(struct basl_table *const table, const void *const bytes,
519*5c4a5fe1SAndy Fiddaman     const uint32_t len)
520*5c4a5fe1SAndy Fiddaman {
521*5c4a5fe1SAndy Fiddaman 	void *end;
522*5c4a5fe1SAndy Fiddaman 
523*5c4a5fe1SAndy Fiddaman 	assert(table != NULL);
524*5c4a5fe1SAndy Fiddaman 	assert(bytes != NULL);
525*5c4a5fe1SAndy Fiddaman 
526*5c4a5fe1SAndy Fiddaman 	if (table->len + len <= table->len) {
527*5c4a5fe1SAndy Fiddaman 		warnx("%s: table too large (table->len 0x%8x len 0x%8x)",
528*5c4a5fe1SAndy Fiddaman 		    __func__, table->len, len);
529*5c4a5fe1SAndy Fiddaman 		return (EFAULT);
530*5c4a5fe1SAndy Fiddaman 	}
531*5c4a5fe1SAndy Fiddaman 
532*5c4a5fe1SAndy Fiddaman 	table->data = reallocf(table->data, table->len + len);
533*5c4a5fe1SAndy Fiddaman 	if (table->data == NULL) {
534*5c4a5fe1SAndy Fiddaman 		warnx("%s: failed to realloc table to length 0x%8x", __func__,
535*5c4a5fe1SAndy Fiddaman 		    table->len + len);
536*5c4a5fe1SAndy Fiddaman 		table->len = 0;
537*5c4a5fe1SAndy Fiddaman 		return (ENOMEM);
538*5c4a5fe1SAndy Fiddaman 	}
539*5c4a5fe1SAndy Fiddaman 
540*5c4a5fe1SAndy Fiddaman 	end = (uint8_t *)table->data + table->len;
541*5c4a5fe1SAndy Fiddaman 	table->len += len;
542*5c4a5fe1SAndy Fiddaman 
543*5c4a5fe1SAndy Fiddaman 	memcpy(end, bytes, len);
544*5c4a5fe1SAndy Fiddaman 
545*5c4a5fe1SAndy Fiddaman 	return (0);
546*5c4a5fe1SAndy Fiddaman }
547*5c4a5fe1SAndy Fiddaman 
548*5c4a5fe1SAndy Fiddaman int
549*5c4a5fe1SAndy Fiddaman basl_table_append_checksum(struct basl_table *const table, const uint32_t start,
550*5c4a5fe1SAndy Fiddaman     const uint32_t len)
551*5c4a5fe1SAndy Fiddaman {
552*5c4a5fe1SAndy Fiddaman 	assert(table != NULL);
553*5c4a5fe1SAndy Fiddaman 
554*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_add_checksum(table, table->len, start, len));
555*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_append_int(table, 0, 1));
556*5c4a5fe1SAndy Fiddaman 
557*5c4a5fe1SAndy Fiddaman 	return (0);
558*5c4a5fe1SAndy Fiddaman }
559*5c4a5fe1SAndy Fiddaman 
560*5c4a5fe1SAndy Fiddaman int
561*5c4a5fe1SAndy Fiddaman basl_table_append_content(struct basl_table *table, void *data, uint32_t len)
562*5c4a5fe1SAndy Fiddaman {
563*5c4a5fe1SAndy Fiddaman 	assert(data != NULL);
564*5c4a5fe1SAndy Fiddaman 	assert(len >= sizeof(ACPI_TABLE_HEADER));
565*5c4a5fe1SAndy Fiddaman 
566*5c4a5fe1SAndy Fiddaman 	return (basl_table_append_bytes(table,
567*5c4a5fe1SAndy Fiddaman 	    (void *)((uintptr_t)(data) + sizeof(ACPI_TABLE_HEADER)),
568*5c4a5fe1SAndy Fiddaman 	    len - sizeof(ACPI_TABLE_HEADER)));
569*5c4a5fe1SAndy Fiddaman }
570*5c4a5fe1SAndy Fiddaman 
571*5c4a5fe1SAndy Fiddaman int
572*5c4a5fe1SAndy Fiddaman basl_table_append_fwcfg(struct basl_table *const table,
573*5c4a5fe1SAndy Fiddaman     const uint8_t *fwcfg_name, const uint32_t alignment, const uint8_t size)
574*5c4a5fe1SAndy Fiddaman {
575*5c4a5fe1SAndy Fiddaman 	assert(table != NULL);
576*5c4a5fe1SAndy Fiddaman 	assert(fwcfg_name != NULL);
577*5c4a5fe1SAndy Fiddaman 	assert(size <= sizeof(uint64_t));
578*5c4a5fe1SAndy Fiddaman 
579*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(qemu_loader_alloc(basl_loader, fwcfg_name, alignment,
580*5c4a5fe1SAndy Fiddaman 	    QEMU_LOADER_ALLOC_HIGH));
581*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(qemu_loader_add_pointer(basl_loader, table->fwcfg_name,
582*5c4a5fe1SAndy Fiddaman 	    fwcfg_name, table->len, size));
583*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_append_int(table, 0, size));
584*5c4a5fe1SAndy Fiddaman 
585*5c4a5fe1SAndy Fiddaman 	return (0);
586*5c4a5fe1SAndy Fiddaman }
587*5c4a5fe1SAndy Fiddaman 
588*5c4a5fe1SAndy Fiddaman int
589*5c4a5fe1SAndy Fiddaman basl_table_append_gas(struct basl_table *const table, const uint8_t space_id,
590*5c4a5fe1SAndy Fiddaman     const uint8_t bit_width, const uint8_t bit_offset,
591*5c4a5fe1SAndy Fiddaman     const uint8_t access_width, const uint64_t address)
592*5c4a5fe1SAndy Fiddaman {
593*5c4a5fe1SAndy Fiddaman 	ACPI_GENERIC_ADDRESS gas_le = {
594*5c4a5fe1SAndy Fiddaman 		.SpaceId = space_id,
595*5c4a5fe1SAndy Fiddaman 		.BitWidth = bit_width,
596*5c4a5fe1SAndy Fiddaman 		.BitOffset = bit_offset,
597*5c4a5fe1SAndy Fiddaman 		.AccessWidth = access_width,
598*5c4a5fe1SAndy Fiddaman 		.Address = htole64(address),
599*5c4a5fe1SAndy Fiddaman 	};
600*5c4a5fe1SAndy Fiddaman 
601*5c4a5fe1SAndy Fiddaman 	return (basl_table_append_bytes(table, &gas_le, sizeof(gas_le)));
602*5c4a5fe1SAndy Fiddaman }
603*5c4a5fe1SAndy Fiddaman 
604*5c4a5fe1SAndy Fiddaman int
605*5c4a5fe1SAndy Fiddaman basl_table_append_header(struct basl_table *const table,
606*5c4a5fe1SAndy Fiddaman     const uint8_t signature[ACPI_NAMESEG_SIZE], const uint8_t revision,
607*5c4a5fe1SAndy Fiddaman     const uint32_t oem_revision)
608*5c4a5fe1SAndy Fiddaman {
609*5c4a5fe1SAndy Fiddaman 	ACPI_TABLE_HEADER header_le;
610*5c4a5fe1SAndy Fiddaman 	/* + 1 is required for the null terminator */
611*5c4a5fe1SAndy Fiddaman 	char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1];
612*5c4a5fe1SAndy Fiddaman 
613*5c4a5fe1SAndy Fiddaman 	assert(table != NULL);
614*5c4a5fe1SAndy Fiddaman 	assert(table->len == 0);
615*5c4a5fe1SAndy Fiddaman 
616*5c4a5fe1SAndy Fiddaman 	memcpy(header_le.Signature, signature, ACPI_NAMESEG_SIZE);
617*5c4a5fe1SAndy Fiddaman 	header_le.Length = 0; /* patched by basl_finish */
618*5c4a5fe1SAndy Fiddaman 	header_le.Revision = revision;
619*5c4a5fe1SAndy Fiddaman 	header_le.Checksum = 0; /* patched by basl_finish */
620*5c4a5fe1SAndy Fiddaman 	memcpy(header_le.OemId, "BHYVE ", ACPI_OEM_ID_SIZE);
621*5c4a5fe1SAndy Fiddaman 	snprintf(oem_table_id, ACPI_OEM_TABLE_ID_SIZE, "BV%.4s  ", signature);
622*5c4a5fe1SAndy Fiddaman 	memcpy(header_le.OemTableId, oem_table_id,
623*5c4a5fe1SAndy Fiddaman 	    sizeof(header_le.OemTableId));
624*5c4a5fe1SAndy Fiddaman 	header_le.OemRevision = htole32(oem_revision);
625*5c4a5fe1SAndy Fiddaman 	memcpy(header_le.AslCompilerId, "BASL", ACPI_NAMESEG_SIZE);
626*5c4a5fe1SAndy Fiddaman 	header_le.AslCompilerRevision = htole32(0x20220504);
627*5c4a5fe1SAndy Fiddaman 
628*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(
629*5c4a5fe1SAndy Fiddaman 	    basl_table_append_bytes(table, &header_le, sizeof(header_le)));
630*5c4a5fe1SAndy Fiddaman 
631*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_add_length(table,
632*5c4a5fe1SAndy Fiddaman 	    offsetof(ACPI_TABLE_HEADER, Length), sizeof(header_le.Length)));
633*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_add_checksum(table,
634*5c4a5fe1SAndy Fiddaman 	    offsetof(ACPI_TABLE_HEADER, Checksum), 0,
635*5c4a5fe1SAndy Fiddaman 	    BASL_TABLE_CHECKSUM_LEN_FULL_TABLE));
636*5c4a5fe1SAndy Fiddaman 
637*5c4a5fe1SAndy Fiddaman 	return (0);
638*5c4a5fe1SAndy Fiddaman }
639*5c4a5fe1SAndy Fiddaman 
640*5c4a5fe1SAndy Fiddaman int
641*5c4a5fe1SAndy Fiddaman basl_table_append_int(struct basl_table *const table, const uint64_t val,
642*5c4a5fe1SAndy Fiddaman     const uint8_t size)
643*5c4a5fe1SAndy Fiddaman {
644*5c4a5fe1SAndy Fiddaman 	char buf[8];
645*5c4a5fe1SAndy Fiddaman 
646*5c4a5fe1SAndy Fiddaman 	assert(size <= sizeof(val));
647*5c4a5fe1SAndy Fiddaman 
648*5c4a5fe1SAndy Fiddaman 	basl_le_enc(buf, val, size);
649*5c4a5fe1SAndy Fiddaman 	return (basl_table_append_bytes(table, buf, size));
650*5c4a5fe1SAndy Fiddaman }
651*5c4a5fe1SAndy Fiddaman 
652*5c4a5fe1SAndy Fiddaman int
653*5c4a5fe1SAndy Fiddaman basl_table_append_length(struct basl_table *const table, const uint8_t size)
654*5c4a5fe1SAndy Fiddaman {
655*5c4a5fe1SAndy Fiddaman 	assert(table != NULL);
656*5c4a5fe1SAndy Fiddaman 	assert(size <= sizeof(table->len));
657*5c4a5fe1SAndy Fiddaman 
658*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_add_length(table, table->len, size));
659*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_append_int(table, 0, size));
660*5c4a5fe1SAndy Fiddaman 
661*5c4a5fe1SAndy Fiddaman 	return (0);
662*5c4a5fe1SAndy Fiddaman }
663*5c4a5fe1SAndy Fiddaman 
664*5c4a5fe1SAndy Fiddaman int
665*5c4a5fe1SAndy Fiddaman basl_table_append_pointer(struct basl_table *const table,
666*5c4a5fe1SAndy Fiddaman     const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint8_t size)
667*5c4a5fe1SAndy Fiddaman {
668*5c4a5fe1SAndy Fiddaman 	assert(table != NULL);
669*5c4a5fe1SAndy Fiddaman 	assert(size == 4 || size == 8);
670*5c4a5fe1SAndy Fiddaman 
671*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_add_pointer(table, src_signature, table->len, size));
672*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_append_int(table, 0, size));
673*5c4a5fe1SAndy Fiddaman 
674*5c4a5fe1SAndy Fiddaman 	return (0);
675*5c4a5fe1SAndy Fiddaman }
676*5c4a5fe1SAndy Fiddaman 
677*5c4a5fe1SAndy Fiddaman int
678*5c4a5fe1SAndy Fiddaman basl_table_create(struct basl_table **const table, struct vmctx *ctx,
679*5c4a5fe1SAndy Fiddaman     const uint8_t *const name, const uint32_t alignment)
680*5c4a5fe1SAndy Fiddaman {
681*5c4a5fe1SAndy Fiddaman 	struct basl_table *new_table;
682*5c4a5fe1SAndy Fiddaman 
683*5c4a5fe1SAndy Fiddaman 	assert(table != NULL);
684*5c4a5fe1SAndy Fiddaman 
685*5c4a5fe1SAndy Fiddaman 	new_table = calloc(1, sizeof(struct basl_table));
686*5c4a5fe1SAndy Fiddaman 	if (new_table == NULL) {
687*5c4a5fe1SAndy Fiddaman 		warnx("%s: failed to allocate table", __func__);
688*5c4a5fe1SAndy Fiddaman 		return (ENOMEM);
689*5c4a5fe1SAndy Fiddaman 	}
690*5c4a5fe1SAndy Fiddaman 
691*5c4a5fe1SAndy Fiddaman 	new_table->ctx = ctx;
692*5c4a5fe1SAndy Fiddaman 
693*5c4a5fe1SAndy Fiddaman #ifdef	__FreeBSD__
694*5c4a5fe1SAndy Fiddaman 	snprintf(new_table->fwcfg_name, sizeof(new_table->fwcfg_name),
695*5c4a5fe1SAndy Fiddaman 	    "etc/acpi/%s", name);
696*5c4a5fe1SAndy Fiddaman #else
697*5c4a5fe1SAndy Fiddaman 	snprintf((char *)new_table->fwcfg_name, sizeof (new_table->fwcfg_name),
698*5c4a5fe1SAndy Fiddaman 	    "etc/acpi/%s", name);
699*5c4a5fe1SAndy Fiddaman #endif
700*5c4a5fe1SAndy Fiddaman 
701*5c4a5fe1SAndy Fiddaman 	new_table->alignment = alignment;
702*5c4a5fe1SAndy Fiddaman 
703*5c4a5fe1SAndy Fiddaman 	STAILQ_INIT(&new_table->checksums);
704*5c4a5fe1SAndy Fiddaman 	STAILQ_INIT(&new_table->lengths);
705*5c4a5fe1SAndy Fiddaman 	STAILQ_INIT(&new_table->pointers);
706*5c4a5fe1SAndy Fiddaman 
707*5c4a5fe1SAndy Fiddaman 	STAILQ_INSERT_TAIL(&basl_tables, new_table, chain);
708*5c4a5fe1SAndy Fiddaman 
709*5c4a5fe1SAndy Fiddaman 	*table = new_table;
710*5c4a5fe1SAndy Fiddaman 
711*5c4a5fe1SAndy Fiddaman 	return (0);
712*5c4a5fe1SAndy Fiddaman }
713*5c4a5fe1SAndy Fiddaman 
714*5c4a5fe1SAndy Fiddaman int
715*5c4a5fe1SAndy Fiddaman basl_table_register_to_rsdt(struct basl_table *table)
716*5c4a5fe1SAndy Fiddaman {
717*5c4a5fe1SAndy Fiddaman 	const ACPI_TABLE_HEADER *header;
718*5c4a5fe1SAndy Fiddaman 
719*5c4a5fe1SAndy Fiddaman 	assert(table != NULL);
720*5c4a5fe1SAndy Fiddaman 
721*5c4a5fe1SAndy Fiddaman 	header = (const ACPI_TABLE_HEADER *)table->data;
722*5c4a5fe1SAndy Fiddaman 
723*5c4a5fe1SAndy Fiddaman #ifdef	__FreeBSD__
724*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_append_pointer(rsdt, header->Signature,
725*5c4a5fe1SAndy Fiddaman 	    ACPI_RSDT_ENTRY_SIZE));
726*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_append_pointer(xsdt, header->Signature,
727*5c4a5fe1SAndy Fiddaman 	    ACPI_XSDT_ENTRY_SIZE));
728*5c4a5fe1SAndy Fiddaman #else
729*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_append_pointer(rsdt, (uint8_t *)header->Signature,
730*5c4a5fe1SAndy Fiddaman 	    ACPI_RSDT_ENTRY_SIZE));
731*5c4a5fe1SAndy Fiddaman 	BASL_EXEC(basl_table_append_pointer(xsdt, (uint8_t *)header->Signature,
732*5c4a5fe1SAndy Fiddaman 	    ACPI_XSDT_ENTRY_SIZE));
733*5c4a5fe1SAndy Fiddaman #endif
734*5c4a5fe1SAndy Fiddaman 
735*5c4a5fe1SAndy Fiddaman 	return (0);
736*5c4a5fe1SAndy Fiddaman }
737