xref: /freebsd/usr.sbin/bhyve/basl.c (revision aa1207ea4f030c50a91bca6a3df950ca25113d5a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
5  */
6 
7 #include <sys/param.h>
8 #include <sys/endian.h>
9 #include <sys/errno.h>
10 #include <sys/queue.h>
11 #include <sys/stat.h>
12 
13 #include <machine/vmm.h>
14 
15 #include <assert.h>
16 #include <err.h>
17 #include <libutil.h>
18 #include <stddef.h>
19 #include <stdio.h>
20 #include <vmmapi.h>
21 
22 #include "basl.h"
23 #include "config.h"
24 #include "qemu_loader.h"
25 
26 struct basl_table_checksum {
27 	STAILQ_ENTRY(basl_table_checksum) chain;
28 	uint32_t off;
29 	uint32_t start;
30 	uint32_t len;
31 };
32 
33 struct basl_table_length {
34 	STAILQ_ENTRY(basl_table_length) chain;
35 	uint32_t off;
36 	uint8_t size;
37 };
38 
39 struct basl_table_pointer {
40 	STAILQ_ENTRY(basl_table_pointer) chain;
41 	uint8_t src_signature[ACPI_NAMESEG_SIZE];
42 	uint32_t off;
43 	uint8_t size;
44 };
45 
46 struct basl_table {
47 	STAILQ_ENTRY(basl_table) chain;
48 	struct vmctx *ctx;
49 	uint8_t fwcfg_name[QEMU_FWCFG_MAX_NAME];
50 	void *data;
51 	uint32_t len;
52 	uint32_t off;
53 	uint32_t alignment;
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;
57 };
58 static STAILQ_HEAD(basl_table_list, basl_table) basl_tables = STAILQ_HEAD_INITIALIZER(
59     basl_tables);
60 
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;
65 
66 static __inline uint64_t
67 basl_le_dec(void *pp, size_t len)
68 {
69 	assert(len <= 8);
70 
71 	switch (len) {
72 	case 1:
73 		return ((uint8_t *)pp)[0];
74 	case 2:
75 		return le16dec(pp);
76 	case 4:
77 		return le32dec(pp);
78 	case 8:
79 		return le64dec(pp);
80 	}
81 
82 	return 0;
83 }
84 
85 static __inline void
86 basl_le_enc(void *pp, uint64_t val, size_t len)
87 {
88 	char buf[8];
89 
90 	assert(len <= 8);
91 
92 	le64enc(buf, val);
93 	memcpy(pp, buf, len);
94 }
95 
96 static int
97 basl_dump_table(const struct basl_table *const table, const bool mem)
98 {
99 	const ACPI_TABLE_HEADER *const header = table->data;
100 	const uint8_t *data;
101 
102 	if (!mem) {
103 		data = table->data;
104 	} else {
105 		data = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off,
106 		    table->len);
107 		if (data == NULL) {
108 			return (ENOMEM);
109 		}
110 	}
111 
112 	printf("%.4s @ %8x (%s)\n", header->Signature,
113 	    BHYVE_ACPI_BASE + table->off, mem ? "Memory" : "FwCfg");
114 	hexdump(data, table->len, NULL, 0);
115 
116 	return (0);
117 }
118 
119 static int __unused
120 basl_dump(const bool mem)
121 {
122 	struct basl_table *table;
123 
124 	STAILQ_FOREACH(table, &basl_tables, chain) {
125 		BASL_EXEC(basl_dump_table(table, mem));
126 	}
127 
128 	return (0);
129 }
130 
131 void
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)
135 {
136 	assert(gas != NULL);
137 
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);
143 }
144 
145 static int
146 basl_finish_install_guest_tables(struct basl_table *const table, uint32_t *const off)
147 {
148 	void *gva;
149 
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);
155 		return (EFAULT);
156 	}
157 
158 	/* Cause guest BIOS to copy the ACPI table into guest memory. */
159 	BASL_EXEC(
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));
163 
164 	if (!load_into_memory) {
165 		return (0);
166 	}
167 
168 	/*
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
172 	 * tables always.
173 	 */
174 	gva = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off, table->len);
175 	if (gva == NULL) {
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);
179 		return (ENOMEM);
180 	}
181 	memcpy(gva, table->data, table->len);
182 
183 	return (0);
184 }
185 
186 static int
187 basl_finish_patch_checksums(struct basl_table *const table)
188 {
189 	struct basl_table_checksum *checksum;
190 
191 	STAILQ_FOREACH(checksum, &table->checksums, chain) {
192 		uint8_t *gva, *checksum_gva;
193 		uint64_t gpa;
194 		uint32_t len;
195 		uint8_t sum;
196 
197 		len = checksum->len;
198 		if (len == BASL_TABLE_CHECKSUM_LEN_FULL_TABLE) {
199 			len = table->len;
200 		}
201 
202 		assert(checksum->off < table->len);
203 		assert(checksum->start < table->len);
204 		assert(checksum->start + len <= table->len);
205 
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));
209 
210 		if (!load_into_memory) {
211 			continue;
212 		}
213 
214 		/*
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.
219 		 */
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);
225 			return (EFAULT);
226 		}
227 
228 		gva = vm_map_gpa(table->ctx, gpa, len);
229 		if (gva == NULL) {
230 			warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
231 			    __func__, gpa, gpa + len);
232 			return (ENOMEM);
233 		}
234 
235 		checksum_gva = gva + checksum->off;
236 		if (checksum_gva < gva) {
237 			warnx("%s: invalid checksum offset 0x%8x", __func__,
238 			    checksum->off);
239 			return (EFAULT);
240 		}
241 
242 		sum = 0;
243 		for (uint32_t i = 0; i < len; ++i) {
244 			sum += *(gva + i);
245 		}
246 		*checksum_gva = -sum;
247 	}
248 
249 	return (0);
250 }
251 
252 static struct basl_table *
253 basl_get_table_by_signature(const uint8_t signature[ACPI_NAMESEG_SIZE])
254 {
255 	struct basl_table *table;
256 
257 	STAILQ_FOREACH(table, &basl_tables, chain) {
258 		const ACPI_TABLE_HEADER *const header =
259 		    (const ACPI_TABLE_HEADER *)table->data;
260 
261 		if (strncmp(header->Signature, signature,
262 			sizeof(header->Signature)) == 0) {
263 			return (table);
264 		}
265 	}
266 
267 	warnx("%s: %.4s not found", __func__, signature);
268 	return (NULL);
269 }
270 
271 static int
272 basl_finish_patch_pointers(struct basl_table *const table)
273 {
274 	struct basl_table_pointer *pointer;
275 
276 	STAILQ_FOREACH(pointer, &table->pointers, chain) {
277 		const struct basl_table *src_table;
278 		uint8_t *gva;
279 		uint64_t gpa, val;
280 
281 		assert(pointer->off < table->len);
282 		assert(pointer->off + pointer->size <= table->len);
283 
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);
288 			return (EFAULT);
289 		}
290 
291 		/* Cause guest BIOS to patch the pointer. */
292 		BASL_EXEC(
293 		    qemu_loader_add_pointer(basl_loader, table->fwcfg_name,
294 			src_table->fwcfg_name, pointer->off, pointer->size));
295 
296 		if (!load_into_memory) {
297 			continue;
298 		}
299 
300 		/*
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.
305 		 */
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);
310 			return (EFAULT);
311 		}
312 
313 		gva = vm_map_gpa(table->ctx, gpa, table->len);
314 		if (gva == NULL) {
315 			warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
316 			    __func__, gpa, gpa + table->len);
317 			return (ENOMEM);
318 		}
319 
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);
323 	}
324 
325 	return (0);
326 }
327 
328 static int
329 basl_finish_set_length(struct basl_table *const table)
330 {
331 	struct basl_table_length *length;
332 
333 	STAILQ_FOREACH(length, &table->lengths, chain) {
334 		assert(length->off < table->len);
335 		assert(length->off + length->size <= table->len);
336 
337 		basl_le_enc((uint8_t *)table->data + length->off, table->len,
338 		    length->size);
339 	}
340 
341 	return (0);
342 }
343 
344 int
345 basl_finish(void)
346 {
347 	struct basl_table *table;
348 	uint32_t off = 0;
349 
350 	if (STAILQ_EMPTY(&basl_tables)) {
351 		warnx("%s: no ACPI tables found", __func__);
352 		return (EINVAL);
353 	}
354 
355 	/*
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.
360 	 */
361 	load_into_memory = get_config_bool_default("acpi_tables_in_memory",
362 	    true);
363 
364 	/*
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
367 	 * patches them.
368 	 */
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));
372 	}
373 	STAILQ_FOREACH(table, &basl_tables, chain) {
374 		BASL_EXEC(basl_finish_patch_pointers(table));
375 
376 		/*
377 		 * Calculate the checksum as last step!
378 		 */
379 		BASL_EXEC(basl_finish_patch_checksums(table));
380 	}
381 	BASL_EXEC(qemu_loader_finish(basl_loader));
382 
383 	return (0);
384 }
385 
386 static int
387 basl_init_rsdt(struct vmctx *const ctx)
388 {
389 	BASL_EXEC(
390 	    basl_table_create(&rsdt, ctx, ACPI_SIG_RSDT, BASL_TABLE_ALIGNMENT));
391 
392 	/* Header */
393 	BASL_EXEC(basl_table_append_header(rsdt, ACPI_SIG_RSDT, 1, 1));
394 	/* Pointers (added by basl_table_register_to_rsdt) */
395 
396 	return (0);
397 }
398 
399 static int
400 basl_init_xsdt(struct vmctx *const ctx)
401 {
402 	BASL_EXEC(
403 	    basl_table_create(&xsdt, ctx, ACPI_SIG_XSDT, BASL_TABLE_ALIGNMENT));
404 
405 	/* Header */
406 	BASL_EXEC(basl_table_append_header(xsdt, ACPI_SIG_XSDT, 1, 1));
407 	/* Pointers (added by basl_table_register_to_rsdt) */
408 
409 	return (0);
410 }
411 
412 int
413 basl_init(struct vmctx *const ctx)
414 {
415 	BASL_EXEC(basl_init_rsdt(ctx));
416 	BASL_EXEC(basl_init_xsdt(ctx));
417 	BASL_EXEC(
418 	    qemu_loader_create(&basl_loader, QEMU_FWCFG_FILE_TABLE_LOADER));
419 
420 	return (0);
421 }
422 
423 int
424 basl_table_add_checksum(struct basl_table *const table, const uint32_t off,
425     const uint32_t start, const uint32_t len)
426 {
427 	struct basl_table_checksum *checksum;
428 
429 	assert(table != NULL);
430 
431 	checksum = calloc(1, sizeof(struct basl_table_checksum));
432 	if (checksum == NULL) {
433 		warnx("%s: failed to allocate checksum", __func__);
434 		return (ENOMEM);
435 	}
436 
437 	checksum->off = off;
438 	checksum->start = start;
439 	checksum->len = len;
440 
441 	STAILQ_INSERT_TAIL(&table->checksums, checksum, chain);
442 
443 	return (0);
444 }
445 
446 int
447 basl_table_add_length(struct basl_table *const table, const uint32_t off,
448     const uint8_t size)
449 {
450 	struct basl_table_length *length;
451 
452 	assert(table != NULL);
453 	assert(size == 4 || size == 8);
454 
455 	length = calloc(1, sizeof(struct basl_table_length));
456 	if (length == NULL) {
457 		warnx("%s: failed to allocate length", __func__);
458 		return (ENOMEM);
459 	}
460 
461 	length->off = off;
462 	length->size = size;
463 
464 	STAILQ_INSERT_TAIL(&table->lengths, length, chain);
465 
466 	return (0);
467 }
468 
469 int
470 basl_table_add_pointer(struct basl_table *const table,
471     const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint32_t off,
472     const uint8_t size)
473 {
474 	struct basl_table_pointer *pointer;
475 
476 	assert(table != NULL);
477 	assert(size == 4 || size == 8);
478 
479 	pointer = calloc(1, sizeof(struct basl_table_pointer));
480 	if (pointer == NULL) {
481 		warnx("%s: failed to allocate pointer", __func__);
482 		return (ENOMEM);
483 	}
484 
485 	memcpy(pointer->src_signature, src_signature,
486 	    sizeof(pointer->src_signature));
487 	pointer->off = off;
488 	pointer->size = size;
489 
490 	STAILQ_INSERT_TAIL(&table->pointers, pointer, chain);
491 
492 	return (0);
493 }
494 
495 int
496 basl_table_append_bytes(struct basl_table *const table, const void *const bytes,
497     const uint32_t len)
498 {
499 	void *end;
500 
501 	assert(table != NULL);
502 	assert(bytes != NULL);
503 
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);
507 		return (EFAULT);
508 	}
509 
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__,
513 		    table->len + len);
514 		table->len = 0;
515 		return (ENOMEM);
516 	}
517 
518 	end = (uint8_t *)table->data + table->len;
519 	table->len += len;
520 
521 	memcpy(end, bytes, len);
522 
523 	return (0);
524 }
525 
526 int
527 basl_table_append_checksum(struct basl_table *const table, const uint32_t start,
528     const uint32_t len)
529 {
530 	assert(table != NULL);
531 
532 	BASL_EXEC(basl_table_add_checksum(table, table->len, start, len));
533 	BASL_EXEC(basl_table_append_int(table, 0, 1));
534 
535 	return (0);
536 }
537 
538 int
539 basl_table_append_content(struct basl_table *table, void *data, uint32_t len)
540 {
541 	assert(data != NULL);
542 	assert(len >= sizeof(ACPI_TABLE_HEADER));
543 
544 	return (basl_table_append_bytes(table,
545 	    (void *)((uintptr_t)(data) + sizeof(ACPI_TABLE_HEADER)),
546 	    len - sizeof(ACPI_TABLE_HEADER)));
547 }
548 
549 int
550 basl_table_append_fwcfg(struct basl_table *const table,
551     const uint8_t *fwcfg_name, const uint32_t alignment, const uint8_t size)
552 {
553 	assert(table != NULL);
554 	assert(fwcfg_name != NULL);
555 	assert(size <= sizeof(uint64_t));
556 
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));
562 
563 	return (0);
564 }
565 
566 int
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)
570 {
571 	ACPI_GENERIC_ADDRESS gas_le = {
572 		.SpaceId = space_id,
573 		.BitWidth = bit_width,
574 		.BitOffset = bit_offset,
575 		.AccessWidth = access_width,
576 		.Address = htole64(address),
577 	};
578 
579 	return (basl_table_append_bytes(table, &gas_le, sizeof(gas_le)));
580 }
581 
582 int
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)
586 {
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];
590 
591 	assert(table != NULL);
592 	assert(table->len == 0);
593 
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);
605 
606 	BASL_EXEC(
607 	    basl_table_append_bytes(table, &header_le, sizeof(header_le)));
608 
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));
614 
615 	return (0);
616 }
617 
618 int
619 basl_table_append_int(struct basl_table *const table, const uint64_t val,
620     const uint8_t size)
621 {
622 	char buf[8];
623 
624 	assert(size <= sizeof(val));
625 
626 	basl_le_enc(buf, val, size);
627 	return (basl_table_append_bytes(table, buf, size));
628 }
629 
630 int
631 basl_table_append_length(struct basl_table *const table, const uint8_t size)
632 {
633 	assert(table != NULL);
634 	assert(size <= sizeof(table->len));
635 
636 	BASL_EXEC(basl_table_add_length(table, table->len, size));
637 	BASL_EXEC(basl_table_append_int(table, 0, size));
638 
639 	return (0);
640 }
641 
642 int
643 basl_table_append_pointer(struct basl_table *const table,
644     const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint8_t size)
645 {
646 	assert(table != NULL);
647 	assert(size == 4 || size == 8);
648 
649 	BASL_EXEC(basl_table_add_pointer(table, src_signature, table->len, size));
650 	BASL_EXEC(basl_table_append_int(table, 0, size));
651 
652 	return (0);
653 }
654 
655 int
656 basl_table_create(struct basl_table **const table, struct vmctx *ctx,
657     const uint8_t *const name, const uint32_t alignment)
658 {
659 	struct basl_table *new_table;
660 
661 	assert(table != NULL);
662 
663 	new_table = calloc(1, sizeof(struct basl_table));
664 	if (new_table == NULL) {
665 		warnx("%s: failed to allocate table", __func__);
666 		return (ENOMEM);
667 	}
668 
669 	new_table->ctx = ctx;
670 
671 	snprintf(new_table->fwcfg_name, sizeof(new_table->fwcfg_name),
672 	    "etc/acpi/%s", name);
673 
674 	new_table->alignment = alignment;
675 
676 	STAILQ_INIT(&new_table->checksums);
677 	STAILQ_INIT(&new_table->lengths);
678 	STAILQ_INIT(&new_table->pointers);
679 
680 	STAILQ_INSERT_TAIL(&basl_tables, new_table, chain);
681 
682 	*table = new_table;
683 
684 	return (0);
685 }
686 
687 int
688 basl_table_register_to_rsdt(struct basl_table *table)
689 {
690 	const ACPI_TABLE_HEADER *header;
691 
692 	assert(table != NULL);
693 
694 	header = (const ACPI_TABLE_HEADER *)table->data;
695 
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));
700 
701 	return (0);
702 }
703