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