1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG 5 * Author: Corvin Köhne <c.koehne@beckhoff.com> 6 */ 7 8 #include <sys/param.h> 9 #ifdef __FreeBSD__ 10 #include <sys/endian.h> 11 #else 12 #include <endian.h> 13 #endif 14 #include <sys/queue.h> 15 #include <sys/stat.h> 16 17 #include <machine/vmm.h> 18 19 #include <err.h> 20 #include <errno.h> 21 #include <fcntl.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 #include "acpi_device.h" 28 #include "bhyverun.h" 29 #include "inout.h" 30 #include "pci_lpc.h" 31 #include "qemu_fwcfg.h" 32 33 #define QEMU_FWCFG_ACPI_DEVICE_NAME "FWCF" 34 #define QEMU_FWCFG_ACPI_HARDWARE_ID "QEMU0002" 35 36 #define QEMU_FWCFG_SELECTOR_PORT_NUMBER 0x510 37 #define QEMU_FWCFG_SELECTOR_PORT_SIZE 1 38 #define QEMU_FWCFG_SELECTOR_PORT_FLAGS IOPORT_F_INOUT 39 #define QEMU_FWCFG_DATA_PORT_NUMBER 0x511 40 #define QEMU_FWCFG_DATA_PORT_SIZE 1 41 #define QEMU_FWCFG_DATA_PORT_FLAGS \ 42 IOPORT_F_INOUT /* QEMU v2.4+ ignores writes */ 43 44 #define QEMU_FWCFG_ARCHITECTURE_MASK 0x0001 45 #define QEMU_FWCFG_INDEX_MASK 0x3FFF 46 47 #define QEMU_FWCFG_SELECT_READ 0 48 #define QEMU_FWCFG_SELECT_WRITE 1 49 50 #define QEMU_FWCFG_ARCHITECTURE_GENERIC 0 51 #define QEMU_FWCFG_ARCHITECTURE_SPECIFIC 1 52 53 #define QEMU_FWCFG_INDEX_SIGNATURE 0x00 54 #define QEMU_FWCFG_INDEX_ID 0x01 55 #define QEMU_FWCFG_INDEX_NB_CPUS 0x05 56 #define QEMU_FWCFG_INDEX_MAX_CPUS 0x0F 57 #define QEMU_FWCFG_INDEX_FILE_DIR 0x19 58 59 #define QEMU_FWCFG_FIRST_FILE_INDEX 0x20 60 61 #define QEMU_FWCFG_MIN_FILES 10 62 63 #pragma pack(1) 64 65 union qemu_fwcfg_selector { 66 struct { 67 uint16_t index : 14; 68 uint16_t writeable : 1; 69 uint16_t architecture : 1; 70 }; 71 uint16_t bits; 72 }; 73 74 struct qemu_fwcfg_signature { 75 uint8_t signature[4]; 76 }; 77 78 struct qemu_fwcfg_id { 79 uint32_t interface : 1; /* always set */ 80 uint32_t DMA : 1; 81 uint32_t reserved : 30; 82 }; 83 84 struct qemu_fwcfg_file { 85 uint32_t be_size; 86 uint16_t be_selector; 87 uint16_t reserved; 88 uint8_t name[QEMU_FWCFG_MAX_NAME]; 89 }; 90 91 struct qemu_fwcfg_directory { 92 uint32_t be_count; 93 struct qemu_fwcfg_file files[0]; 94 }; 95 96 #pragma pack() 97 98 struct qemu_fwcfg_softc { 99 struct acpi_device *acpi_dev; 100 101 uint32_t data_offset; 102 union qemu_fwcfg_selector selector; 103 struct qemu_fwcfg_item items[QEMU_FWCFG_MAX_ARCHS] 104 [QEMU_FWCFG_MAX_ENTRIES]; 105 struct qemu_fwcfg_directory *directory; 106 }; 107 108 static struct qemu_fwcfg_softc fwcfg_sc; 109 110 struct qemu_fwcfg_user_file { 111 STAILQ_ENTRY(qemu_fwcfg_user_file) chain; 112 uint8_t name[QEMU_FWCFG_MAX_NAME]; 113 uint32_t size; 114 void *data; 115 }; 116 static STAILQ_HEAD(qemu_fwcfg_user_file_list, 117 qemu_fwcfg_user_file) user_files = STAILQ_HEAD_INITIALIZER(user_files); 118 119 static int 120 qemu_fwcfg_selector_port_handler(struct vmctx *const ctx __unused, const int in, 121 const int port __unused, const int bytes, uint32_t *const eax, 122 void *const arg __unused) 123 { 124 if (bytes != sizeof(uint16_t)) { 125 warnx("%s: invalid size (%d) of IO port access", __func__, 126 bytes); 127 return (-1); 128 } 129 130 if (in) { 131 *eax = htole16(fwcfg_sc.selector.bits); 132 return (0); 133 } 134 135 fwcfg_sc.data_offset = 0; 136 fwcfg_sc.selector.bits = le16toh(*eax); 137 138 return (0); 139 } 140 141 static int 142 qemu_fwcfg_data_port_handler(struct vmctx *const ctx __unused, const int in, 143 const int port __unused, const int bytes, uint32_t *const eax, 144 void *const arg __unused) 145 { 146 if (bytes != sizeof(uint8_t)) { 147 warnx("%s: invalid size (%d) of IO port access", __func__, 148 bytes); 149 return (-1); 150 } 151 152 if (!in) { 153 warnx("%s: Writes to qemu fwcfg data port aren't allowed", 154 __func__); 155 return (-1); 156 } 157 158 /* get fwcfg item */ 159 struct qemu_fwcfg_item *const item = 160 &fwcfg_sc.items[fwcfg_sc.selector.architecture] 161 [fwcfg_sc.selector.index]; 162 if (item->data == NULL) { 163 warnx( 164 "%s: qemu fwcfg item doesn't exist (architecture %s index 0x%x)", 165 __func__, 166 fwcfg_sc.selector.architecture ? "specific" : "generic", 167 fwcfg_sc.selector.index); 168 *eax = 0x00; 169 return (0); 170 } else if (fwcfg_sc.data_offset >= item->size) { 171 warnx( 172 "%s: qemu fwcfg item read exceeds size (architecture %s index 0x%x size 0x%x offset 0x%x)", 173 __func__, 174 fwcfg_sc.selector.architecture ? "specific" : "generic", 175 fwcfg_sc.selector.index, item->size, fwcfg_sc.data_offset); 176 *eax = 0x00; 177 return (0); 178 } 179 180 /* return item data */ 181 *eax = item->data[fwcfg_sc.data_offset]; 182 fwcfg_sc.data_offset++; 183 184 return (0); 185 } 186 187 static int 188 qemu_fwcfg_add_item(const uint16_t architecture, const uint16_t index, 189 const uint32_t size, void *const data) 190 { 191 /* truncate architecture and index to their desired size */ 192 const uint16_t arch = architecture & QEMU_FWCFG_ARCHITECTURE_MASK; 193 const uint16_t idx = index & QEMU_FWCFG_INDEX_MASK; 194 195 /* get pointer to item specified by selector */ 196 struct qemu_fwcfg_item *const fwcfg_item = &fwcfg_sc.items[arch][idx]; 197 198 /* check if item is already used */ 199 if (fwcfg_item->data != NULL) { 200 warnx("%s: qemu fwcfg item exists (architecture %s index 0x%x)", 201 __func__, arch ? "specific" : "generic", idx); 202 return (EEXIST); 203 } 204 205 /* save data of the item */ 206 fwcfg_item->size = size; 207 fwcfg_item->data = data; 208 209 return (0); 210 } 211 212 static int 213 qemu_fwcfg_add_item_file_dir(void) 214 { 215 const size_t size = sizeof(struct qemu_fwcfg_directory) + 216 QEMU_FWCFG_MIN_FILES * sizeof(struct qemu_fwcfg_file); 217 #ifdef __FreeBSD__ 218 struct qemu_fwcfg_directory *const fwcfg_directory = calloc(1, size); 219 #else 220 // SMATCH: double check that we're allocating correct size: 4 vs 644 221 void *ptr = calloc(1, size); 222 struct qemu_fwcfg_directory *const fwcfg_directory = 223 (struct qemu_fwcfg_directory *)ptr; 224 #endif 225 if (fwcfg_directory == NULL) { 226 return (ENOMEM); 227 } 228 229 fwcfg_sc.directory = fwcfg_directory; 230 231 return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC, 232 QEMU_FWCFG_INDEX_FILE_DIR, sizeof(struct qemu_fwcfg_directory), 233 (uint8_t *)fwcfg_sc.directory)); 234 } 235 236 static int 237 qemu_fwcfg_add_item_id(void) 238 { 239 struct qemu_fwcfg_id *const fwcfg_id = calloc(1, 240 sizeof(struct qemu_fwcfg_id)); 241 if (fwcfg_id == NULL) { 242 return (ENOMEM); 243 } 244 245 fwcfg_id->interface = 1; 246 fwcfg_id->DMA = 0; 247 248 uint32_t *const le_fwcfg_id_ptr = (uint32_t *)fwcfg_id; 249 *le_fwcfg_id_ptr = htole32(*le_fwcfg_id_ptr); 250 251 return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC, 252 QEMU_FWCFG_INDEX_ID, sizeof(struct qemu_fwcfg_id), 253 (uint8_t *)fwcfg_id)); 254 } 255 256 static int 257 qemu_fwcfg_add_item_max_cpus(void) 258 { 259 uint16_t *fwcfg_max_cpus = calloc(1, sizeof(uint16_t)); 260 if (fwcfg_max_cpus == NULL) { 261 return (ENOMEM); 262 } 263 264 /* 265 * We don't support cpu hotplug yet. For that reason, use guest_ncpus instead 266 * of maxcpus. 267 */ 268 *fwcfg_max_cpus = htole16(guest_ncpus); 269 270 return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC, 271 QEMU_FWCFG_INDEX_MAX_CPUS, sizeof(uint16_t), fwcfg_max_cpus)); 272 } 273 274 static int 275 qemu_fwcfg_add_item_nb_cpus(void) 276 { 277 uint16_t *fwcfg_max_cpus = calloc(1, sizeof(uint16_t)); 278 if (fwcfg_max_cpus == NULL) { 279 return (ENOMEM); 280 } 281 282 *fwcfg_max_cpus = htole16(guest_ncpus); 283 284 return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC, 285 QEMU_FWCFG_INDEX_NB_CPUS, sizeof(uint16_t), fwcfg_max_cpus)); 286 } 287 288 static int 289 qemu_fwcfg_add_item_signature(void) 290 { 291 struct qemu_fwcfg_signature *const fwcfg_signature = calloc(1, 292 sizeof(struct qemu_fwcfg_signature)); 293 if (fwcfg_signature == NULL) { 294 return (ENOMEM); 295 } 296 297 fwcfg_signature->signature[0] = 'Q'; 298 fwcfg_signature->signature[1] = 'E'; 299 fwcfg_signature->signature[2] = 'M'; 300 fwcfg_signature->signature[3] = 'U'; 301 302 return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC, 303 QEMU_FWCFG_INDEX_SIGNATURE, sizeof(struct qemu_fwcfg_signature), 304 (uint8_t *)fwcfg_signature)); 305 } 306 307 static int 308 qemu_fwcfg_register_port(const char *const name, const int port, const int size, 309 const int flags, const inout_func_t handler) 310 { 311 struct inout_port iop; 312 313 bzero(&iop, sizeof(iop)); 314 iop.name = name; 315 iop.port = port; 316 iop.size = size; 317 iop.flags = flags; 318 iop.handler = handler; 319 320 return (register_inout(&iop)); 321 } 322 323 int 324 qemu_fwcfg_add_file(const char *name, const uint32_t size, void *const data) 325 { 326 if (strlen(name) >= QEMU_FWCFG_MAX_NAME) 327 return (EINVAL); 328 329 /* 330 * QEMU specifies count as big endian. 331 * Convert it to host endian to work with it. 332 */ 333 const uint32_t count = be32toh(fwcfg_sc.directory->be_count) + 1; 334 335 /* add file to items list */ 336 const uint32_t index = QEMU_FWCFG_FIRST_FILE_INDEX + count - 1; 337 const int error = qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC, 338 index, size, data); 339 if (error != 0) { 340 return (error); 341 } 342 343 /* 344 * files should be sorted alphabetical, get index for new file 345 */ 346 uint32_t file_index; 347 for (file_index = 0; file_index < count - 1; ++file_index) { 348 #ifdef __FreeBSD__ 349 if (strcmp(name, fwcfg_sc.directory->files[file_index].name) < 350 0) 351 break; 352 #else 353 if (strcmp((char *)name, 354 (char *)fwcfg_sc.directory->files[file_index].name) < 0) { 355 break; 356 } 357 #endif 358 } 359 360 if (count > QEMU_FWCFG_MIN_FILES) { 361 /* alloc new file directory */ 362 const uint64_t new_size = sizeof(struct qemu_fwcfg_directory) + 363 count * sizeof(struct qemu_fwcfg_file); 364 struct qemu_fwcfg_directory *const new_directory = calloc(1, 365 new_size); 366 if (new_directory == NULL) { 367 warnx( 368 "%s: Unable to allocate a new qemu fwcfg files directory (count %d)", 369 __func__, count); 370 return (ENOMEM); 371 } 372 373 /* copy files below file_index to new directory */ 374 memcpy(new_directory->files, fwcfg_sc.directory->files, 375 file_index * sizeof(struct qemu_fwcfg_file)); 376 377 /* copy files above file_index to directory */ 378 memcpy(&new_directory->files[file_index + 1], 379 &fwcfg_sc.directory->files[file_index], 380 (count - file_index - 1) * sizeof(struct qemu_fwcfg_file)); 381 382 /* free old directory */ 383 free(fwcfg_sc.directory); 384 385 /* set directory pointer to new directory */ 386 fwcfg_sc.directory = new_directory; 387 388 /* adjust directory pointer */ 389 fwcfg_sc.items[0][QEMU_FWCFG_INDEX_FILE_DIR].data = 390 (uint8_t *)fwcfg_sc.directory; 391 } else { 392 /* shift files behind file_index */ 393 for (uint32_t i = QEMU_FWCFG_MIN_FILES - 1; i > file_index; 394 --i) { 395 memcpy(&fwcfg_sc.directory->files[i], 396 &fwcfg_sc.directory->files[i - 1], 397 sizeof(struct qemu_fwcfg_file)); 398 } 399 } 400 401 /* 402 * QEMU specifies count, size and index as big endian. 403 * Save these values in big endian to simplify guest reads of these 404 * values. 405 */ 406 fwcfg_sc.directory->be_count = htobe32(count); 407 fwcfg_sc.directory->files[file_index].be_size = htobe32(size); 408 fwcfg_sc.directory->files[file_index].be_selector = htobe16(index); 409 #ifdef __FreeBSD__ 410 strcpy(fwcfg_sc.directory->files[file_index].name, name); 411 #else 412 strcpy((char *)fwcfg_sc.directory->files[file_index].name, 413 (char *)name); 414 #endif 415 416 /* set new size for the fwcfg_file_directory */ 417 fwcfg_sc.items[0][QEMU_FWCFG_INDEX_FILE_DIR].size = 418 sizeof(struct qemu_fwcfg_directory) + 419 count * sizeof(struct qemu_fwcfg_file); 420 421 return (0); 422 } 423 424 static int 425 qemu_fwcfg_add_user_files(void) 426 { 427 const struct qemu_fwcfg_user_file *fwcfg_file; 428 int error; 429 430 STAILQ_FOREACH(fwcfg_file, &user_files, chain) { 431 #ifdef __FreeBSD__ 432 error = qemu_fwcfg_add_file(fwcfg_file->name, fwcfg_file->size, 433 fwcfg_file->data); 434 #else 435 error = qemu_fwcfg_add_file((char *)fwcfg_file->name, 436 fwcfg_file->size, fwcfg_file->data); 437 #endif 438 if (error) 439 return (error); 440 } 441 442 return (0); 443 } 444 445 static const struct acpi_device_emul qemu_fwcfg_acpi_device_emul = { 446 .name = QEMU_FWCFG_ACPI_DEVICE_NAME, 447 .hid = QEMU_FWCFG_ACPI_HARDWARE_ID, 448 }; 449 450 int 451 qemu_fwcfg_init(struct vmctx *const ctx) 452 { 453 int error; 454 455 /* 456 * Bhyve supports fwctl (bhyve) and fwcfg (qemu) as firmware interfaces. 457 * Both are using the same ports. So, it's not possible to provide both 458 * interfaces at the same time to the guest. Therefore, only create acpi 459 * tables and register io ports for fwcfg, if it's used. 460 */ 461 if (strcmp(lpc_fwcfg(), "qemu") == 0) { 462 error = acpi_device_create(&fwcfg_sc.acpi_dev, &fwcfg_sc, ctx, 463 &qemu_fwcfg_acpi_device_emul); 464 if (error) { 465 warnx("%s: failed to create ACPI device for QEMU FwCfg", 466 __func__); 467 goto done; 468 } 469 470 error = acpi_device_add_res_fixed_ioport(fwcfg_sc.acpi_dev, 471 QEMU_FWCFG_SELECTOR_PORT_NUMBER, 2); 472 if (error) { 473 warnx("%s: failed to add fixed IO port for QEMU FwCfg", 474 __func__); 475 goto done; 476 } 477 478 /* add handlers for fwcfg ports */ 479 if ((error = qemu_fwcfg_register_port("qemu_fwcfg_selector", 480 QEMU_FWCFG_SELECTOR_PORT_NUMBER, 481 QEMU_FWCFG_SELECTOR_PORT_SIZE, 482 QEMU_FWCFG_SELECTOR_PORT_FLAGS, 483 qemu_fwcfg_selector_port_handler)) != 0) { 484 warnx( 485 "%s: Unable to register qemu fwcfg selector port 0x%x", 486 __func__, QEMU_FWCFG_SELECTOR_PORT_NUMBER); 487 goto done; 488 } 489 if ((error = qemu_fwcfg_register_port("qemu_fwcfg_data", 490 QEMU_FWCFG_DATA_PORT_NUMBER, QEMU_FWCFG_DATA_PORT_SIZE, 491 QEMU_FWCFG_DATA_PORT_FLAGS, 492 qemu_fwcfg_data_port_handler)) != 0) { 493 warnx( 494 "%s: Unable to register qemu fwcfg data port 0x%x", 495 __func__, QEMU_FWCFG_DATA_PORT_NUMBER); 496 goto done; 497 } 498 } 499 500 /* add common fwcfg items */ 501 if ((error = qemu_fwcfg_add_item_signature()) != 0) { 502 warnx("%s: Unable to add signature item", __func__); 503 goto done; 504 } 505 if ((error = qemu_fwcfg_add_item_id()) != 0) { 506 warnx("%s: Unable to add id item", __func__); 507 goto done; 508 } 509 if ((error = qemu_fwcfg_add_item_nb_cpus()) != 0) { 510 warnx("%s: Unable to add nb_cpus item", __func__); 511 goto done; 512 } 513 if ((error = qemu_fwcfg_add_item_max_cpus()) != 0) { 514 warnx("%s: Unable to add max_cpus item", __func__); 515 goto done; 516 } 517 if ((error = qemu_fwcfg_add_item_file_dir()) != 0) { 518 warnx("%s: Unable to add file_dir item", __func__); 519 goto done; 520 } 521 522 /* add user defined fwcfg files */ 523 if ((error = qemu_fwcfg_add_user_files()) != 0) { 524 warnx("%s: Unable to add user files", __func__); 525 goto done; 526 } 527 528 done: 529 if (error) { 530 acpi_device_destroy(fwcfg_sc.acpi_dev); 531 } 532 533 return (error); 534 } 535 536 static void 537 qemu_fwcfg_usage(const char *opt) 538 { 539 warnx("Invalid fw_cfg option \"%s\"", opt); 540 warnx("-f [name=]<name>,(string|file)=<value>"); 541 } 542 543 /* 544 * Parses the cmdline argument for user defined fw_cfg items. The cmdline 545 * argument has the format: 546 * "-f [name=]<name>,(string|file)=<value>" 547 * 548 * E.g.: "-f opt/com.page/example,string=Hello" 549 */ 550 int 551 qemu_fwcfg_parse_cmdline_arg(const char *opt) 552 { 553 struct qemu_fwcfg_user_file *fwcfg_file; 554 struct stat sb; 555 const char *opt_ptr, *opt_end; 556 ssize_t bytes_read; 557 int fd; 558 559 fwcfg_file = malloc(sizeof(*fwcfg_file)); 560 if (fwcfg_file == NULL) { 561 warnx("Unable to allocate fw_cfg_user_file"); 562 return (ENOMEM); 563 } 564 565 /* get pointer to <name> */ 566 opt_ptr = opt; 567 /* If [name=] is specified, skip it */ 568 if (strncmp(opt_ptr, "name=", sizeof("name=") - 1) == 0) { 569 opt_ptr += sizeof("name=") - 1; 570 } 571 572 /* get the end of <name> */ 573 opt_end = strchr(opt_ptr, ','); 574 if (opt_end == NULL) { 575 qemu_fwcfg_usage(opt); 576 #ifndef __FreeBSD__ 577 free(fwcfg_file); 578 #endif 579 return (EINVAL); 580 } 581 582 /* check if <name> is too long */ 583 if (opt_end - opt_ptr >= QEMU_FWCFG_MAX_NAME) { 584 warnx("fw_cfg name too long: \"%s\"", opt); 585 #ifndef __FreeBSD__ 586 free(fwcfg_file); 587 #endif 588 return (EINVAL); 589 } 590 591 /* save <name> */ 592 #ifdef __FreeBSD__ 593 strncpy(fwcfg_file->name, opt_ptr, opt_end - opt_ptr); 594 #else 595 strncpy((char *)fwcfg_file->name, opt_ptr, opt_end - opt_ptr); 596 #endif 597 fwcfg_file->name[opt_end - opt_ptr] = '\0'; 598 599 /* set opt_ptr and opt_end to <value> */ 600 opt_ptr = opt_end + 1; 601 opt_end = opt_ptr + strlen(opt_ptr); 602 603 if (strncmp(opt_ptr, "string=", sizeof("string=") - 1) == 0) { 604 opt_ptr += sizeof("string=") - 1; 605 fwcfg_file->data = strdup(opt_ptr); 606 if (fwcfg_file->data == NULL) { 607 warnx("Can't duplicate fw_cfg_user_file string \"%s\"", 608 opt_ptr); 609 return (ENOMEM); 610 } 611 fwcfg_file->size = strlen(opt_ptr) + 1; 612 } else if (strncmp(opt_ptr, "file=", sizeof("file=") - 1) == 0) { 613 opt_ptr += sizeof("file=") - 1; 614 615 fd = open(opt_ptr, O_RDONLY); 616 if (fd < 0) { 617 warn("Can't open fw_cfg_user_file file \"%s\"", 618 opt_ptr); 619 return (EINVAL); 620 } 621 622 if (fstat(fd, &sb) < 0) { 623 warn("Unable to get size of file \"%s\"", opt_ptr); 624 close(fd); 625 return (-1); 626 } 627 628 fwcfg_file->data = malloc(sb.st_size); 629 if (fwcfg_file->data == NULL) { 630 warnx( 631 "Can't allocate fw_cfg_user_file file \"%s\" (size: 0x%16lx)", 632 opt_ptr, sb.st_size); 633 close(fd); 634 return (ENOMEM); 635 } 636 bytes_read = read(fd, fwcfg_file->data, sb.st_size); 637 if (bytes_read < 0 || bytes_read != sb.st_size) { 638 warn("Unable to read file \"%s\"", opt_ptr); 639 free(fwcfg_file->data); 640 close(fd); 641 return (-1); 642 } 643 fwcfg_file->size = bytes_read; 644 645 close(fd); 646 } else { 647 qemu_fwcfg_usage(opt); 648 return (EINVAL); 649 } 650 651 STAILQ_INSERT_TAIL(&user_files, fwcfg_file, chain); 652 653 return (0); 654 } 655