1 /* $OpenBSD: fw_cfg.c,v 1.10 2024/09/26 01:45:13 jsg Exp $ */ 2 /* 3 * Copyright (c) 2018 Claudio Jeker <claudio@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include <sys/types.h> 18 #include <machine/biosvar.h> /* bios_memmap_t */ 19 #include <dev/pv/virtioreg.h> 20 #include <dev/vmm/vmm.h> 21 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #include "atomicio.h" 27 #include "pci.h" 28 #include "vmd.h" 29 #include "vmm.h" 30 #include "fw_cfg.h" 31 32 #define FW_CFG_SIGNATURE 0x0000 33 #define FW_CFG_ID 0x0001 34 #define FW_CFG_NOGRAPHIC 0x0004 35 #define FW_CFG_FILE_DIR 0x0019 36 #define FW_CFG_FILE_FIRST 0x0020 37 38 #define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* QEMU CFG */ 39 40 struct fw_cfg_dma_access { 41 uint32_t control; 42 #define FW_CFG_DMA_ERROR 0x0001 43 #define FW_CFG_DMA_READ 0x0002 44 #define FW_CFG_DMA_SKIP 0x0004 45 #define FW_CFG_DMA_SELECT 0x0008 46 #define FW_CFG_DMA_WRITE 0x0010 /* not implemented */ 47 uint32_t length; 48 uint64_t address; 49 }; 50 51 struct fw_cfg_file { 52 uint32_t size; 53 uint16_t selector; 54 uint16_t reserved; 55 char name[56]; 56 }; 57 58 extern char *__progname; 59 60 static struct fw_cfg_state { 61 size_t offset; 62 size_t size; 63 uint8_t *data; 64 } fw_cfg_state; 65 66 static uint64_t fw_cfg_dma_addr; 67 68 static bios_memmap_t e820[VMM_MAX_MEM_RANGES]; 69 70 static int fw_cfg_select_file(uint16_t); 71 static void fw_cfg_file_dir(void); 72 73 void 74 fw_cfg_init(struct vmop_create_params *vmc) 75 { 76 unsigned int sd = 0; 77 size_t i, e820_len = 0; 78 char bootorder[64]; 79 const char *bootfmt; 80 int bootidx = -1; 81 82 /* Define e820 memory ranges. */ 83 memset(&e820, 0, sizeof(e820)); 84 for (i = 0; i < vmc->vmc_params.vcp_nmemranges; i++) { 85 struct vm_mem_range *range = &vmc->vmc_params.vcp_memranges[i]; 86 bios_memmap_t *entry = &e820[i]; 87 entry->addr = range->vmr_gpa; 88 entry->size = range->vmr_size; 89 if (range->vmr_type == VM_MEM_RAM) 90 entry->type = BIOS_MAP_FREE; 91 else 92 entry->type = BIOS_MAP_RES; 93 e820_len += sizeof(bios_memmap_t); 94 } 95 fw_cfg_add_file("etc/e820", &e820, e820_len); 96 97 /* do not double print chars on serial port */ 98 fw_cfg_add_file("etc/screen-and-debug", &sd, sizeof(sd)); 99 100 switch (vmc->vmc_bootdevice) { 101 case VMBOOTDEV_DISK: 102 bootidx = pci_find_first_device(PCI_PRODUCT_VIRTIO_BLOCK); 103 bootfmt = "/pci@i0cf8/*@%d\nHALT"; 104 break; 105 case VMBOOTDEV_CDROM: 106 bootidx = pci_find_first_device(PCI_PRODUCT_VIRTIO_SCSI); 107 bootfmt = "/pci@i0cf8/*@%d/*@0/*@0,40000100\nHALT"; 108 break; 109 case VMBOOTDEV_NET: 110 /* XXX not yet */ 111 bootidx = pci_find_first_device(PCI_PRODUCT_VIRTIO_NETWORK); 112 bootfmt = "HALT"; 113 break; 114 } 115 if (bootidx > -1) { 116 snprintf(bootorder, sizeof(bootorder), bootfmt, bootidx); 117 log_debug("%s: bootorder: %s", __func__, bootorder); 118 fw_cfg_add_file("bootorder", bootorder, strlen(bootorder) + 1); 119 } 120 } 121 122 int 123 fw_cfg_dump(int fd) 124 { 125 log_debug("%s: sending fw_cfg state", __func__); 126 if (atomicio(vwrite, fd, &fw_cfg_dma_addr, 127 sizeof(fw_cfg_dma_addr)) != sizeof(fw_cfg_dma_addr)) { 128 log_warnx("%s: error writing fw_cfg to fd", __func__); 129 return -1; 130 } 131 if (atomicio(vwrite, fd, &fw_cfg_state.offset, 132 sizeof(fw_cfg_state.offset)) != sizeof(fw_cfg_state.offset)) { 133 log_warnx("%s: error writing fw_cfg to fd", __func__); 134 return -1; 135 } 136 if (atomicio(vwrite, fd, &fw_cfg_state.size, 137 sizeof(fw_cfg_state.size)) != sizeof(fw_cfg_state.size)) { 138 log_warnx("%s: error writing fw_cfg to fd", __func__); 139 return -1; 140 } 141 if (fw_cfg_state.size != 0) 142 if (atomicio(vwrite, fd, fw_cfg_state.data, 143 fw_cfg_state.size) != fw_cfg_state.size) { 144 log_warnx("%s: error writing fw_cfg to fd", __func__); 145 return (-1); 146 } 147 return 0; 148 } 149 150 int 151 fw_cfg_restore(int fd) 152 { 153 log_debug("%s: receiving fw_cfg state", __func__); 154 if (atomicio(read, fd, &fw_cfg_dma_addr, 155 sizeof(fw_cfg_dma_addr)) != sizeof(fw_cfg_dma_addr)) { 156 log_warnx("%s: error reading fw_cfg from fd", __func__); 157 return -1; 158 } 159 if (atomicio(read, fd, &fw_cfg_state.offset, 160 sizeof(fw_cfg_state.offset)) != sizeof(fw_cfg_state.offset)) { 161 log_warnx("%s: error reading fw_cfg from fd", __func__); 162 return -1; 163 } 164 if (atomicio(read, fd, &fw_cfg_state.size, 165 sizeof(fw_cfg_state.size)) != sizeof(fw_cfg_state.size)) { 166 log_warnx("%s: error reading fw_cfg from fd", __func__); 167 return -1; 168 } 169 fw_cfg_state.data = NULL; 170 if (fw_cfg_state.size != 0) { 171 if ((fw_cfg_state.data = malloc(fw_cfg_state.size)) == NULL) 172 fatal("%s", __func__); 173 if (atomicio(read, fd, fw_cfg_state.data, 174 fw_cfg_state.size) != fw_cfg_state.size) { 175 log_warnx("%s: error reading fw_cfg from fd", __func__); 176 return -1; 177 } 178 } 179 return 0; 180 } 181 182 static void 183 fw_cfg_reset_state(void) 184 { 185 free(fw_cfg_state.data); 186 fw_cfg_state.offset = 0; 187 fw_cfg_state.size = 0; 188 fw_cfg_state.data = NULL; 189 } 190 191 static void 192 fw_cfg_set_state(void *data, size_t len) 193 { 194 if ((fw_cfg_state.data = malloc(len)) == NULL) { 195 log_warn("%s", __func__); 196 return; 197 } 198 memcpy(fw_cfg_state.data, data, len); 199 fw_cfg_state.size = len; 200 fw_cfg_state.offset = 0; 201 } 202 203 static void 204 fw_cfg_select(uint16_t selector) 205 { 206 uint16_t one = 1; 207 uint32_t id = htole32(0x3); 208 209 fw_cfg_reset_state(); 210 switch (selector) { 211 case FW_CFG_SIGNATURE: 212 fw_cfg_set_state("QEMU", 4); 213 break; 214 case FW_CFG_ID: 215 fw_cfg_set_state(&id, sizeof(id)); 216 break; 217 case FW_CFG_NOGRAPHIC: 218 fw_cfg_set_state(&one, sizeof(one)); 219 break; 220 case FW_CFG_FILE_DIR: 221 fw_cfg_file_dir(); 222 break; 223 default: 224 if (!fw_cfg_select_file(selector)) 225 log_debug("%s: unhandled selector %x", 226 __func__, selector); 227 break; 228 } 229 } 230 231 static void 232 fw_cfg_handle_dma(struct fw_cfg_dma_access *fw) 233 { 234 uint32_t len = 0, control = fw->control; 235 236 fw->control = 0; 237 if (control & FW_CFG_DMA_SELECT) { 238 uint16_t selector = control >> 16; 239 log_debug("%s: selector 0x%04x", __func__, selector); 240 fw_cfg_select(selector); 241 } 242 243 /* calculate correct length of operation */ 244 if (fw_cfg_state.offset < fw_cfg_state.size) 245 len = fw_cfg_state.size - fw_cfg_state.offset; 246 if (len > fw->length) 247 len = fw->length; 248 249 if (control & FW_CFG_DMA_WRITE) { 250 fw->control |= FW_CFG_DMA_ERROR; 251 } else if (control & FW_CFG_DMA_READ) { 252 if (write_mem(fw->address, 253 fw_cfg_state.data + fw_cfg_state.offset, len)) { 254 log_warnx("%s: write_mem error", __func__); 255 fw->control |= FW_CFG_DMA_ERROR; 256 } 257 /* clear rest of buffer */ 258 if (len < fw->length) 259 if (write_mem(fw->address + len, NULL, 260 fw->length - len)) { 261 log_warnx("%s: write_mem error", __func__); 262 fw->control |= FW_CFG_DMA_ERROR; 263 } 264 } 265 fw_cfg_state.offset += len; 266 267 if (fw_cfg_state.offset == fw_cfg_state.size) 268 fw_cfg_reset_state(); 269 } 270 271 uint8_t 272 vcpu_exit_fw_cfg(struct vm_run_params *vrp) 273 { 274 uint32_t data = 0; 275 struct vm_exit *vei = vrp->vrp_exit; 276 277 get_input_data(vei, &data); 278 279 switch (vei->vei.vei_port) { 280 case FW_CFG_IO_SELECT: 281 if (vei->vei.vei_dir == VEI_DIR_IN) { 282 log_warnx("%s: fw_cfg: read from selector port " 283 "unsupported", __progname); 284 set_return_data(vei, 0); 285 break; 286 } 287 log_debug("%s: selector 0x%04x", __func__, data); 288 fw_cfg_select(data); 289 break; 290 case FW_CFG_IO_DATA: 291 if (vei->vei.vei_dir == VEI_DIR_OUT) { 292 log_debug("%s: fw_cfg: discarding data written to " 293 "data port", __progname); 294 break; 295 } 296 /* fw_cfg only defines 1-byte reads via IO port */ 297 if (fw_cfg_state.offset < fw_cfg_state.size) { 298 set_return_data(vei, 299 fw_cfg_state.data[fw_cfg_state.offset++]); 300 if (fw_cfg_state.offset == fw_cfg_state.size) 301 fw_cfg_reset_state(); 302 } else 303 set_return_data(vei, 0); 304 break; 305 } 306 307 return 0xFF; 308 } 309 310 uint8_t 311 vcpu_exit_fw_cfg_dma(struct vm_run_params *vrp) 312 { 313 struct fw_cfg_dma_access fw_dma; 314 uint32_t data = 0; 315 struct vm_exit *vei = vrp->vrp_exit; 316 317 if (vei->vei.vei_size != 4) { 318 log_debug("%s: fw_cfg_dma: discarding data written to " 319 "dma addr", __progname); 320 if (vei->vei.vei_dir == VEI_DIR_OUT) 321 fw_cfg_dma_addr = 0; 322 return 0xFF; 323 } 324 325 if (vei->vei.vei_dir == VEI_DIR_OUT) { 326 get_input_data(vei, &data); 327 switch (vei->vei.vei_port) { 328 case FW_CFG_IO_DMA_ADDR_HIGH: 329 fw_cfg_dma_addr = (uint64_t)be32toh(data) << 32; 330 break; 331 case FW_CFG_IO_DMA_ADDR_LOW: 332 fw_cfg_dma_addr |= be32toh(data); 333 334 /* writing least significant half triggers operation */ 335 if (read_mem(fw_cfg_dma_addr, &fw_dma, sizeof(fw_dma))) 336 break; 337 /* adjust byteorder */ 338 fw_dma.control = be32toh(fw_dma.control); 339 fw_dma.length = be32toh(fw_dma.length); 340 fw_dma.address = be64toh(fw_dma.address); 341 342 fw_cfg_handle_dma(&fw_dma); 343 344 /* just write control byte back */ 345 data = be32toh(fw_dma.control); 346 if (write_mem(fw_cfg_dma_addr, &data, sizeof(data))) 347 break; 348 349 /* done, reset base address */ 350 fw_cfg_dma_addr = 0; 351 break; 352 } 353 } else { 354 uint64_t sig = htobe64(FW_CFG_DMA_SIGNATURE); 355 switch (vei->vei.vei_port) { 356 case FW_CFG_IO_DMA_ADDR_HIGH: 357 set_return_data(vei, sig >> 32); 358 break; 359 case FW_CFG_IO_DMA_ADDR_LOW: 360 set_return_data(vei, sig & 0xffffffff); 361 break; 362 } 363 } 364 return 0xFF; 365 } 366 367 static uint16_t file_id = FW_CFG_FILE_FIRST; 368 369 struct fw_cfg_file_entry { 370 TAILQ_ENTRY(fw_cfg_file_entry) entry; 371 struct fw_cfg_file file; 372 void *data; 373 }; 374 375 TAILQ_HEAD(, fw_cfg_file_entry) fw_cfg_files = 376 TAILQ_HEAD_INITIALIZER(fw_cfg_files); 377 378 static struct fw_cfg_file_entry * 379 fw_cfg_lookup_file(const char *name) 380 { 381 struct fw_cfg_file_entry *f; 382 383 TAILQ_FOREACH(f, &fw_cfg_files, entry) { 384 if (strcmp(name, f->file.name) == 0) 385 return f; 386 } 387 return NULL; 388 } 389 390 void 391 fw_cfg_add_file(const char *name, const void *data, size_t len) 392 { 393 struct fw_cfg_file_entry *f; 394 395 if (fw_cfg_lookup_file(name)) 396 fatalx("%s: fw_cfg: file %s exists", __progname, name); 397 398 if ((f = calloc(1, sizeof(*f))) == NULL) 399 fatal("%s", __func__); 400 401 if ((f->data = malloc(len)) == NULL) 402 fatal("%s", __func__); 403 404 if (strlcpy(f->file.name, name, sizeof(f->file.name)) >= 405 sizeof(f->file.name)) 406 fatalx("%s: fw_cfg: file name too long", __progname); 407 408 f->file.size = htobe32(len); 409 f->file.selector = htobe16(file_id++); 410 memcpy(f->data, data, len); 411 412 TAILQ_INSERT_TAIL(&fw_cfg_files, f, entry); 413 } 414 415 static int 416 fw_cfg_select_file(uint16_t id) 417 { 418 struct fw_cfg_file_entry *f; 419 420 id = htobe16(id); 421 TAILQ_FOREACH(f, &fw_cfg_files, entry) 422 if (f->file.selector == id) { 423 size_t size = be32toh(f->file.size); 424 fw_cfg_set_state(f->data, size); 425 log_debug("%s: accessing file %s", __func__, 426 f->file.name); 427 return 1; 428 } 429 return 0; 430 } 431 432 static void 433 fw_cfg_file_dir(void) 434 { 435 struct fw_cfg_file_entry *f; 436 struct fw_cfg_file *fp; 437 uint32_t count = 0; 438 uint32_t *data; 439 size_t size; 440 441 TAILQ_FOREACH(f, &fw_cfg_files, entry) 442 count++; 443 444 size = sizeof(count) + count * sizeof(struct fw_cfg_file); 445 if ((data = malloc(size)) == NULL) 446 fatal("%s", __func__); 447 *data = htobe32(count); 448 fp = (struct fw_cfg_file *)(data + 1); 449 450 log_debug("%s: file directory with %d files", __func__, count); 451 TAILQ_FOREACH(f, &fw_cfg_files, entry) { 452 log_debug(" %6dB %04x %s", be32toh(f->file.size), 453 be16toh(f->file.selector), f->file.name); 454 memcpy(fp, &f->file, sizeof(f->file)); 455 fp++; 456 } 457 458 /* XXX should sort by name but SeaBIOS does not care */ 459 460 fw_cfg_set_state(data, size); 461 } 462