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