]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/basl.c
zfs: merge openzfs/zfs@95f71c019
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / basl.c
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 }