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