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 } 680