Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/bhyve/basl.c
104958 views
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
704