| File: | src/usr.sbin/vmd/fw_cfg.c |
| Warning: | line 433, column 1 Potential leak of memory pointed to by 'data' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: fw_cfg.c,v 1.5 2021/11/05 10:18:50 jan 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_SIGNATURE0x0000 0x0000 | |||
| 31 | #define FW_CFG_ID0x0001 0x0001 | |||
| 32 | #define FW_CFG_NOGRAPHIC0x0004 0x0004 | |||
| 33 | #define FW_CFG_FILE_DIR0x0019 0x0019 | |||
| 34 | #define FW_CFG_FILE_FIRST0x0020 0x0020 | |||
| 35 | ||||
| 36 | #define FW_CFG_DMA_SIGNATURE0x51454d5520434647ULL 0x51454d5520434647ULL /* QEMU CFG */ | |||
| 37 | ||||
| 38 | struct fw_cfg_dma_access { | |||
| 39 | uint32_t control; | |||
| 40 | #define FW_CFG_DMA_ERROR0x0001 0x0001 | |||
| 41 | #define FW_CFG_DMA_READ0x0002 0x0002 | |||
| 42 | #define FW_CFG_DMA_SKIP0x0004 0x0004 | |||
| 43 | #define FW_CFG_DMA_SELECT0x0008 0x0008 | |||
| 44 | #define FW_CFG_DMA_WRITE0x0010 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((void *)0); | |||
| 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_DISK1: | |||
| 80 | bootorder = "/pci@i0cf8/*@3\nHALT"; | |||
| 81 | break; | |||
| 82 | case VMBOOTDEV_CDROM2: | |||
| 83 | bootorder = "/pci@i0cf8/*@4/*@0/*@0,40000100\nHALT"; | |||
| 84 | break; | |||
| 85 | case VMBOOTDEV_NET3: | |||
| 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(ssize_t (*)(int, void *, size_t))write, 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(ssize_t (*)(int, void *, size_t))write, 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(ssize_t (*)(int, void *, size_t))write, 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(ssize_t (*)(int, void *, size_t))write, 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((void *)0); | |||
| 142 | if (fw_cfg_state.size != 0) { | |||
| 143 | if ((fw_cfg_state.data = malloc(fw_cfg_state.size)) == NULL((void *)0)) | |||
| 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((void *)0); | |||
| 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((void *)0)) { | |||
| 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)((__uint32_t)(0x3)); | |||
| 180 | ||||
| 181 | fw_cfg_reset_state(); | |||
| 182 | switch (selector) { | |||
| 183 | case FW_CFG_SIGNATURE0x0000: | |||
| 184 | fw_cfg_set_state("QEMU", 4); | |||
| 185 | break; | |||
| 186 | case FW_CFG_ID0x0001: | |||
| 187 | fw_cfg_set_state(&id, sizeof(id)); | |||
| 188 | break; | |||
| 189 | case FW_CFG_NOGRAPHIC0x0004: | |||
| 190 | fw_cfg_set_state(&one, sizeof(one)); | |||
| 191 | break; | |||
| 192 | case FW_CFG_FILE_DIR0x0019: | |||
| 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_SELECT0x0008) { | |||
| 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_WRITE0x0010) { | |||
| 222 | fw->control |= FW_CFG_DMA_ERROR0x0001; | |||
| 223 | } else if (control & FW_CFG_DMA_READ0x0002) { | |||
| 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_ERROR0x0001; | |||
| 228 | } | |||
| 229 | /* clear rest of buffer */ | |||
| 230 | if (len < fw->length) | |||
| 231 | if (write_mem(fw->address + len, NULL((void *)0), | |||
| 232 | fw->length - len)) { | |||
| 233 | log_warnx("%s: write_mem error", __func__); | |||
| 234 | fw->control |= FW_CFG_DMA_ERROR0x0001; | |||
| 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_SELECT0x510: | |||
| 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_DATA0x511: | |||
| 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_HIGH0x514: | |||
| 301 | fw_cfg_dma_addr = (uint64_t)be32toh(data)(__uint32_t)(__builtin_constant_p(data) ? (__uint32_t)(((__uint32_t )(data) & 0xff) << 24 | ((__uint32_t)(data) & 0xff00 ) << 8 | ((__uint32_t)(data) & 0xff0000) >> 8 | ((__uint32_t)(data) & 0xff000000) >> 24) : __swap32md (data)) << 32; | |||
| 302 | break; | |||
| 303 | case FW_CFG_IO_DMA_ADDR_LOW0x518: | |||
| 304 | fw_cfg_dma_addr |= be32toh(data)(__uint32_t)(__builtin_constant_p(data) ? (__uint32_t)(((__uint32_t )(data) & 0xff) << 24 | ((__uint32_t)(data) & 0xff00 ) << 8 | ((__uint32_t)(data) & 0xff0000) >> 8 | ((__uint32_t)(data) & 0xff000000) >> 24) : __swap32md (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)(__uint32_t)(__builtin_constant_p(fw_dma.control) ? (__uint32_t )(((__uint32_t)(fw_dma.control) & 0xff) << 24 | ((__uint32_t )(fw_dma.control) & 0xff00) << 8 | ((__uint32_t)(fw_dma .control) & 0xff0000) >> 8 | ((__uint32_t)(fw_dma.control ) & 0xff000000) >> 24) : __swap32md(fw_dma.control) ); | |||
| 311 | fw_dma.length = be32toh(fw_dma.length)(__uint32_t)(__builtin_constant_p(fw_dma.length) ? (__uint32_t )(((__uint32_t)(fw_dma.length) & 0xff) << 24 | ((__uint32_t )(fw_dma.length) & 0xff00) << 8 | ((__uint32_t)(fw_dma .length) & 0xff0000) >> 8 | ((__uint32_t)(fw_dma.length ) & 0xff000000) >> 24) : __swap32md(fw_dma.length)); | |||
| 312 | fw_dma.address = be64toh(fw_dma.address)(__uint64_t)(__builtin_constant_p(fw_dma.address) ? (__uint64_t )((((__uint64_t)(fw_dma.address) & 0xff) << 56) | ( (__uint64_t)(fw_dma.address) & 0xff00ULL) << 40 | ( (__uint64_t)(fw_dma.address) & 0xff0000ULL) << 24 | ((__uint64_t)(fw_dma.address) & 0xff000000ULL) << 8 | ((__uint64_t)(fw_dma.address) & 0xff00000000ULL) >> 8 | ((__uint64_t)(fw_dma.address) & 0xff0000000000ULL) >> 24 | ((__uint64_t)(fw_dma.address) & 0xff000000000000ULL ) >> 40 | ((__uint64_t)(fw_dma.address) & 0xff00000000000000ULL ) >> 56) : __swap64md(fw_dma.address)); | |||
| 313 | ||||
| 314 | fw_cfg_handle_dma(&fw_dma); | |||
| 315 | ||||
| 316 | /* just write control byte back */ | |||
| 317 | data = be32toh(fw_dma.control)(__uint32_t)(__builtin_constant_p(fw_dma.control) ? (__uint32_t )(((__uint32_t)(fw_dma.control) & 0xff) << 24 | ((__uint32_t )(fw_dma.control) & 0xff00) << 8 | ((__uint32_t)(fw_dma .control) & 0xff0000) >> 8 | ((__uint32_t)(fw_dma.control ) & 0xff000000) >> 24) : __swap32md(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)(__uint64_t)(__builtin_constant_p(0x51454d5520434647ULL) ? (__uint64_t )((((__uint64_t)(0x51454d5520434647ULL) & 0xff) << 56 ) | ((__uint64_t)(0x51454d5520434647ULL) & 0xff00ULL) << 40 | ((__uint64_t)(0x51454d5520434647ULL) & 0xff0000ULL) << 24 | ((__uint64_t)(0x51454d5520434647ULL) & 0xff000000ULL ) << 8 | ((__uint64_t)(0x51454d5520434647ULL) & 0xff00000000ULL ) >> 8 | ((__uint64_t)(0x51454d5520434647ULL) & 0xff0000000000ULL ) >> 24 | ((__uint64_t)(0x51454d5520434647ULL) & 0xff000000000000ULL ) >> 40 | ((__uint64_t)(0x51454d5520434647ULL) & 0xff00000000000000ULL ) >> 56) : __swap64md(0x51454d5520434647ULL)); | |||
| 327 | switch (vei->vei.vei_port) { | |||
| 328 | case FW_CFG_IO_DMA_ADDR_HIGH0x514: | |||
| 329 | set_return_data(vei, sig >> 32); | |||
| 330 | break; | |||
| 331 | case FW_CFG_IO_DMA_ADDR_LOW0x518: | |||
| 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_FIRST0x0020; | |||
| 340 | ||||
| 341 | struct fw_cfg_file_entry { | |||
| 342 | TAILQ_ENTRY(fw_cfg_file_entry)struct { struct fw_cfg_file_entry *tqe_next; struct fw_cfg_file_entry **tqe_prev; } entry; | |||
| 343 | struct fw_cfg_file file; | |||
| 344 | void *data; | |||
| 345 | }; | |||
| 346 | ||||
| 347 | TAILQ_HEAD(, fw_cfg_file_entry)struct { struct fw_cfg_file_entry *tqh_first; struct fw_cfg_file_entry **tqh_last; } fw_cfg_files = | |||
| 348 | TAILQ_HEAD_INITIALIZER(fw_cfg_files){ ((void *)0), &(fw_cfg_files).tqh_first }; | |||
| 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)for((f) = ((&fw_cfg_files)->tqh_first); (f) != ((void * )0); (f) = ((f)->entry.tqe_next)) { | |||
| 356 | if (strcmp(name, f->file.name) == 0) | |||
| 357 | return f; | |||
| 358 | } | |||
| 359 | return NULL((void *)0); | |||
| 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((void *)0)) | |||
| 371 | fatal("%s", __func__); | |||
| 372 | ||||
| 373 | if ((f->data = malloc(len)) == NULL((void *)0)) | |||
| 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)(__uint32_t)(__builtin_constant_p(len) ? (__uint32_t)(((__uint32_t )(len) & 0xff) << 24 | ((__uint32_t)(len) & 0xff00 ) << 8 | ((__uint32_t)(len) & 0xff0000) >> 8 | ((__uint32_t)(len) & 0xff000000) >> 24) : __swap32md (len)); | |||
| 381 | f->file.selector = htobe16(file_id++)(__uint16_t)(__builtin_constant_p(file_id++) ? (__uint16_t)(( (__uint16_t)(file_id++) & 0xffU) << 8 | ((__uint16_t )(file_id++) & 0xff00U) >> 8) : __swap16md(file_id++ )); | |||
| 382 | memcpy(f->data, data, len); | |||
| 383 | ||||
| 384 | TAILQ_INSERT_TAIL(&fw_cfg_files, f, entry)do { (f)->entry.tqe_next = ((void *)0); (f)->entry.tqe_prev = (&fw_cfg_files)->tqh_last; *(&fw_cfg_files)-> tqh_last = (f); (&fw_cfg_files)->tqh_last = &(f)-> entry.tqe_next; } while (0); | |||
| 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)(__uint16_t)(__builtin_constant_p(id) ? (__uint16_t)(((__uint16_t )(id) & 0xffU) << 8 | ((__uint16_t)(id) & 0xff00U ) >> 8) : __swap16md(id)); | |||
| 393 | TAILQ_FOREACH(f, &fw_cfg_files, entry)for((f) = ((&fw_cfg_files)->tqh_first); (f) != ((void * )0); (f) = ((f)->entry.tqe_next)) | |||
| 394 | if (f->file.selector == id) { | |||
| 395 | size_t size = be32toh(f->file.size)(__uint32_t)(__builtin_constant_p(f->file.size) ? (__uint32_t )(((__uint32_t)(f->file.size) & 0xff) << 24 | (( __uint32_t)(f->file.size) & 0xff00) << 8 | ((__uint32_t )(f->file.size) & 0xff0000) >> 8 | ((__uint32_t) (f->file.size) & 0xff000000) >> 24) : __swap32md (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)for((f) = ((&fw_cfg_files)->tqh_first); (f) != ((void * )0); (f) = ((f)->entry.tqe_next)) | |||
| 414 | count++; | |||
| 415 | ||||
| 416 | size = sizeof(count) + count * sizeof(struct fw_cfg_file); | |||
| 417 | if ((data = malloc(size)) == NULL((void *)0)) | |||
| 418 | fatal("%s", __func__); | |||
| 419 | *data = htobe32(count)(__uint32_t)(__builtin_constant_p(count) ? (__uint32_t)(((__uint32_t )(count) & 0xff) << 24 | ((__uint32_t)(count) & 0xff00) << 8 | ((__uint32_t)(count) & 0xff0000) >> 8 | ((__uint32_t)(count) & 0xff000000) >> 24) : __swap32md (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)for((f) = ((&fw_cfg_files)->tqh_first); (f) != ((void * )0); (f) = ((f)->entry.tqe_next)) { | |||
| 424 | log_debug(" %6dB %04x %s", be32toh(f->file.size)(__uint32_t)(__builtin_constant_p(f->file.size) ? (__uint32_t )(((__uint32_t)(f->file.size) & 0xff) << 24 | (( __uint32_t)(f->file.size) & 0xff00) << 8 | ((__uint32_t )(f->file.size) & 0xff0000) >> 8 | ((__uint32_t) (f->file.size) & 0xff000000) >> 24) : __swap32md (f->file.size)), | |||
| 425 | be16toh(f->file.selector)(__uint16_t)(__builtin_constant_p(f->file.selector) ? (__uint16_t )(((__uint16_t)(f->file.selector) & 0xffU) << 8 | ((__uint16_t)(f->file.selector) & 0xff00U) >> 8 ) : __swap16md(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 | } | |||
|