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