]>
Commit | Line | Data |
---|---|---|
5fe141fd FB |
1 | /* |
2 | * QEMU Executable loader | |
5fafdf24 | 3 | * |
5fe141fd | 4 | * Copyright (c) 2006 Fabrice Bellard |
5fafdf24 | 5 | * |
5fe141fd FB |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
5a123577 AL |
23 | * |
24 | * Gunzip functionality in this file is derived from u-boot: | |
25 | * | |
26 | * (C) Copyright 2008 Semihalf | |
27 | * | |
28 | * (C) Copyright 2000-2005 | |
29 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | |
30 | * | |
31 | * This program is free software; you can redistribute it and/or | |
32 | * modify it under the terms of the GNU General Public License as | |
33 | * published by the Free Software Foundation; either version 2 of | |
34 | * the License, or (at your option) any later version. | |
35 | * | |
36 | * This program is distributed in the hope that it will be useful, | |
37 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
38 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
39 | * GNU General Public License for more details. | |
40 | * | |
fad6cb1a | 41 | * You should have received a copy of the GNU General Public License along |
8167ee88 | 42 | * with this program; if not, see <http://www.gnu.org/licenses/>. |
5fe141fd | 43 | */ |
5a123577 | 44 | |
18c86e2b | 45 | #include "qemu/osdep.h" |
2c65db5e | 46 | #include "qemu/datadir.h" |
da34e65c | 47 | #include "qapi/error.h" |
dd98234c DB |
48 | #include "qapi/qapi-commands-machine.h" |
49 | #include "qapi/type-helpers.h" | |
26b8e6dc | 50 | #include "trace.h" |
83c9f4ca | 51 | #include "hw/hw.h" |
76cad711 | 52 | #include "disas/disas.h" |
d6454270 | 53 | #include "migration/vmstate.h" |
83c9089e | 54 | #include "monitor/monitor.h" |
71e8a915 | 55 | #include "sysemu/reset.h" |
9c17d615 | 56 | #include "sysemu/sysemu.h" |
47b43a1f | 57 | #include "uboot_image.h" |
83c9f4ca | 58 | #include "hw/loader.h" |
0d09e41a | 59 | #include "hw/nvram/fw_cfg.h" |
022c62cb | 60 | #include "exec/memory.h" |
71ae9e94 | 61 | #include "hw/boards.h" |
f348b6d1 | 62 | #include "qemu/cutils.h" |
355477f8 | 63 | #include "sysemu/runstate.h" |
5fe141fd | 64 | |
5a123577 AL |
65 | #include <zlib.h> |
66 | ||
97fe84f5 PB |
67 | static int roms_loaded; |
68 | ||
5fe141fd | 69 | /* return the size or -1 if error */ |
f3839fda | 70 | int64_t get_image_size(const char *filename) |
5fe141fd | 71 | { |
f3839fda LZ |
72 | int fd; |
73 | int64_t size; | |
5fe141fd FB |
74 | fd = open(filename, O_RDONLY | O_BINARY); |
75 | if (fd < 0) | |
76 | return -1; | |
77 | size = lseek(fd, 0, SEEK_END); | |
78 | close(fd); | |
79 | return size; | |
80 | } | |
81 | ||
ea87616d BH |
82 | /* return the size or -1 if error */ |
83 | ssize_t load_image_size(const char *filename, void *addr, size_t size) | |
84 | { | |
85 | int fd; | |
1f40547f | 86 | ssize_t actsize, l = 0; |
ea87616d BH |
87 | |
88 | fd = open(filename, O_RDONLY | O_BINARY); | |
89 | if (fd < 0) { | |
90 | return -1; | |
91 | } | |
92 | ||
1f40547f LZ |
93 | while ((actsize = read(fd, addr + l, size - l)) > 0) { |
94 | l += actsize; | |
ea87616d | 95 | } |
1f40547f | 96 | |
ea87616d BH |
97 | close(fd); |
98 | ||
1f40547f | 99 | return actsize < 0 ? -1 : l; |
ea87616d BH |
100 | } |
101 | ||
293f78bc | 102 | /* read()-like version */ |
725e14e9 | 103 | ssize_t read_targphys(const char *name, |
a8170e5e | 104 | int fd, hwaddr dst_addr, size_t nbytes) |
293f78bc | 105 | { |
45a50b16 | 106 | uint8_t *buf; |
725e14e9 | 107 | ssize_t did; |
45a50b16 | 108 | |
7267c094 | 109 | buf = g_malloc(nbytes); |
45a50b16 GH |
110 | did = read(fd, buf, nbytes); |
111 | if (did > 0) | |
112 | rom_add_blob_fixed("read", buf, did, dst_addr); | |
7267c094 | 113 | g_free(buf); |
45a50b16 | 114 | return did; |
293f78bc BS |
115 | } |
116 | ||
af975131 JI |
117 | ssize_t load_image_targphys(const char *filename, |
118 | hwaddr addr, uint64_t max_sz) | |
93ffc7c7 AF |
119 | { |
120 | return load_image_targphys_as(filename, addr, max_sz, NULL); | |
121 | } | |
122 | ||
123 | /* return the size or -1 if error */ | |
af975131 JI |
124 | ssize_t load_image_targphys_as(const char *filename, |
125 | hwaddr addr, uint64_t max_sz, AddressSpace *as) | |
293f78bc | 126 | { |
af975131 | 127 | ssize_t size; |
293f78bc | 128 | |
45a50b16 | 129 | size = get_image_size(filename); |
2a4e2e49 | 130 | if (size < 0 || size > max_sz) { |
17df768c BH |
131 | return -1; |
132 | } | |
133 | if (size > 0) { | |
32fb354b PMD |
134 | if (rom_add_file_fixed_as(filename, addr, -1, as) < 0) { |
135 | return -1; | |
136 | } | |
17df768c | 137 | } |
45a50b16 | 138 | return size; |
293f78bc BS |
139 | } |
140 | ||
af975131 | 141 | ssize_t load_image_mr(const char *filename, MemoryRegion *mr) |
76151cac | 142 | { |
af975131 | 143 | ssize_t size; |
76151cac PM |
144 | |
145 | if (!memory_access_is_direct(mr, false)) { | |
146 | /* Can only load an image into RAM or ROM */ | |
147 | return -1; | |
148 | } | |
149 | ||
150 | size = get_image_size(filename); | |
151 | ||
2a4e2e49 | 152 | if (size < 0 || size > memory_region_size(mr)) { |
76151cac PM |
153 | return -1; |
154 | } | |
155 | if (size > 0) { | |
156 | if (rom_add_file_mr(filename, mr, -1) < 0) { | |
157 | return -1; | |
158 | } | |
159 | } | |
160 | return size; | |
161 | } | |
162 | ||
a8170e5e | 163 | void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size, |
293f78bc BS |
164 | const char *source) |
165 | { | |
293f78bc | 166 | const char *nulp; |
3c178e72 | 167 | char *ptr; |
293f78bc BS |
168 | |
169 | if (buf_size <= 0) return; | |
170 | nulp = memchr(source, 0, buf_size); | |
171 | if (nulp) { | |
3c178e72 | 172 | rom_add_blob_fixed(name, source, (nulp - source) + 1, dest); |
293f78bc | 173 | } else { |
3c178e72 | 174 | rom_add_blob_fixed(name, source, buf_size, dest); |
0f0f8b61 | 175 | ptr = rom_ptr(dest + buf_size - 1, sizeof(*ptr)); |
3c178e72 | 176 | *ptr = 0; |
293f78bc BS |
177 | } |
178 | } | |
179 | ||
5fe141fd FB |
180 | /* A.OUT loader */ |
181 | ||
182 | struct exec | |
183 | { | |
184 | uint32_t a_info; /* Use macros N_MAGIC, etc for access */ | |
185 | uint32_t a_text; /* length of text, in bytes */ | |
186 | uint32_t a_data; /* length of data, in bytes */ | |
187 | uint32_t a_bss; /* length of uninitialized data area, in bytes */ | |
188 | uint32_t a_syms; /* length of symbol table data in file, in bytes */ | |
189 | uint32_t a_entry; /* start address */ | |
190 | uint32_t a_trsize; /* length of relocation info for text, in bytes */ | |
191 | uint32_t a_drsize; /* length of relocation info for data, in bytes */ | |
192 | }; | |
193 | ||
5fe141fd FB |
194 | static void bswap_ahdr(struct exec *e) |
195 | { | |
196 | bswap32s(&e->a_info); | |
197 | bswap32s(&e->a_text); | |
198 | bswap32s(&e->a_data); | |
199 | bswap32s(&e->a_bss); | |
200 | bswap32s(&e->a_syms); | |
201 | bswap32s(&e->a_entry); | |
202 | bswap32s(&e->a_trsize); | |
203 | bswap32s(&e->a_drsize); | |
204 | } | |
5fe141fd FB |
205 | |
206 | #define N_MAGIC(exec) ((exec).a_info & 0xffff) | |
207 | #define OMAGIC 0407 | |
208 | #define NMAGIC 0410 | |
209 | #define ZMAGIC 0413 | |
210 | #define QMAGIC 0314 | |
211 | #define _N_HDROFF(x) (1024 - sizeof (struct exec)) | |
212 | #define N_TXTOFF(x) \ | |
213 | (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \ | |
214 | (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec))) | |
ca20cf32 BS |
215 | #define N_TXTADDR(x, target_page_size) (N_MAGIC(x) == QMAGIC ? target_page_size : 0) |
216 | #define _N_SEGMENT_ROUND(x, target_page_size) (((x) + target_page_size - 1) & ~(target_page_size - 1)) | |
5fe141fd | 217 | |
ca20cf32 | 218 | #define _N_TXTENDADDR(x, target_page_size) (N_TXTADDR(x, target_page_size)+(x).a_text) |
5fe141fd | 219 | |
ca20cf32 BS |
220 | #define N_DATADDR(x, target_page_size) \ |
221 | (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x, target_page_size)) \ | |
222 | : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x, target_page_size), target_page_size))) | |
5fe141fd FB |
223 | |
224 | ||
af975131 JI |
225 | ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, |
226 | int bswap_needed, hwaddr target_page_size) | |
5fe141fd | 227 | { |
725e14e9 MA |
228 | int fd; |
229 | ssize_t size, ret; | |
5fe141fd FB |
230 | struct exec e; |
231 | uint32_t magic; | |
232 | ||
233 | fd = open(filename, O_RDONLY | O_BINARY); | |
234 | if (fd < 0) | |
235 | return -1; | |
236 | ||
237 | size = read(fd, &e, sizeof(e)); | |
238 | if (size < 0) | |
239 | goto fail; | |
240 | ||
ca20cf32 BS |
241 | if (bswap_needed) { |
242 | bswap_ahdr(&e); | |
243 | } | |
5fe141fd FB |
244 | |
245 | magic = N_MAGIC(e); | |
246 | switch (magic) { | |
247 | case ZMAGIC: | |
248 | case QMAGIC: | |
249 | case OMAGIC: | |
293f78bc BS |
250 | if (e.a_text + e.a_data > max_sz) |
251 | goto fail; | |
7d37435b PB |
252 | lseek(fd, N_TXTOFF(e), SEEK_SET); |
253 | size = read_targphys(filename, fd, addr, e.a_text + e.a_data); | |
254 | if (size < 0) | |
255 | goto fail; | |
256 | break; | |
5fe141fd | 257 | case NMAGIC: |
ca20cf32 | 258 | if (N_DATADDR(e, target_page_size) + e.a_data > max_sz) |
293f78bc | 259 | goto fail; |
7d37435b PB |
260 | lseek(fd, N_TXTOFF(e), SEEK_SET); |
261 | size = read_targphys(filename, fd, addr, e.a_text); | |
262 | if (size < 0) | |
263 | goto fail; | |
45a50b16 | 264 | ret = read_targphys(filename, fd, addr + N_DATADDR(e, target_page_size), |
ca20cf32 | 265 | e.a_data); |
7d37435b PB |
266 | if (ret < 0) |
267 | goto fail; | |
268 | size += ret; | |
269 | break; | |
5fe141fd | 270 | default: |
7d37435b | 271 | goto fail; |
5fe141fd FB |
272 | } |
273 | close(fd); | |
274 | return size; | |
275 | fail: | |
276 | close(fd); | |
277 | return -1; | |
278 | } | |
279 | ||
280 | /* ELF loader */ | |
281 | ||
6cbfb86f | 282 | static void *load_at(int fd, off_t offset, size_t size) |
5fe141fd FB |
283 | { |
284 | void *ptr; | |
285 | if (lseek(fd, offset, SEEK_SET) < 0) | |
286 | return NULL; | |
7267c094 | 287 | ptr = g_malloc(size); |
5fe141fd | 288 | if (read(fd, ptr, size) != size) { |
7267c094 | 289 | g_free(ptr); |
5fe141fd FB |
290 | return NULL; |
291 | } | |
292 | return ptr; | |
293 | } | |
294 | ||
3efa9a67 | 295 | #ifdef ELF_CLASS |
296 | #undef ELF_CLASS | |
297 | #endif | |
5fe141fd FB |
298 | |
299 | #define ELF_CLASS ELFCLASS32 | |
300 | #include "elf.h" | |
301 | ||
302 | #define SZ 32 | |
303 | #define elf_word uint32_t | |
82790064 | 304 | #define elf_sword int32_t |
5fe141fd | 305 | #define bswapSZs bswap32s |
83c9f4ca | 306 | #include "hw/elf_ops.h" |
5fe141fd FB |
307 | |
308 | #undef elfhdr | |
309 | #undef elf_phdr | |
310 | #undef elf_shdr | |
311 | #undef elf_sym | |
5dce07e1 | 312 | #undef elf_rela |
5fe141fd FB |
313 | #undef elf_note |
314 | #undef elf_word | |
82790064 | 315 | #undef elf_sword |
5fe141fd FB |
316 | #undef bswapSZs |
317 | #undef SZ | |
318 | #define elfhdr elf64_hdr | |
319 | #define elf_phdr elf64_phdr | |
320 | #define elf_note elf64_note | |
321 | #define elf_shdr elf64_shdr | |
322 | #define elf_sym elf64_sym | |
5dce07e1 | 323 | #define elf_rela elf64_rela |
5fe141fd | 324 | #define elf_word uint64_t |
82790064 | 325 | #define elf_sword int64_t |
5fe141fd FB |
326 | #define bswapSZs bswap64s |
327 | #define SZ 64 | |
83c9f4ca | 328 | #include "hw/elf_ops.h" |
5fe141fd | 329 | |
8975eb89 | 330 | const char *load_elf_strerror(ssize_t error) |
18674b26 AK |
331 | { |
332 | switch (error) { | |
333 | case 0: | |
334 | return "No error"; | |
335 | case ELF_LOAD_FAILED: | |
336 | return "Failed to load ELF"; | |
337 | case ELF_LOAD_NOT_ELF: | |
338 | return "The image is not ELF"; | |
339 | case ELF_LOAD_WRONG_ARCH: | |
340 | return "The image is from incompatible architecture"; | |
341 | case ELF_LOAD_WRONG_ENDIAN: | |
342 | return "The image has incorrect endianness"; | |
41a26351 SG |
343 | case ELF_LOAD_TOO_BIG: |
344 | return "The image segments are too big to load"; | |
18674b26 AK |
345 | default: |
346 | return "Unknown error"; | |
347 | } | |
348 | } | |
349 | ||
04ae712a PC |
350 | void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp) |
351 | { | |
352 | int fd; | |
353 | uint8_t e_ident_local[EI_NIDENT]; | |
354 | uint8_t *e_ident; | |
355 | size_t hdr_size, off; | |
356 | bool is64l; | |
357 | ||
358 | if (!hdr) { | |
359 | hdr = e_ident_local; | |
360 | } | |
361 | e_ident = hdr; | |
362 | ||
363 | fd = open(filename, O_RDONLY | O_BINARY); | |
364 | if (fd < 0) { | |
365 | error_setg_errno(errp, errno, "Failed to open file: %s", filename); | |
366 | return; | |
367 | } | |
368 | if (read(fd, hdr, EI_NIDENT) != EI_NIDENT) { | |
369 | error_setg_errno(errp, errno, "Failed to read file: %s", filename); | |
370 | goto fail; | |
371 | } | |
372 | if (e_ident[0] != ELFMAG0 || | |
373 | e_ident[1] != ELFMAG1 || | |
374 | e_ident[2] != ELFMAG2 || | |
375 | e_ident[3] != ELFMAG3) { | |
376 | error_setg(errp, "Bad ELF magic"); | |
377 | goto fail; | |
378 | } | |
379 | ||
380 | is64l = e_ident[EI_CLASS] == ELFCLASS64; | |
381 | hdr_size = is64l ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr); | |
382 | if (is64) { | |
383 | *is64 = is64l; | |
384 | } | |
385 | ||
386 | off = EI_NIDENT; | |
387 | while (hdr != e_ident_local && off < hdr_size) { | |
388 | size_t br = read(fd, hdr + off, hdr_size - off); | |
389 | switch (br) { | |
390 | case 0: | |
391 | error_setg(errp, "File too short: %s", filename); | |
392 | goto fail; | |
393 | case -1: | |
394 | error_setg_errno(errp, errno, "Failed to read file: %s", | |
395 | filename); | |
396 | goto fail; | |
397 | } | |
398 | off += br; | |
399 | } | |
400 | ||
401 | fail: | |
402 | close(fd); | |
403 | } | |
404 | ||
5fe141fd | 405 | /* return < 0 if error, otherwise the number of bytes loaded in memory */ |
8975eb89 LM |
406 | ssize_t load_elf(const char *filename, |
407 | uint64_t (*elf_note_fn)(void *, void *, bool), | |
408 | uint64_t (*translate_fn)(void *, uint64_t), | |
409 | void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, | |
410 | uint64_t *highaddr, uint32_t *pflags, int big_endian, | |
411 | int elf_machine, int clear_lsb, int data_swab) | |
70bb1d16 | 412 | { |
4366e1db | 413 | return load_elf_as(filename, elf_note_fn, translate_fn, translate_opaque, |
6cdda0ff AM |
414 | pentry, lowaddr, highaddr, pflags, big_endian, |
415 | elf_machine, clear_lsb, data_swab, NULL); | |
70bb1d16 AF |
416 | } |
417 | ||
418 | /* return < 0 if error, otherwise the number of bytes loaded in memory */ | |
8975eb89 LM |
419 | ssize_t load_elf_as(const char *filename, |
420 | uint64_t (*elf_note_fn)(void *, void *, bool), | |
421 | uint64_t (*translate_fn)(void *, uint64_t), | |
422 | void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, | |
423 | uint64_t *highaddr, uint32_t *pflags, int big_endian, | |
424 | int elf_machine, int clear_lsb, int data_swab, | |
425 | AddressSpace *as) | |
34f1b23f | 426 | { |
4366e1db | 427 | return load_elf_ram(filename, elf_note_fn, translate_fn, translate_opaque, |
6cdda0ff AM |
428 | pentry, lowaddr, highaddr, pflags, big_endian, |
429 | elf_machine, clear_lsb, data_swab, as, true); | |
34f1b23f FA |
430 | } |
431 | ||
432 | /* return < 0 if error, otherwise the number of bytes loaded in memory */ | |
8975eb89 LM |
433 | ssize_t load_elf_ram(const char *filename, |
434 | uint64_t (*elf_note_fn)(void *, void *, bool), | |
435 | uint64_t (*translate_fn)(void *, uint64_t), | |
436 | void *translate_opaque, uint64_t *pentry, | |
437 | uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, | |
438 | int big_endian, int elf_machine, int clear_lsb, | |
439 | int data_swab, AddressSpace *as, bool load_rom) | |
a2480ffa | 440 | { |
4366e1db LM |
441 | return load_elf_ram_sym(filename, elf_note_fn, |
442 | translate_fn, translate_opaque, | |
6cdda0ff | 443 | pentry, lowaddr, highaddr, pflags, big_endian, |
a2480ffa MC |
444 | elf_machine, clear_lsb, data_swab, as, |
445 | load_rom, NULL); | |
446 | } | |
447 | ||
448 | /* return < 0 if error, otherwise the number of bytes loaded in memory */ | |
8975eb89 LM |
449 | ssize_t load_elf_ram_sym(const char *filename, |
450 | uint64_t (*elf_note_fn)(void *, void *, bool), | |
451 | uint64_t (*translate_fn)(void *, uint64_t), | |
452 | void *translate_opaque, uint64_t *pentry, | |
453 | uint64_t *lowaddr, uint64_t *highaddr, | |
454 | uint32_t *pflags, int big_endian, int elf_machine, | |
455 | int clear_lsb, int data_swab, | |
456 | AddressSpace *as, bool load_rom, symbol_fn_t sym_cb) | |
5fe141fd | 457 | { |
8975eb89 LM |
458 | int fd, data_order, target_data_order, must_swab; |
459 | ssize_t ret = ELF_LOAD_FAILED; | |
5fe141fd FB |
460 | uint8_t e_ident[EI_NIDENT]; |
461 | ||
699e4642 | 462 | fd = open(filename, O_RDONLY | O_BINARY); |
5fe141fd FB |
463 | if (fd < 0) { |
464 | perror(filename); | |
465 | return -1; | |
466 | } | |
467 | if (read(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident)) | |
468 | goto fail; | |
469 | if (e_ident[0] != ELFMAG0 || | |
470 | e_ident[1] != ELFMAG1 || | |
471 | e_ident[2] != ELFMAG2 || | |
18674b26 AK |
472 | e_ident[3] != ELFMAG3) { |
473 | ret = ELF_LOAD_NOT_ELF; | |
5fe141fd | 474 | goto fail; |
18674b26 | 475 | } |
e03b5686 | 476 | #if HOST_BIG_ENDIAN |
5fe141fd FB |
477 | data_order = ELFDATA2MSB; |
478 | #else | |
479 | data_order = ELFDATA2LSB; | |
480 | #endif | |
481 | must_swab = data_order != e_ident[EI_DATA]; | |
ca20cf32 BS |
482 | if (big_endian) { |
483 | target_data_order = ELFDATA2MSB; | |
484 | } else { | |
485 | target_data_order = ELFDATA2LSB; | |
486 | } | |
9042c0e2 | 487 | |
cedf9a6f | 488 | if (target_data_order != e_ident[EI_DATA]) { |
18674b26 | 489 | ret = ELF_LOAD_WRONG_ENDIAN; |
cedf9a6f BS |
490 | goto fail; |
491 | } | |
9042c0e2 | 492 | |
5fe141fd FB |
493 | lseek(fd, 0, SEEK_SET); |
494 | if (e_ident[EI_CLASS] == ELFCLASS64) { | |
4366e1db LM |
495 | ret = load_elf64(filename, fd, elf_note_fn, |
496 | translate_fn, translate_opaque, must_swab, | |
6cdda0ff AM |
497 | pentry, lowaddr, highaddr, pflags, elf_machine, |
498 | clear_lsb, data_swab, as, load_rom, sym_cb); | |
5fe141fd | 499 | } else { |
4366e1db LM |
500 | ret = load_elf32(filename, fd, elf_note_fn, |
501 | translate_fn, translate_opaque, must_swab, | |
6cdda0ff AM |
502 | pentry, lowaddr, highaddr, pflags, elf_machine, |
503 | clear_lsb, data_swab, as, load_rom, sym_cb); | |
5fe141fd FB |
504 | } |
505 | ||
5fe141fd FB |
506 | fail: |
507 | close(fd); | |
18674b26 | 508 | return ret; |
5fe141fd | 509 | } |
1c7b3754 | 510 | |
c227f099 | 511 | static void bswap_uboot_header(uboot_image_header_t *hdr) |
1c7b3754 | 512 | { |
e03b5686 | 513 | #if !HOST_BIG_ENDIAN |
1c7b3754 PB |
514 | bswap32s(&hdr->ih_magic); |
515 | bswap32s(&hdr->ih_hcrc); | |
516 | bswap32s(&hdr->ih_time); | |
517 | bswap32s(&hdr->ih_size); | |
518 | bswap32s(&hdr->ih_load); | |
519 | bswap32s(&hdr->ih_ep); | |
520 | bswap32s(&hdr->ih_dcrc); | |
521 | #endif | |
522 | } | |
523 | ||
5a123577 AL |
524 | |
525 | #define ZALLOC_ALIGNMENT 16 | |
526 | ||
527 | static void *zalloc(void *x, unsigned items, unsigned size) | |
528 | { | |
529 | void *p; | |
530 | ||
531 | size *= items; | |
532 | size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1); | |
533 | ||
7267c094 | 534 | p = g_malloc(size); |
5a123577 AL |
535 | |
536 | return (p); | |
537 | } | |
538 | ||
d084eab6 | 539 | static void zfree(void *x, void *addr) |
5a123577 | 540 | { |
7267c094 | 541 | g_free(addr); |
5a123577 AL |
542 | } |
543 | ||
544 | ||
545 | #define HEAD_CRC 2 | |
546 | #define EXTRA_FIELD 4 | |
547 | #define ORIG_NAME 8 | |
548 | #define COMMENT 0x10 | |
549 | #define RESERVED 0xe0 | |
550 | ||
551 | #define DEFLATED 8 | |
552 | ||
51b58561 | 553 | ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen) |
5a123577 AL |
554 | { |
555 | z_stream s; | |
556 | ssize_t dstbytes; | |
557 | int r, i, flags; | |
558 | ||
559 | /* skip header */ | |
560 | i = 10; | |
312c496a PM |
561 | if (srclen < 4) { |
562 | goto toosmall; | |
563 | } | |
5a123577 AL |
564 | flags = src[3]; |
565 | if (src[2] != DEFLATED || (flags & RESERVED) != 0) { | |
566 | puts ("Error: Bad gzipped data\n"); | |
567 | return -1; | |
568 | } | |
312c496a PM |
569 | if ((flags & EXTRA_FIELD) != 0) { |
570 | if (srclen < 12) { | |
571 | goto toosmall; | |
572 | } | |
5a123577 | 573 | i = 12 + src[10] + (src[11] << 8); |
312c496a PM |
574 | } |
575 | if ((flags & ORIG_NAME) != 0) { | |
576 | while (i < srclen && src[i++] != 0) { | |
577 | /* do nothing */ | |
578 | } | |
579 | } | |
580 | if ((flags & COMMENT) != 0) { | |
581 | while (i < srclen && src[i++] != 0) { | |
582 | /* do nothing */ | |
583 | } | |
584 | } | |
585 | if ((flags & HEAD_CRC) != 0) { | |
5a123577 | 586 | i += 2; |
312c496a | 587 | } |
5a123577 | 588 | if (i >= srclen) { |
312c496a | 589 | goto toosmall; |
5a123577 AL |
590 | } |
591 | ||
592 | s.zalloc = zalloc; | |
d084eab6 | 593 | s.zfree = zfree; |
5a123577 AL |
594 | |
595 | r = inflateInit2(&s, -MAX_WBITS); | |
596 | if (r != Z_OK) { | |
597 | printf ("Error: inflateInit2() returned %d\n", r); | |
598 | return (-1); | |
599 | } | |
600 | s.next_in = src + i; | |
601 | s.avail_in = srclen - i; | |
602 | s.next_out = dst; | |
603 | s.avail_out = dstlen; | |
604 | r = inflate(&s, Z_FINISH); | |
605 | if (r != Z_OK && r != Z_STREAM_END) { | |
606 | printf ("Error: inflate() returned %d\n", r); | |
607 | return -1; | |
608 | } | |
609 | dstbytes = s.next_out - (unsigned char *) dst; | |
610 | inflateEnd(&s); | |
611 | ||
612 | return dstbytes; | |
312c496a PM |
613 | |
614 | toosmall: | |
615 | puts("Error: gunzip out of data in header\n"); | |
616 | return -1; | |
5a123577 AL |
617 | } |
618 | ||
1c7b3754 | 619 | /* Load a U-Boot image. */ |
af975131 JI |
620 | static ssize_t load_uboot_image(const char *filename, hwaddr *ep, |
621 | hwaddr *loadaddr, int *is_linux, | |
622 | uint8_t image_type, | |
623 | uint64_t (*translate_fn)(void *, uint64_t), | |
624 | void *translate_opaque, AddressSpace *as) | |
1c7b3754 | 625 | { |
1c7b3754 | 626 | int fd; |
af975131 | 627 | ssize_t size; |
84aee0de | 628 | hwaddr address; |
c227f099 AL |
629 | uboot_image_header_t h; |
630 | uboot_image_header_t *hdr = &h; | |
1c7b3754 | 631 | uint8_t *data = NULL; |
265ca29a | 632 | int ret = -1; |
84aee0de | 633 | int do_uncompress = 0; |
1c7b3754 PB |
634 | |
635 | fd = open(filename, O_RDONLY | O_BINARY); | |
636 | if (fd < 0) | |
637 | return -1; | |
638 | ||
c227f099 | 639 | size = read(fd, hdr, sizeof(uboot_image_header_t)); |
a18e9312 | 640 | if (size < sizeof(uboot_image_header_t)) { |
265ca29a | 641 | goto out; |
a18e9312 | 642 | } |
1c7b3754 PB |
643 | |
644 | bswap_uboot_header(hdr); | |
645 | ||
646 | if (hdr->ih_magic != IH_MAGIC) | |
265ca29a | 647 | goto out; |
1c7b3754 | 648 | |
84aee0de | 649 | if (hdr->ih_type != image_type) { |
f831f955 NH |
650 | if (!(image_type == IH_TYPE_KERNEL && |
651 | hdr->ih_type == IH_TYPE_KERNEL_NOLOAD)) { | |
652 | fprintf(stderr, "Wrong image type %d, expected %d\n", hdr->ih_type, | |
653 | image_type); | |
654 | goto out; | |
655 | } | |
1c7b3754 PB |
656 | } |
657 | ||
84aee0de SB |
658 | /* TODO: Implement other image types. */ |
659 | switch (hdr->ih_type) { | |
f831f955 NH |
660 | case IH_TYPE_KERNEL_NOLOAD: |
661 | if (!loadaddr || *loadaddr == LOAD_UIMAGE_LOADADDR_INVALID) { | |
662 | fprintf(stderr, "this image format (kernel_noload) cannot be " | |
663 | "loaded on this machine type"); | |
664 | goto out; | |
665 | } | |
666 | ||
667 | hdr->ih_load = *loadaddr + sizeof(*hdr); | |
668 | hdr->ih_ep += hdr->ih_load; | |
669 | /* fall through */ | |
84aee0de SB |
670 | case IH_TYPE_KERNEL: |
671 | address = hdr->ih_load; | |
25bda50a MF |
672 | if (translate_fn) { |
673 | address = translate_fn(translate_opaque, address); | |
674 | } | |
84aee0de SB |
675 | if (loadaddr) { |
676 | *loadaddr = hdr->ih_load; | |
677 | } | |
678 | ||
679 | switch (hdr->ih_comp) { | |
680 | case IH_COMP_NONE: | |
681 | break; | |
682 | case IH_COMP_GZIP: | |
683 | do_uncompress = 1; | |
684 | break; | |
685 | default: | |
686 | fprintf(stderr, | |
687 | "Unable to load u-boot images with compression type %d\n", | |
688 | hdr->ih_comp); | |
689 | goto out; | |
690 | } | |
691 | ||
692 | if (ep) { | |
693 | *ep = hdr->ih_ep; | |
694 | } | |
695 | ||
696 | /* TODO: Check CPU type. */ | |
697 | if (is_linux) { | |
698 | if (hdr->ih_os == IH_OS_LINUX) { | |
699 | *is_linux = 1; | |
8fe63fe8 BM |
700 | } else if (hdr->ih_os == IH_OS_VXWORKS) { |
701 | /* | |
702 | * VxWorks 7 uses the same boot interface as the Linux kernel | |
703 | * on Arm (64-bit only), PowerPC and RISC-V architectures. | |
704 | */ | |
705 | switch (hdr->ih_arch) { | |
706 | case IH_ARCH_ARM64: | |
707 | case IH_ARCH_PPC: | |
708 | case IH_ARCH_RISCV: | |
709 | *is_linux = 1; | |
710 | break; | |
711 | default: | |
712 | *is_linux = 0; | |
713 | break; | |
714 | } | |
84aee0de SB |
715 | } else { |
716 | *is_linux = 0; | |
717 | } | |
718 | } | |
719 | ||
720 | break; | |
721 | case IH_TYPE_RAMDISK: | |
722 | address = *loadaddr; | |
5a123577 AL |
723 | break; |
724 | default: | |
84aee0de | 725 | fprintf(stderr, "Unsupported u-boot image type %d\n", hdr->ih_type); |
265ca29a | 726 | goto out; |
1c7b3754 PB |
727 | } |
728 | ||
7267c094 | 729 | data = g_malloc(hdr->ih_size); |
1c7b3754 PB |
730 | |
731 | if (read(fd, data, hdr->ih_size) != hdr->ih_size) { | |
732 | fprintf(stderr, "Error reading file\n"); | |
265ca29a | 733 | goto out; |
1c7b3754 PB |
734 | } |
735 | ||
84aee0de | 736 | if (do_uncompress) { |
5a123577 AL |
737 | uint8_t *compressed_data; |
738 | size_t max_bytes; | |
739 | ssize_t bytes; | |
740 | ||
741 | compressed_data = data; | |
742 | max_bytes = UBOOT_MAX_GUNZIP_BYTES; | |
7267c094 | 743 | data = g_malloc(max_bytes); |
5a123577 AL |
744 | |
745 | bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size); | |
7267c094 | 746 | g_free(compressed_data); |
5a123577 AL |
747 | if (bytes < 0) { |
748 | fprintf(stderr, "Unable to decompress gzipped image!\n"); | |
749 | goto out; | |
750 | } | |
751 | hdr->ih_size = bytes; | |
752 | } | |
753 | ||
5e774eb3 | 754 | rom_add_blob_fixed_as(filename, data, hdr->ih_size, address, as); |
21cafd08 | 755 | |
265ca29a | 756 | ret = hdr->ih_size; |
1c7b3754 | 757 | |
265ca29a | 758 | out: |
ef1e1e07 | 759 | g_free(data); |
1c7b3754 | 760 | close(fd); |
265ca29a | 761 | return ret; |
1c7b3754 | 762 | } |
45a50b16 | 763 | |
af975131 JI |
764 | ssize_t load_uimage(const char *filename, hwaddr *ep, hwaddr *loadaddr, |
765 | int *is_linux, | |
766 | uint64_t (*translate_fn)(void *, uint64_t), | |
767 | void *translate_opaque) | |
84aee0de | 768 | { |
25bda50a | 769 | return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL, |
5e774eb3 AF |
770 | translate_fn, translate_opaque, NULL); |
771 | } | |
772 | ||
af975131 JI |
773 | ssize_t load_uimage_as(const char *filename, hwaddr *ep, hwaddr *loadaddr, |
774 | int *is_linux, | |
775 | uint64_t (*translate_fn)(void *, uint64_t), | |
776 | void *translate_opaque, AddressSpace *as) | |
5e774eb3 AF |
777 | { |
778 | return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL, | |
779 | translate_fn, translate_opaque, as); | |
84aee0de SB |
780 | } |
781 | ||
782 | /* Load a ramdisk. */ | |
af975131 | 783 | ssize_t load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz) |
97df5fee PM |
784 | { |
785 | return load_ramdisk_as(filename, addr, max_sz, NULL); | |
786 | } | |
787 | ||
af975131 JI |
788 | ssize_t load_ramdisk_as(const char *filename, hwaddr addr, uint64_t max_sz, |
789 | AddressSpace *as) | |
84aee0de | 790 | { |
25bda50a | 791 | return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK, |
97df5fee | 792 | NULL, NULL, as); |
84aee0de SB |
793 | } |
794 | ||
7d48a0f7 | 795 | /* Load a gzip-compressed kernel to a dynamically allocated buffer. */ |
af975131 JI |
796 | ssize_t load_image_gzipped_buffer(const char *filename, uint64_t max_sz, |
797 | uint8_t **buffer) | |
235e74af RJ |
798 | { |
799 | uint8_t *compressed_data = NULL; | |
800 | uint8_t *data = NULL; | |
801 | gsize len; | |
802 | ssize_t bytes; | |
803 | int ret = -1; | |
804 | ||
805 | if (!g_file_get_contents(filename, (char **) &compressed_data, &len, | |
806 | NULL)) { | |
807 | goto out; | |
808 | } | |
809 | ||
810 | /* Is it a gzip-compressed file? */ | |
811 | if (len < 2 || | |
812 | compressed_data[0] != 0x1f || | |
813 | compressed_data[1] != 0x8b) { | |
814 | goto out; | |
815 | } | |
816 | ||
817 | if (max_sz > LOAD_IMAGE_MAX_GUNZIP_BYTES) { | |
818 | max_sz = LOAD_IMAGE_MAX_GUNZIP_BYTES; | |
819 | } | |
820 | ||
821 | data = g_malloc(max_sz); | |
822 | bytes = gunzip(data, max_sz, compressed_data, len); | |
823 | if (bytes < 0) { | |
824 | fprintf(stderr, "%s: unable to decompress gzipped kernel file\n", | |
825 | filename); | |
826 | goto out; | |
827 | } | |
828 | ||
7d48a0f7 LE |
829 | /* trim to actual size and return to caller */ |
830 | *buffer = g_realloc(data, bytes); | |
235e74af | 831 | ret = bytes; |
7d48a0f7 LE |
832 | /* ownership has been transferred to caller */ |
833 | data = NULL; | |
235e74af RJ |
834 | |
835 | out: | |
836 | g_free(compressed_data); | |
837 | g_free(data); | |
838 | return ret; | |
839 | } | |
840 | ||
7d48a0f7 | 841 | /* Load a gzip-compressed kernel. */ |
af975131 | 842 | ssize_t load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) |
7d48a0f7 | 843 | { |
af975131 | 844 | ssize_t bytes; |
7d48a0f7 LE |
845 | uint8_t *data; |
846 | ||
847 | bytes = load_image_gzipped_buffer(filename, max_sz, &data); | |
848 | if (bytes != -1) { | |
849 | rom_add_blob_fixed(filename, data, bytes, addr); | |
850 | g_free(data); | |
851 | } | |
852 | return bytes; | |
853 | } | |
854 | ||
45a50b16 GH |
855 | /* |
856 | * Functions for reboot-persistent memory regions. | |
857 | * - used for vga bios and option roms. | |
858 | * - also linux kernel (-kernel / -initrd). | |
859 | */ | |
860 | ||
861 | typedef struct Rom Rom; | |
862 | ||
863 | struct Rom { | |
864 | char *name; | |
865 | char *path; | |
d60fa42e FC |
866 | |
867 | /* datasize is the amount of memory allocated in "data". If datasize is less | |
868 | * than romsize, it means that the area from datasize to romsize is filled | |
869 | * with zeros. | |
870 | */ | |
45a50b16 | 871 | size_t romsize; |
d60fa42e FC |
872 | size_t datasize; |
873 | ||
45a50b16 | 874 | uint8_t *data; |
04920fc0 | 875 | MemoryRegion *mr; |
3e76099a | 876 | AddressSpace *as; |
45a50b16 | 877 | int isrom; |
379526a4 GH |
878 | char *fw_dir; |
879 | char *fw_file; | |
fef28891 | 880 | GMappedFile *mapped_file; |
45a50b16 | 881 | |
e2336043 SH |
882 | bool committed; |
883 | ||
a8170e5e | 884 | hwaddr addr; |
45a50b16 GH |
885 | QTAILQ_ENTRY(Rom) next; |
886 | }; | |
887 | ||
8832cb80 | 888 | static FWCfgState *fw_cfg; |
45a50b16 GH |
889 | static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms); |
890 | ||
fef28891 SG |
891 | /* |
892 | * rom->data can be heap-allocated or memory-mapped (e.g. when added with | |
893 | * rom_add_elf_program()) | |
894 | */ | |
895 | static void rom_free_data(Rom *rom) | |
896 | { | |
897 | if (rom->mapped_file) { | |
898 | g_mapped_file_unref(rom->mapped_file); | |
899 | rom->mapped_file = NULL; | |
900 | } else { | |
901 | g_free(rom->data); | |
902 | } | |
903 | ||
904 | rom->data = NULL; | |
905 | } | |
906 | ||
e7f59933 SH |
907 | static void rom_free(Rom *rom) |
908 | { | |
fef28891 | 909 | rom_free_data(rom); |
e7f59933 SH |
910 | g_free(rom->path); |
911 | g_free(rom->name); | |
912 | g_free(rom->fw_dir); | |
913 | g_free(rom->fw_file); | |
914 | g_free(rom); | |
915 | } | |
916 | ||
3e76099a AF |
917 | static inline bool rom_order_compare(Rom *rom, Rom *item) |
918 | { | |
1b57bd4f | 919 | return ((uintptr_t)(void *)rom->as > (uintptr_t)(void *)item->as) || |
3e76099a AF |
920 | (rom->as == item->as && rom->addr >= item->addr); |
921 | } | |
922 | ||
45a50b16 GH |
923 | static void rom_insert(Rom *rom) |
924 | { | |
925 | Rom *item; | |
926 | ||
97fe84f5 PB |
927 | if (roms_loaded) { |
928 | hw_error ("ROM images must be loaded at startup\n"); | |
929 | } | |
930 | ||
3e76099a AF |
931 | /* The user didn't specify an address space, this is the default */ |
932 | if (!rom->as) { | |
933 | rom->as = &address_space_memory; | |
934 | } | |
935 | ||
e2336043 SH |
936 | rom->committed = false; |
937 | ||
3e76099a | 938 | /* List is ordered by load address in the same address space */ |
45a50b16 | 939 | QTAILQ_FOREACH(item, &roms, next) { |
3e76099a | 940 | if (rom_order_compare(rom, item)) { |
45a50b16 | 941 | continue; |
3e76099a | 942 | } |
45a50b16 GH |
943 | QTAILQ_INSERT_BEFORE(item, rom, next); |
944 | return; | |
945 | } | |
946 | QTAILQ_INSERT_TAIL(&roms, rom, next); | |
947 | } | |
948 | ||
a1666142 MT |
949 | static void fw_cfg_resized(const char *id, uint64_t length, void *host) |
950 | { | |
951 | if (fw_cfg) { | |
952 | fw_cfg_modify_file(fw_cfg, id + strlen("/rom@"), host, length); | |
953 | } | |
954 | } | |
955 | ||
baf2d5bf | 956 | static void *rom_set_mr(Rom *rom, Object *owner, const char *name, bool ro) |
04920fc0 MT |
957 | { |
958 | void *data; | |
959 | ||
960 | rom->mr = g_malloc(sizeof(*rom->mr)); | |
a1666142 MT |
961 | memory_region_init_resizeable_ram(rom->mr, owner, name, |
962 | rom->datasize, rom->romsize, | |
963 | fw_cfg_resized, | |
df8abec8 | 964 | &error_fatal); |
baf2d5bf | 965 | memory_region_set_readonly(rom->mr, ro); |
04920fc0 MT |
966 | vmstate_register_ram_global(rom->mr); |
967 | ||
968 | data = memory_region_get_ram_ptr(rom->mr); | |
969 | memcpy(data, rom->data, rom->datasize); | |
970 | ||
971 | return data; | |
972 | } | |
973 | ||
af975131 JI |
974 | ssize_t rom_add_file(const char *file, const char *fw_dir, |
975 | hwaddr addr, int32_t bootindex, | |
976 | bool option_rom, MemoryRegion *mr, | |
977 | AddressSpace *as) | |
45a50b16 | 978 | { |
71ae9e94 | 979 | MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); |
45a50b16 | 980 | Rom *rom; |
af975131 JI |
981 | ssize_t rc; |
982 | int fd = -1; | |
2e55e842 | 983 | char devpath[100]; |
45a50b16 | 984 | |
3e76099a AF |
985 | if (as && mr) { |
986 | fprintf(stderr, "Specifying an Address Space and Memory Region is " \ | |
987 | "not valid when loading a rom\n"); | |
988 | /* We haven't allocated anything so we don't need any cleanup */ | |
989 | return -1; | |
990 | } | |
991 | ||
7267c094 AL |
992 | rom = g_malloc0(sizeof(*rom)); |
993 | rom->name = g_strdup(file); | |
45a50b16 | 994 | rom->path = qemu_find_file(QEMU_FILE_TYPE_BIOS, rom->name); |
3e76099a | 995 | rom->as = as; |
45a50b16 | 996 | if (rom->path == NULL) { |
7267c094 | 997 | rom->path = g_strdup(file); |
45a50b16 GH |
998 | } |
999 | ||
cef290b8 | 1000 | fd = open(rom->path, O_RDONLY | O_BINARY); |
45a50b16 GH |
1001 | if (fd == -1) { |
1002 | fprintf(stderr, "Could not open option rom '%s': %s\n", | |
1003 | rom->path, strerror(errno)); | |
1004 | goto err; | |
1005 | } | |
1006 | ||
bdb5ee30 | 1007 | if (fw_dir) { |
7267c094 AL |
1008 | rom->fw_dir = g_strdup(fw_dir); |
1009 | rom->fw_file = g_strdup(file); | |
bdb5ee30 | 1010 | } |
d60fa42e FC |
1011 | rom->addr = addr; |
1012 | rom->romsize = lseek(fd, 0, SEEK_END); | |
ddd2eab7 GA |
1013 | if (rom->romsize == -1) { |
1014 | fprintf(stderr, "rom: file %-20s: get size error: %s\n", | |
1015 | rom->name, strerror(errno)); | |
1016 | goto err; | |
1017 | } | |
1018 | ||
d60fa42e FC |
1019 | rom->datasize = rom->romsize; |
1020 | rom->data = g_malloc0(rom->datasize); | |
45a50b16 | 1021 | lseek(fd, 0, SEEK_SET); |
d60fa42e FC |
1022 | rc = read(fd, rom->data, rom->datasize); |
1023 | if (rc != rom->datasize) { | |
af975131 | 1024 | fprintf(stderr, "rom: file %-20s: read error: rc=%zd (expected %zd)\n", |
d60fa42e | 1025 | rom->name, rc, rom->datasize); |
45a50b16 GH |
1026 | goto err; |
1027 | } | |
1028 | close(fd); | |
1029 | rom_insert(rom); | |
de1f34cb GN |
1030 | if (rom->fw_file && fw_cfg) { |
1031 | const char *basename; | |
35c12e60 | 1032 | char fw_file_name[FW_CFG_MAX_FILE_PATH]; |
04920fc0 | 1033 | void *data; |
de1f34cb GN |
1034 | |
1035 | basename = strrchr(rom->fw_file, '/'); | |
1036 | if (basename) { | |
1037 | basename++; | |
1038 | } else { | |
1039 | basename = rom->fw_file; | |
1040 | } | |
1041 | snprintf(fw_file_name, sizeof(fw_file_name), "%s/%s", rom->fw_dir, | |
1042 | basename); | |
2e55e842 | 1043 | snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); |
04920fc0 | 1044 | |
71ae9e94 | 1045 | if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) { |
baf2d5bf | 1046 | data = rom_set_mr(rom, OBJECT(fw_cfg), devpath, true); |
04920fc0 MT |
1047 | } else { |
1048 | data = rom->data; | |
1049 | } | |
1050 | ||
1051 | fw_cfg_add_file(fw_cfg, fw_file_name, data, rom->romsize); | |
2e55e842 | 1052 | } else { |
76151cac PM |
1053 | if (mr) { |
1054 | rom->mr = mr; | |
1055 | snprintf(devpath, sizeof(devpath), "/rom@%s", file); | |
1056 | } else { | |
1057 | snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr); | |
1058 | } | |
de1f34cb | 1059 | } |
2e55e842 GN |
1060 | |
1061 | add_boot_device_path(bootindex, NULL, devpath); | |
45a50b16 GH |
1062 | return 0; |
1063 | ||
1064 | err: | |
1065 | if (fd != -1) | |
1066 | close(fd); | |
ed2f3bc1 | 1067 | |
e7f59933 | 1068 | rom_free(rom); |
45a50b16 GH |
1069 | return -1; |
1070 | } | |
1071 | ||
339240b5 | 1072 | MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, |
a1666142 | 1073 | size_t max_len, hwaddr addr, const char *fw_file_name, |
6f6f4aec | 1074 | FWCfgCallback fw_callback, void *callback_opaque, |
baf2d5bf | 1075 | AddressSpace *as, bool read_only) |
45a50b16 | 1076 | { |
71ae9e94 | 1077 | MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); |
45a50b16 | 1078 | Rom *rom; |
339240b5 | 1079 | MemoryRegion *mr = NULL; |
45a50b16 | 1080 | |
d60fa42e FC |
1081 | rom = g_malloc0(sizeof(*rom)); |
1082 | rom->name = g_strdup(name); | |
aa6c6ae8 | 1083 | rom->as = as; |
d60fa42e | 1084 | rom->addr = addr; |
a1666142 | 1085 | rom->romsize = max_len ? max_len : len; |
d60fa42e | 1086 | rom->datasize = len; |
85fad7e1 | 1087 | g_assert(rom->romsize >= rom->datasize); |
d60fa42e | 1088 | rom->data = g_malloc0(rom->datasize); |
45a50b16 GH |
1089 | memcpy(rom->data, blob, len); |
1090 | rom_insert(rom); | |
48354cc5 MT |
1091 | if (fw_file_name && fw_cfg) { |
1092 | char devpath[100]; | |
ad5b88b1 | 1093 | void *data; |
48354cc5 | 1094 | |
baf2d5bf MT |
1095 | if (read_only) { |
1096 | snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); | |
1097 | } else { | |
1098 | snprintf(devpath, sizeof(devpath), "/ram@%s", fw_file_name); | |
1099 | } | |
48354cc5 | 1100 | |
71ae9e94 | 1101 | if (mc->rom_file_has_mr) { |
baf2d5bf | 1102 | data = rom_set_mr(rom, OBJECT(fw_cfg), devpath, read_only); |
339240b5 | 1103 | mr = rom->mr; |
48354cc5 MT |
1104 | } else { |
1105 | data = rom->data; | |
1106 | } | |
1107 | ||
1108 | fw_cfg_add_file_callback(fw_cfg, fw_file_name, | |
5f9252f7 | 1109 | fw_callback, NULL, callback_opaque, |
baf2d5bf | 1110 | data, rom->datasize, read_only); |
48354cc5 | 1111 | } |
339240b5 | 1112 | return mr; |
45a50b16 GH |
1113 | } |
1114 | ||
d60fa42e FC |
1115 | /* This function is specific for elf program because we don't need to allocate |
1116 | * all the rom. We just allocate the first part and the rest is just zeros. This | |
fef28891 SG |
1117 | * is why romsize and datasize are different. Also, this function takes its own |
1118 | * reference to "mapped_file", so we don't have to allocate and copy the buffer. | |
d60fa42e | 1119 | */ |
fef28891 SG |
1120 | int rom_add_elf_program(const char *name, GMappedFile *mapped_file, void *data, |
1121 | size_t datasize, size_t romsize, hwaddr addr, | |
1122 | AddressSpace *as) | |
d60fa42e FC |
1123 | { |
1124 | Rom *rom; | |
1125 | ||
1126 | rom = g_malloc0(sizeof(*rom)); | |
1127 | rom->name = g_strdup(name); | |
1128 | rom->addr = addr; | |
1129 | rom->datasize = datasize; | |
1130 | rom->romsize = romsize; | |
1131 | rom->data = data; | |
3e76099a | 1132 | rom->as = as; |
fef28891 SG |
1133 | |
1134 | if (mapped_file && data) { | |
1135 | g_mapped_file_ref(mapped_file); | |
1136 | rom->mapped_file = mapped_file; | |
1137 | } | |
1138 | ||
d60fa42e FC |
1139 | rom_insert(rom); |
1140 | return 0; | |
1141 | } | |
1142 | ||
af975131 | 1143 | ssize_t rom_add_vga(const char *file) |
de2aff17 | 1144 | { |
3e76099a | 1145 | return rom_add_file(file, "vgaroms", 0, -1, true, NULL, NULL); |
de2aff17 GH |
1146 | } |
1147 | ||
af975131 | 1148 | ssize_t rom_add_option(const char *file, int32_t bootindex) |
de2aff17 | 1149 | { |
3e76099a | 1150 | return rom_add_file(file, "genroms", 0, bootindex, true, NULL, NULL); |
de2aff17 GH |
1151 | } |
1152 | ||
45a50b16 GH |
1153 | static void rom_reset(void *unused) |
1154 | { | |
1155 | Rom *rom; | |
1156 | ||
1157 | QTAILQ_FOREACH(rom, &roms, next) { | |
e405a2ba AK |
1158 | if (rom->fw_file) { |
1159 | continue; | |
1160 | } | |
5073b5d3 DDAG |
1161 | /* |
1162 | * We don't need to fill in the RAM with ROM data because we'll fill | |
1163 | * the data in during the next incoming migration in all cases. Note | |
1164 | * that some of those RAMs can actually be modified by the guest. | |
1165 | */ | |
1166 | if (runstate_check(RUN_STATE_INMIGRATE)) { | |
1167 | if (rom->data && rom->isrom) { | |
1168 | /* | |
1169 | * Free it so that a rom_reset after migration doesn't | |
1170 | * overwrite a potentially modified 'rom'. | |
1171 | */ | |
1172 | rom_free_data(rom); | |
1173 | } | |
1174 | continue; | |
1175 | } | |
1176 | ||
bdb5ee30 | 1177 | if (rom->data == NULL) { |
45a50b16 | 1178 | continue; |
bdb5ee30 | 1179 | } |
04920fc0 MT |
1180 | if (rom->mr) { |
1181 | void *host = memory_region_get_ram_ptr(rom->mr); | |
1182 | memcpy(host, rom->data, rom->datasize); | |
b4c4c1f1 | 1183 | memset(host + rom->datasize, 0, rom->romsize - rom->datasize); |
04920fc0 | 1184 | } else { |
3c8133f9 PM |
1185 | address_space_write_rom(rom->as, rom->addr, MEMTXATTRS_UNSPECIFIED, |
1186 | rom->data, rom->datasize); | |
b4c4c1f1 LV |
1187 | address_space_set(rom->as, rom->addr + rom->datasize, 0, |
1188 | rom->romsize - rom->datasize, | |
1189 | MEMTXATTRS_UNSPECIFIED); | |
04920fc0 | 1190 | } |
45a50b16 GH |
1191 | if (rom->isrom) { |
1192 | /* rom needs to be written only once */ | |
fef28891 | 1193 | rom_free_data(rom); |
45a50b16 | 1194 | } |
582b55a9 AG |
1195 | /* |
1196 | * The rom loader is really on the same level as firmware in the guest | |
1197 | * shadowing a ROM into RAM. Such a shadowing mechanism needs to ensure | |
1198 | * that the instruction cache for that new region is clear, so that the | |
1199 | * CPU definitely fetches its instructions from the just written data. | |
1200 | */ | |
1201 | cpu_flush_icache_range(rom->addr, rom->datasize); | |
26b8e6dc AK |
1202 | |
1203 | trace_loader_write_rom(rom->name, rom->addr, rom->datasize, rom->isrom); | |
45a50b16 GH |
1204 | } |
1205 | } | |
1206 | ||
5b1de520 PM |
1207 | /* Return true if two consecutive ROMs in the ROM list overlap */ |
1208 | static bool roms_overlap(Rom *last_rom, Rom *this_rom) | |
1209 | { | |
1210 | if (!last_rom) { | |
1211 | return false; | |
1212 | } | |
1213 | return last_rom->as == this_rom->as && | |
1214 | last_rom->addr + last_rom->romsize > this_rom->addr; | |
1215 | } | |
1216 | ||
837a0595 PM |
1217 | static const char *rom_as_name(Rom *rom) |
1218 | { | |
1219 | const char *name = rom->as ? rom->as->name : NULL; | |
1220 | return name ?: "anonymous"; | |
1221 | } | |
1222 | ||
1223 | static void rom_print_overlap_error_header(void) | |
1224 | { | |
1225 | error_report("Some ROM regions are overlapping"); | |
1226 | error_printf( | |
1227 | "These ROM regions might have been loaded by " | |
1228 | "direct user request or by default.\n" | |
1229 | "They could be BIOS/firmware images, a guest kernel, " | |
1230 | "initrd or some other file loaded into guest memory.\n" | |
1231 | "Check whether you intended to load all this guest code, and " | |
1232 | "whether it has been built to load to the correct addresses.\n"); | |
1233 | } | |
1234 | ||
1235 | static void rom_print_one_overlap_error(Rom *last_rom, Rom *rom) | |
1236 | { | |
1237 | error_printf( | |
1238 | "\nThe following two regions overlap (in the %s address space):\n", | |
1239 | rom_as_name(rom)); | |
1240 | error_printf( | |
1241 | " %s (addresses 0x" TARGET_FMT_plx " - 0x" TARGET_FMT_plx ")\n", | |
1242 | last_rom->name, last_rom->addr, last_rom->addr + last_rom->romsize); | |
1243 | error_printf( | |
1244 | " %s (addresses 0x" TARGET_FMT_plx " - 0x" TARGET_FMT_plx ")\n", | |
1245 | rom->name, rom->addr, rom->addr + rom->romsize); | |
1246 | } | |
1247 | ||
6b3f7f63 | 1248 | int rom_check_and_register_reset(void) |
45a50b16 | 1249 | { |
dcc5cd33 | 1250 | MemoryRegionSection section; |
5b1de520 | 1251 | Rom *rom, *last_rom = NULL; |
837a0595 | 1252 | bool found_overlap = false; |
45a50b16 GH |
1253 | |
1254 | QTAILQ_FOREACH(rom, &roms, next) { | |
e405a2ba AK |
1255 | if (rom->fw_file) { |
1256 | continue; | |
1257 | } | |
ca316c11 | 1258 | if (!rom->mr) { |
5b1de520 | 1259 | if (roms_overlap(last_rom, rom)) { |
837a0595 PM |
1260 | if (!found_overlap) { |
1261 | found_overlap = true; | |
1262 | rom_print_overlap_error_header(); | |
1263 | } | |
1264 | rom_print_one_overlap_error(last_rom, rom); | |
1265 | /* Keep going through the list so we report all overlaps */ | |
ca316c11 | 1266 | } |
5b1de520 | 1267 | last_rom = rom; |
45a50b16 | 1268 | } |
d6ac342a AF |
1269 | section = memory_region_find(rom->mr ? rom->mr : get_system_memory(), |
1270 | rom->addr, 1); | |
052e87b0 | 1271 | rom->isrom = int128_nz(section.size) && memory_region_is_rom(section.mr); |
dfde4e6e | 1272 | memory_region_unref(section.mr); |
45a50b16 | 1273 | } |
837a0595 PM |
1274 | if (found_overlap) { |
1275 | return -1; | |
1276 | } | |
1277 | ||
45a50b16 | 1278 | qemu_register_reset(rom_reset, NULL); |
d916b464 | 1279 | roms_loaded = 1; |
6b3f7f63 | 1280 | return 0; |
d916b464 MT |
1281 | } |
1282 | ||
a88b362c | 1283 | void rom_set_fw(FWCfgState *f) |
379526a4 | 1284 | { |
8832cb80 | 1285 | fw_cfg = f; |
379526a4 GH |
1286 | } |
1287 | ||
bab47d9a GH |
1288 | void rom_set_order_override(int order) |
1289 | { | |
1290 | if (!fw_cfg) | |
1291 | return; | |
1292 | fw_cfg_set_order_override(fw_cfg, order); | |
1293 | } | |
1294 | ||
1295 | void rom_reset_order_override(void) | |
1296 | { | |
1297 | if (!fw_cfg) | |
1298 | return; | |
1299 | fw_cfg_reset_order_override(fw_cfg); | |
1300 | } | |
1301 | ||
e2336043 SH |
1302 | void rom_transaction_begin(void) |
1303 | { | |
1304 | Rom *rom; | |
1305 | ||
1306 | /* Ignore ROMs added without the transaction API */ | |
1307 | QTAILQ_FOREACH(rom, &roms, next) { | |
1308 | rom->committed = true; | |
1309 | } | |
1310 | } | |
1311 | ||
1312 | void rom_transaction_end(bool commit) | |
1313 | { | |
1314 | Rom *rom; | |
1315 | Rom *tmp; | |
1316 | ||
1317 | QTAILQ_FOREACH_SAFE(rom, &roms, next, tmp) { | |
1318 | if (rom->committed) { | |
1319 | continue; | |
1320 | } | |
1321 | if (commit) { | |
1322 | rom->committed = true; | |
1323 | } else { | |
1324 | QTAILQ_REMOVE(&roms, rom, next); | |
1325 | rom_free(rom); | |
1326 | } | |
1327 | } | |
1328 | } | |
1329 | ||
0f0f8b61 | 1330 | static Rom *find_rom(hwaddr addr, size_t size) |
3c178e72 GH |
1331 | { |
1332 | Rom *rom; | |
1333 | ||
1334 | QTAILQ_FOREACH(rom, &roms, next) { | |
f21a59c2 AJ |
1335 | if (rom->fw_file) { |
1336 | continue; | |
1337 | } | |
04920fc0 MT |
1338 | if (rom->mr) { |
1339 | continue; | |
1340 | } | |
bdb5ee30 | 1341 | if (rom->addr > addr) { |
3c178e72 | 1342 | continue; |
bdb5ee30 | 1343 | } |
0f0f8b61 | 1344 | if (rom->addr + rom->romsize < addr + size) { |
3c178e72 | 1345 | continue; |
bdb5ee30 | 1346 | } |
3c178e72 GH |
1347 | return rom; |
1348 | } | |
1349 | return NULL; | |
1350 | } | |
1351 | ||
5fc983af AB |
1352 | typedef struct RomSec { |
1353 | hwaddr base; | |
1354 | int se; /* start/end flag */ | |
1355 | } RomSec; | |
1356 | ||
1357 | ||
1358 | /* | |
1359 | * Sort into address order. We break ties between rom-startpoints | |
1360 | * and rom-endpoints in favour of the startpoint, by sorting the 0->1 | |
1361 | * transition before the 1->0 transition. Either way round would | |
1362 | * work, but this way saves a little work later by avoiding | |
1363 | * dealing with "gaps" of 0 length. | |
1364 | */ | |
1365 | static gint sort_secs(gconstpointer a, gconstpointer b) | |
1366 | { | |
1367 | RomSec *ra = (RomSec *) a; | |
1368 | RomSec *rb = (RomSec *) b; | |
1369 | ||
1370 | if (ra->base == rb->base) { | |
1371 | return ra->se - rb->se; | |
1372 | } | |
1373 | return ra->base > rb->base ? 1 : -1; | |
1374 | } | |
1375 | ||
1376 | static GList *add_romsec_to_list(GList *secs, hwaddr base, int se) | |
1377 | { | |
1378 | RomSec *cand = g_new(RomSec, 1); | |
1379 | cand->base = base; | |
1380 | cand->se = se; | |
1381 | return g_list_prepend(secs, cand); | |
1382 | } | |
1383 | ||
1384 | RomGap rom_find_largest_gap_between(hwaddr base, size_t size) | |
1385 | { | |
1386 | Rom *rom; | |
1387 | RomSec *cand; | |
1388 | RomGap res = {0, 0}; | |
1389 | hwaddr gapstart = base; | |
1390 | GList *it, *secs = NULL; | |
1391 | int count = 0; | |
1392 | ||
1393 | QTAILQ_FOREACH(rom, &roms, next) { | |
1394 | /* Ignore blobs being loaded to special places */ | |
1395 | if (rom->mr || rom->fw_file) { | |
1396 | continue; | |
1397 | } | |
1398 | /* ignore anything finishing bellow base */ | |
1399 | if (rom->addr + rom->romsize <= base) { | |
1400 | continue; | |
1401 | } | |
1402 | /* ignore anything starting above the region */ | |
1403 | if (rom->addr >= base + size) { | |
1404 | continue; | |
1405 | } | |
1406 | ||
1407 | /* Save the start and end of each relevant ROM */ | |
1408 | secs = add_romsec_to_list(secs, rom->addr, 1); | |
1409 | ||
1410 | if (rom->addr + rom->romsize < base + size) { | |
1411 | secs = add_romsec_to_list(secs, rom->addr + rom->romsize, -1); | |
1412 | } | |
1413 | } | |
1414 | ||
1415 | /* sentinel */ | |
1416 | secs = add_romsec_to_list(secs, base + size, 1); | |
1417 | ||
1418 | secs = g_list_sort(secs, sort_secs); | |
1419 | ||
1420 | for (it = g_list_first(secs); it; it = g_list_next(it)) { | |
1421 | cand = (RomSec *) it->data; | |
1422 | if (count == 0 && count + cand->se == 1) { | |
1423 | size_t gap = cand->base - gapstart; | |
1424 | if (gap > res.size) { | |
1425 | res.base = gapstart; | |
1426 | res.size = gap; | |
1427 | } | |
1428 | } else if (count == 1 && count + cand->se == 0) { | |
1429 | gapstart = cand->base; | |
1430 | } | |
1431 | count += cand->se; | |
1432 | } | |
1433 | ||
1434 | g_list_free_full(secs, g_free); | |
1435 | return res; | |
1436 | } | |
1437 | ||
935effc2 KW |
1438 | /* |
1439 | * Copies memory from registered ROMs to dest. Any memory that is contained in | |
1440 | * a ROM between addr and addr + size is copied. Note that this can involve | |
1441 | * multiple ROMs, which need not start at addr and need not end at addr + size. | |
1442 | */ | |
a8170e5e | 1443 | int rom_copy(uint8_t *dest, hwaddr addr, size_t size) |
235f86ef | 1444 | { |
a8170e5e | 1445 | hwaddr end = addr + size; |
235f86ef AG |
1446 | uint8_t *s, *d = dest; |
1447 | size_t l = 0; | |
1448 | Rom *rom; | |
1449 | ||
1450 | QTAILQ_FOREACH(rom, &roms, next) { | |
f21a59c2 AJ |
1451 | if (rom->fw_file) { |
1452 | continue; | |
1453 | } | |
04920fc0 MT |
1454 | if (rom->mr) { |
1455 | continue; | |
1456 | } | |
bdb5ee30 | 1457 | if (rom->addr + rom->romsize < addr) { |
632cf034 | 1458 | continue; |
bdb5ee30 | 1459 | } |
e423455c | 1460 | if (rom->addr > end || rom->addr < addr) { |
235f86ef | 1461 | break; |
bdb5ee30 | 1462 | } |
235f86ef | 1463 | |
632cf034 | 1464 | d = dest + (rom->addr - addr); |
235f86ef | 1465 | s = rom->data; |
d60fa42e | 1466 | l = rom->datasize; |
235f86ef | 1467 | |
235f86ef AG |
1468 | if ((d + l) > (dest + size)) { |
1469 | l = dest - d; | |
1470 | } | |
1471 | ||
0dd5ce38 MB |
1472 | if (l > 0) { |
1473 | memcpy(d, s, l); | |
1474 | } | |
d60fa42e FC |
1475 | |
1476 | if (rom->romsize > rom->datasize) { | |
1477 | /* If datasize is less than romsize, it means that we didn't | |
1478 | * allocate all the ROM because the trailing data are only zeros. | |
1479 | */ | |
1480 | ||
1481 | d += l; | |
1482 | l = rom->romsize - rom->datasize; | |
1483 | ||
1484 | if ((d + l) > (dest + size)) { | |
1485 | /* Rom size doesn't fit in the destination area. Adjust to avoid | |
1486 | * overflow. | |
1487 | */ | |
1488 | l = dest - d; | |
1489 | } | |
1490 | ||
1491 | if (l > 0) { | |
1492 | memset(d, 0x0, l); | |
1493 | } | |
1494 | } | |
235f86ef AG |
1495 | } |
1496 | ||
1497 | return (d + l) - dest; | |
1498 | } | |
1499 | ||
0f0f8b61 | 1500 | void *rom_ptr(hwaddr addr, size_t size) |
3c178e72 GH |
1501 | { |
1502 | Rom *rom; | |
1503 | ||
0f0f8b61 | 1504 | rom = find_rom(addr, size); |
3c178e72 GH |
1505 | if (!rom || !rom->data) |
1506 | return NULL; | |
632cf034 | 1507 | return rom->data + (addr - rom->addr); |
3c178e72 GH |
1508 | } |
1509 | ||
1228c459 PM |
1510 | typedef struct FindRomCBData { |
1511 | size_t size; /* Amount of data we want from ROM, in bytes */ | |
1512 | MemoryRegion *mr; /* MR at the unaliased guest addr */ | |
1513 | hwaddr xlat; /* Offset of addr within mr */ | |
1514 | void *rom; /* Output: rom data pointer, if found */ | |
1515 | } FindRomCBData; | |
1516 | ||
1517 | static bool find_rom_cb(Int128 start, Int128 len, const MemoryRegion *mr, | |
1518 | hwaddr offset_in_region, void *opaque) | |
1519 | { | |
1520 | FindRomCBData *cbdata = opaque; | |
1521 | hwaddr alias_addr; | |
1522 | ||
1523 | if (mr != cbdata->mr) { | |
1524 | return false; | |
1525 | } | |
1526 | ||
1527 | alias_addr = int128_get64(start) + cbdata->xlat - offset_in_region; | |
1528 | cbdata->rom = rom_ptr(alias_addr, cbdata->size); | |
1529 | if (!cbdata->rom) { | |
1530 | return false; | |
1531 | } | |
1532 | /* Found a match, stop iterating */ | |
1533 | return true; | |
1534 | } | |
1535 | ||
1536 | void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size) | |
1537 | { | |
1538 | /* | |
1539 | * Find any ROM data for the given guest address range. If there | |
1540 | * is a ROM blob then return a pointer to the host memory | |
1541 | * corresponding to 'addr'; otherwise return NULL. | |
1542 | * | |
1543 | * We look not only for ROM blobs that were loaded directly to | |
1544 | * addr, but also for ROM blobs that were loaded to aliases of | |
1545 | * that memory at other addresses within the AddressSpace. | |
1546 | * | |
1547 | * Note that we do not check @as against the 'as' member in the | |
1548 | * 'struct Rom' returned by rom_ptr(). The Rom::as is the | |
1549 | * AddressSpace which the rom blob should be written to, whereas | |
1550 | * our @as argument is the AddressSpace which we are (effectively) | |
1551 | * reading from, and the same underlying RAM will often be visible | |
1552 | * in multiple AddressSpaces. (A common example is a ROM blob | |
1553 | * written to the 'system' address space but then read back via a | |
1554 | * CPU's cpu->as pointer.) This does mean we might potentially | |
1555 | * return a false-positive match if a ROM blob was loaded into an | |
1556 | * AS which is entirely separate and distinct from the one we're | |
1557 | * querying, but this issue exists also for rom_ptr() and hasn't | |
1558 | * caused any problems in practice. | |
1559 | */ | |
1560 | FlatView *fv; | |
1561 | void *rom; | |
1562 | hwaddr len_unused; | |
1563 | FindRomCBData cbdata = {}; | |
1564 | ||
1565 | /* Easy case: there's data at the actual address */ | |
1566 | rom = rom_ptr(addr, size); | |
1567 | if (rom) { | |
1568 | return rom; | |
1569 | } | |
1570 | ||
1571 | RCU_READ_LOCK_GUARD(); | |
1572 | ||
1573 | fv = address_space_to_flatview(as); | |
1574 | cbdata.mr = flatview_translate(fv, addr, &cbdata.xlat, &len_unused, | |
1575 | false, MEMTXATTRS_UNSPECIFIED); | |
1576 | if (!cbdata.mr) { | |
1577 | /* Nothing at this address, so there can't be any aliasing */ | |
1578 | return NULL; | |
1579 | } | |
1580 | cbdata.size = size; | |
1581 | flatview_for_each_range(fv, find_rom_cb, &cbdata); | |
1582 | return cbdata.rom; | |
1583 | } | |
1584 | ||
dd98234c | 1585 | HumanReadableText *qmp_x_query_roms(Error **errp) |
45a50b16 GH |
1586 | { |
1587 | Rom *rom; | |
dd98234c | 1588 | g_autoptr(GString) buf = g_string_new(""); |
45a50b16 GH |
1589 | |
1590 | QTAILQ_FOREACH(rom, &roms, next) { | |
04920fc0 | 1591 | if (rom->mr) { |
dd98234c DB |
1592 | g_string_append_printf(buf, "%s" |
1593 | " size=0x%06zx name=\"%s\"\n", | |
1594 | memory_region_name(rom->mr), | |
1595 | rom->romsize, | |
1596 | rom->name); | |
04920fc0 | 1597 | } else if (!rom->fw_file) { |
dd98234c DB |
1598 | g_string_append_printf(buf, "addr=" TARGET_FMT_plx |
1599 | " size=0x%06zx mem=%s name=\"%s\"\n", | |
1600 | rom->addr, rom->romsize, | |
1601 | rom->isrom ? "rom" : "ram", | |
1602 | rom->name); | |
632cf034 | 1603 | } else { |
dd98234c DB |
1604 | g_string_append_printf(buf, "fw=%s/%s" |
1605 | " size=0x%06zx name=\"%s\"\n", | |
1606 | rom->fw_dir, | |
1607 | rom->fw_file, | |
1608 | rom->romsize, | |
1609 | rom->name); | |
632cf034 | 1610 | } |
45a50b16 | 1611 | } |
dd98234c DB |
1612 | |
1613 | return human_readable_text_from_str(buf); | |
45a50b16 | 1614 | } |
e4a25ed9 SH |
1615 | |
1616 | typedef enum HexRecord HexRecord; | |
1617 | enum HexRecord { | |
1618 | DATA_RECORD = 0, | |
1619 | EOF_RECORD, | |
1620 | EXT_SEG_ADDR_RECORD, | |
1621 | START_SEG_ADDR_RECORD, | |
1622 | EXT_LINEAR_ADDR_RECORD, | |
1623 | START_LINEAR_ADDR_RECORD, | |
1624 | }; | |
1625 | ||
1626 | /* Each record contains a 16-bit address which is combined with the upper 16 | |
1627 | * bits of the implicit "next address" to form a 32-bit address. | |
1628 | */ | |
1629 | #define NEXT_ADDR_MASK 0xffff0000 | |
1630 | ||
1631 | #define DATA_FIELD_MAX_LEN 0xff | |
1632 | #define LEN_EXCEPT_DATA 0x5 | |
1633 | /* 0x5 = sizeof(byte_count) + sizeof(address) + sizeof(record_type) + | |
1634 | * sizeof(checksum) */ | |
1635 | typedef struct { | |
1636 | uint8_t byte_count; | |
1637 | uint16_t address; | |
1638 | uint8_t record_type; | |
1639 | uint8_t data[DATA_FIELD_MAX_LEN]; | |
1640 | uint8_t checksum; | |
1641 | } HexLine; | |
1642 | ||
1643 | /* return 0 or -1 if error */ | |
1644 | static bool parse_record(HexLine *line, uint8_t *our_checksum, const uint8_t c, | |
1645 | uint32_t *index, const bool in_process) | |
1646 | { | |
1647 | /* +-------+---------------+-------+---------------------+--------+ | |
1648 | * | byte | |record | | | | |
1649 | * | count | address | type | data |checksum| | |
1650 | * +-------+---------------+-------+---------------------+--------+ | |
1651 | * ^ ^ ^ ^ ^ ^ | |
1652 | * |1 byte | 2 bytes |1 byte | 0-255 bytes | 1 byte | | |
1653 | */ | |
1654 | uint8_t value = 0; | |
1655 | uint32_t idx = *index; | |
1656 | /* ignore space */ | |
1657 | if (g_ascii_isspace(c)) { | |
1658 | return true; | |
1659 | } | |
1660 | if (!g_ascii_isxdigit(c) || !in_process) { | |
1661 | return false; | |
1662 | } | |
1663 | value = g_ascii_xdigit_value(c); | |
1664 | value = (idx & 0x1) ? (value & 0xf) : (value << 4); | |
1665 | if (idx < 2) { | |
1666 | line->byte_count |= value; | |
1667 | } else if (2 <= idx && idx < 6) { | |
1668 | line->address <<= 4; | |
1669 | line->address += g_ascii_xdigit_value(c); | |
1670 | } else if (6 <= idx && idx < 8) { | |
1671 | line->record_type |= value; | |
1672 | } else if (8 <= idx && idx < 8 + 2 * line->byte_count) { | |
1673 | line->data[(idx - 8) >> 1] |= value; | |
1674 | } else if (8 + 2 * line->byte_count <= idx && | |
1675 | idx < 10 + 2 * line->byte_count) { | |
1676 | line->checksum |= value; | |
1677 | } else { | |
1678 | return false; | |
1679 | } | |
1680 | *our_checksum += value; | |
1681 | ++(*index); | |
1682 | return true; | |
1683 | } | |
1684 | ||
1685 | typedef struct { | |
1686 | const char *filename; | |
1687 | HexLine line; | |
1688 | uint8_t *bin_buf; | |
1689 | hwaddr *start_addr; | |
1690 | int total_size; | |
1691 | uint32_t next_address_to_write; | |
1692 | uint32_t current_address; | |
1693 | uint32_t current_rom_index; | |
1694 | uint32_t rom_start_address; | |
1695 | AddressSpace *as; | |
58d5e749 | 1696 | bool complete; |
e4a25ed9 SH |
1697 | } HexParser; |
1698 | ||
1699 | /* return size or -1 if error */ | |
1700 | static int handle_record_type(HexParser *parser) | |
1701 | { | |
1702 | HexLine *line = &(parser->line); | |
1703 | switch (line->record_type) { | |
1704 | case DATA_RECORD: | |
1705 | parser->current_address = | |
1706 | (parser->next_address_to_write & NEXT_ADDR_MASK) | line->address; | |
1707 | /* verify this is a contiguous block of memory */ | |
1708 | if (parser->current_address != parser->next_address_to_write) { | |
1709 | if (parser->current_rom_index != 0) { | |
1710 | rom_add_blob_fixed_as(parser->filename, parser->bin_buf, | |
1711 | parser->current_rom_index, | |
1712 | parser->rom_start_address, parser->as); | |
1713 | } | |
1714 | parser->rom_start_address = parser->current_address; | |
1715 | parser->current_rom_index = 0; | |
1716 | } | |
1717 | ||
1718 | /* copy from line buffer to output bin_buf */ | |
1719 | memcpy(parser->bin_buf + parser->current_rom_index, line->data, | |
1720 | line->byte_count); | |
1721 | parser->current_rom_index += line->byte_count; | |
1722 | parser->total_size += line->byte_count; | |
1723 | /* save next address to write */ | |
1724 | parser->next_address_to_write = | |
1725 | parser->current_address + line->byte_count; | |
1726 | break; | |
1727 | ||
1728 | case EOF_RECORD: | |
1729 | if (parser->current_rom_index != 0) { | |
1730 | rom_add_blob_fixed_as(parser->filename, parser->bin_buf, | |
1731 | parser->current_rom_index, | |
1732 | parser->rom_start_address, parser->as); | |
1733 | } | |
58d5e749 | 1734 | parser->complete = true; |
e4a25ed9 SH |
1735 | return parser->total_size; |
1736 | case EXT_SEG_ADDR_RECORD: | |
1737 | case EXT_LINEAR_ADDR_RECORD: | |
1738 | if (line->byte_count != 2 && line->address != 0) { | |
1739 | return -1; | |
1740 | } | |
1741 | ||
1742 | if (parser->current_rom_index != 0) { | |
1743 | rom_add_blob_fixed_as(parser->filename, parser->bin_buf, | |
1744 | parser->current_rom_index, | |
1745 | parser->rom_start_address, parser->as); | |
1746 | } | |
1747 | ||
1748 | /* save next address to write, | |
1749 | * in case of non-contiguous block of memory */ | |
1750 | parser->next_address_to_write = (line->data[0] << 12) | | |
1751 | (line->data[1] << 4); | |
1752 | if (line->record_type == EXT_LINEAR_ADDR_RECORD) { | |
1753 | parser->next_address_to_write <<= 12; | |
1754 | } | |
1755 | ||
1756 | parser->rom_start_address = parser->next_address_to_write; | |
1757 | parser->current_rom_index = 0; | |
1758 | break; | |
1759 | ||
1760 | case START_SEG_ADDR_RECORD: | |
1761 | if (line->byte_count != 4 && line->address != 0) { | |
1762 | return -1; | |
1763 | } | |
1764 | ||
1765 | /* x86 16-bit CS:IP segmented addressing */ | |
1766 | *(parser->start_addr) = (((line->data[0] << 8) | line->data[1]) << 4) + | |
1767 | ((line->data[2] << 8) | line->data[3]); | |
1768 | break; | |
1769 | ||
1770 | case START_LINEAR_ADDR_RECORD: | |
1771 | if (line->byte_count != 4 && line->address != 0) { | |
1772 | return -1; | |
1773 | } | |
1774 | ||
1775 | *(parser->start_addr) = ldl_be_p(line->data); | |
1776 | break; | |
1777 | ||
1778 | default: | |
1779 | return -1; | |
1780 | } | |
1781 | ||
1782 | return parser->total_size; | |
1783 | } | |
1784 | ||
1785 | /* return size or -1 if error */ | |
1786 | static int parse_hex_blob(const char *filename, hwaddr *addr, uint8_t *hex_blob, | |
1787 | size_t hex_blob_size, AddressSpace *as) | |
1788 | { | |
1789 | bool in_process = false; /* avoid re-enter and | |
1790 | * check whether record begin with ':' */ | |
1791 | uint8_t *end = hex_blob + hex_blob_size; | |
1792 | uint8_t our_checksum = 0; | |
1793 | uint32_t record_index = 0; | |
1794 | HexParser parser = { | |
1795 | .filename = filename, | |
1796 | .bin_buf = g_malloc(hex_blob_size), | |
1797 | .start_addr = addr, | |
1798 | .as = as, | |
58d5e749 | 1799 | .complete = false |
e4a25ed9 SH |
1800 | }; |
1801 | ||
1802 | rom_transaction_begin(); | |
1803 | ||
58d5e749 | 1804 | for (; hex_blob < end && !parser.complete; ++hex_blob) { |
e4a25ed9 SH |
1805 | switch (*hex_blob) { |
1806 | case '\r': | |
1807 | case '\n': | |
1808 | if (!in_process) { | |
1809 | break; | |
1810 | } | |
1811 | ||
1812 | in_process = false; | |
1813 | if ((LEN_EXCEPT_DATA + parser.line.byte_count) * 2 != | |
1814 | record_index || | |
1815 | our_checksum != 0) { | |
1816 | parser.total_size = -1; | |
1817 | goto out; | |
1818 | } | |
1819 | ||
1820 | if (handle_record_type(&parser) == -1) { | |
1821 | parser.total_size = -1; | |
1822 | goto out; | |
1823 | } | |
1824 | break; | |
1825 | ||
1826 | /* start of a new record. */ | |
1827 | case ':': | |
1828 | memset(&parser.line, 0, sizeof(HexLine)); | |
1829 | in_process = true; | |
1830 | record_index = 0; | |
1831 | break; | |
1832 | ||
1833 | /* decoding lines */ | |
1834 | default: | |
1835 | if (!parse_record(&parser.line, &our_checksum, *hex_blob, | |
1836 | &record_index, in_process)) { | |
1837 | parser.total_size = -1; | |
1838 | goto out; | |
1839 | } | |
1840 | break; | |
1841 | } | |
1842 | } | |
1843 | ||
1844 | out: | |
1845 | g_free(parser.bin_buf); | |
1846 | rom_transaction_end(parser.total_size != -1); | |
1847 | return parser.total_size; | |
1848 | } | |
1849 | ||
1850 | /* return size or -1 if error */ | |
af975131 JI |
1851 | ssize_t load_targphys_hex_as(const char *filename, hwaddr *entry, |
1852 | AddressSpace *as) | |
e4a25ed9 SH |
1853 | { |
1854 | gsize hex_blob_size; | |
1855 | gchar *hex_blob; | |
af975131 | 1856 | ssize_t total_size = 0; |
e4a25ed9 SH |
1857 | |
1858 | if (!g_file_get_contents(filename, &hex_blob, &hex_blob_size, NULL)) { | |
1859 | return -1; | |
1860 | } | |
1861 | ||
1862 | total_size = parse_hex_blob(filename, entry, (uint8_t *)hex_blob, | |
1863 | hex_blob_size, as); | |
1864 | ||
1865 | g_free(hex_blob); | |
1866 | return total_size; | |
1867 | } |