]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/basl.c
OpenSSL: update to 3.0.10
[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 "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 }