Angel Pons | 118a9c7 | 2020-04-02 23:48:34 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
ebiederm | c779889 | 2009-04-01 11:03:32 +0000 | [diff] [blame] | 2 | |
Julius Werner | 98eeb96 | 2019-12-11 15:47:42 -0800 | [diff] [blame] | 3 | #include <commonlib/bsd/compression.h> |
George Trudeau | c1b98b9 | 2016-04-04 00:19:02 -0400 | [diff] [blame] | 4 | #include <commonlib/endian.h> |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 5 | #include <console/console.h> |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 6 | #include <string.h> |
Julius Werner | ec5e5e0 | 2014-08-20 15:29:56 -0700 | [diff] [blame] | 7 | #include <symbols.h> |
Peter Stuge | 483b7bb | 2009-04-14 07:40:01 +0000 | [diff] [blame] | 8 | #include <cbfs.h> |
Myles Watson | 5817078 | 2009-10-28 16:13:28 +0000 | [diff] [blame] | 9 | #include <lib.h> |
Aaron Durbin | ceebc05 | 2014-02-25 00:21:10 -0600 | [diff] [blame] | 10 | #include <bootmem.h> |
Aaron Durbin | 04654a2 | 2015-03-17 11:43:44 -0500 | [diff] [blame] | 11 | #include <program_loading.h> |
Julius Werner | 09f2921 | 2015-09-29 13:51:35 -0700 | [diff] [blame] | 12 | #include <timestamp.h> |
Patrick Rudolph | 59b8f27 | 2018-04-26 09:53:16 +0200 | [diff] [blame] | 13 | #include <cbmem.h> |
Elyes HAOUAS | 93a195c | 2021-12-31 18:46:13 +0100 | [diff] [blame] | 14 | #include <types.h> |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 15 | |
Ronald G. Minnich | c308554 | 2018-10-24 15:46:51 -0700 | [diff] [blame] | 16 | /* The type syntax for C is essentially unparsable. -- Rob Pike */ |
Ting Shen | 0553226 | 2019-01-28 17:22:22 +0800 | [diff] [blame] | 17 | typedef int (*checker_t)(struct cbfs_payload_segment *cbfssegs, void *args); |
Ronald G. Minnich | c308554 | 2018-10-24 15:46:51 -0700 | [diff] [blame] | 18 | |
George Trudeau | c1b98b9 | 2016-04-04 00:19:02 -0400 | [diff] [blame] | 19 | /* Decode a serialized cbfs payload segment |
| 20 | * from memory into native endianness. |
| 21 | */ |
| 22 | static void cbfs_decode_payload_segment(struct cbfs_payload_segment *segment, |
| 23 | const struct cbfs_payload_segment *src) |
| 24 | { |
| 25 | segment->type = read_be32(&src->type); |
| 26 | segment->compression = read_be32(&src->compression); |
| 27 | segment->offset = read_be32(&src->offset); |
| 28 | segment->load_addr = read_be64(&src->load_addr); |
| 29 | segment->len = read_be32(&src->len); |
| 30 | segment->mem_len = read_be32(&src->mem_len); |
| 31 | } |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 32 | |
Ting Shen | 0553226 | 2019-01-28 17:22:22 +0800 | [diff] [blame] | 33 | static int segment_targets_type(void *dest, unsigned long memsz, |
| 34 | enum bootmem_type dest_type) |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 35 | { |
Shelley Chen | 2033afa | 2021-06-25 14:39:18 -0700 | [diff] [blame] | 36 | /* No bootmem to check in earlier stages, caller should not use |
| 37 | selfload_check(). */ |
| 38 | if (!ENV_RAMSTAGE) { |
| 39 | printk(BIOS_ERR, |
| 40 | "Callers not supposed to call selfload_check() in romstage"); |
| 41 | return 0; |
| 42 | } |
| 43 | |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 44 | uintptr_t d = (uintptr_t) dest; |
Ting Shen | 0553226 | 2019-01-28 17:22:22 +0800 | [diff] [blame] | 45 | if (bootmem_region_targets_type(d, memsz, dest_type)) |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 46 | return 1; |
George Trudeau | c1b98b9 | 2016-04-04 00:19:02 -0400 | [diff] [blame] | 47 | |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 48 | if (payload_arch_usable_ram_quirk(d, memsz)) |
| 49 | return 1; |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 50 | |
Julius Werner | 540a980 | 2019-12-09 13:03:29 -0800 | [diff] [blame] | 51 | printk(BIOS_ERR, "SELF segment doesn't target RAM: %p, %lu bytes\n", dest, memsz); |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 52 | bootmem_dump_ranges(); |
Kyösti Mälkki | a48433d | 2018-06-07 06:31:43 +0300 | [diff] [blame] | 53 | return 0; |
| 54 | } |
| 55 | |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 56 | static int load_one_segment(uint8_t *dest, |
| 57 | uint8_t *src, |
| 58 | size_t len, |
| 59 | size_t memsz, |
| 60 | uint32_t compression, |
| 61 | int flags) |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 62 | { |
Angel Pons | 6f4dc8f | 2021-02-08 18:45:21 +0100 | [diff] [blame] | 63 | unsigned char *middle, *end; |
| 64 | printk(BIOS_DEBUG, "Loading Segment: addr: %p memsz: 0x%016zx filesz: 0x%016zx\n", |
| 65 | dest, memsz, len); |
Patrick Georgi | 5eceb32 | 2009-05-13 16:27:25 +0000 | [diff] [blame] | 66 | |
Angel Pons | 6f4dc8f | 2021-02-08 18:45:21 +0100 | [diff] [blame] | 67 | /* Compute the boundaries of the segment */ |
| 68 | end = dest + memsz; |
Zheng Bao | e6ad7fa | 2009-11-05 10:02:59 +0000 | [diff] [blame] | 69 | |
Angel Pons | 6f4dc8f | 2021-02-08 18:45:21 +0100 | [diff] [blame] | 70 | /* Copy data from the initial buffer */ |
| 71 | switch (compression) { |
| 72 | case CBFS_COMPRESS_LZMA: { |
| 73 | printk(BIOS_DEBUG, "using LZMA\n"); |
Jakub Czapiga | ad6157e | 2022-02-15 11:50:31 +0100 | [diff] [blame] | 74 | timestamp_add_now(TS_ULZMA_START); |
Angel Pons | 6f4dc8f | 2021-02-08 18:45:21 +0100 | [diff] [blame] | 75 | len = ulzman(src, len, dest, memsz); |
Jakub Czapiga | ad6157e | 2022-02-15 11:50:31 +0100 | [diff] [blame] | 76 | timestamp_add_now(TS_ULZMA_END); |
Angel Pons | 6f4dc8f | 2021-02-08 18:45:21 +0100 | [diff] [blame] | 77 | if (!len) /* Decompression Error. */ |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 78 | return 0; |
Angel Pons | 6f4dc8f | 2021-02-08 18:45:21 +0100 | [diff] [blame] | 79 | break; |
| 80 | } |
| 81 | case CBFS_COMPRESS_LZ4: { |
| 82 | printk(BIOS_DEBUG, "using LZ4\n"); |
Jakub Czapiga | ad6157e | 2022-02-15 11:50:31 +0100 | [diff] [blame] | 83 | timestamp_add_now(TS_ULZ4F_START); |
Angel Pons | 6f4dc8f | 2021-02-08 18:45:21 +0100 | [diff] [blame] | 84 | len = ulz4fn(src, len, dest, memsz); |
Jakub Czapiga | ad6157e | 2022-02-15 11:50:31 +0100 | [diff] [blame] | 85 | timestamp_add_now(TS_ULZ4F_END); |
Angel Pons | 6f4dc8f | 2021-02-08 18:45:21 +0100 | [diff] [blame] | 86 | if (!len) /* Decompression Error. */ |
| 87 | return 0; |
| 88 | break; |
| 89 | } |
| 90 | case CBFS_COMPRESS_NONE: { |
| 91 | printk(BIOS_DEBUG, "it's not compressed!\n"); |
| 92 | memcpy(dest, src, len); |
| 93 | break; |
| 94 | } |
| 95 | default: |
Elyes Haouas | 8b8ada6 | 2022-11-22 17:36:02 +0100 | [diff] [blame] | 96 | printk(BIOS_INFO, "CBFS: Unknown compression type %d\n", compression); |
Angel Pons | 6f4dc8f | 2021-02-08 18:45:21 +0100 | [diff] [blame] | 97 | return 0; |
| 98 | } |
| 99 | /* Calculate middle after any changes to len. */ |
| 100 | middle = dest + len; |
| 101 | printk(BIOS_SPEW, "[ 0x%08lx, %08lx, 0x%08lx) <- %08lx\n", |
| 102 | (unsigned long)dest, |
| 103 | (unsigned long)middle, |
| 104 | (unsigned long)end, |
| 105 | (unsigned long)src); |
| 106 | |
| 107 | /* Zero the extra bytes between middle & end */ |
| 108 | if (middle < end) { |
| 109 | printk(BIOS_DEBUG, |
| 110 | "Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n", |
Aaron Durbin | 044fb53 | 2016-07-11 15:36:50 -0500 | [diff] [blame] | 111 | (unsigned long)middle, |
Angel Pons | 6f4dc8f | 2021-02-08 18:45:21 +0100 | [diff] [blame] | 112 | (unsigned long)(end - middle)); |
Aaron Durbin | 044fb53 | 2016-07-11 15:36:50 -0500 | [diff] [blame] | 113 | |
Angel Pons | 6f4dc8f | 2021-02-08 18:45:21 +0100 | [diff] [blame] | 114 | /* Zero the extra bytes */ |
| 115 | memset(middle, 0, end - middle); |
| 116 | } |
Aaron Durbin | 044fb53 | 2016-07-11 15:36:50 -0500 | [diff] [blame] | 117 | |
Angel Pons | 6f4dc8f | 2021-02-08 18:45:21 +0100 | [diff] [blame] | 118 | /* |
| 119 | * Each architecture can perform additional operations |
| 120 | * on the loaded segment |
| 121 | */ |
| 122 | prog_segment_loaded((uintptr_t)dest, memsz, flags); |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 123 | |
| 124 | return 1; |
| 125 | } |
| 126 | |
| 127 | /* Note: this function is a bit dangerous so is not exported. |
| 128 | * It assumes you're smart enough not to call it with the very |
| 129 | * last segment, since it uses seg + 1 */ |
| 130 | static int last_loadable_segment(struct cbfs_payload_segment *seg) |
| 131 | { |
| 132 | return read_be32(&(seg + 1)->type) == PAYLOAD_SEGMENT_ENTRY; |
| 133 | } |
| 134 | |
Ting Shen | 0553226 | 2019-01-28 17:22:22 +0800 | [diff] [blame] | 135 | static int check_payload_segments(struct cbfs_payload_segment *cbfssegs, |
Julius Werner | 965846f | 2021-01-11 16:07:02 -0800 | [diff] [blame] | 136 | enum bootmem_type dest_type) |
Ronald G. Minnich | c308554 | 2018-10-24 15:46:51 -0700 | [diff] [blame] | 137 | { |
| 138 | uint8_t *dest; |
| 139 | size_t memsz; |
Elyes HAOUAS | d84e20b | 2019-04-23 22:17:04 +0200 | [diff] [blame] | 140 | struct cbfs_payload_segment *seg, segment; |
Julius Werner | 965846f | 2021-01-11 16:07:02 -0800 | [diff] [blame] | 141 | |
| 142 | /* dest_type == INVALID means we're not supposed to check anything. */ |
| 143 | if (dest_type == BM_MEM_INVALID) |
| 144 | return 0; |
Ronald G. Minnich | c308554 | 2018-10-24 15:46:51 -0700 | [diff] [blame] | 145 | |
Elyes HAOUAS | d84e20b | 2019-04-23 22:17:04 +0200 | [diff] [blame] | 146 | for (seg = cbfssegs;; ++seg) { |
Julius Werner | 540a980 | 2019-12-09 13:03:29 -0800 | [diff] [blame] | 147 | printk(BIOS_DEBUG, "Checking segment from ROM address %p\n", seg); |
Ronald G. Minnich | c308554 | 2018-10-24 15:46:51 -0700 | [diff] [blame] | 148 | cbfs_decode_payload_segment(&segment, seg); |
| 149 | dest = (uint8_t *)(uintptr_t)segment.load_addr; |
| 150 | memsz = segment.mem_len; |
| 151 | if (segment.type == PAYLOAD_SEGMENT_ENTRY) |
| 152 | break; |
Ting Shen | 0553226 | 2019-01-28 17:22:22 +0800 | [diff] [blame] | 153 | if (!segment_targets_type(dest, memsz, dest_type)) |
Ronald G. Minnich | c308554 | 2018-10-24 15:46:51 -0700 | [diff] [blame] | 154 | return -1; |
| 155 | } |
| 156 | return 0; |
| 157 | } |
| 158 | |
| 159 | static int load_payload_segments(struct cbfs_payload_segment *cbfssegs, uintptr_t *entry) |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 160 | { |
| 161 | uint8_t *dest, *src; |
| 162 | size_t filesz, memsz; |
| 163 | uint32_t compression; |
| 164 | struct cbfs_payload_segment *first_segment, *seg, segment; |
| 165 | int flags = 0; |
| 166 | |
| 167 | for (first_segment = seg = cbfssegs;; ++seg) { |
Julius Werner | 540a980 | 2019-12-09 13:03:29 -0800 | [diff] [blame] | 168 | printk(BIOS_DEBUG, "Loading segment from ROM address %p\n", seg); |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 169 | |
| 170 | cbfs_decode_payload_segment(&segment, seg); |
| 171 | dest = (uint8_t *)(uintptr_t)segment.load_addr; |
| 172 | memsz = segment.mem_len; |
| 173 | compression = segment.compression; |
| 174 | filesz = segment.len; |
| 175 | |
| 176 | switch (segment.type) { |
| 177 | case PAYLOAD_SEGMENT_CODE: |
| 178 | case PAYLOAD_SEGMENT_DATA: |
| 179 | printk(BIOS_DEBUG, " %s (compression=%x)\n", |
| 180 | segment.type == PAYLOAD_SEGMENT_CODE |
| 181 | ? "code" : "data", segment.compression); |
| 182 | src = ((uint8_t *)first_segment) + segment.offset; |
| 183 | printk(BIOS_DEBUG, |
Julius Werner | 540a980 | 2019-12-09 13:03:29 -0800 | [diff] [blame] | 184 | " New segment dstaddr %p memsize 0x%zx srcaddr %p filesize 0x%zx\n", |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 185 | dest, memsz, src, filesz); |
| 186 | |
| 187 | /* Clean up the values */ |
| 188 | if (filesz > memsz) { |
| 189 | filesz = memsz; |
| 190 | printk(BIOS_DEBUG, " cleaned up filesize 0x%zx\n", filesz); |
| 191 | } |
| 192 | break; |
| 193 | |
| 194 | case PAYLOAD_SEGMENT_BSS: |
Julius Werner | 540a980 | 2019-12-09 13:03:29 -0800 | [diff] [blame] | 195 | printk(BIOS_DEBUG, " BSS %p (%d byte)\n", (void *) |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 196 | (intptr_t)segment.load_addr, segment.mem_len); |
| 197 | filesz = 0; |
| 198 | src = ((uint8_t *)first_segment) + segment.offset; |
| 199 | compression = CBFS_COMPRESS_NONE; |
| 200 | break; |
| 201 | |
| 202 | case PAYLOAD_SEGMENT_ENTRY: |
Julius Werner | 540a980 | 2019-12-09 13:03:29 -0800 | [diff] [blame] | 203 | printk(BIOS_DEBUG, " Entry Point %p\n", (void *) |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 204 | (intptr_t)segment.load_addr); |
| 205 | |
| 206 | *entry = segment.load_addr; |
| 207 | /* Per definition, a payload always has the entry point |
| 208 | * as last segment. Thus, we use the occurrence of the |
| 209 | * entry point as break condition for the loop. |
| 210 | */ |
| 211 | return 0; |
| 212 | |
| 213 | default: |
| 214 | /* We found something that we don't know about. Throw |
| 215 | * hands into the sky and run away! |
| 216 | */ |
| 217 | printk(BIOS_EMERG, "Bad segment type %x\n", segment.type); |
| 218 | return -1; |
| 219 | } |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 220 | /* Note that the 'seg + 1' is safe as we only call this |
| 221 | * function on "not the last" * items, since entry |
| 222 | * is always last. */ |
| 223 | if (last_loadable_segment(seg)) |
| 224 | flags = SEG_FINAL; |
| 225 | if (!load_one_segment(dest, src, filesz, memsz, compression, flags)) |
| 226 | return -1; |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 227 | } |
Ionela Voinescu | 00903e5 | 2015-01-09 13:14:20 +0000 | [diff] [blame] | 228 | |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 229 | return 1; |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 230 | } |
| 231 | |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 232 | __weak int payload_arch_usable_ram_quirk(uint64_t start, uint64_t size) |
| 233 | { |
| 234 | return 0; |
| 235 | } |
| 236 | |
Julius Werner | 965846f | 2021-01-11 16:07:02 -0800 | [diff] [blame] | 237 | bool selfload_mapped(struct prog *payload, void *mapping, |
| 238 | enum bootmem_type dest_type) |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 239 | { |
Ronald G. Minnich | ef40209 | 2013-11-11 10:36:28 -0800 | [diff] [blame] | 240 | uintptr_t entry = 0; |
Ronald G. Minnich | 83bd46e | 2018-09-16 09:59:54 -0700 | [diff] [blame] | 241 | struct cbfs_payload_segment *cbfssegs; |
Aaron Durbin | 899d13d | 2015-05-15 23:39:23 -0500 | [diff] [blame] | 242 | |
Julius Werner | 965846f | 2021-01-11 16:07:02 -0800 | [diff] [blame] | 243 | cbfssegs = &((struct cbfs_payload *)mapping)->segments; |
| 244 | |
| 245 | if (check_payload_segments(cbfssegs, dest_type)) |
Patrick Rudolph | 59b8f27 | 2018-04-26 09:53:16 +0200 | [diff] [blame] | 246 | return false; |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 247 | |
Ronald G. Minnich | c308554 | 2018-10-24 15:46:51 -0700 | [diff] [blame] | 248 | if (load_payload_segments(cbfssegs, &entry)) |
Julius Werner | 965846f | 2021-01-11 16:07:02 -0800 | [diff] [blame] | 249 | return false; |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 250 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 251 | printk(BIOS_SPEW, "Loaded segments\n"); |
Ronald G. Minnich | ae63126 | 2009-04-01 10:48:39 +0000 | [diff] [blame] | 252 | |
Patrick Rudolph | 59b8f27 | 2018-04-26 09:53:16 +0200 | [diff] [blame] | 253 | /* Pass cbtables to payload if architecture desires it. */ |
| 254 | prog_set_entry(payload, (void *)entry, cbmem_find(CBMEM_ID_CBTABLE)); |
Aaron Durbin | 001de1a | 2013-04-24 22:59:45 -0500 | [diff] [blame] | 255 | |
Patrick Rudolph | 59b8f27 | 2018-04-26 09:53:16 +0200 | [diff] [blame] | 256 | return true; |
Aaron Durbin | 001de1a | 2013-04-24 22:59:45 -0500 | [diff] [blame] | 257 | } |
Ronald G. Minnich | c308554 | 2018-10-24 15:46:51 -0700 | [diff] [blame] | 258 | |
Ting Shen | 0553226 | 2019-01-28 17:22:22 +0800 | [diff] [blame] | 259 | bool selfload_check(struct prog *payload, enum bootmem_type dest_type) |
Ronald G. Minnich | c308554 | 2018-10-24 15:46:51 -0700 | [diff] [blame] | 260 | { |
Julius Werner | 965846f | 2021-01-11 16:07:02 -0800 | [diff] [blame] | 261 | if (prog_locate_hook(payload)) |
| 262 | return false; |
| 263 | |
| 264 | payload->cbfs_type = CBFS_TYPE_SELF; |
| 265 | void *mapping = cbfs_type_map(prog_name(payload), NULL, &payload->cbfs_type); |
| 266 | if (!mapping) |
| 267 | return false; |
| 268 | |
| 269 | bool ret = selfload_mapped(payload, mapping, dest_type); |
| 270 | |
| 271 | cbfs_unmap(mapping); |
| 272 | return ret; |
Ronald G. Minnich | c308554 | 2018-10-24 15:46:51 -0700 | [diff] [blame] | 273 | } |
| 274 | |
| 275 | bool selfload(struct prog *payload) |
| 276 | { |
Julius Werner | 965846f | 2021-01-11 16:07:02 -0800 | [diff] [blame] | 277 | return selfload_check(payload, BM_MEM_INVALID); |
Ronald G. Minnich | c308554 | 2018-10-24 15:46:51 -0700 | [diff] [blame] | 278 | } |