blob: 70ab7199dd71693e275789c194a9510952200530 [file] [log] [blame]
Angel Pons118a9c72020-04-02 23:48:34 +02001/* SPDX-License-Identifier: GPL-2.0-only */
ebiedermc7798892009-04-01 11:03:32 +00002
Julius Werner98eeb962019-12-11 15:47:42 -08003#include <commonlib/bsd/compression.h>
George Trudeauc1b98b92016-04-04 00:19:02 -04004#include <commonlib/endian.h>
Ronald G. Minnichae631262009-04-01 10:48:39 +00005#include <console/console.h>
Ronald G. Minnichae631262009-04-01 10:48:39 +00006#include <stdint.h>
7#include <stdlib.h>
8#include <string.h>
Julius Wernerec5e5e02014-08-20 15:29:56 -07009#include <symbols.h>
Peter Stuge483b7bb2009-04-14 07:40:01 +000010#include <cbfs.h>
Myles Watson58170782009-10-28 16:13:28 +000011#include <lib.h>
Aaron Durbinceebc052014-02-25 00:21:10 -060012#include <bootmem.h>
Aaron Durbin04654a22015-03-17 11:43:44 -050013#include <program_loading.h>
Julius Werner09f29212015-09-29 13:51:35 -070014#include <timestamp.h>
Patrick Rudolph59b8f272018-04-26 09:53:16 +020015#include <cbmem.h>
Ronald G. Minnichae631262009-04-01 10:48:39 +000016
Ronald G. Minnichc3085542018-10-24 15:46:51 -070017/* The type syntax for C is essentially unparsable. -- Rob Pike */
Ting Shen05532262019-01-28 17:22:22 +080018typedef int (*checker_t)(struct cbfs_payload_segment *cbfssegs, void *args);
Ronald G. Minnichc3085542018-10-24 15:46:51 -070019
George Trudeauc1b98b92016-04-04 00:19:02 -040020/* Decode a serialized cbfs payload segment
21 * from memory into native endianness.
22 */
23static 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. Minnichae631262009-04-01 10:48:39 +000033
Ting Shen05532262019-01-28 17:22:22 +080034static int segment_targets_type(void *dest, unsigned long memsz,
35 enum bootmem_type dest_type)
Ronald G. Minnichae631262009-04-01 10:48:39 +000036{
Shelley Chen2033afa2021-06-25 14:39:18 -070037 /* 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. Minnich83bd46e2018-09-16 09:59:54 -070045 uintptr_t d = (uintptr_t) dest;
Ting Shen05532262019-01-28 17:22:22 +080046 if (bootmem_region_targets_type(d, memsz, dest_type))
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070047 return 1;
George Trudeauc1b98b92016-04-04 00:19:02 -040048
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070049 if (payload_arch_usable_ram_quirk(d, memsz))
50 return 1;
Ronald G. Minnichae631262009-04-01 10:48:39 +000051
Julius Werner540a9802019-12-09 13:03:29 -080052 printk(BIOS_ERR, "SELF segment doesn't target RAM: %p, %lu bytes\n", dest, memsz);
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070053 bootmem_dump_ranges();
Kyösti Mälkkia48433d2018-06-07 06:31:43 +030054 return 0;
55}
56
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070057static 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. Minnichae631262009-04-01 10:48:39 +000063{
Angel Pons6f4dc8f2021-02-08 18:45:21 +010064 unsigned char *middle, *end;
65 printk(BIOS_DEBUG, "Loading Segment: addr: %p memsz: 0x%016zx filesz: 0x%016zx\n",
66 dest, memsz, len);
Patrick Georgi5eceb322009-05-13 16:27:25 +000067
Angel Pons6f4dc8f2021-02-08 18:45:21 +010068 /* Compute the boundaries of the segment */
69 end = dest + memsz;
Zheng Baoe6ad7fa2009-11-05 10:02:59 +000070
Angel Pons6f4dc8f2021-02-08 18:45:21 +010071 /* Copy data from the initial buffer */
72 switch (compression) {
73 case CBFS_COMPRESS_LZMA: {
74 printk(BIOS_DEBUG, "using LZMA\n");
Jakub Czapigaad6157e2022-02-15 11:50:31 +010075 timestamp_add_now(TS_ULZMA_START);
Angel Pons6f4dc8f2021-02-08 18:45:21 +010076 len = ulzman(src, len, dest, memsz);
Jakub Czapigaad6157e2022-02-15 11:50:31 +010077 timestamp_add_now(TS_ULZMA_END);
Angel Pons6f4dc8f2021-02-08 18:45:21 +010078 if (!len) /* Decompression Error. */
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070079 return 0;
Angel Pons6f4dc8f2021-02-08 18:45:21 +010080 break;
81 }
82 case CBFS_COMPRESS_LZ4: {
83 printk(BIOS_DEBUG, "using LZ4\n");
Jakub Czapigaad6157e2022-02-15 11:50:31 +010084 timestamp_add_now(TS_ULZ4F_START);
Angel Pons6f4dc8f2021-02-08 18:45:21 +010085 len = ulz4fn(src, len, dest, memsz);
Jakub Czapigaad6157e2022-02-15 11:50:31 +010086 timestamp_add_now(TS_ULZ4F_END);
Angel Pons6f4dc8f2021-02-08 18:45:21 +010087 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 Durbin044fb532016-07-11 15:36:50 -0500112 (unsigned long)middle,
Angel Pons6f4dc8f2021-02-08 18:45:21 +0100113 (unsigned long)(end - middle));
Aaron Durbin044fb532016-07-11 15:36:50 -0500114
Angel Pons6f4dc8f2021-02-08 18:45:21 +0100115 /* Zero the extra bytes */
116 memset(middle, 0, end - middle);
117 }
Aaron Durbin044fb532016-07-11 15:36:50 -0500118
Angel Pons6f4dc8f2021-02-08 18:45:21 +0100119 /*
120 * Each architecture can perform additional operations
121 * on the loaded segment
122 */
123 prog_segment_loaded((uintptr_t)dest, memsz, flags);
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700124
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 */
131static int last_loadable_segment(struct cbfs_payload_segment *seg)
132{
133 return read_be32(&(seg + 1)->type) == PAYLOAD_SEGMENT_ENTRY;
134}
135
Ting Shen05532262019-01-28 17:22:22 +0800136static int check_payload_segments(struct cbfs_payload_segment *cbfssegs,
Julius Werner965846f2021-01-11 16:07:02 -0800137 enum bootmem_type dest_type)
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700138{
139 uint8_t *dest;
140 size_t memsz;
Elyes HAOUASd84e20b2019-04-23 22:17:04 +0200141 struct cbfs_payload_segment *seg, segment;
Julius Werner965846f2021-01-11 16:07:02 -0800142
143 /* dest_type == INVALID means we're not supposed to check anything. */
144 if (dest_type == BM_MEM_INVALID)
145 return 0;
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700146
Elyes HAOUASd84e20b2019-04-23 22:17:04 +0200147 for (seg = cbfssegs;; ++seg) {
Julius Werner540a9802019-12-09 13:03:29 -0800148 printk(BIOS_DEBUG, "Checking segment from ROM address %p\n", seg);
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700149 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 Shen05532262019-01-28 17:22:22 +0800154 if (!segment_targets_type(dest, memsz, dest_type))
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700155 return -1;
156 }
157 return 0;
158}
159
160static int load_payload_segments(struct cbfs_payload_segment *cbfssegs, uintptr_t *entry)
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700161{
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 Werner540a9802019-12-09 13:03:29 -0800169 printk(BIOS_DEBUG, "Loading segment from ROM address %p\n", seg);
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700170
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 Werner540a9802019-12-09 13:03:29 -0800185 " New segment dstaddr %p memsize 0x%zx srcaddr %p filesize 0x%zx\n",
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700186 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 Werner540a9802019-12-09 13:03:29 -0800196 printk(BIOS_DEBUG, " BSS %p (%d byte)\n", (void *)
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700197 (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 Werner540a9802019-12-09 13:03:29 -0800204 printk(BIOS_DEBUG, " Entry Point %p\n", (void *)
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700205 (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. Minnich83bd46e2018-09-16 09:59:54 -0700221 /* 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. Minnichae631262009-04-01 10:48:39 +0000228 }
Ionela Voinescu00903e52015-01-09 13:14:20 +0000229
Ronald G. Minnichae631262009-04-01 10:48:39 +0000230 return 1;
Ronald G. Minnichae631262009-04-01 10:48:39 +0000231}
232
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700233__weak int payload_arch_usable_ram_quirk(uint64_t start, uint64_t size)
234{
235 return 0;
236}
237
Julius Werner965846f2021-01-11 16:07:02 -0800238bool selfload_mapped(struct prog *payload, void *mapping,
239 enum bootmem_type dest_type)
Ronald G. Minnichae631262009-04-01 10:48:39 +0000240{
Ronald G. Minnichef402092013-11-11 10:36:28 -0800241 uintptr_t entry = 0;
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700242 struct cbfs_payload_segment *cbfssegs;
Aaron Durbin899d13d2015-05-15 23:39:23 -0500243
Julius Werner965846f2021-01-11 16:07:02 -0800244 cbfssegs = &((struct cbfs_payload *)mapping)->segments;
245
246 if (check_payload_segments(cbfssegs, dest_type))
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200247 return false;
Ronald G. Minnichae631262009-04-01 10:48:39 +0000248
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700249 if (load_payload_segments(cbfssegs, &entry))
Julius Werner965846f2021-01-11 16:07:02 -0800250 return false;
Ronald G. Minnichae631262009-04-01 10:48:39 +0000251
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000252 printk(BIOS_SPEW, "Loaded segments\n");
Ronald G. Minnichae631262009-04-01 10:48:39 +0000253
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200254 /* Pass cbtables to payload if architecture desires it. */
255 prog_set_entry(payload, (void *)entry, cbmem_find(CBMEM_ID_CBTABLE));
Aaron Durbin001de1a2013-04-24 22:59:45 -0500256
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200257 return true;
Aaron Durbin001de1a2013-04-24 22:59:45 -0500258}
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700259
Ting Shen05532262019-01-28 17:22:22 +0800260bool selfload_check(struct prog *payload, enum bootmem_type dest_type)
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700261{
Julius Werner965846f2021-01-11 16:07:02 -0800262 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. Minnichc3085542018-10-24 15:46:51 -0700274}
275
276bool selfload(struct prog *payload)
277{
Julius Werner965846f2021-01-11 16:07:02 -0800278 return selfload_check(payload, BM_MEM_INVALID);
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700279}