1*65bbee46Sjsg /* $OpenBSD: fw_cfg.c,v 1.10 2024/09/26 01:45:13 jsg Exp $ */ 2622c1441Sclaudio /* 3622c1441Sclaudio * Copyright (c) 2018 Claudio Jeker <claudio@openbsd.org> 4622c1441Sclaudio * 5622c1441Sclaudio * Permission to use, copy, modify, and distribute this software for any 6622c1441Sclaudio * purpose with or without fee is hereby granted, provided that the above 7622c1441Sclaudio * copyright notice and this permission notice appear in all copies. 8622c1441Sclaudio * 9622c1441Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10622c1441Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11622c1441Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12622c1441Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13622c1441Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14622c1441Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15622c1441Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16622c1441Sclaudio */ 17622c1441Sclaudio #include <sys/types.h> 18cf08ffabSdv #include <machine/biosvar.h> /* bios_memmap_t */ 191e8eabf2Sdv #include <dev/pv/virtioreg.h> 20ba66f564Sdv #include <dev/vmm/vmm.h> 21622c1441Sclaudio 22622c1441Sclaudio #include <stdlib.h> 23622c1441Sclaudio #include <string.h> 24622c1441Sclaudio #include <unistd.h> 25622c1441Sclaudio 26622c1441Sclaudio #include "atomicio.h" 271e8eabf2Sdv #include "pci.h" 28622c1441Sclaudio #include "vmd.h" 29622c1441Sclaudio #include "vmm.h" 30622c1441Sclaudio #include "fw_cfg.h" 31622c1441Sclaudio 32622c1441Sclaudio #define FW_CFG_SIGNATURE 0x0000 33622c1441Sclaudio #define FW_CFG_ID 0x0001 34622c1441Sclaudio #define FW_CFG_NOGRAPHIC 0x0004 35622c1441Sclaudio #define FW_CFG_FILE_DIR 0x0019 36622c1441Sclaudio #define FW_CFG_FILE_FIRST 0x0020 37622c1441Sclaudio 38622c1441Sclaudio #define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* QEMU CFG */ 39622c1441Sclaudio 40622c1441Sclaudio struct fw_cfg_dma_access { 41622c1441Sclaudio uint32_t control; 42622c1441Sclaudio #define FW_CFG_DMA_ERROR 0x0001 43622c1441Sclaudio #define FW_CFG_DMA_READ 0x0002 44622c1441Sclaudio #define FW_CFG_DMA_SKIP 0x0004 45622c1441Sclaudio #define FW_CFG_DMA_SELECT 0x0008 46622c1441Sclaudio #define FW_CFG_DMA_WRITE 0x0010 /* not implemented */ 47622c1441Sclaudio uint32_t length; 48622c1441Sclaudio uint64_t address; 49622c1441Sclaudio }; 50622c1441Sclaudio 51622c1441Sclaudio struct fw_cfg_file { 52622c1441Sclaudio uint32_t size; 53622c1441Sclaudio uint16_t selector; 54622c1441Sclaudio uint16_t reserved; 55622c1441Sclaudio char name[56]; 56622c1441Sclaudio }; 57622c1441Sclaudio 58622c1441Sclaudio extern char *__progname; 59622c1441Sclaudio 60622c1441Sclaudio static struct fw_cfg_state { 61622c1441Sclaudio size_t offset; 62622c1441Sclaudio size_t size; 63622c1441Sclaudio uint8_t *data; 64622c1441Sclaudio } fw_cfg_state; 65622c1441Sclaudio 66622c1441Sclaudio static uint64_t fw_cfg_dma_addr; 67622c1441Sclaudio 68cf08ffabSdv static bios_memmap_t e820[VMM_MAX_MEM_RANGES]; 69cf08ffabSdv 70622c1441Sclaudio static int fw_cfg_select_file(uint16_t); 71622c1441Sclaudio static void fw_cfg_file_dir(void); 72622c1441Sclaudio 73622c1441Sclaudio void 74622c1441Sclaudio fw_cfg_init(struct vmop_create_params *vmc) 75622c1441Sclaudio { 76622c1441Sclaudio unsigned int sd = 0; 77cf08ffabSdv size_t i, e820_len = 0; 781e8eabf2Sdv char bootorder[64]; 791e8eabf2Sdv const char *bootfmt; 801e8eabf2Sdv int bootidx = -1; 81cf08ffabSdv 82cf08ffabSdv /* Define e820 memory ranges. */ 83cf08ffabSdv memset(&e820, 0, sizeof(e820)); 84cf08ffabSdv for (i = 0; i < vmc->vmc_params.vcp_nmemranges; i++) { 85cf08ffabSdv struct vm_mem_range *range = &vmc->vmc_params.vcp_memranges[i]; 86cf08ffabSdv bios_memmap_t *entry = &e820[i]; 87cf08ffabSdv entry->addr = range->vmr_gpa; 88cf08ffabSdv entry->size = range->vmr_size; 89cf08ffabSdv if (range->vmr_type == VM_MEM_RAM) 90cf08ffabSdv entry->type = BIOS_MAP_FREE; 91cf08ffabSdv else 92cf08ffabSdv entry->type = BIOS_MAP_RES; 93cf08ffabSdv e820_len += sizeof(bios_memmap_t); 94cf08ffabSdv } 95cf08ffabSdv fw_cfg_add_file("etc/e820", &e820, e820_len); 96622c1441Sclaudio 97622c1441Sclaudio /* do not double print chars on serial port */ 98622c1441Sclaudio fw_cfg_add_file("etc/screen-and-debug", &sd, sizeof(sd)); 99622c1441Sclaudio 100622c1441Sclaudio switch (vmc->vmc_bootdevice) { 101622c1441Sclaudio case VMBOOTDEV_DISK: 1021e8eabf2Sdv bootidx = pci_find_first_device(PCI_PRODUCT_VIRTIO_BLOCK); 1031e8eabf2Sdv bootfmt = "/pci@i0cf8/*@%d\nHALT"; 104622c1441Sclaudio break; 105622c1441Sclaudio case VMBOOTDEV_CDROM: 1061e8eabf2Sdv bootidx = pci_find_first_device(PCI_PRODUCT_VIRTIO_SCSI); 1071e8eabf2Sdv bootfmt = "/pci@i0cf8/*@%d/*@0/*@0,40000100\nHALT"; 108622c1441Sclaudio break; 109622c1441Sclaudio case VMBOOTDEV_NET: 110622c1441Sclaudio /* XXX not yet */ 1111e8eabf2Sdv bootidx = pci_find_first_device(PCI_PRODUCT_VIRTIO_NETWORK); 1121e8eabf2Sdv bootfmt = "HALT"; 113622c1441Sclaudio break; 114622c1441Sclaudio } 1151e8eabf2Sdv if (bootidx > -1) { 1161e8eabf2Sdv snprintf(bootorder, sizeof(bootorder), bootfmt, bootidx); 1171e8eabf2Sdv log_debug("%s: bootorder: %s", __func__, bootorder); 118622c1441Sclaudio fw_cfg_add_file("bootorder", bootorder, strlen(bootorder) + 1); 119622c1441Sclaudio } 1201e8eabf2Sdv } 121622c1441Sclaudio 122622c1441Sclaudio int 123622c1441Sclaudio fw_cfg_dump(int fd) 124622c1441Sclaudio { 125622c1441Sclaudio log_debug("%s: sending fw_cfg state", __func__); 126622c1441Sclaudio if (atomicio(vwrite, fd, &fw_cfg_dma_addr, 127622c1441Sclaudio sizeof(fw_cfg_dma_addr)) != sizeof(fw_cfg_dma_addr)) { 128622c1441Sclaudio log_warnx("%s: error writing fw_cfg to fd", __func__); 129622c1441Sclaudio return -1; 130622c1441Sclaudio } 131622c1441Sclaudio if (atomicio(vwrite, fd, &fw_cfg_state.offset, 132622c1441Sclaudio sizeof(fw_cfg_state.offset)) != sizeof(fw_cfg_state.offset)) { 133622c1441Sclaudio log_warnx("%s: error writing fw_cfg to fd", __func__); 134622c1441Sclaudio return -1; 135622c1441Sclaudio } 136622c1441Sclaudio if (atomicio(vwrite, fd, &fw_cfg_state.size, 137622c1441Sclaudio sizeof(fw_cfg_state.size)) != sizeof(fw_cfg_state.size)) { 138622c1441Sclaudio log_warnx("%s: error writing fw_cfg to fd", __func__); 139622c1441Sclaudio return -1; 140622c1441Sclaudio } 141622c1441Sclaudio if (fw_cfg_state.size != 0) 142622c1441Sclaudio if (atomicio(vwrite, fd, fw_cfg_state.data, 143622c1441Sclaudio fw_cfg_state.size) != fw_cfg_state.size) { 144622c1441Sclaudio log_warnx("%s: error writing fw_cfg to fd", __func__); 145622c1441Sclaudio return (-1); 146622c1441Sclaudio } 147622c1441Sclaudio return 0; 148622c1441Sclaudio } 149622c1441Sclaudio 150622c1441Sclaudio int 151622c1441Sclaudio fw_cfg_restore(int fd) 152622c1441Sclaudio { 153622c1441Sclaudio log_debug("%s: receiving fw_cfg state", __func__); 154622c1441Sclaudio if (atomicio(read, fd, &fw_cfg_dma_addr, 155622c1441Sclaudio sizeof(fw_cfg_dma_addr)) != sizeof(fw_cfg_dma_addr)) { 156622c1441Sclaudio log_warnx("%s: error reading fw_cfg from fd", __func__); 157622c1441Sclaudio return -1; 158622c1441Sclaudio } 159622c1441Sclaudio if (atomicio(read, fd, &fw_cfg_state.offset, 160622c1441Sclaudio sizeof(fw_cfg_state.offset)) != sizeof(fw_cfg_state.offset)) { 161622c1441Sclaudio log_warnx("%s: error reading fw_cfg from fd", __func__); 162622c1441Sclaudio return -1; 163622c1441Sclaudio } 164622c1441Sclaudio if (atomicio(read, fd, &fw_cfg_state.size, 165622c1441Sclaudio sizeof(fw_cfg_state.size)) != sizeof(fw_cfg_state.size)) { 166622c1441Sclaudio log_warnx("%s: error reading fw_cfg from fd", __func__); 167622c1441Sclaudio return -1; 168622c1441Sclaudio } 169622c1441Sclaudio fw_cfg_state.data = NULL; 170622c1441Sclaudio if (fw_cfg_state.size != 0) { 171622c1441Sclaudio if ((fw_cfg_state.data = malloc(fw_cfg_state.size)) == NULL) 172622c1441Sclaudio fatal("%s", __func__); 173622c1441Sclaudio if (atomicio(read, fd, fw_cfg_state.data, 174622c1441Sclaudio fw_cfg_state.size) != fw_cfg_state.size) { 175622c1441Sclaudio log_warnx("%s: error reading fw_cfg from fd", __func__); 176622c1441Sclaudio return -1; 177622c1441Sclaudio } 178622c1441Sclaudio } 179622c1441Sclaudio return 0; 180622c1441Sclaudio } 181622c1441Sclaudio 182622c1441Sclaudio static void 183622c1441Sclaudio fw_cfg_reset_state(void) 184622c1441Sclaudio { 185622c1441Sclaudio free(fw_cfg_state.data); 186622c1441Sclaudio fw_cfg_state.offset = 0; 187622c1441Sclaudio fw_cfg_state.size = 0; 188622c1441Sclaudio fw_cfg_state.data = NULL; 189622c1441Sclaudio } 190622c1441Sclaudio 191622c1441Sclaudio static void 192622c1441Sclaudio fw_cfg_set_state(void *data, size_t len) 193622c1441Sclaudio { 194622c1441Sclaudio if ((fw_cfg_state.data = malloc(len)) == NULL) { 195622c1441Sclaudio log_warn("%s", __func__); 196622c1441Sclaudio return; 197622c1441Sclaudio } 198622c1441Sclaudio memcpy(fw_cfg_state.data, data, len); 199622c1441Sclaudio fw_cfg_state.size = len; 200622c1441Sclaudio fw_cfg_state.offset = 0; 201622c1441Sclaudio } 202622c1441Sclaudio 203622c1441Sclaudio static void 204622c1441Sclaudio fw_cfg_select(uint16_t selector) 205622c1441Sclaudio { 206622c1441Sclaudio uint16_t one = 1; 207622c1441Sclaudio uint32_t id = htole32(0x3); 208622c1441Sclaudio 209622c1441Sclaudio fw_cfg_reset_state(); 210622c1441Sclaudio switch (selector) { 211622c1441Sclaudio case FW_CFG_SIGNATURE: 212622c1441Sclaudio fw_cfg_set_state("QEMU", 4); 213622c1441Sclaudio break; 214622c1441Sclaudio case FW_CFG_ID: 215622c1441Sclaudio fw_cfg_set_state(&id, sizeof(id)); 216622c1441Sclaudio break; 217622c1441Sclaudio case FW_CFG_NOGRAPHIC: 218622c1441Sclaudio fw_cfg_set_state(&one, sizeof(one)); 219622c1441Sclaudio break; 220622c1441Sclaudio case FW_CFG_FILE_DIR: 221622c1441Sclaudio fw_cfg_file_dir(); 222622c1441Sclaudio break; 223622c1441Sclaudio default: 224622c1441Sclaudio if (!fw_cfg_select_file(selector)) 225622c1441Sclaudio log_debug("%s: unhandled selector %x", 226622c1441Sclaudio __func__, selector); 227622c1441Sclaudio break; 228622c1441Sclaudio } 229622c1441Sclaudio } 230622c1441Sclaudio 231622c1441Sclaudio static void 232622c1441Sclaudio fw_cfg_handle_dma(struct fw_cfg_dma_access *fw) 233622c1441Sclaudio { 234622c1441Sclaudio uint32_t len = 0, control = fw->control; 235622c1441Sclaudio 236622c1441Sclaudio fw->control = 0; 237622c1441Sclaudio if (control & FW_CFG_DMA_SELECT) { 238622c1441Sclaudio uint16_t selector = control >> 16; 239622c1441Sclaudio log_debug("%s: selector 0x%04x", __func__, selector); 240622c1441Sclaudio fw_cfg_select(selector); 241622c1441Sclaudio } 242622c1441Sclaudio 243622c1441Sclaudio /* calculate correct length of operation */ 244622c1441Sclaudio if (fw_cfg_state.offset < fw_cfg_state.size) 245622c1441Sclaudio len = fw_cfg_state.size - fw_cfg_state.offset; 246622c1441Sclaudio if (len > fw->length) 247622c1441Sclaudio len = fw->length; 248622c1441Sclaudio 249622c1441Sclaudio if (control & FW_CFG_DMA_WRITE) { 250622c1441Sclaudio fw->control |= FW_CFG_DMA_ERROR; 251622c1441Sclaudio } else if (control & FW_CFG_DMA_READ) { 252622c1441Sclaudio if (write_mem(fw->address, 253622c1441Sclaudio fw_cfg_state.data + fw_cfg_state.offset, len)) { 254622c1441Sclaudio log_warnx("%s: write_mem error", __func__); 255622c1441Sclaudio fw->control |= FW_CFG_DMA_ERROR; 256622c1441Sclaudio } 257622c1441Sclaudio /* clear rest of buffer */ 258622c1441Sclaudio if (len < fw->length) 259622c1441Sclaudio if (write_mem(fw->address + len, NULL, 260622c1441Sclaudio fw->length - len)) { 261622c1441Sclaudio log_warnx("%s: write_mem error", __func__); 262622c1441Sclaudio fw->control |= FW_CFG_DMA_ERROR; 263622c1441Sclaudio } 264622c1441Sclaudio } 265622c1441Sclaudio fw_cfg_state.offset += len; 266622c1441Sclaudio 267622c1441Sclaudio if (fw_cfg_state.offset == fw_cfg_state.size) 268622c1441Sclaudio fw_cfg_reset_state(); 269622c1441Sclaudio } 270622c1441Sclaudio 271622c1441Sclaudio uint8_t 272622c1441Sclaudio vcpu_exit_fw_cfg(struct vm_run_params *vrp) 273622c1441Sclaudio { 274622c1441Sclaudio uint32_t data = 0; 275622c1441Sclaudio struct vm_exit *vei = vrp->vrp_exit; 276622c1441Sclaudio 277622c1441Sclaudio get_input_data(vei, &data); 278622c1441Sclaudio 279622c1441Sclaudio switch (vei->vei.vei_port) { 280622c1441Sclaudio case FW_CFG_IO_SELECT: 281622c1441Sclaudio if (vei->vei.vei_dir == VEI_DIR_IN) { 282622c1441Sclaudio log_warnx("%s: fw_cfg: read from selector port " 283622c1441Sclaudio "unsupported", __progname); 284622c1441Sclaudio set_return_data(vei, 0); 285622c1441Sclaudio break; 286622c1441Sclaudio } 287622c1441Sclaudio log_debug("%s: selector 0x%04x", __func__, data); 288622c1441Sclaudio fw_cfg_select(data); 289622c1441Sclaudio break; 290622c1441Sclaudio case FW_CFG_IO_DATA: 291622c1441Sclaudio if (vei->vei.vei_dir == VEI_DIR_OUT) { 292622c1441Sclaudio log_debug("%s: fw_cfg: discarding data written to " 293622c1441Sclaudio "data port", __progname); 294622c1441Sclaudio break; 295622c1441Sclaudio } 296622c1441Sclaudio /* fw_cfg only defines 1-byte reads via IO port */ 297622c1441Sclaudio if (fw_cfg_state.offset < fw_cfg_state.size) { 298622c1441Sclaudio set_return_data(vei, 299622c1441Sclaudio fw_cfg_state.data[fw_cfg_state.offset++]); 300622c1441Sclaudio if (fw_cfg_state.offset == fw_cfg_state.size) 301622c1441Sclaudio fw_cfg_reset_state(); 302622c1441Sclaudio } else 303622c1441Sclaudio set_return_data(vei, 0); 304622c1441Sclaudio break; 305622c1441Sclaudio } 306622c1441Sclaudio 307622c1441Sclaudio return 0xFF; 308622c1441Sclaudio } 309622c1441Sclaudio 310622c1441Sclaudio uint8_t 311622c1441Sclaudio vcpu_exit_fw_cfg_dma(struct vm_run_params *vrp) 312622c1441Sclaudio { 313622c1441Sclaudio struct fw_cfg_dma_access fw_dma; 314622c1441Sclaudio uint32_t data = 0; 315622c1441Sclaudio struct vm_exit *vei = vrp->vrp_exit; 316622c1441Sclaudio 317622c1441Sclaudio if (vei->vei.vei_size != 4) { 318622c1441Sclaudio log_debug("%s: fw_cfg_dma: discarding data written to " 319622c1441Sclaudio "dma addr", __progname); 320622c1441Sclaudio if (vei->vei.vei_dir == VEI_DIR_OUT) 321622c1441Sclaudio fw_cfg_dma_addr = 0; 322622c1441Sclaudio return 0xFF; 323622c1441Sclaudio } 324622c1441Sclaudio 325622c1441Sclaudio if (vei->vei.vei_dir == VEI_DIR_OUT) { 326622c1441Sclaudio get_input_data(vei, &data); 327622c1441Sclaudio switch (vei->vei.vei_port) { 328622c1441Sclaudio case FW_CFG_IO_DMA_ADDR_HIGH: 329622c1441Sclaudio fw_cfg_dma_addr = (uint64_t)be32toh(data) << 32; 330622c1441Sclaudio break; 331622c1441Sclaudio case FW_CFG_IO_DMA_ADDR_LOW: 332622c1441Sclaudio fw_cfg_dma_addr |= be32toh(data); 333622c1441Sclaudio 334622c1441Sclaudio /* writing least significant half triggers operation */ 335622c1441Sclaudio if (read_mem(fw_cfg_dma_addr, &fw_dma, sizeof(fw_dma))) 336622c1441Sclaudio break; 337622c1441Sclaudio /* adjust byteorder */ 338622c1441Sclaudio fw_dma.control = be32toh(fw_dma.control); 339622c1441Sclaudio fw_dma.length = be32toh(fw_dma.length); 340622c1441Sclaudio fw_dma.address = be64toh(fw_dma.address); 341622c1441Sclaudio 342622c1441Sclaudio fw_cfg_handle_dma(&fw_dma); 343622c1441Sclaudio 344622c1441Sclaudio /* just write control byte back */ 345622c1441Sclaudio data = be32toh(fw_dma.control); 346622c1441Sclaudio if (write_mem(fw_cfg_dma_addr, &data, sizeof(data))) 347622c1441Sclaudio break; 348622c1441Sclaudio 349622c1441Sclaudio /* done, reset base address */ 350622c1441Sclaudio fw_cfg_dma_addr = 0; 351622c1441Sclaudio break; 352622c1441Sclaudio } 353622c1441Sclaudio } else { 354622c1441Sclaudio uint64_t sig = htobe64(FW_CFG_DMA_SIGNATURE); 355622c1441Sclaudio switch (vei->vei.vei_port) { 356622c1441Sclaudio case FW_CFG_IO_DMA_ADDR_HIGH: 357622c1441Sclaudio set_return_data(vei, sig >> 32); 358622c1441Sclaudio break; 359622c1441Sclaudio case FW_CFG_IO_DMA_ADDR_LOW: 360622c1441Sclaudio set_return_data(vei, sig & 0xffffffff); 361622c1441Sclaudio break; 362622c1441Sclaudio } 363622c1441Sclaudio } 364622c1441Sclaudio return 0xFF; 365622c1441Sclaudio } 366622c1441Sclaudio 367622c1441Sclaudio static uint16_t file_id = FW_CFG_FILE_FIRST; 368622c1441Sclaudio 369622c1441Sclaudio struct fw_cfg_file_entry { 370622c1441Sclaudio TAILQ_ENTRY(fw_cfg_file_entry) entry; 371622c1441Sclaudio struct fw_cfg_file file; 372622c1441Sclaudio void *data; 373622c1441Sclaudio }; 374622c1441Sclaudio 375622c1441Sclaudio TAILQ_HEAD(, fw_cfg_file_entry) fw_cfg_files = 376622c1441Sclaudio TAILQ_HEAD_INITIALIZER(fw_cfg_files); 377622c1441Sclaudio 378622c1441Sclaudio static struct fw_cfg_file_entry * 379622c1441Sclaudio fw_cfg_lookup_file(const char *name) 380622c1441Sclaudio { 381622c1441Sclaudio struct fw_cfg_file_entry *f; 382622c1441Sclaudio 383622c1441Sclaudio TAILQ_FOREACH(f, &fw_cfg_files, entry) { 384622c1441Sclaudio if (strcmp(name, f->file.name) == 0) 385622c1441Sclaudio return f; 386622c1441Sclaudio } 387622c1441Sclaudio return NULL; 388622c1441Sclaudio } 389622c1441Sclaudio 390622c1441Sclaudio void 391622c1441Sclaudio fw_cfg_add_file(const char *name, const void *data, size_t len) 392622c1441Sclaudio { 393622c1441Sclaudio struct fw_cfg_file_entry *f; 394622c1441Sclaudio 395622c1441Sclaudio if (fw_cfg_lookup_file(name)) 396622c1441Sclaudio fatalx("%s: fw_cfg: file %s exists", __progname, name); 397622c1441Sclaudio 398160bdb59Sdv if ((f = calloc(1, sizeof(*f))) == NULL) 399622c1441Sclaudio fatal("%s", __func__); 400622c1441Sclaudio 401622c1441Sclaudio if ((f->data = malloc(len)) == NULL) 402622c1441Sclaudio fatal("%s", __func__); 403622c1441Sclaudio 404622c1441Sclaudio if (strlcpy(f->file.name, name, sizeof(f->file.name)) >= 405622c1441Sclaudio sizeof(f->file.name)) 406622c1441Sclaudio fatalx("%s: fw_cfg: file name too long", __progname); 407622c1441Sclaudio 408622c1441Sclaudio f->file.size = htobe32(len); 409622c1441Sclaudio f->file.selector = htobe16(file_id++); 410622c1441Sclaudio memcpy(f->data, data, len); 411622c1441Sclaudio 412622c1441Sclaudio TAILQ_INSERT_TAIL(&fw_cfg_files, f, entry); 413622c1441Sclaudio } 414622c1441Sclaudio 415622c1441Sclaudio static int 416622c1441Sclaudio fw_cfg_select_file(uint16_t id) 417622c1441Sclaudio { 418622c1441Sclaudio struct fw_cfg_file_entry *f; 419622c1441Sclaudio 420622c1441Sclaudio id = htobe16(id); 421622c1441Sclaudio TAILQ_FOREACH(f, &fw_cfg_files, entry) 422622c1441Sclaudio if (f->file.selector == id) { 423622c1441Sclaudio size_t size = be32toh(f->file.size); 424622c1441Sclaudio fw_cfg_set_state(f->data, size); 425622c1441Sclaudio log_debug("%s: accessing file %s", __func__, 426622c1441Sclaudio f->file.name); 427622c1441Sclaudio return 1; 428622c1441Sclaudio } 429622c1441Sclaudio return 0; 430622c1441Sclaudio } 431622c1441Sclaudio 432622c1441Sclaudio static void 433622c1441Sclaudio fw_cfg_file_dir(void) 434622c1441Sclaudio { 435622c1441Sclaudio struct fw_cfg_file_entry *f; 436622c1441Sclaudio struct fw_cfg_file *fp; 437622c1441Sclaudio uint32_t count = 0; 438622c1441Sclaudio uint32_t *data; 439622c1441Sclaudio size_t size; 440622c1441Sclaudio 441622c1441Sclaudio TAILQ_FOREACH(f, &fw_cfg_files, entry) 442622c1441Sclaudio count++; 443622c1441Sclaudio 444622c1441Sclaudio size = sizeof(count) + count * sizeof(struct fw_cfg_file); 445622c1441Sclaudio if ((data = malloc(size)) == NULL) 446622c1441Sclaudio fatal("%s", __func__); 447622c1441Sclaudio *data = htobe32(count); 448622c1441Sclaudio fp = (struct fw_cfg_file *)(data + 1); 449622c1441Sclaudio 450622c1441Sclaudio log_debug("%s: file directory with %d files", __func__, count); 451622c1441Sclaudio TAILQ_FOREACH(f, &fw_cfg_files, entry) { 452622c1441Sclaudio log_debug(" %6dB %04x %s", be32toh(f->file.size), 453622c1441Sclaudio be16toh(f->file.selector), f->file.name); 454622c1441Sclaudio memcpy(fp, &f->file, sizeof(f->file)); 455622c1441Sclaudio fp++; 456622c1441Sclaudio } 457622c1441Sclaudio 458622c1441Sclaudio /* XXX should sort by name but SeaBIOS does not care */ 459622c1441Sclaudio 460622c1441Sclaudio fw_cfg_set_state(data, size); 461622c1441Sclaudio } 462