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