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