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