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