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