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