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 #ifndef __FreeBSD__ 23 #include "hexdump.h" 24 #endif 25 26 #include "basl.h" 27 #include "config.h" 28 #include "qemu_loader.h" 29 30 struct basl_table_checksum { 31 STAILQ_ENTRY(basl_table_checksum) chain; 32 uint32_t off; 33 uint32_t start; 34 uint32_t len; 35 }; 36 37 struct basl_table_length { 38 STAILQ_ENTRY(basl_table_length) chain; 39 uint32_t off; 40 uint8_t size; 41 }; 42 43 struct basl_table_pointer { 44 STAILQ_ENTRY(basl_table_pointer) chain; 45 uint8_t src_signature[ACPI_NAMESEG_SIZE]; 46 uint32_t off; 47 uint8_t size; 48 }; 49 50 struct basl_table { 51 STAILQ_ENTRY(basl_table) chain; 52 struct vmctx *ctx; 53 uint8_t fwcfg_name[QEMU_FWCFG_MAX_NAME]; 54 void *data; 55 uint32_t len; 56 uint32_t off; 57 uint32_t alignment; 58 STAILQ_HEAD(basl_table_checksum_list, basl_table_checksum) checksums; 59 STAILQ_HEAD(basl_table_length_list, basl_table_length) lengths; 60 STAILQ_HEAD(basl_table_pointer_list, basl_table_pointer) pointers; 61 }; 62 static STAILQ_HEAD(basl_table_list, basl_table) basl_tables = STAILQ_HEAD_INITIALIZER( 63 basl_tables); 64 65 static struct qemu_loader *basl_loader; 66 static struct basl_table *rsdt; 67 static struct basl_table *xsdt; 68 static bool load_into_memory; 69 70 static __inline uint64_t 71 basl_le_dec(void *pp, size_t len) 72 { 73 assert(len <= 8); 74 75 switch (len) { 76 case 1: 77 return ((uint8_t *)pp)[0]; 78 case 2: 79 return le16dec(pp); 80 case 4: 81 return le32dec(pp); 82 case 8: 83 return le64dec(pp); 84 } 85 86 return 0; 87 } 88 89 static __inline void 90 basl_le_enc(void *pp, uint64_t val, size_t len) 91 { 92 char buf[8]; 93 94 assert(len <= 8); 95 96 le64enc(buf, val); 97 memcpy(pp, buf, len); 98 } 99 100 static int 101 basl_dump_table(const struct basl_table *const table, const bool mem) 102 { 103 const ACPI_TABLE_HEADER *const header = table->data; 104 const uint8_t *data; 105 106 if (!mem) { 107 data = table->data; 108 } else { 109 data = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off, 110 table->len); 111 if (data == NULL) { 112 return (ENOMEM); 113 } 114 } 115 116 printf("%.4s @ %8x (%s)\n", header->Signature, 117 BHYVE_ACPI_BASE + table->off, mem ? "Memory" : "FwCfg"); 118 hexdump(data, table->len, NULL, 0); 119 120 return (0); 121 } 122 123 static int __unused 124 basl_dump(const bool mem) 125 { 126 struct basl_table *table; 127 128 STAILQ_FOREACH(table, &basl_tables, chain) { 129 BASL_EXEC(basl_dump_table(table, mem)); 130 } 131 132 return (0); 133 } 134 135 void 136 basl_fill_gas(ACPI_GENERIC_ADDRESS *const gas, const uint8_t space_id, 137 const uint8_t bit_width, const uint8_t bit_offset, 138 const uint8_t access_width, const uint64_t address) 139 { 140 assert(gas != NULL); 141 142 gas->SpaceId = space_id; 143 gas->BitWidth = bit_width; 144 gas->BitOffset = bit_offset; 145 gas->AccessWidth = access_width; 146 gas->Address = htole64(address); 147 } 148 149 static int 150 basl_finish_install_guest_tables(struct basl_table *const table, uint32_t *const off) 151 { 152 void *gva; 153 154 table->off = roundup2(*off, table->alignment); 155 *off = table->off + table->len; 156 if (*off <= table->off) { 157 warnx("%s: invalid table length 0x%8x @ offset 0x%8x", __func__, 158 table->len, table->off); 159 return (EFAULT); 160 } 161 162 /* Cause guest BIOS to copy the ACPI table into guest memory. */ 163 #ifdef __FreeBSD__ 164 BASL_EXEC( 165 qemu_fwcfg_add_file(table->fwcfg_name, table->len, table->data)); 166 #else 167 BASL_EXEC( 168 qemu_fwcfg_add_file((const char *)table->fwcfg_name, 169 table->len, table->data)); 170 #endif 171 BASL_EXEC(qemu_loader_alloc(basl_loader, table->fwcfg_name, 172 table->alignment, QEMU_LOADER_ALLOC_HIGH)); 173 174 if (!load_into_memory) { 175 return (0); 176 } 177 178 /* 179 * Install ACPI tables directly in guest memory for use by guests which 180 * do not boot via EFI. EFI ROMs provide a pointer to the firmware 181 * generated ACPI tables instead, but it doesn't hurt to install the 182 * tables always. 183 */ 184 gva = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off, table->len); 185 if (gva == NULL) { 186 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]", __func__, 187 (uint64_t)BHYVE_ACPI_BASE + table->off, 188 (uint64_t)BHYVE_ACPI_BASE + table->off + table->len); 189 return (ENOMEM); 190 } 191 memcpy(gva, table->data, table->len); 192 193 return (0); 194 } 195 196 static int 197 basl_finish_patch_checksums(struct basl_table *const table) 198 { 199 struct basl_table_checksum *checksum; 200 201 STAILQ_FOREACH(checksum, &table->checksums, chain) { 202 uint8_t *gva, *checksum_gva; 203 uint64_t gpa; 204 uint32_t len; 205 uint8_t sum; 206 207 len = checksum->len; 208 if (len == BASL_TABLE_CHECKSUM_LEN_FULL_TABLE) { 209 len = table->len; 210 } 211 212 assert(checksum->off < table->len); 213 assert(checksum->start < table->len); 214 assert(checksum->start + len <= table->len); 215 216 /* Cause guest BIOS to patch the checksum. */ 217 BASL_EXEC(qemu_loader_add_checksum(basl_loader, 218 table->fwcfg_name, checksum->off, checksum->start, len)); 219 220 if (!load_into_memory) { 221 continue; 222 } 223 224 /* 225 * Install ACPI tables directly in guest memory for use by 226 * guests which do not boot via EFI. EFI ROMs provide a pointer 227 * to the firmware generated ACPI tables instead, but it doesn't 228 * hurt to install the tables always. 229 */ 230 gpa = BHYVE_ACPI_BASE + table->off + checksum->start; 231 if ((gpa < BHYVE_ACPI_BASE) || 232 (gpa < BHYVE_ACPI_BASE + table->off)) { 233 warnx("%s: invalid gpa (off 0x%8x start 0x%8x)", 234 __func__, table->off, checksum->start); 235 return (EFAULT); 236 } 237 238 gva = vm_map_gpa(table->ctx, gpa, len); 239 if (gva == NULL) { 240 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]", 241 __func__, gpa, gpa + len); 242 return (ENOMEM); 243 } 244 245 checksum_gva = gva + checksum->off; 246 if (checksum_gva < gva) { 247 warnx("%s: invalid checksum offset 0x%8x", __func__, 248 checksum->off); 249 return (EFAULT); 250 } 251 252 sum = 0; 253 for (uint32_t i = 0; i < len; ++i) { 254 sum += *(gva + i); 255 } 256 *checksum_gva = -sum; 257 } 258 259 return (0); 260 } 261 262 static struct basl_table * 263 basl_get_table_by_signature(const uint8_t signature[ACPI_NAMESEG_SIZE]) 264 { 265 struct basl_table *table; 266 267 STAILQ_FOREACH(table, &basl_tables, chain) { 268 const ACPI_TABLE_HEADER *const header = 269 (const ACPI_TABLE_HEADER *)table->data; 270 271 #ifdef __FreeBSD__ 272 if (strncmp(header->Signature, signature, 273 sizeof(header->Signature)) == 0) { 274 return (table); 275 #else 276 if (strncmp(header->Signature, (char *)signature, 277 sizeof(header->Signature)) == 0) { 278 return (table); 279 #endif 280 } 281 } 282 283 warnx("%s: %.4s not found", __func__, signature); 284 return (NULL); 285 } 286 287 static int 288 basl_finish_patch_pointers(struct basl_table *const table) 289 { 290 struct basl_table_pointer *pointer; 291 292 STAILQ_FOREACH(pointer, &table->pointers, chain) { 293 const struct basl_table *src_table; 294 uint8_t *gva; 295 uint64_t gpa, val; 296 297 assert(pointer->off < table->len); 298 assert(pointer->off + pointer->size <= table->len); 299 300 src_table = basl_get_table_by_signature(pointer->src_signature); 301 if (src_table == NULL) { 302 warnx("%s: could not find ACPI table %.4s", __func__, 303 pointer->src_signature); 304 return (EFAULT); 305 } 306 307 /* Cause guest BIOS to patch the pointer. */ 308 BASL_EXEC( 309 qemu_loader_add_pointer(basl_loader, table->fwcfg_name, 310 src_table->fwcfg_name, pointer->off, pointer->size)); 311 312 if (!load_into_memory) { 313 continue; 314 } 315 316 /* 317 * Install ACPI tables directly in guest memory for use by 318 * guests which do not boot via EFI. EFI ROMs provide a pointer 319 * to the firmware generated ACPI tables instead, but it doesn't 320 * hurt to install the tables always. 321 */ 322 gpa = BHYVE_ACPI_BASE + table->off; 323 if (gpa < BHYVE_ACPI_BASE) { 324 warnx("%s: table offset of 0x%8x is too large", 325 __func__, table->off); 326 return (EFAULT); 327 } 328 329 gva = vm_map_gpa(table->ctx, gpa, table->len); 330 if (gva == NULL) { 331 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]", 332 __func__, gpa, gpa + table->len); 333 return (ENOMEM); 334 } 335 336 val = basl_le_dec(gva + pointer->off, pointer->size); 337 val += BHYVE_ACPI_BASE + src_table->off; 338 basl_le_enc(gva + pointer->off, val, pointer->size); 339 } 340 341 return (0); 342 } 343 344 static int 345 basl_finish_set_length(struct basl_table *const table) 346 { 347 struct basl_table_length *length; 348 349 STAILQ_FOREACH(length, &table->lengths, chain) { 350 assert(length->off < table->len); 351 assert(length->off + length->size <= table->len); 352 353 basl_le_enc((uint8_t *)table->data + length->off, table->len, 354 length->size); 355 } 356 357 return (0); 358 } 359 360 int 361 basl_finish(void) 362 { 363 struct basl_table *table; 364 uint32_t off = 0; 365 366 if (STAILQ_EMPTY(&basl_tables)) { 367 warnx("%s: no ACPI tables found", __func__); 368 return (EINVAL); 369 } 370 371 /* 372 * If we install ACPI tables by FwCfg and by memory, Windows will use 373 * the tables from memory. This can cause issues when using advanced 374 * features like a TPM log because we aren't able to patch the memory 375 * tables accordingly. 376 */ 377 load_into_memory = get_config_bool_default("acpi_tables_in_memory", 378 true); 379 380 /* 381 * We have to install all tables before we can patch them. Therefore, 382 * use two loops. The first one installs all tables and the second one 383 * patches them. 384 */ 385 STAILQ_FOREACH(table, &basl_tables, chain) { 386 BASL_EXEC(basl_finish_set_length(table)); 387 BASL_EXEC(basl_finish_install_guest_tables(table, &off)); 388 } 389 STAILQ_FOREACH(table, &basl_tables, chain) { 390 BASL_EXEC(basl_finish_patch_pointers(table)); 391 392 /* 393 * Calculate the checksum as last step! 394 */ 395 BASL_EXEC(basl_finish_patch_checksums(table)); 396 } 397 BASL_EXEC(qemu_loader_finish(basl_loader)); 398 399 return (0); 400 } 401 402 static int 403 basl_init_rsdt(struct vmctx *const ctx) 404 { 405 BASL_EXEC( 406 basl_table_create(&rsdt, ctx, ACPI_SIG_RSDT, BASL_TABLE_ALIGNMENT)); 407 408 /* Header */ 409 BASL_EXEC(basl_table_append_header(rsdt, ACPI_SIG_RSDT, 1, 1)); 410 /* Pointers (added by basl_table_register_to_rsdt) */ 411 412 return (0); 413 } 414 415 static int 416 basl_init_xsdt(struct vmctx *const ctx) 417 { 418 BASL_EXEC( 419 basl_table_create(&xsdt, ctx, ACPI_SIG_XSDT, BASL_TABLE_ALIGNMENT)); 420 421 /* Header */ 422 BASL_EXEC(basl_table_append_header(xsdt, ACPI_SIG_XSDT, 1, 1)); 423 /* Pointers (added by basl_table_register_to_rsdt) */ 424 425 return (0); 426 } 427 428 int 429 basl_init(struct vmctx *const ctx) 430 { 431 BASL_EXEC(basl_init_rsdt(ctx)); 432 BASL_EXEC(basl_init_xsdt(ctx)); 433 #ifdef __FreeBSD__ 434 BASL_EXEC( 435 qemu_loader_create(&basl_loader, QEMU_FWCFG_FILE_TABLE_LOADER)); 436 #else 437 BASL_EXEC( 438 qemu_loader_create(&basl_loader, 439 (uint8_t *)QEMU_FWCFG_FILE_TABLE_LOADER)); 440 #endif 441 442 return (0); 443 } 444 445 int 446 basl_table_add_checksum(struct basl_table *const table, const uint32_t off, 447 const uint32_t start, const uint32_t len) 448 { 449 struct basl_table_checksum *checksum; 450 451 assert(table != NULL); 452 453 checksum = calloc(1, sizeof(struct basl_table_checksum)); 454 if (checksum == NULL) { 455 warnx("%s: failed to allocate checksum", __func__); 456 return (ENOMEM); 457 } 458 459 checksum->off = off; 460 checksum->start = start; 461 checksum->len = len; 462 463 STAILQ_INSERT_TAIL(&table->checksums, checksum, chain); 464 465 return (0); 466 } 467 468 int 469 basl_table_add_length(struct basl_table *const table, const uint32_t off, 470 const uint8_t size) 471 { 472 struct basl_table_length *length; 473 474 assert(table != NULL); 475 assert(size == 4 || size == 8); 476 477 length = calloc(1, sizeof(struct basl_table_length)); 478 if (length == NULL) { 479 warnx("%s: failed to allocate length", __func__); 480 return (ENOMEM); 481 } 482 483 length->off = off; 484 length->size = size; 485 486 STAILQ_INSERT_TAIL(&table->lengths, length, chain); 487 488 return (0); 489 } 490 491 int 492 basl_table_add_pointer(struct basl_table *const table, 493 const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint32_t off, 494 const uint8_t size) 495 { 496 struct basl_table_pointer *pointer; 497 498 assert(table != NULL); 499 assert(size == 4 || size == 8); 500 501 pointer = calloc(1, sizeof(struct basl_table_pointer)); 502 if (pointer == NULL) { 503 warnx("%s: failed to allocate pointer", __func__); 504 return (ENOMEM); 505 } 506 507 memcpy(pointer->src_signature, src_signature, 508 sizeof(pointer->src_signature)); 509 pointer->off = off; 510 pointer->size = size; 511 512 STAILQ_INSERT_TAIL(&table->pointers, pointer, chain); 513 514 return (0); 515 } 516 517 int 518 basl_table_append_bytes(struct basl_table *const table, const void *const bytes, 519 const uint32_t len) 520 { 521 void *end; 522 523 assert(table != NULL); 524 assert(bytes != NULL); 525 526 if (table->len + len <= table->len) { 527 warnx("%s: table too large (table->len 0x%8x len 0x%8x)", 528 __func__, table->len, len); 529 return (EFAULT); 530 } 531 532 table->data = reallocf(table->data, table->len + len); 533 if (table->data == NULL) { 534 warnx("%s: failed to realloc table to length 0x%8x", __func__, 535 table->len + len); 536 table->len = 0; 537 return (ENOMEM); 538 } 539 540 end = (uint8_t *)table->data + table->len; 541 table->len += len; 542 543 memcpy(end, bytes, len); 544 545 return (0); 546 } 547 548 int 549 basl_table_append_checksum(struct basl_table *const table, const uint32_t start, 550 const uint32_t len) 551 { 552 assert(table != NULL); 553 554 BASL_EXEC(basl_table_add_checksum(table, table->len, start, len)); 555 BASL_EXEC(basl_table_append_int(table, 0, 1)); 556 557 return (0); 558 } 559 560 int 561 basl_table_append_content(struct basl_table *table, void *data, uint32_t len) 562 { 563 assert(data != NULL); 564 assert(len >= sizeof(ACPI_TABLE_HEADER)); 565 566 return (basl_table_append_bytes(table, 567 (void *)((uintptr_t)(data) + sizeof(ACPI_TABLE_HEADER)), 568 len - sizeof(ACPI_TABLE_HEADER))); 569 } 570 571 int 572 basl_table_append_fwcfg(struct basl_table *const table, 573 const uint8_t *fwcfg_name, const uint32_t alignment, const uint8_t size) 574 { 575 assert(table != NULL); 576 assert(fwcfg_name != NULL); 577 assert(size <= sizeof(uint64_t)); 578 579 BASL_EXEC(qemu_loader_alloc(basl_loader, fwcfg_name, alignment, 580 QEMU_LOADER_ALLOC_HIGH)); 581 BASL_EXEC(qemu_loader_add_pointer(basl_loader, table->fwcfg_name, 582 fwcfg_name, table->len, size)); 583 BASL_EXEC(basl_table_append_int(table, 0, size)); 584 585 return (0); 586 } 587 588 int 589 basl_table_append_gas(struct basl_table *const table, const uint8_t space_id, 590 const uint8_t bit_width, const uint8_t bit_offset, 591 const uint8_t access_width, const uint64_t address) 592 { 593 ACPI_GENERIC_ADDRESS gas_le = { 594 .SpaceId = space_id, 595 .BitWidth = bit_width, 596 .BitOffset = bit_offset, 597 .AccessWidth = access_width, 598 .Address = htole64(address), 599 }; 600 601 return (basl_table_append_bytes(table, &gas_le, sizeof(gas_le))); 602 } 603 604 int 605 basl_table_append_header(struct basl_table *const table, 606 const uint8_t signature[ACPI_NAMESEG_SIZE], const uint8_t revision, 607 const uint32_t oem_revision) 608 { 609 ACPI_TABLE_HEADER header_le; 610 /* + 1 is required for the null terminator */ 611 char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1]; 612 613 assert(table != NULL); 614 assert(table->len == 0); 615 616 memcpy(header_le.Signature, signature, ACPI_NAMESEG_SIZE); 617 header_le.Length = 0; /* patched by basl_finish */ 618 header_le.Revision = revision; 619 header_le.Checksum = 0; /* patched by basl_finish */ 620 memcpy(header_le.OemId, "BHYVE ", ACPI_OEM_ID_SIZE); 621 snprintf(oem_table_id, ACPI_OEM_TABLE_ID_SIZE, "BV%.4s ", signature); 622 memcpy(header_le.OemTableId, oem_table_id, 623 sizeof(header_le.OemTableId)); 624 header_le.OemRevision = htole32(oem_revision); 625 memcpy(header_le.AslCompilerId, "BASL", ACPI_NAMESEG_SIZE); 626 header_le.AslCompilerRevision = htole32(0x20220504); 627 628 BASL_EXEC( 629 basl_table_append_bytes(table, &header_le, sizeof(header_le))); 630 631 BASL_EXEC(basl_table_add_length(table, 632 offsetof(ACPI_TABLE_HEADER, Length), sizeof(header_le.Length))); 633 BASL_EXEC(basl_table_add_checksum(table, 634 offsetof(ACPI_TABLE_HEADER, Checksum), 0, 635 BASL_TABLE_CHECKSUM_LEN_FULL_TABLE)); 636 637 return (0); 638 } 639 640 int 641 basl_table_append_int(struct basl_table *const table, const uint64_t val, 642 const uint8_t size) 643 { 644 char buf[8]; 645 646 assert(size <= sizeof(val)); 647 648 basl_le_enc(buf, val, size); 649 return (basl_table_append_bytes(table, buf, size)); 650 } 651 652 int 653 basl_table_append_length(struct basl_table *const table, const uint8_t size) 654 { 655 assert(table != NULL); 656 assert(size <= sizeof(table->len)); 657 658 BASL_EXEC(basl_table_add_length(table, table->len, size)); 659 BASL_EXEC(basl_table_append_int(table, 0, size)); 660 661 return (0); 662 } 663 664 int 665 basl_table_append_pointer(struct basl_table *const table, 666 const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint8_t size) 667 { 668 assert(table != NULL); 669 assert(size == 4 || size == 8); 670 671 BASL_EXEC(basl_table_add_pointer(table, src_signature, table->len, size)); 672 BASL_EXEC(basl_table_append_int(table, 0, size)); 673 674 return (0); 675 } 676 677 int 678 basl_table_create(struct basl_table **const table, struct vmctx *ctx, 679 const uint8_t *const name, const uint32_t alignment) 680 { 681 struct basl_table *new_table; 682 683 assert(table != NULL); 684 685 new_table = calloc(1, sizeof(struct basl_table)); 686 if (new_table == NULL) { 687 warnx("%s: failed to allocate table", __func__); 688 return (ENOMEM); 689 } 690 691 new_table->ctx = ctx; 692 693 #ifdef __FreeBSD__ 694 snprintf(new_table->fwcfg_name, sizeof(new_table->fwcfg_name), 695 "etc/acpi/%s", name); 696 #else 697 snprintf((char *)new_table->fwcfg_name, sizeof (new_table->fwcfg_name), 698 "etc/acpi/%s", name); 699 #endif 700 701 new_table->alignment = alignment; 702 703 STAILQ_INIT(&new_table->checksums); 704 STAILQ_INIT(&new_table->lengths); 705 STAILQ_INIT(&new_table->pointers); 706 707 STAILQ_INSERT_TAIL(&basl_tables, new_table, chain); 708 709 *table = new_table; 710 711 return (0); 712 } 713 714 int 715 basl_table_register_to_rsdt(struct basl_table *table) 716 { 717 const ACPI_TABLE_HEADER *header; 718 719 assert(table != NULL); 720 721 header = (const ACPI_TABLE_HEADER *)table->data; 722 723 #ifdef __FreeBSD__ 724 BASL_EXEC(basl_table_append_pointer(rsdt, header->Signature, 725 ACPI_RSDT_ENTRY_SIZE)); 726 BASL_EXEC(basl_table_append_pointer(xsdt, header->Signature, 727 ACPI_XSDT_ENTRY_SIZE)); 728 #else 729 BASL_EXEC(basl_table_append_pointer(rsdt, (uint8_t *)header->Signature, 730 ACPI_RSDT_ENTRY_SIZE)); 731 BASL_EXEC(basl_table_append_pointer(xsdt, (uint8_t *)header->Signature, 732 ACPI_XSDT_ENTRY_SIZE)); 733 #endif 734 735 return (0); 736 } 737