bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 1 | /* |
| 2 | * QEMU Executable loader |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 3 | * |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 4 | * Copyright (c) 2006 Fabrice Bellard |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 5 | * |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 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. |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 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 | * |
aurel32 | fad6cb1 | 2009-01-04 22:05:52 +0000 | [diff] [blame] | 41 | * You should have received a copy of the GNU General Public License along |
Blue Swirl | 8167ee8 | 2009-07-16 20:47:01 +0000 | [diff] [blame] | 42 | * with this program; if not, see <http://www.gnu.org/licenses/>. |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 43 | */ |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 44 | |
Peter Maydell | 18c86e2 | 2016-01-26 18:17:29 +0000 | [diff] [blame] | 45 | #include "qemu/osdep.h" |
Paolo Bonzini | 2c65db5 | 2020-10-28 07:36:57 -0400 | [diff] [blame] | 46 | #include "qemu/datadir.h" |
Markus Armbruster | da34e65 | 2016-03-14 09:01:28 +0100 | [diff] [blame] | 47 | #include "qapi/error.h" |
Daniel P. Berrangé | dd98234 | 2021-09-08 10:35:43 +0100 | [diff] [blame] | 48 | #include "qapi/qapi-commands-machine.h" |
| 49 | #include "qapi/type-helpers.h" |
Alexey Kardashevskiy | 26b8e6d | 2019-06-13 15:09:37 +1000 | [diff] [blame] | 50 | #include "trace.h" |
Paolo Bonzini | 83c9f4c | 2013-02-04 15:40:22 +0100 | [diff] [blame] | 51 | #include "hw/hw.h" |
Paolo Bonzini | 76cad71 | 2012-10-24 11:12:21 +0200 | [diff] [blame] | 52 | #include "disas/disas.h" |
Markus Armbruster | d645427 | 2019-08-12 07:23:45 +0200 | [diff] [blame] | 53 | #include "migration/vmstate.h" |
Paolo Bonzini | 83c9089 | 2012-12-17 18:19:49 +0100 | [diff] [blame] | 54 | #include "monitor/monitor.h" |
Markus Armbruster | 71e8a91 | 2019-08-12 07:23:38 +0200 | [diff] [blame] | 55 | #include "sysemu/reset.h" |
Paolo Bonzini | 9c17d61 | 2012-12-17 18:20:04 +0100 | [diff] [blame] | 56 | #include "sysemu/sysemu.h" |
Paolo Bonzini | 47b43a1 | 2013-03-18 17:36:02 +0100 | [diff] [blame] | 57 | #include "uboot_image.h" |
Paolo Bonzini | 83c9f4c | 2013-02-04 15:40:22 +0100 | [diff] [blame] | 58 | #include "hw/loader.h" |
Paolo Bonzini | 0d09e41 | 2013-02-05 17:06:20 +0100 | [diff] [blame] | 59 | #include "hw/nvram/fw_cfg.h" |
Paolo Bonzini | 022c62c | 2012-12-17 18:19:49 +0100 | [diff] [blame] | 60 | #include "exec/memory.h" |
Eduardo Habkost | 71ae9e9 | 2015-12-01 20:58:08 -0200 | [diff] [blame] | 61 | #include "hw/boards.h" |
Veronia Bahaa | f348b6d | 2016-03-20 19:16:19 +0200 | [diff] [blame] | 62 | #include "qemu/cutils.h" |
Catherine Ho | 355477f | 2019-04-08 04:42:13 -0400 | [diff] [blame] | 63 | #include "sysemu/runstate.h" |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 64 | |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 65 | #include <zlib.h> |
| 66 | |
Paul Brook | 97fe84f | 2009-11-11 18:17:24 +0000 | [diff] [blame] | 67 | static int roms_loaded; |
| 68 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 69 | /* return the size or -1 if error */ |
Li Zhijian | f3839fd | 2018-09-13 18:07:13 +0800 | [diff] [blame] | 70 | int64_t get_image_size(const char *filename) |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 71 | { |
Li Zhijian | f3839fd | 2018-09-13 18:07:13 +0800 | [diff] [blame] | 72 | int fd; |
| 73 | int64_t size; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 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 | |
| 82 | /* return the size or -1 if error */ |
Benjamin Herrenschmidt | ea87616 | 2014-07-21 13:02:03 +1000 | [diff] [blame] | 83 | ssize_t load_image_size(const char *filename, void *addr, size_t size) |
| 84 | { |
| 85 | int fd; |
Li Zhijian | 1f40547 | 2019-01-17 20:49:02 +0800 | [diff] [blame] | 86 | ssize_t actsize, l = 0; |
Benjamin Herrenschmidt | ea87616 | 2014-07-21 13:02:03 +1000 | [diff] [blame] | 87 | |
| 88 | fd = open(filename, O_RDONLY | O_BINARY); |
| 89 | if (fd < 0) { |
| 90 | return -1; |
| 91 | } |
| 92 | |
Li Zhijian | 1f40547 | 2019-01-17 20:49:02 +0800 | [diff] [blame] | 93 | while ((actsize = read(fd, addr + l, size - l)) > 0) { |
| 94 | l += actsize; |
Benjamin Herrenschmidt | ea87616 | 2014-07-21 13:02:03 +1000 | [diff] [blame] | 95 | } |
Li Zhijian | 1f40547 | 2019-01-17 20:49:02 +0800 | [diff] [blame] | 96 | |
Benjamin Herrenschmidt | ea87616 | 2014-07-21 13:02:03 +1000 | [diff] [blame] | 97 | close(fd); |
| 98 | |
Li Zhijian | 1f40547 | 2019-01-17 20:49:02 +0800 | [diff] [blame] | 99 | return actsize < 0 ? -1 : l; |
Benjamin Herrenschmidt | ea87616 | 2014-07-21 13:02:03 +1000 | [diff] [blame] | 100 | } |
| 101 | |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 102 | /* read()-like version */ |
Markus Armbruster | 725e14e | 2011-11-16 19:41:56 +0100 | [diff] [blame] | 103 | ssize_t read_targphys(const char *name, |
Avi Kivity | a8170e5 | 2012-10-23 12:30:10 +0200 | [diff] [blame] | 104 | int fd, hwaddr dst_addr, size_t nbytes) |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 105 | { |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 106 | uint8_t *buf; |
Markus Armbruster | 725e14e | 2011-11-16 19:41:56 +0100 | [diff] [blame] | 107 | ssize_t did; |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 108 | |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 109 | buf = g_malloc(nbytes); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 110 | did = read(fd, buf, nbytes); |
| 111 | if (did > 0) |
| 112 | rom_add_blob_fixed("read", buf, did, dst_addr); |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 113 | g_free(buf); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 114 | return did; |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 115 | } |
| 116 | |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 117 | ssize_t load_image_targphys(const char *filename, |
| 118 | hwaddr addr, uint64_t max_sz) |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 119 | { |
Alistair Francis | 93ffc7c | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 120 | return load_image_targphys_as(filename, addr, max_sz, NULL); |
| 121 | } |
| 122 | |
| 123 | /* return the size or -1 if error */ |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 124 | ssize_t load_image_targphys_as(const char *filename, |
| 125 | hwaddr addr, uint64_t max_sz, AddressSpace *as) |
Alistair Francis | 93ffc7c | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 126 | { |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 127 | ssize_t size; |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 128 | |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 129 | size = get_image_size(filename); |
Philippe Mathieu-Daudé | 2a4e2e4 | 2017-07-26 23:42:07 -0300 | [diff] [blame] | 130 | if (size < 0 || size > max_sz) { |
Benjamin Herrenschmidt | 17df768 | 2012-01-11 19:46:20 +0000 | [diff] [blame] | 131 | return -1; |
| 132 | } |
| 133 | if (size > 0) { |
Philippe Mathieu-Daudé | 32fb354 | 2017-06-23 13:45:56 -0300 | [diff] [blame] | 134 | if (rom_add_file_fixed_as(filename, addr, -1, as) < 0) { |
| 135 | return -1; |
| 136 | } |
Benjamin Herrenschmidt | 17df768 | 2012-01-11 19:46:20 +0000 | [diff] [blame] | 137 | } |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 138 | return size; |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 139 | } |
| 140 | |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 141 | ssize_t load_image_mr(const char *filename, MemoryRegion *mr) |
Peter Maydell | 76151ca | 2016-03-04 11:30:17 +0000 | [diff] [blame] | 142 | { |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 143 | ssize_t size; |
Peter Maydell | 76151ca | 2016-03-04 11:30:17 +0000 | [diff] [blame] | 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 | |
Philippe Mathieu-Daudé | 2a4e2e4 | 2017-07-26 23:42:07 -0300 | [diff] [blame] | 152 | if (size < 0 || size > memory_region_size(mr)) { |
Peter Maydell | 76151ca | 2016-03-04 11:30:17 +0000 | [diff] [blame] | 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 | |
Avi Kivity | a8170e5 | 2012-10-23 12:30:10 +0200 | [diff] [blame] | 163 | void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size, |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 164 | const char *source) |
| 165 | { |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 166 | const char *nulp; |
Gerd Hoffmann | 3c178e7 | 2009-10-07 13:37:06 +0200 | [diff] [blame] | 167 | char *ptr; |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 168 | |
| 169 | if (buf_size <= 0) return; |
| 170 | nulp = memchr(source, 0, buf_size); |
| 171 | if (nulp) { |
Gerd Hoffmann | 3c178e7 | 2009-10-07 13:37:06 +0200 | [diff] [blame] | 172 | rom_add_blob_fixed(name, source, (nulp - source) + 1, dest); |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 173 | } else { |
Gerd Hoffmann | 3c178e7 | 2009-10-07 13:37:06 +0200 | [diff] [blame] | 174 | rom_add_blob_fixed(name, source, buf_size, dest); |
Thomas Huth | 0f0f8b6 | 2018-06-26 11:35:40 +0200 | [diff] [blame] | 175 | ptr = rom_ptr(dest + buf_size - 1, sizeof(*ptr)); |
Gerd Hoffmann | 3c178e7 | 2009-10-07 13:37:06 +0200 | [diff] [blame] | 176 | *ptr = 0; |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 177 | } |
| 178 | } |
| 179 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 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 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 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 | } |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 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))) |
Blue Swirl | ca20cf3 | 2009-09-20 14:58:02 +0000 | [diff] [blame] | 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)) |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 217 | |
Blue Swirl | ca20cf3 | 2009-09-20 14:58:02 +0000 | [diff] [blame] | 218 | #define _N_TXTENDADDR(x, target_page_size) (N_TXTADDR(x, target_page_size)+(x).a_text) |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 219 | |
Blue Swirl | ca20cf3 | 2009-09-20 14:58:02 +0000 | [diff] [blame] | 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))) |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 223 | |
| 224 | |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 225 | ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, |
| 226 | int bswap_needed, hwaddr target_page_size) |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 227 | { |
Markus Armbruster | 725e14e | 2011-11-16 19:41:56 +0100 | [diff] [blame] | 228 | int fd; |
| 229 | ssize_t size, ret; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 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 | |
Blue Swirl | ca20cf3 | 2009-09-20 14:58:02 +0000 | [diff] [blame] | 241 | if (bswap_needed) { |
| 242 | bswap_ahdr(&e); |
| 243 | } |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 244 | |
| 245 | magic = N_MAGIC(e); |
| 246 | switch (magic) { |
| 247 | case ZMAGIC: |
| 248 | case QMAGIC: |
| 249 | case OMAGIC: |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 250 | if (e.a_text + e.a_data > max_sz) |
| 251 | goto fail; |
Paolo Bonzini | 7d37435 | 2018-12-13 23:37:37 +0100 | [diff] [blame] | 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; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 257 | case NMAGIC: |
Blue Swirl | ca20cf3 | 2009-09-20 14:58:02 +0000 | [diff] [blame] | 258 | if (N_DATADDR(e, target_page_size) + e.a_data > max_sz) |
blueswir1 | 293f78b | 2008-05-12 17:22:13 +0000 | [diff] [blame] | 259 | goto fail; |
Paolo Bonzini | 7d37435 | 2018-12-13 23:37:37 +0100 | [diff] [blame] | 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; |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 264 | ret = read_targphys(filename, fd, addr + N_DATADDR(e, target_page_size), |
Blue Swirl | ca20cf3 | 2009-09-20 14:58:02 +0000 | [diff] [blame] | 265 | e.a_data); |
Paolo Bonzini | 7d37435 | 2018-12-13 23:37:37 +0100 | [diff] [blame] | 266 | if (ret < 0) |
| 267 | goto fail; |
| 268 | size += ret; |
| 269 | break; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 270 | default: |
Paolo Bonzini | 7d37435 | 2018-12-13 23:37:37 +0100 | [diff] [blame] | 271 | goto fail; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 272 | } |
| 273 | close(fd); |
| 274 | return size; |
| 275 | fail: |
| 276 | close(fd); |
| 277 | return -1; |
| 278 | } |
| 279 | |
| 280 | /* ELF loader */ |
| 281 | |
Stefan Weil | 6cbfb86 | 2015-03-14 09:37:08 +0100 | [diff] [blame] | 282 | static void *load_at(int fd, off_t offset, size_t size) |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 283 | { |
| 284 | void *ptr; |
| 285 | if (lseek(fd, offset, SEEK_SET) < 0) |
| 286 | return NULL; |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 287 | ptr = g_malloc(size); |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 288 | if (read(fd, ptr, size) != size) { |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 289 | g_free(ptr); |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 290 | return NULL; |
| 291 | } |
| 292 | return ptr; |
| 293 | } |
| 294 | |
malc | 3efa9a6 | 2009-07-18 13:10:12 +0400 | [diff] [blame] | 295 | #ifdef ELF_CLASS |
| 296 | #undef ELF_CLASS |
| 297 | #endif |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 298 | |
| 299 | #define ELF_CLASS ELFCLASS32 |
| 300 | #include "elf.h" |
| 301 | |
| 302 | #define SZ 32 |
| 303 | #define elf_word uint32_t |
ths | 8279006 | 2007-10-17 23:07:31 +0000 | [diff] [blame] | 304 | #define elf_sword int32_t |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 305 | #define bswapSZs bswap32s |
Paolo Bonzini | 83c9f4c | 2013-02-04 15:40:22 +0100 | [diff] [blame] | 306 | #include "hw/elf_ops.h" |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 307 | |
| 308 | #undef elfhdr |
| 309 | #undef elf_phdr |
| 310 | #undef elf_shdr |
| 311 | #undef elf_sym |
Thomas Huth | 5dce07e | 2015-03-09 11:12:52 +0100 | [diff] [blame] | 312 | #undef elf_rela |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 313 | #undef elf_note |
| 314 | #undef elf_word |
ths | 8279006 | 2007-10-17 23:07:31 +0000 | [diff] [blame] | 315 | #undef elf_sword |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 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 |
Thomas Huth | 5dce07e | 2015-03-09 11:12:52 +0100 | [diff] [blame] | 323 | #define elf_rela elf64_rela |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 324 | #define elf_word uint64_t |
ths | 8279006 | 2007-10-17 23:07:31 +0000 | [diff] [blame] | 325 | #define elf_sword int64_t |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 326 | #define bswapSZs bswap64s |
| 327 | #define SZ 64 |
Paolo Bonzini | 83c9f4c | 2013-02-04 15:40:22 +0100 | [diff] [blame] | 328 | #include "hw/elf_ops.h" |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 329 | |
Luc Michel | 8975eb8 | 2021-10-14 21:43:25 +0200 | [diff] [blame] | 330 | const char *load_elf_strerror(ssize_t error) |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 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"; |
Stefano Garzarella | 41a2635 | 2019-09-10 16:22:23 +0200 | [diff] [blame] | 343 | case ELF_LOAD_TOO_BIG: |
| 344 | return "The image segments are too big to load"; |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 345 | default: |
| 346 | return "Unknown error"; |
| 347 | } |
| 348 | } |
| 349 | |
Peter Crosthwaite | 04ae712 | 2016-03-04 11:30:21 +0000 | [diff] [blame] | 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 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 405 | /* return < 0 if error, otherwise the number of bytes loaded in memory */ |
Luc Michel | 8975eb8 | 2021-10-14 21:43:25 +0200 | [diff] [blame] | 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) |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 412 | { |
Liam Merwick | 4366e1d | 2019-01-15 12:18:03 +0000 | [diff] [blame] | 413 | return load_elf_as(filename, elf_note_fn, translate_fn, translate_opaque, |
Aleksandar Markovic | 6cdda0f | 2020-01-26 23:55:04 +0100 | [diff] [blame] | 414 | pentry, lowaddr, highaddr, pflags, big_endian, |
| 415 | elf_machine, clear_lsb, data_swab, NULL); |
Alistair Francis | 70bb1d1 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 416 | } |
| 417 | |
| 418 | /* return < 0 if error, otherwise the number of bytes loaded in memory */ |
Luc Michel | 8975eb8 | 2021-10-14 21:43:25 +0200 | [diff] [blame] | 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) |
Alistair Francis | 70bb1d1 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 426 | { |
Liam Merwick | 4366e1d | 2019-01-15 12:18:03 +0000 | [diff] [blame] | 427 | return load_elf_ram(filename, elf_note_fn, translate_fn, translate_opaque, |
Aleksandar Markovic | 6cdda0f | 2020-01-26 23:55:04 +0100 | [diff] [blame] | 428 | pentry, lowaddr, highaddr, pflags, big_endian, |
| 429 | elf_machine, clear_lsb, data_swab, as, true); |
Farhan Ali | 34f1b23 | 2016-11-07 10:50:30 -0500 | [diff] [blame] | 430 | } |
| 431 | |
| 432 | /* return < 0 if error, otherwise the number of bytes loaded in memory */ |
Luc Michel | 8975eb8 | 2021-10-14 21:43:25 +0200 | [diff] [blame] | 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) |
Farhan Ali | 34f1b23 | 2016-11-07 10:50:30 -0500 | [diff] [blame] | 440 | { |
Liam Merwick | 4366e1d | 2019-01-15 12:18:03 +0000 | [diff] [blame] | 441 | return load_elf_ram_sym(filename, elf_note_fn, |
| 442 | translate_fn, translate_opaque, |
Aleksandar Markovic | 6cdda0f | 2020-01-26 23:55:04 +0100 | [diff] [blame] | 443 | pentry, lowaddr, highaddr, pflags, big_endian, |
Michael Clark | a2480ff | 2018-03-03 01:31:12 +1300 | [diff] [blame] | 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 */ |
Luc Michel | 8975eb8 | 2021-10-14 21:43:25 +0200 | [diff] [blame] | 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) |
Michael Clark | a2480ff | 2018-03-03 01:31:12 +1300 | [diff] [blame] | 457 | { |
Luc Michel | 8975eb8 | 2021-10-14 21:43:25 +0200 | [diff] [blame] | 458 | int fd, data_order, target_data_order, must_swab; |
| 459 | ssize_t ret = ELF_LOAD_FAILED; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 460 | uint8_t e_ident[EI_NIDENT]; |
| 461 | |
bellard | 699e464 | 2006-05-07 18:06:27 +0000 | [diff] [blame] | 462 | fd = open(filename, O_RDONLY | O_BINARY); |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 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 || |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 472 | e_ident[3] != ELFMAG3) { |
| 473 | ret = ELF_LOAD_NOT_ELF; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 474 | goto fail; |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 475 | } |
Marc-André Lureau | e03b568 | 2022-03-23 19:57:17 +0400 | [diff] [blame] | 476 | #if HOST_BIG_ENDIAN |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 477 | data_order = ELFDATA2MSB; |
| 478 | #else |
| 479 | data_order = ELFDATA2LSB; |
| 480 | #endif |
| 481 | must_swab = data_order != e_ident[EI_DATA]; |
Blue Swirl | ca20cf3 | 2009-09-20 14:58:02 +0000 | [diff] [blame] | 482 | if (big_endian) { |
| 483 | target_data_order = ELFDATA2MSB; |
| 484 | } else { |
| 485 | target_data_order = ELFDATA2LSB; |
| 486 | } |
ths | 9042c0e | 2006-12-23 14:18:40 +0000 | [diff] [blame] | 487 | |
Blue Swirl | cedf9a6 | 2011-01-12 19:48:57 +0000 | [diff] [blame] | 488 | if (target_data_order != e_ident[EI_DATA]) { |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 489 | ret = ELF_LOAD_WRONG_ENDIAN; |
Blue Swirl | cedf9a6 | 2011-01-12 19:48:57 +0000 | [diff] [blame] | 490 | goto fail; |
| 491 | } |
ths | 9042c0e | 2006-12-23 14:18:40 +0000 | [diff] [blame] | 492 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 493 | lseek(fd, 0, SEEK_SET); |
| 494 | if (e_ident[EI_CLASS] == ELFCLASS64) { |
Liam Merwick | 4366e1d | 2019-01-15 12:18:03 +0000 | [diff] [blame] | 495 | ret = load_elf64(filename, fd, elf_note_fn, |
| 496 | translate_fn, translate_opaque, must_swab, |
Aleksandar Markovic | 6cdda0f | 2020-01-26 23:55:04 +0100 | [diff] [blame] | 497 | pentry, lowaddr, highaddr, pflags, elf_machine, |
| 498 | clear_lsb, data_swab, as, load_rom, sym_cb); |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 499 | } else { |
Liam Merwick | 4366e1d | 2019-01-15 12:18:03 +0000 | [diff] [blame] | 500 | ret = load_elf32(filename, fd, elf_note_fn, |
| 501 | translate_fn, translate_opaque, must_swab, |
Aleksandar Markovic | 6cdda0f | 2020-01-26 23:55:04 +0100 | [diff] [blame] | 502 | pentry, lowaddr, highaddr, pflags, elf_machine, |
| 503 | clear_lsb, data_swab, as, load_rom, sym_cb); |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 504 | } |
| 505 | |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 506 | fail: |
| 507 | close(fd); |
Alexey Kardashevskiy | 18674b2 | 2014-02-04 15:04:18 +1100 | [diff] [blame] | 508 | return ret; |
bellard | 5fe141f | 2006-04-23 17:12:42 +0000 | [diff] [blame] | 509 | } |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 510 | |
Anthony Liguori | c227f09 | 2009-10-01 16:12:16 -0500 | [diff] [blame] | 511 | static void bswap_uboot_header(uboot_image_header_t *hdr) |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 512 | { |
Marc-André Lureau | e03b568 | 2022-03-23 19:57:17 +0400 | [diff] [blame] | 513 | #if !HOST_BIG_ENDIAN |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 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 | |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 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 | |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 534 | p = g_malloc(size); |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 535 | |
| 536 | return (p); |
| 537 | } |
| 538 | |
Stefan Weil | d084eab | 2009-06-09 23:36:03 +0200 | [diff] [blame] | 539 | static void zfree(void *x, void *addr) |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 540 | { |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 541 | g_free(addr); |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 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 | |
Paul Burton | 51b5856 | 2016-09-08 15:51:56 +0100 | [diff] [blame] | 553 | ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen) |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 554 | { |
| 555 | z_stream s; |
| 556 | ssize_t dstbytes; |
| 557 | int r, i, flags; |
| 558 | |
| 559 | /* skip header */ |
| 560 | i = 10; |
Peter Maydell | 312c496 | 2021-08-12 15:18:03 +0100 | [diff] [blame] | 561 | if (srclen < 4) { |
| 562 | goto toosmall; |
| 563 | } |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 564 | flags = src[3]; |
| 565 | if (src[2] != DEFLATED || (flags & RESERVED) != 0) { |
| 566 | puts ("Error: Bad gzipped data\n"); |
| 567 | return -1; |
| 568 | } |
Peter Maydell | 312c496 | 2021-08-12 15:18:03 +0100 | [diff] [blame] | 569 | if ((flags & EXTRA_FIELD) != 0) { |
| 570 | if (srclen < 12) { |
| 571 | goto toosmall; |
| 572 | } |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 573 | i = 12 + src[10] + (src[11] << 8); |
Peter Maydell | 312c496 | 2021-08-12 15:18:03 +0100 | [diff] [blame] | 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) { |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 586 | i += 2; |
Peter Maydell | 312c496 | 2021-08-12 15:18:03 +0100 | [diff] [blame] | 587 | } |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 588 | if (i >= srclen) { |
Peter Maydell | 312c496 | 2021-08-12 15:18:03 +0100 | [diff] [blame] | 589 | goto toosmall; |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 590 | } |
| 591 | |
| 592 | s.zalloc = zalloc; |
Stefan Weil | d084eab | 2009-06-09 23:36:03 +0200 | [diff] [blame] | 593 | s.zfree = zfree; |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 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; |
Peter Maydell | 312c496 | 2021-08-12 15:18:03 +0100 | [diff] [blame] | 613 | |
| 614 | toosmall: |
| 615 | puts("Error: gunzip out of data in header\n"); |
| 616 | return -1; |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 617 | } |
| 618 | |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 619 | /* Load a U-Boot image. */ |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 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) |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 625 | { |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 626 | int fd; |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 627 | ssize_t size; |
Soren Brinkmann | 84aee0d | 2013-07-08 15:40:01 -0700 | [diff] [blame] | 628 | hwaddr address; |
Anthony Liguori | c227f09 | 2009-10-01 16:12:16 -0500 | [diff] [blame] | 629 | uboot_image_header_t h; |
| 630 | uboot_image_header_t *hdr = &h; |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 631 | uint8_t *data = NULL; |
aliguori | 265ca29 | 2008-11-20 22:02:56 +0000 | [diff] [blame] | 632 | int ret = -1; |
Soren Brinkmann | 84aee0d | 2013-07-08 15:40:01 -0700 | [diff] [blame] | 633 | int do_uncompress = 0; |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 634 | |
| 635 | fd = open(filename, O_RDONLY | O_BINARY); |
| 636 | if (fd < 0) |
| 637 | return -1; |
| 638 | |
Anthony Liguori | c227f09 | 2009-10-01 16:12:16 -0500 | [diff] [blame] | 639 | size = read(fd, hdr, sizeof(uboot_image_header_t)); |
Andrew Jones | a18e931 | 2017-06-02 11:51:46 +0100 | [diff] [blame] | 640 | if (size < sizeof(uboot_image_header_t)) { |
aliguori | 265ca29 | 2008-11-20 22:02:56 +0000 | [diff] [blame] | 641 | goto out; |
Andrew Jones | a18e931 | 2017-06-02 11:51:46 +0100 | [diff] [blame] | 642 | } |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 643 | |
| 644 | bswap_uboot_header(hdr); |
| 645 | |
| 646 | if (hdr->ih_magic != IH_MAGIC) |
aliguori | 265ca29 | 2008-11-20 22:02:56 +0000 | [diff] [blame] | 647 | goto out; |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 648 | |
Soren Brinkmann | 84aee0d | 2013-07-08 15:40:01 -0700 | [diff] [blame] | 649 | if (hdr->ih_type != image_type) { |
Nick Hudson | f831f95 | 2019-01-07 08:31:50 +0000 | [diff] [blame] | 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 | } |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 656 | } |
| 657 | |
Soren Brinkmann | 84aee0d | 2013-07-08 15:40:01 -0700 | [diff] [blame] | 658 | /* TODO: Implement other image types. */ |
| 659 | switch (hdr->ih_type) { |
Nick Hudson | f831f95 | 2019-01-07 08:31:50 +0000 | [diff] [blame] | 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 */ |
Soren Brinkmann | 84aee0d | 2013-07-08 15:40:01 -0700 | [diff] [blame] | 670 | case IH_TYPE_KERNEL: |
| 671 | address = hdr->ih_load; |
Max Filippov | 25bda50 | 2014-10-19 07:42:22 +0400 | [diff] [blame] | 672 | if (translate_fn) { |
| 673 | address = translate_fn(translate_opaque, address); |
| 674 | } |
Soren Brinkmann | 84aee0d | 2013-07-08 15:40:01 -0700 | [diff] [blame] | 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; |
Bin Meng | 8fe63fe | 2022-03-24 21:48:12 +0800 | [diff] [blame] | 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 | } |
Soren Brinkmann | 84aee0d | 2013-07-08 15:40:01 -0700 | [diff] [blame] | 715 | } else { |
| 716 | *is_linux = 0; |
| 717 | } |
| 718 | } |
| 719 | |
| 720 | break; |
| 721 | case IH_TYPE_RAMDISK: |
| 722 | address = *loadaddr; |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 723 | break; |
| 724 | default: |
Soren Brinkmann | 84aee0d | 2013-07-08 15:40:01 -0700 | [diff] [blame] | 725 | fprintf(stderr, "Unsupported u-boot image type %d\n", hdr->ih_type); |
aliguori | 265ca29 | 2008-11-20 22:02:56 +0000 | [diff] [blame] | 726 | goto out; |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 727 | } |
| 728 | |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 729 | data = g_malloc(hdr->ih_size); |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 730 | |
| 731 | if (read(fd, data, hdr->ih_size) != hdr->ih_size) { |
| 732 | fprintf(stderr, "Error reading file\n"); |
aliguori | 265ca29 | 2008-11-20 22:02:56 +0000 | [diff] [blame] | 733 | goto out; |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 734 | } |
| 735 | |
Soren Brinkmann | 84aee0d | 2013-07-08 15:40:01 -0700 | [diff] [blame] | 736 | if (do_uncompress) { |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 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; |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 743 | data = g_malloc(max_bytes); |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 744 | |
| 745 | bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size); |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 746 | g_free(compressed_data); |
aliguori | 5a12357 | 2008-11-20 22:04:01 +0000 | [diff] [blame] | 747 | if (bytes < 0) { |
| 748 | fprintf(stderr, "Unable to decompress gzipped image!\n"); |
| 749 | goto out; |
| 750 | } |
| 751 | hdr->ih_size = bytes; |
| 752 | } |
| 753 | |
Alistair Francis | 5e774eb | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 754 | rom_add_blob_fixed_as(filename, data, hdr->ih_size, address, as); |
aliguori | 21cafd0 | 2008-11-20 22:11:52 +0000 | [diff] [blame] | 755 | |
aliguori | 265ca29 | 2008-11-20 22:02:56 +0000 | [diff] [blame] | 756 | ret = hdr->ih_size; |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 757 | |
aliguori | 265ca29 | 2008-11-20 22:02:56 +0000 | [diff] [blame] | 758 | out: |
Daniel P. Berrange | ef1e1e0 | 2015-08-26 12:17:18 +0100 | [diff] [blame] | 759 | g_free(data); |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 760 | close(fd); |
aliguori | 265ca29 | 2008-11-20 22:02:56 +0000 | [diff] [blame] | 761 | return ret; |
pbrook | 1c7b375 | 2007-03-06 23:52:01 +0000 | [diff] [blame] | 762 | } |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 763 | |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 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) |
Soren Brinkmann | 84aee0d | 2013-07-08 15:40:01 -0700 | [diff] [blame] | 768 | { |
Max Filippov | 25bda50 | 2014-10-19 07:42:22 +0400 | [diff] [blame] | 769 | return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL, |
Alistair Francis | 5e774eb | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 770 | translate_fn, translate_opaque, NULL); |
| 771 | } |
| 772 | |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 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) |
Alistair Francis | 5e774eb | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 777 | { |
| 778 | return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL, |
| 779 | translate_fn, translate_opaque, as); |
Soren Brinkmann | 84aee0d | 2013-07-08 15:40:01 -0700 | [diff] [blame] | 780 | } |
| 781 | |
| 782 | /* Load a ramdisk. */ |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 783 | ssize_t load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz) |
Soren Brinkmann | 84aee0d | 2013-07-08 15:40:01 -0700 | [diff] [blame] | 784 | { |
Peter Maydell | 97df5fe | 2018-03-02 10:45:35 +0000 | [diff] [blame] | 785 | return load_ramdisk_as(filename, addr, max_sz, NULL); |
| 786 | } |
| 787 | |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 788 | ssize_t load_ramdisk_as(const char *filename, hwaddr addr, uint64_t max_sz, |
| 789 | AddressSpace *as) |
Peter Maydell | 97df5fe | 2018-03-02 10:45:35 +0000 | [diff] [blame] | 790 | { |
Max Filippov | 25bda50 | 2014-10-19 07:42:22 +0400 | [diff] [blame] | 791 | return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK, |
Peter Maydell | 97df5fe | 2018-03-02 10:45:35 +0000 | [diff] [blame] | 792 | NULL, NULL, as); |
Soren Brinkmann | 84aee0d | 2013-07-08 15:40:01 -0700 | [diff] [blame] | 793 | } |
| 794 | |
Laszlo Ersek | 7d48a0f | 2014-12-22 13:11:43 +0100 | [diff] [blame] | 795 | /* Load a gzip-compressed kernel to a dynamically allocated buffer. */ |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 796 | ssize_t load_image_gzipped_buffer(const char *filename, uint64_t max_sz, |
| 797 | uint8_t **buffer) |
Richard W.M. Jones | 235e74a | 2014-08-19 18:56:28 +0100 | [diff] [blame] | 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 | |
Laszlo Ersek | 7d48a0f | 2014-12-22 13:11:43 +0100 | [diff] [blame] | 829 | /* trim to actual size and return to caller */ |
| 830 | *buffer = g_realloc(data, bytes); |
Richard W.M. Jones | 235e74a | 2014-08-19 18:56:28 +0100 | [diff] [blame] | 831 | ret = bytes; |
Laszlo Ersek | 7d48a0f | 2014-12-22 13:11:43 +0100 | [diff] [blame] | 832 | /* ownership has been transferred to caller */ |
| 833 | data = NULL; |
Richard W.M. Jones | 235e74a | 2014-08-19 18:56:28 +0100 | [diff] [blame] | 834 | |
| 835 | out: |
| 836 | g_free(compressed_data); |
| 837 | g_free(data); |
| 838 | return ret; |
| 839 | } |
| 840 | |
Laszlo Ersek | 7d48a0f | 2014-12-22 13:11:43 +0100 | [diff] [blame] | 841 | /* Load a gzip-compressed kernel. */ |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 842 | ssize_t load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) |
Laszlo Ersek | 7d48a0f | 2014-12-22 13:11:43 +0100 | [diff] [blame] | 843 | { |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 844 | ssize_t bytes; |
Laszlo Ersek | 7d48a0f | 2014-12-22 13:11:43 +0100 | [diff] [blame] | 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 | |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 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; |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 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 | */ |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 871 | size_t romsize; |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 872 | size_t datasize; |
| 873 | |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 874 | uint8_t *data; |
Michael S. Tsirkin | 04920fc | 2013-08-19 17:26:55 +0300 | [diff] [blame] | 875 | MemoryRegion *mr; |
Alistair Francis | 3e76099 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 876 | AddressSpace *as; |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 877 | int isrom; |
Gerd Hoffmann | 379526a | 2009-12-18 12:01:11 +0100 | [diff] [blame] | 878 | char *fw_dir; |
| 879 | char *fw_file; |
Stefano Garzarella | fef2889 | 2019-07-24 16:31:03 +0200 | [diff] [blame] | 880 | GMappedFile *mapped_file; |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 881 | |
Stefan Hajnoczi | e233604 | 2018-08-16 14:05:28 +0100 | [diff] [blame] | 882 | bool committed; |
| 883 | |
Avi Kivity | a8170e5 | 2012-10-23 12:30:10 +0200 | [diff] [blame] | 884 | hwaddr addr; |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 885 | QTAILQ_ENTRY(Rom) next; |
| 886 | }; |
| 887 | |
Gerd Hoffmann | 8832cb8 | 2010-01-08 15:25:40 +0100 | [diff] [blame] | 888 | static FWCfgState *fw_cfg; |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 889 | static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms); |
| 890 | |
Stefano Garzarella | fef2889 | 2019-07-24 16:31:03 +0200 | [diff] [blame] | 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 | |
Stefan Hajnoczi | e7f5993 | 2018-08-16 14:05:28 +0100 | [diff] [blame] | 907 | static void rom_free(Rom *rom) |
| 908 | { |
Stefano Garzarella | fef2889 | 2019-07-24 16:31:03 +0200 | [diff] [blame] | 909 | rom_free_data(rom); |
Stefan Hajnoczi | e7f5993 | 2018-08-16 14:05:28 +0100 | [diff] [blame] | 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 | |
Alistair Francis | 3e76099 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 917 | static inline bool rom_order_compare(Rom *rom, Rom *item) |
| 918 | { |
Laszlo Ersek | 1b57bd4 | 2016-11-29 20:55:33 +0100 | [diff] [blame] | 919 | return ((uintptr_t)(void *)rom->as > (uintptr_t)(void *)item->as) || |
Alistair Francis | 3e76099 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 920 | (rom->as == item->as && rom->addr >= item->addr); |
| 921 | } |
| 922 | |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 923 | static void rom_insert(Rom *rom) |
| 924 | { |
| 925 | Rom *item; |
| 926 | |
Paul Brook | 97fe84f | 2009-11-11 18:17:24 +0000 | [diff] [blame] | 927 | if (roms_loaded) { |
| 928 | hw_error ("ROM images must be loaded at startup\n"); |
| 929 | } |
| 930 | |
Alistair Francis | 3e76099 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 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 | |
Stefan Hajnoczi | e233604 | 2018-08-16 14:05:28 +0100 | [diff] [blame] | 936 | rom->committed = false; |
| 937 | |
Alistair Francis | 3e76099 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 938 | /* List is ordered by load address in the same address space */ |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 939 | QTAILQ_FOREACH(item, &roms, next) { |
Alistair Francis | 3e76099 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 940 | if (rom_order_compare(rom, item)) { |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 941 | continue; |
Alistair Francis | 3e76099 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 942 | } |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 943 | QTAILQ_INSERT_BEFORE(item, rom, next); |
| 944 | return; |
| 945 | } |
| 946 | QTAILQ_INSERT_TAIL(&roms, rom, next); |
| 947 | } |
| 948 | |
Michael S. Tsirkin | a166614 | 2014-11-17 07:51:50 +0200 | [diff] [blame] | 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 | |
Michael S. Tsirkin | baf2d5b | 2017-01-12 19:24:14 +0100 | [diff] [blame] | 956 | static void *rom_set_mr(Rom *rom, Object *owner, const char *name, bool ro) |
Michael S. Tsirkin | 04920fc | 2013-08-19 17:26:55 +0300 | [diff] [blame] | 957 | { |
| 958 | void *data; |
| 959 | |
| 960 | rom->mr = g_malloc(sizeof(*rom->mr)); |
Michael S. Tsirkin | a166614 | 2014-11-17 07:51:50 +0200 | [diff] [blame] | 961 | memory_region_init_resizeable_ram(rom->mr, owner, name, |
| 962 | rom->datasize, rom->romsize, |
| 963 | fw_cfg_resized, |
Markus Armbruster | df8abec | 2015-09-11 16:51:44 +0200 | [diff] [blame] | 964 | &error_fatal); |
Michael S. Tsirkin | baf2d5b | 2017-01-12 19:24:14 +0100 | [diff] [blame] | 965 | memory_region_set_readonly(rom->mr, ro); |
Michael S. Tsirkin | 04920fc | 2013-08-19 17:26:55 +0300 | [diff] [blame] | 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 | |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 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) |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 978 | { |
Eduardo Habkost | 71ae9e9 | 2015-12-01 20:58:08 -0200 | [diff] [blame] | 979 | MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 980 | Rom *rom; |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 981 | ssize_t rc; |
| 982 | int fd = -1; |
Gleb Natapov | 2e55e84 | 2010-12-08 13:35:07 +0200 | [diff] [blame] | 983 | char devpath[100]; |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 984 | |
Alistair Francis | 3e76099 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 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 | |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 992 | rom = g_malloc0(sizeof(*rom)); |
| 993 | rom->name = g_strdup(file); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 994 | rom->path = qemu_find_file(QEMU_FILE_TYPE_BIOS, rom->name); |
Alistair Francis | 3e76099 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 995 | rom->as = as; |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 996 | if (rom->path == NULL) { |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 997 | rom->path = g_strdup(file); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 998 | } |
| 999 | |
malc | cef290b | 2009-10-10 19:02:40 +0400 | [diff] [blame] | 1000 | fd = open(rom->path, O_RDONLY | O_BINARY); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 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 | |
Gerd Hoffmann | bdb5ee3 | 2010-01-08 15:25:38 +0100 | [diff] [blame] | 1007 | if (fw_dir) { |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 1008 | rom->fw_dir = g_strdup(fw_dir); |
| 1009 | rom->fw_file = g_strdup(file); |
Gerd Hoffmann | bdb5ee3 | 2010-01-08 15:25:38 +0100 | [diff] [blame] | 1010 | } |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 1011 | rom->addr = addr; |
| 1012 | rom->romsize = lseek(fd, 0, SEEK_END); |
Gonglei | ddd2eab | 2014-11-15 18:06:43 +0800 | [diff] [blame] | 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 | |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 1019 | rom->datasize = rom->romsize; |
| 1020 | rom->data = g_malloc0(rom->datasize); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1021 | lseek(fd, 0, SEEK_SET); |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 1022 | rc = read(fd, rom->data, rom->datasize); |
| 1023 | if (rc != rom->datasize) { |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 1024 | fprintf(stderr, "rom: file %-20s: read error: rc=%zd (expected %zd)\n", |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 1025 | rom->name, rc, rom->datasize); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1026 | goto err; |
| 1027 | } |
| 1028 | close(fd); |
| 1029 | rom_insert(rom); |
Gleb Natapov | de1f34c | 2010-12-08 13:35:06 +0200 | [diff] [blame] | 1030 | if (rom->fw_file && fw_cfg) { |
| 1031 | const char *basename; |
Michael S. Tsirkin | 35c12e6 | 2013-07-24 18:56:05 +0300 | [diff] [blame] | 1032 | char fw_file_name[FW_CFG_MAX_FILE_PATH]; |
Michael S. Tsirkin | 04920fc | 2013-08-19 17:26:55 +0300 | [diff] [blame] | 1033 | void *data; |
Gleb Natapov | de1f34c | 2010-12-08 13:35:06 +0200 | [diff] [blame] | 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); |
Gleb Natapov | 2e55e84 | 2010-12-08 13:35:07 +0200 | [diff] [blame] | 1043 | snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); |
Michael S. Tsirkin | 04920fc | 2013-08-19 17:26:55 +0300 | [diff] [blame] | 1044 | |
Eduardo Habkost | 71ae9e9 | 2015-12-01 20:58:08 -0200 | [diff] [blame] | 1045 | if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) { |
Michael S. Tsirkin | baf2d5b | 2017-01-12 19:24:14 +0100 | [diff] [blame] | 1046 | data = rom_set_mr(rom, OBJECT(fw_cfg), devpath, true); |
Michael S. Tsirkin | 04920fc | 2013-08-19 17:26:55 +0300 | [diff] [blame] | 1047 | } else { |
| 1048 | data = rom->data; |
| 1049 | } |
| 1050 | |
| 1051 | fw_cfg_add_file(fw_cfg, fw_file_name, data, rom->romsize); |
Gleb Natapov | 2e55e84 | 2010-12-08 13:35:07 +0200 | [diff] [blame] | 1052 | } else { |
Peter Maydell | 76151ca | 2016-03-04 11:30:17 +0000 | [diff] [blame] | 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 | } |
Gleb Natapov | de1f34c | 2010-12-08 13:35:06 +0200 | [diff] [blame] | 1059 | } |
Gleb Natapov | 2e55e84 | 2010-12-08 13:35:07 +0200 | [diff] [blame] | 1060 | |
| 1061 | add_boot_device_path(bootindex, NULL, devpath); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1062 | return 0; |
| 1063 | |
| 1064 | err: |
| 1065 | if (fd != -1) |
| 1066 | close(fd); |
Cao jin | ed2f3bc | 2016-02-24 17:12:47 +0800 | [diff] [blame] | 1067 | |
Stefan Hajnoczi | e7f5993 | 2018-08-16 14:05:28 +0100 | [diff] [blame] | 1068 | rom_free(rom); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1069 | return -1; |
| 1070 | } |
| 1071 | |
Paolo Bonzini | 339240b | 2015-03-23 10:24:16 +0100 | [diff] [blame] | 1072 | MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, |
Michael S. Tsirkin | a166614 | 2014-11-17 07:51:50 +0200 | [diff] [blame] | 1073 | size_t max_len, hwaddr addr, const char *fw_file_name, |
Marc-André Lureau | 6f6f4ae | 2017-08-07 20:16:11 +0200 | [diff] [blame] | 1074 | FWCfgCallback fw_callback, void *callback_opaque, |
Michael S. Tsirkin | baf2d5b | 2017-01-12 19:24:14 +0100 | [diff] [blame] | 1075 | AddressSpace *as, bool read_only) |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1076 | { |
Eduardo Habkost | 71ae9e9 | 2015-12-01 20:58:08 -0200 | [diff] [blame] | 1077 | MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1078 | Rom *rom; |
Paolo Bonzini | 339240b | 2015-03-23 10:24:16 +0100 | [diff] [blame] | 1079 | MemoryRegion *mr = NULL; |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1080 | |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 1081 | rom = g_malloc0(sizeof(*rom)); |
| 1082 | rom->name = g_strdup(name); |
Laszlo Ersek | aa6c6ae | 2016-11-29 20:55:32 +0100 | [diff] [blame] | 1083 | rom->as = as; |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 1084 | rom->addr = addr; |
Michael S. Tsirkin | a166614 | 2014-11-17 07:51:50 +0200 | [diff] [blame] | 1085 | rom->romsize = max_len ? max_len : len; |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 1086 | rom->datasize = len; |
Igor Mammedov | 85fad7e | 2019-04-11 13:28:18 +0200 | [diff] [blame] | 1087 | g_assert(rom->romsize >= rom->datasize); |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 1088 | rom->data = g_malloc0(rom->datasize); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1089 | memcpy(rom->data, blob, len); |
| 1090 | rom_insert(rom); |
Michael S. Tsirkin | 48354cc | 2013-08-18 17:02:33 +0300 | [diff] [blame] | 1091 | if (fw_file_name && fw_cfg) { |
| 1092 | char devpath[100]; |
Michael S. Tsirkin | ad5b88b | 2014-11-17 07:49:21 +0200 | [diff] [blame] | 1093 | void *data; |
Michael S. Tsirkin | 48354cc | 2013-08-18 17:02:33 +0300 | [diff] [blame] | 1094 | |
Michael S. Tsirkin | baf2d5b | 2017-01-12 19:24:14 +0100 | [diff] [blame] | 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 | } |
Michael S. Tsirkin | 48354cc | 2013-08-18 17:02:33 +0300 | [diff] [blame] | 1100 | |
Eduardo Habkost | 71ae9e9 | 2015-12-01 20:58:08 -0200 | [diff] [blame] | 1101 | if (mc->rom_file_has_mr) { |
Michael S. Tsirkin | baf2d5b | 2017-01-12 19:24:14 +0100 | [diff] [blame] | 1102 | data = rom_set_mr(rom, OBJECT(fw_cfg), devpath, read_only); |
Paolo Bonzini | 339240b | 2015-03-23 10:24:16 +0100 | [diff] [blame] | 1103 | mr = rom->mr; |
Michael S. Tsirkin | 48354cc | 2013-08-18 17:02:33 +0300 | [diff] [blame] | 1104 | } else { |
| 1105 | data = rom->data; |
| 1106 | } |
| 1107 | |
| 1108 | fw_cfg_add_file_callback(fw_cfg, fw_file_name, |
Marc-André Lureau | 5f9252f | 2017-09-11 18:59:23 +0200 | [diff] [blame] | 1109 | fw_callback, NULL, callback_opaque, |
Michael S. Tsirkin | baf2d5b | 2017-01-12 19:24:14 +0100 | [diff] [blame] | 1110 | data, rom->datasize, read_only); |
Michael S. Tsirkin | 48354cc | 2013-08-18 17:02:33 +0300 | [diff] [blame] | 1111 | } |
Paolo Bonzini | 339240b | 2015-03-23 10:24:16 +0100 | [diff] [blame] | 1112 | return mr; |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1113 | } |
| 1114 | |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 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 |
Stefano Garzarella | fef2889 | 2019-07-24 16:31:03 +0200 | [diff] [blame] | 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. |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 1119 | */ |
Stefano Garzarella | fef2889 | 2019-07-24 16:31:03 +0200 | [diff] [blame] | 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) |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 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; |
Alistair Francis | 3e76099 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 1132 | rom->as = as; |
Stefano Garzarella | fef2889 | 2019-07-24 16:31:03 +0200 | [diff] [blame] | 1133 | |
| 1134 | if (mapped_file && data) { |
| 1135 | g_mapped_file_ref(mapped_file); |
| 1136 | rom->mapped_file = mapped_file; |
| 1137 | } |
| 1138 | |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 1139 | rom_insert(rom); |
| 1140 | return 0; |
| 1141 | } |
| 1142 | |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 1143 | ssize_t rom_add_vga(const char *file) |
Gerd Hoffmann | de2aff1 | 2009-10-26 12:18:25 +0100 | [diff] [blame] | 1144 | { |
Alistair Francis | 3e76099 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 1145 | return rom_add_file(file, "vgaroms", 0, -1, true, NULL, NULL); |
Gerd Hoffmann | de2aff1 | 2009-10-26 12:18:25 +0100 | [diff] [blame] | 1146 | } |
| 1147 | |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 1148 | ssize_t rom_add_option(const char *file, int32_t bootindex) |
Gerd Hoffmann | de2aff1 | 2009-10-26 12:18:25 +0100 | [diff] [blame] | 1149 | { |
Alistair Francis | 3e76099 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 1150 | return rom_add_file(file, "genroms", 0, bootindex, true, NULL, NULL); |
Gerd Hoffmann | de2aff1 | 2009-10-26 12:18:25 +0100 | [diff] [blame] | 1151 | } |
| 1152 | |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1153 | static void rom_reset(void *unused) |
| 1154 | { |
| 1155 | Rom *rom; |
| 1156 | |
| 1157 | QTAILQ_FOREACH(rom, &roms, next) { |
Avi Kivity | e405a2b | 2009-12-22 11:57:02 +0200 | [diff] [blame] | 1158 | if (rom->fw_file) { |
| 1159 | continue; |
| 1160 | } |
Dr. David Alan Gilbert | 5073b5d | 2020-03-13 15:59:39 +0000 | [diff] [blame] | 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 | |
Gerd Hoffmann | bdb5ee3 | 2010-01-08 15:25:38 +0100 | [diff] [blame] | 1177 | if (rom->data == NULL) { |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1178 | continue; |
Gerd Hoffmann | bdb5ee3 | 2010-01-08 15:25:38 +0100 | [diff] [blame] | 1179 | } |
Michael S. Tsirkin | 04920fc | 2013-08-19 17:26:55 +0300 | [diff] [blame] | 1180 | if (rom->mr) { |
| 1181 | void *host = memory_region_get_ram_ptr(rom->mr); |
| 1182 | memcpy(host, rom->data, rom->datasize); |
Laurent Vivier | b4c4c1f | 2022-01-15 21:37:24 +0100 | [diff] [blame] | 1183 | memset(host + rom->datasize, 0, rom->romsize - rom->datasize); |
Michael S. Tsirkin | 04920fc | 2013-08-19 17:26:55 +0300 | [diff] [blame] | 1184 | } else { |
Peter Maydell | 3c8133f | 2018-12-14 13:30:48 +0000 | [diff] [blame] | 1185 | address_space_write_rom(rom->as, rom->addr, MEMTXATTRS_UNSPECIFIED, |
| 1186 | rom->data, rom->datasize); |
Laurent Vivier | b4c4c1f | 2022-01-15 21:37:24 +0100 | [diff] [blame] | 1187 | address_space_set(rom->as, rom->addr + rom->datasize, 0, |
| 1188 | rom->romsize - rom->datasize, |
| 1189 | MEMTXATTRS_UNSPECIFIED); |
Michael S. Tsirkin | 04920fc | 2013-08-19 17:26:55 +0300 | [diff] [blame] | 1190 | } |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1191 | if (rom->isrom) { |
| 1192 | /* rom needs to be written only once */ |
Stefano Garzarella | fef2889 | 2019-07-24 16:31:03 +0200 | [diff] [blame] | 1193 | rom_free_data(rom); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1194 | } |
Alexander Graf | 582b55a | 2013-12-11 14:17:44 +0100 | [diff] [blame] | 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); |
Alexey Kardashevskiy | 26b8e6d | 2019-06-13 15:09:37 +1000 | [diff] [blame] | 1202 | |
| 1203 | trace_loader_write_rom(rom->name, rom->addr, rom->datasize, rom->isrom); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1204 | } |
| 1205 | } |
| 1206 | |
Peter Maydell | 5b1de52 | 2020-11-29 20:39:20 +0000 | [diff] [blame] | 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 | |
Peter Maydell | 837a059 | 2020-11-29 20:39:21 +0000 | [diff] [blame] | 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 | |
Eric Auger | 6b3f7f6 | 2015-06-16 17:07:54 +0100 | [diff] [blame] | 1248 | int rom_check_and_register_reset(void) |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1249 | { |
Avi Kivity | dcc5cd3 | 2011-12-08 16:12:18 +0200 | [diff] [blame] | 1250 | MemoryRegionSection section; |
Peter Maydell | 5b1de52 | 2020-11-29 20:39:20 +0000 | [diff] [blame] | 1251 | Rom *rom, *last_rom = NULL; |
Peter Maydell | 837a059 | 2020-11-29 20:39:21 +0000 | [diff] [blame] | 1252 | bool found_overlap = false; |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1253 | |
| 1254 | QTAILQ_FOREACH(rom, &roms, next) { |
Avi Kivity | e405a2b | 2009-12-22 11:57:02 +0200 | [diff] [blame] | 1255 | if (rom->fw_file) { |
| 1256 | continue; |
| 1257 | } |
Mark Cave-Ayland | ca316c1 | 2018-02-23 11:10:17 +0000 | [diff] [blame] | 1258 | if (!rom->mr) { |
Peter Maydell | 5b1de52 | 2020-11-29 20:39:20 +0000 | [diff] [blame] | 1259 | if (roms_overlap(last_rom, rom)) { |
Peter Maydell | 837a059 | 2020-11-29 20:39:21 +0000 | [diff] [blame] | 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 */ |
Mark Cave-Ayland | ca316c1 | 2018-02-23 11:10:17 +0000 | [diff] [blame] | 1266 | } |
Peter Maydell | 5b1de52 | 2020-11-29 20:39:20 +0000 | [diff] [blame] | 1267 | last_rom = rom; |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1268 | } |
Alistair Francis | d6ac342 | 2016-09-22 18:13:08 +0100 | [diff] [blame] | 1269 | section = memory_region_find(rom->mr ? rom->mr : get_system_memory(), |
| 1270 | rom->addr, 1); |
Paolo Bonzini | 052e87b | 2013-05-27 10:08:27 +0200 | [diff] [blame] | 1271 | rom->isrom = int128_nz(section.size) && memory_region_is_rom(section.mr); |
Paolo Bonzini | dfde4e6 | 2013-05-06 10:46:11 +0200 | [diff] [blame] | 1272 | memory_region_unref(section.mr); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1273 | } |
Peter Maydell | 837a059 | 2020-11-29 20:39:21 +0000 | [diff] [blame] | 1274 | if (found_overlap) { |
| 1275 | return -1; |
| 1276 | } |
| 1277 | |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1278 | qemu_register_reset(rom_reset, NULL); |
Michael S. Tsirkin | d916b46 | 2013-07-24 18:56:08 +0300 | [diff] [blame] | 1279 | roms_loaded = 1; |
Eric Auger | 6b3f7f6 | 2015-06-16 17:07:54 +0100 | [diff] [blame] | 1280 | return 0; |
Michael S. Tsirkin | d916b46 | 2013-07-24 18:56:08 +0300 | [diff] [blame] | 1281 | } |
| 1282 | |
Laszlo Ersek | a88b362 | 2013-04-16 02:24:08 +0200 | [diff] [blame] | 1283 | void rom_set_fw(FWCfgState *f) |
Gerd Hoffmann | 379526a | 2009-12-18 12:01:11 +0100 | [diff] [blame] | 1284 | { |
Gerd Hoffmann | 8832cb8 | 2010-01-08 15:25:40 +0100 | [diff] [blame] | 1285 | fw_cfg = f; |
Gerd Hoffmann | 379526a | 2009-12-18 12:01:11 +0100 | [diff] [blame] | 1286 | } |
| 1287 | |
Gerd Hoffmann | bab47d9 | 2016-04-07 09:12:58 -0500 | [diff] [blame] | 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 | |
Stefan Hajnoczi | e233604 | 2018-08-16 14:05:28 +0100 | [diff] [blame] | 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 | |
Thomas Huth | 0f0f8b6 | 2018-06-26 11:35:40 +0200 | [diff] [blame] | 1330 | static Rom *find_rom(hwaddr addr, size_t size) |
Gerd Hoffmann | 3c178e7 | 2009-10-07 13:37:06 +0200 | [diff] [blame] | 1331 | { |
| 1332 | Rom *rom; |
| 1333 | |
| 1334 | QTAILQ_FOREACH(rom, &roms, next) { |
Aurelien Jarno | f21a59c | 2009-12-24 19:30:25 +0100 | [diff] [blame] | 1335 | if (rom->fw_file) { |
| 1336 | continue; |
| 1337 | } |
Michael S. Tsirkin | 04920fc | 2013-08-19 17:26:55 +0300 | [diff] [blame] | 1338 | if (rom->mr) { |
| 1339 | continue; |
| 1340 | } |
Gerd Hoffmann | bdb5ee3 | 2010-01-08 15:25:38 +0100 | [diff] [blame] | 1341 | if (rom->addr > addr) { |
Gerd Hoffmann | 3c178e7 | 2009-10-07 13:37:06 +0200 | [diff] [blame] | 1342 | continue; |
Gerd Hoffmann | bdb5ee3 | 2010-01-08 15:25:38 +0100 | [diff] [blame] | 1343 | } |
Thomas Huth | 0f0f8b6 | 2018-06-26 11:35:40 +0200 | [diff] [blame] | 1344 | if (rom->addr + rom->romsize < addr + size) { |
Gerd Hoffmann | 3c178e7 | 2009-10-07 13:37:06 +0200 | [diff] [blame] | 1345 | continue; |
Gerd Hoffmann | bdb5ee3 | 2010-01-08 15:25:38 +0100 | [diff] [blame] | 1346 | } |
Gerd Hoffmann | 3c178e7 | 2009-10-07 13:37:06 +0200 | [diff] [blame] | 1347 | return rom; |
| 1348 | } |
| 1349 | return NULL; |
| 1350 | } |
| 1351 | |
Alex Bennée | 5fc983a | 2022-02-25 17:20:20 +0000 | [diff] [blame] | 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 | |
Kevin Wolf | 935effc | 2009-12-19 21:15:20 +0100 | [diff] [blame] | 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 | */ |
Avi Kivity | a8170e5 | 2012-10-23 12:30:10 +0200 | [diff] [blame] | 1443 | int rom_copy(uint8_t *dest, hwaddr addr, size_t size) |
Alexander Graf | 235f86e | 2009-11-12 21:53:11 +0100 | [diff] [blame] | 1444 | { |
Avi Kivity | a8170e5 | 2012-10-23 12:30:10 +0200 | [diff] [blame] | 1445 | hwaddr end = addr + size; |
Alexander Graf | 235f86e | 2009-11-12 21:53:11 +0100 | [diff] [blame] | 1446 | uint8_t *s, *d = dest; |
| 1447 | size_t l = 0; |
| 1448 | Rom *rom; |
| 1449 | |
| 1450 | QTAILQ_FOREACH(rom, &roms, next) { |
Aurelien Jarno | f21a59c | 2009-12-24 19:30:25 +0100 | [diff] [blame] | 1451 | if (rom->fw_file) { |
| 1452 | continue; |
| 1453 | } |
Michael S. Tsirkin | 04920fc | 2013-08-19 17:26:55 +0300 | [diff] [blame] | 1454 | if (rom->mr) { |
| 1455 | continue; |
| 1456 | } |
Gerd Hoffmann | bdb5ee3 | 2010-01-08 15:25:38 +0100 | [diff] [blame] | 1457 | if (rom->addr + rom->romsize < addr) { |
Gerd Hoffmann | 632cf03 | 2009-12-18 12:01:12 +0100 | [diff] [blame] | 1458 | continue; |
Gerd Hoffmann | bdb5ee3 | 2010-01-08 15:25:38 +0100 | [diff] [blame] | 1459 | } |
Thomas Huth | e423455 | 2019-09-25 14:16:43 +0200 | [diff] [blame] | 1460 | if (rom->addr > end || rom->addr < addr) { |
Alexander Graf | 235f86e | 2009-11-12 21:53:11 +0100 | [diff] [blame] | 1461 | break; |
Gerd Hoffmann | bdb5ee3 | 2010-01-08 15:25:38 +0100 | [diff] [blame] | 1462 | } |
Alexander Graf | 235f86e | 2009-11-12 21:53:11 +0100 | [diff] [blame] | 1463 | |
Gerd Hoffmann | 632cf03 | 2009-12-18 12:01:12 +0100 | [diff] [blame] | 1464 | d = dest + (rom->addr - addr); |
Alexander Graf | 235f86e | 2009-11-12 21:53:11 +0100 | [diff] [blame] | 1465 | s = rom->data; |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 1466 | l = rom->datasize; |
Alexander Graf | 235f86e | 2009-11-12 21:53:11 +0100 | [diff] [blame] | 1467 | |
Alexander Graf | 235f86e | 2009-11-12 21:53:11 +0100 | [diff] [blame] | 1468 | if ((d + l) > (dest + size)) { |
| 1469 | l = dest - d; |
| 1470 | } |
| 1471 | |
Martijn van den Broek | 0dd5ce3 | 2013-08-06 20:45:39 +0200 | [diff] [blame] | 1472 | if (l > 0) { |
| 1473 | memcpy(d, s, l); |
| 1474 | } |
Fabien Chouteau | d60fa42 | 2013-02-19 04:41:11 +0000 | [diff] [blame] | 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 | } |
Alexander Graf | 235f86e | 2009-11-12 21:53:11 +0100 | [diff] [blame] | 1495 | } |
| 1496 | |
| 1497 | return (d + l) - dest; |
| 1498 | } |
| 1499 | |
Thomas Huth | 0f0f8b6 | 2018-06-26 11:35:40 +0200 | [diff] [blame] | 1500 | void *rom_ptr(hwaddr addr, size_t size) |
Gerd Hoffmann | 3c178e7 | 2009-10-07 13:37:06 +0200 | [diff] [blame] | 1501 | { |
| 1502 | Rom *rom; |
| 1503 | |
Thomas Huth | 0f0f8b6 | 2018-06-26 11:35:40 +0200 | [diff] [blame] | 1504 | rom = find_rom(addr, size); |
Gerd Hoffmann | 3c178e7 | 2009-10-07 13:37:06 +0200 | [diff] [blame] | 1505 | if (!rom || !rom->data) |
| 1506 | return NULL; |
Gerd Hoffmann | 632cf03 | 2009-12-18 12:01:12 +0100 | [diff] [blame] | 1507 | return rom->data + (addr - rom->addr); |
Gerd Hoffmann | 3c178e7 | 2009-10-07 13:37:06 +0200 | [diff] [blame] | 1508 | } |
| 1509 | |
Peter Maydell | 1228c45 | 2021-03-18 17:48:22 +0000 | [diff] [blame] | 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 | |
Daniel P. Berrangé | dd98234 | 2021-09-08 10:35:43 +0100 | [diff] [blame] | 1585 | HumanReadableText *qmp_x_query_roms(Error **errp) |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1586 | { |
| 1587 | Rom *rom; |
Daniel P. Berrangé | dd98234 | 2021-09-08 10:35:43 +0100 | [diff] [blame] | 1588 | g_autoptr(GString) buf = g_string_new(""); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1589 | |
| 1590 | QTAILQ_FOREACH(rom, &roms, next) { |
Michael S. Tsirkin | 04920fc | 2013-08-19 17:26:55 +0300 | [diff] [blame] | 1591 | if (rom->mr) { |
Daniel P. Berrangé | dd98234 | 2021-09-08 10:35:43 +0100 | [diff] [blame] | 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); |
Michael S. Tsirkin | 04920fc | 2013-08-19 17:26:55 +0300 | [diff] [blame] | 1597 | } else if (!rom->fw_file) { |
Daniel P. Berrangé | dd98234 | 2021-09-08 10:35:43 +0100 | [diff] [blame] | 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); |
Gerd Hoffmann | 632cf03 | 2009-12-18 12:01:12 +0100 | [diff] [blame] | 1603 | } else { |
Daniel P. Berrangé | dd98234 | 2021-09-08 10:35:43 +0100 | [diff] [blame] | 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); |
Gerd Hoffmann | 632cf03 | 2009-12-18 12:01:12 +0100 | [diff] [blame] | 1610 | } |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1611 | } |
Daniel P. Berrangé | dd98234 | 2021-09-08 10:35:43 +0100 | [diff] [blame] | 1612 | |
| 1613 | return human_readable_text_from_str(buf); |
Gerd Hoffmann | 45a50b1 | 2009-10-01 16:42:33 +0200 | [diff] [blame] | 1614 | } |
Su Hang | e4a25ed | 2018-08-16 14:05:28 +0100 | [diff] [blame] | 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; |
Alex Bennée | 58d5e74 | 2020-04-03 20:11:49 +0100 | [diff] [blame] | 1696 | bool complete; |
Su Hang | e4a25ed | 2018-08-16 14:05:28 +0100 | [diff] [blame] | 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 | } |
Alex Bennée | 58d5e74 | 2020-04-03 20:11:49 +0100 | [diff] [blame] | 1734 | parser->complete = true; |
Su Hang | e4a25ed | 2018-08-16 14:05:28 +0100 | [diff] [blame] | 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, |
Alex Bennée | 58d5e74 | 2020-04-03 20:11:49 +0100 | [diff] [blame] | 1799 | .complete = false |
Su Hang | e4a25ed | 2018-08-16 14:05:28 +0100 | [diff] [blame] | 1800 | }; |
| 1801 | |
| 1802 | rom_transaction_begin(); |
| 1803 | |
Alex Bennée | 58d5e74 | 2020-04-03 20:11:49 +0100 | [diff] [blame] | 1804 | for (; hex_blob < end && !parser.complete; ++hex_blob) { |
Su Hang | e4a25ed | 2018-08-16 14:05:28 +0100 | [diff] [blame] | 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 */ |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 1851 | ssize_t load_targphys_hex_as(const char *filename, hwaddr *entry, |
| 1852 | AddressSpace *as) |
Su Hang | e4a25ed | 2018-08-16 14:05:28 +0100 | [diff] [blame] | 1853 | { |
| 1854 | gsize hex_blob_size; |
| 1855 | gchar *hex_blob; |
Jamie Iles | af97513 | 2021-11-11 14:11:40 +0000 | [diff] [blame] | 1856 | ssize_t total_size = 0; |
Su Hang | e4a25ed | 2018-08-16 14:05:28 +0100 | [diff] [blame] | 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 | } |