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 static int 125 basl_finish_install_guest_tables(struct basl_table *const table) 126 { 127 void *gva; 128 129 /* 130 * Install ACPI tables directly in guest memory for use by guests which 131 * do not boot via EFI. EFI ROMs provide a pointer to the firmware 132 * generated ACPI tables instead, but it doesn't hurt to install the 133 * tables always. 134 */ 135 gva = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off, table->len); 136 if (gva == NULL) { 137 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]", __func__, 138 (uint64_t)BHYVE_ACPI_BASE + table->off, 139 (uint64_t)BHYVE_ACPI_BASE + table->off + table->len); 140 return (ENOMEM); 141 } 142 memcpy(gva, table->data, table->len); 143 144 return (0); 145 } 146 147 static int 148 basl_finish_patch_checksums(struct basl_table *const table) 149 { 150 struct basl_table_checksum *checksum; 151 152 STAILQ_FOREACH(checksum, &table->checksums, chain) { 153 uint8_t *gva, *checksum_gva; 154 uint64_t gpa; 155 uint32_t len; 156 uint8_t sum; 157 158 len = checksum->len; 159 if (len == BASL_TABLE_CHECKSUM_LEN_FULL_TABLE) { 160 len = table->len; 161 } 162 163 assert(checksum->off < table->len); 164 assert(checksum->start < table->len); 165 assert(checksum->start + len <= table->len); 166 167 /* 168 * Install ACPI tables directly in guest memory for use by 169 * guests which do not boot via EFI. EFI ROMs provide a pointer 170 * to the firmware generated ACPI tables instead, but it doesn't 171 * hurt to install the tables always. 172 */ 173 gpa = BHYVE_ACPI_BASE + table->off + checksum->start; 174 if ((gpa < BHYVE_ACPI_BASE) || 175 (gpa < BHYVE_ACPI_BASE + table->off)) { 176 warnx("%s: invalid gpa (off 0x%8x start 0x%8x)", 177 __func__, table->off, checksum->start); 178 return (EFAULT); 179 } 180 181 gva = vm_map_gpa(table->ctx, gpa, len); 182 if (gva == NULL) { 183 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]", 184 __func__, gpa, gpa + len); 185 return (ENOMEM); 186 } 187 188 checksum_gva = gva + checksum->off; 189 if (checksum_gva < gva) { 190 warnx("%s: invalid checksum offset 0x%8x", __func__, 191 checksum->off); 192 return (EFAULT); 193 } 194 195 sum = 0; 196 for (uint32_t i = 0; i < len; ++i) { 197 sum += *(gva + i); 198 } 199 *checksum_gva = -sum; 200 } 201 202 return (0); 203 } 204 205 static struct basl_table * 206 basl_get_table_by_signature(const uint8_t signature[ACPI_NAMESEG_SIZE]) 207 { 208 struct basl_table *table; 209 210 STAILQ_FOREACH(table, &basl_tables, chain) { 211 const ACPI_TABLE_HEADER *const header = 212 (const ACPI_TABLE_HEADER *)table->data; 213 214 if (strncmp(header->Signature, signature, 215 sizeof(header->Signature)) == 0) { 216 return (table); 217 } 218 } 219 220 warnx("%s: %.4s not found", __func__, signature); 221 return (NULL); 222 } 223 224 static int 225 basl_finish_patch_pointers(struct basl_table *const table) 226 { 227 struct basl_table_pointer *pointer; 228 229 STAILQ_FOREACH(pointer, &table->pointers, chain) { 230 const struct basl_table *src_table; 231 uint8_t *gva; 232 uint64_t gpa, val; 233 234 assert(pointer->off < table->len); 235 assert(pointer->off + pointer->size <= table->len); 236 237 src_table = basl_get_table_by_signature(pointer->src_signature); 238 if (src_table == NULL) { 239 warnx("%s: could not find ACPI table %.4s", __func__, 240 pointer->src_signature); 241 return (EFAULT); 242 } 243 244 /* 245 * Install ACPI tables directly in guest memory for use by 246 * guests which do not boot via EFI. EFI ROMs provide a pointer 247 * to the firmware generated ACPI tables instead, but it doesn't 248 * hurt to install the tables always. 249 */ 250 gpa = BHYVE_ACPI_BASE + table->off; 251 if (gpa < BHYVE_ACPI_BASE) { 252 warnx("%s: table offset of 0x%8x is too large", 253 __func__, table->off); 254 return (EFAULT); 255 } 256 257 gva = vm_map_gpa(table->ctx, gpa, table->len); 258 if (gva == NULL) { 259 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]", 260 __func__, gpa, gpa + table->len); 261 return (ENOMEM); 262 } 263 264 val = basl_le_dec(gva + pointer->off, pointer->size); 265 val += BHYVE_ACPI_BASE + src_table->off; 266 basl_le_enc(gva + pointer->off, val, pointer->size); 267 } 268 269 return (0); 270 } 271 272 static int 273 basl_finish_set_length(struct basl_table *const table) 274 { 275 struct basl_table_length *length; 276 277 STAILQ_FOREACH(length, &table->lengths, chain) { 278 assert(length->off < table->len); 279 assert(length->off + length->size <= table->len); 280 281 basl_le_enc((uint8_t *)table->data + length->off, table->len, 282 length->size); 283 } 284 285 return (0); 286 } 287 288 int 289 basl_finish(void) 290 { 291 struct basl_table *table; 292 293 if (STAILQ_EMPTY(&basl_tables)) { 294 warnx("%s: no ACPI tables found", __func__); 295 return (EINVAL); 296 } 297 298 /* 299 * We have to install all tables before we can patch them. Therefore, 300 * use two loops. The first one installs all tables and the second one 301 * patches them. 302 */ 303 STAILQ_FOREACH(table, &basl_tables, chain) { 304 BASL_EXEC(basl_finish_set_length(table)); 305 BASL_EXEC(basl_finish_install_guest_tables(table)); 306 } 307 STAILQ_FOREACH(table, &basl_tables, chain) { 308 BASL_EXEC(basl_finish_patch_pointers(table)); 309 310 /* 311 * Calculate the checksum as last step! 312 */ 313 BASL_EXEC(basl_finish_patch_checksums(table)); 314 } 315 316 return (0); 317 } 318 319 int 320 basl_init(void) 321 { 322 return (0); 323 } 324 325 static int 326 basl_table_add_checksum(struct basl_table *const table, const uint32_t off, 327 const uint32_t start, const uint32_t len) 328 { 329 struct basl_table_checksum *checksum; 330 331 checksum = calloc(1, sizeof(struct basl_table_checksum)); 332 if (checksum == NULL) { 333 warnx("%s: failed to allocate checksum", __func__); 334 return (ENOMEM); 335 } 336 337 checksum->off = off; 338 checksum->start = start; 339 checksum->len = len; 340 341 STAILQ_INSERT_TAIL(&table->checksums, checksum, chain); 342 343 return (0); 344 } 345 346 static int 347 basl_table_add_length(struct basl_table *const table, const uint32_t off, 348 const uint8_t size) 349 { 350 struct basl_table_length *length; 351 352 length = calloc(1, sizeof(struct basl_table_length)); 353 if (length == NULL) { 354 warnx("%s: failed to allocate length", __func__); 355 return (ENOMEM); 356 } 357 358 length->off = off; 359 length->size = size; 360 361 STAILQ_INSERT_TAIL(&table->lengths, length, chain); 362 363 return (0); 364 } 365 366 static int 367 basl_table_add_pointer(struct basl_table *const table, 368 const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint32_t off, 369 const uint8_t size) 370 { 371 struct basl_table_pointer *pointer; 372 373 pointer = calloc(1, sizeof(struct basl_table_pointer)); 374 if (pointer == NULL) { 375 warnx("%s: failed to allocate pointer", __func__); 376 return (ENOMEM); 377 } 378 379 memcpy(pointer->src_signature, src_signature, 380 sizeof(pointer->src_signature)); 381 pointer->off = off; 382 pointer->size = size; 383 384 STAILQ_INSERT_TAIL(&table->pointers, pointer, chain); 385 386 return (0); 387 } 388 389 int 390 basl_table_append_bytes(struct basl_table *const table, const void *const bytes, 391 const uint32_t len) 392 { 393 void *end; 394 395 assert(table != NULL); 396 assert(bytes != NULL); 397 398 if (table->len + len <= table->len) { 399 warnx("%s: table too large (table->len 0x%8x len 0x%8x)", 400 __func__, table->len, len); 401 return (EFAULT); 402 } 403 404 table->data = reallocf(table->data, table->len + len); 405 if (table->data == NULL) { 406 warnx("%s: failed to realloc table to length 0x%8x", __func__, 407 table->len + len); 408 table->len = 0; 409 return (ENOMEM); 410 } 411 412 end = (uint8_t *)table->data + table->len; 413 table->len += len; 414 415 memcpy(end, bytes, len); 416 417 return (0); 418 } 419 420 int 421 basl_table_append_checksum(struct basl_table *const table, const uint32_t start, 422 const uint32_t len) 423 { 424 assert(table != NULL); 425 426 BASL_EXEC(basl_table_add_checksum(table, table->len, start, len)); 427 BASL_EXEC(basl_table_append_int(table, 0, 1)); 428 429 return (0); 430 } 431 432 int 433 basl_table_append_gas(struct basl_table *const table, const uint8_t space_id, 434 const uint8_t bit_width, const uint8_t bit_offset, 435 const uint8_t access_width, const uint64_t address) 436 { 437 ACPI_GENERIC_ADDRESS gas_le = { 438 .SpaceId = space_id, 439 .BitWidth = bit_width, 440 .BitOffset = bit_offset, 441 .AccessWidth = access_width, 442 .Address = htole64(address), 443 }; 444 445 return (basl_table_append_bytes(table, &gas_le, sizeof(gas_le))); 446 } 447 448 int 449 basl_table_append_header(struct basl_table *const table, 450 const uint8_t signature[ACPI_NAMESEG_SIZE], const uint8_t revision, 451 const uint32_t oem_revision) 452 { 453 ACPI_TABLE_HEADER header_le; 454 /* + 1 is required for the null terminator */ 455 char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1]; 456 457 assert(table != NULL); 458 assert(table->len == 0); 459 460 memcpy(header_le.Signature, signature, ACPI_NAMESEG_SIZE); 461 header_le.Length = 0; /* patched by basl_finish */ 462 header_le.Revision = revision; 463 header_le.Checksum = 0; /* patched by basl_finish */ 464 memcpy(header_le.OemId, "BHYVE ", ACPI_OEM_ID_SIZE); 465 snprintf(oem_table_id, ACPI_OEM_TABLE_ID_SIZE, "BV%.4s ", signature); 466 memcpy(header_le.OemTableId, oem_table_id, 467 sizeof(header_le.OemTableId)); 468 header_le.OemRevision = htole32(oem_revision); 469 memcpy(header_le.AslCompilerId, "BASL", ACPI_NAMESEG_SIZE); 470 header_le.AslCompilerRevision = htole32(0x20220504); 471 472 BASL_EXEC( 473 basl_table_append_bytes(table, &header_le, sizeof(header_le))); 474 475 BASL_EXEC(basl_table_add_length(table, 476 offsetof(ACPI_TABLE_HEADER, Length), sizeof(header_le.Length))); 477 BASL_EXEC(basl_table_add_checksum(table, 478 offsetof(ACPI_TABLE_HEADER, Checksum), 0, 479 BASL_TABLE_CHECKSUM_LEN_FULL_TABLE)); 480 481 return (0); 482 } 483 484 int 485 basl_table_append_int(struct basl_table *const table, const uint64_t val, 486 const uint8_t size) 487 { 488 char buf[8]; 489 490 assert(size <= sizeof(val)); 491 492 basl_le_enc(buf, val, size); 493 return (basl_table_append_bytes(table, buf, size)); 494 } 495 496 int 497 basl_table_append_length(struct basl_table *const table, const uint8_t size) 498 { 499 assert(table != NULL); 500 assert(size <= sizeof(table->len)); 501 502 BASL_EXEC(basl_table_add_length(table, table->len, size)); 503 BASL_EXEC(basl_table_append_int(table, 0, size)); 504 505 return (0); 506 } 507 508 int 509 basl_table_append_pointer(struct basl_table *const table, 510 const uint8_t src_signature[ACPI_NAMESEG_SIZE], const uint8_t size) 511 { 512 assert(table != NULL); 513 assert(size == 4 || size == 8); 514 515 BASL_EXEC(basl_table_add_pointer(table, src_signature, table->len, size)); 516 BASL_EXEC(basl_table_append_int(table, 0, size)); 517 518 return (0); 519 } 520 521 int 522 basl_table_create(struct basl_table **const table, struct vmctx *ctx, 523 const uint8_t *const name, const uint32_t alignment, 524 const uint32_t off) 525 { 526 struct basl_table *new_table; 527 528 assert(table != NULL); 529 530 new_table = calloc(1, sizeof(struct basl_table)); 531 if (new_table == NULL) { 532 warnx("%s: failed to allocate table", __func__); 533 return (ENOMEM); 534 } 535 536 new_table->ctx = ctx; 537 538 snprintf(new_table->fwcfg_name, sizeof(new_table->fwcfg_name), 539 "etc/acpi/%s", name); 540 541 new_table->alignment = alignment; 542 new_table->off = off; 543 544 STAILQ_INIT(&new_table->checksums); 545 STAILQ_INIT(&new_table->lengths); 546 STAILQ_INIT(&new_table->pointers); 547 548 STAILQ_INSERT_TAIL(&basl_tables, new_table, chain); 549 550 *table = new_table; 551 552 return (0); 553 } 554