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