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