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