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