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