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