blob: e21c4937016e3f3c18cdae98eb0dc7fb1307f2c9 [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 <string.h>
Julius Wernerec5e5e02014-08-20 15:29:56 -07007#include <symbols.h>
Peter Stuge483b7bb2009-04-14 07:40:01 +00008#include <cbfs.h>
Myles Watson58170782009-10-28 16:13:28 +00009#include <lib.h>
Aaron Durbinceebc052014-02-25 00:21:10 -060010#include <bootmem.h>
Aaron Durbin04654a22015-03-17 11:43:44 -050011#include <program_loading.h>
Julius Werner09f29212015-09-29 13:51:35 -070012#include <timestamp.h>
Patrick Rudolph59b8f272018-04-26 09:53:16 +020013#include <cbmem.h>
Elyes HAOUAS93a195c2021-12-31 18:46:13 +010014#include <types.h>
Ronald G. Minnichae631262009-04-01 10:48:39 +000015
Ronald G. Minnichc3085542018-10-24 15:46:51 -070016/* The type syntax for C is essentially unparsable. -- Rob Pike */
Ting Shen05532262019-01-28 17:22:22 +080017typedef int (*checker_t)(struct cbfs_payload_segment *cbfssegs, void *args);
Ronald G. Minnichc3085542018-10-24 15:46:51 -070018
George Trudeauc1b98b92016-04-04 00:19:02 -040019/* Decode a serialized cbfs payload segment
20 * from memory into native endianness.
21 */
22static 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. Minnichae631262009-04-01 10:48:39 +000032
Ting Shen05532262019-01-28 17:22:22 +080033static int segment_targets_type(void *dest, unsigned long memsz,
34 enum bootmem_type dest_type)
Ronald G. Minnichae631262009-04-01 10:48:39 +000035{
Shelley Chen2033afa2021-06-25 14:39:18 -070036 /* 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. Minnich83bd46e2018-09-16 09:59:54 -070044 uintptr_t d = (uintptr_t) dest;
Ting Shen05532262019-01-28 17:22:22 +080045 if (bootmem_region_targets_type(d, memsz, dest_type))
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070046 return 1;
George Trudeauc1b98b92016-04-04 00:19:02 -040047
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070048 if (payload_arch_usable_ram_quirk(d, memsz))
49 return 1;
Ronald G. Minnichae631262009-04-01 10:48:39 +000050
Julius Werner540a9802019-12-09 13:03:29 -080051 printk(BIOS_ERR, "SELF segment doesn't target RAM: %p, %lu bytes\n", dest, memsz);
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070052 bootmem_dump_ranges();
Kyösti Mälkkia48433d2018-06-07 06:31:43 +030053 return 0;
54}
55
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070056static 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. Minnichae631262009-04-01 10:48:39 +000062{
Angel Pons6f4dc8f2021-02-08 18:45:21 +010063 unsigned char *middle, *end;
64 printk(BIOS_DEBUG, "Loading Segment: addr: %p memsz: 0x%016zx filesz: 0x%016zx\n",
65 dest, memsz, len);
Patrick Georgi5eceb322009-05-13 16:27:25 +000066
Angel Pons6f4dc8f2021-02-08 18:45:21 +010067 /* Compute the boundaries of the segment */
68 end = dest + memsz;
Zheng Baoe6ad7fa2009-11-05 10:02:59 +000069
Angel Pons6f4dc8f2021-02-08 18:45:21 +010070 /* Copy data from the initial buffer */
71 switch (compression) {
72 case CBFS_COMPRESS_LZMA: {
73 printk(BIOS_DEBUG, "using LZMA\n");
Jakub Czapigaad6157e2022-02-15 11:50:31 +010074 timestamp_add_now(TS_ULZMA_START);
Angel Pons6f4dc8f2021-02-08 18:45:21 +010075 len = ulzman(src, len, dest, memsz);
Jakub Czapigaad6157e2022-02-15 11:50:31 +010076 timestamp_add_now(TS_ULZMA_END);
Angel Pons6f4dc8f2021-02-08 18:45:21 +010077 if (!len) /* Decompression Error. */
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070078 return 0;
Angel Pons6f4dc8f2021-02-08 18:45:21 +010079 break;
80 }
81 case CBFS_COMPRESS_LZ4: {
82 printk(BIOS_DEBUG, "using LZ4\n");
Jakub Czapigaad6157e2022-02-15 11:50:31 +010083 timestamp_add_now(TS_ULZ4F_START);
Angel Pons6f4dc8f2021-02-08 18:45:21 +010084 len = ulz4fn(src, len, dest, memsz);
Jakub Czapigaad6157e2022-02-15 11:50:31 +010085 timestamp_add_now(TS_ULZ4F_END);
Angel Pons6f4dc8f2021-02-08 18:45:21 +010086 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 Haouas8b8ada62022-11-22 17:36:02 +010096 printk(BIOS_INFO, "CBFS: Unknown compression type %d\n", compression);
Angel Pons6f4dc8f2021-02-08 18:45:21 +010097 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 Durbin044fb532016-07-11 15:36:50 -0500111 (unsigned long)middle,
Angel Pons6f4dc8f2021-02-08 18:45:21 +0100112 (unsigned long)(end - middle));
Aaron Durbin044fb532016-07-11 15:36:50 -0500113
Angel Pons6f4dc8f2021-02-08 18:45:21 +0100114 /* Zero the extra bytes */
115 memset(middle, 0, end - middle);
116 }
Aaron Durbin044fb532016-07-11 15:36:50 -0500117
Angel Pons6f4dc8f2021-02-08 18:45:21 +0100118 /*
119 * Each architecture can perform additional operations
120 * on the loaded segment
121 */
122 prog_segment_loaded((uintptr_t)dest, memsz, flags);
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700123
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 */
130static int last_loadable_segment(struct cbfs_payload_segment *seg)
131{
132 return read_be32(&(seg + 1)->type) == PAYLOAD_SEGMENT_ENTRY;
133}
134
Ting Shen05532262019-01-28 17:22:22 +0800135static int check_payload_segments(struct cbfs_payload_segment *cbfssegs,
Julius Werner965846f2021-01-11 16:07:02 -0800136 enum bootmem_type dest_type)
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700137{
138 uint8_t *dest;
139 size_t memsz;
Elyes HAOUASd84e20b2019-04-23 22:17:04 +0200140 struct cbfs_payload_segment *seg, segment;
Julius Werner965846f2021-01-11 16:07:02 -0800141
142 /* dest_type == INVALID means we're not supposed to check anything. */
143 if (dest_type == BM_MEM_INVALID)
144 return 0;
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700145
Elyes HAOUASd84e20b2019-04-23 22:17:04 +0200146 for (seg = cbfssegs;; ++seg) {
Julius Werner540a9802019-12-09 13:03:29 -0800147 printk(BIOS_DEBUG, "Checking segment from ROM address %p\n", seg);
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700148 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 Shen05532262019-01-28 17:22:22 +0800153 if (!segment_targets_type(dest, memsz, dest_type))
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700154 return -1;
155 }
156 return 0;
157}
158
159static int load_payload_segments(struct cbfs_payload_segment *cbfssegs, uintptr_t *entry)
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700160{
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 Werner540a9802019-12-09 13:03:29 -0800168 printk(BIOS_DEBUG, "Loading segment from ROM address %p\n", seg);
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700169
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 Werner540a9802019-12-09 13:03:29 -0800184 " New segment dstaddr %p memsize 0x%zx srcaddr %p filesize 0x%zx\n",
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700185 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 Werner540a9802019-12-09 13:03:29 -0800195 printk(BIOS_DEBUG, " BSS %p (%d byte)\n", (void *)
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700196 (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 Werner540a9802019-12-09 13:03:29 -0800203 printk(BIOS_DEBUG, " Entry Point %p\n", (void *)
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700204 (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. Minnich83bd46e2018-09-16 09:59:54 -0700220 /* 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. Minnichae631262009-04-01 10:48:39 +0000227 }
Ionela Voinescu00903e52015-01-09 13:14:20 +0000228
Ronald G. Minnichae631262009-04-01 10:48:39 +0000229 return 1;
Ronald G. Minnichae631262009-04-01 10:48:39 +0000230}
231
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700232__weak int payload_arch_usable_ram_quirk(uint64_t start, uint64_t size)
233{
234 return 0;
235}
236
Julius Werner965846f2021-01-11 16:07:02 -0800237bool selfload_mapped(struct prog *payload, void *mapping,
238 enum bootmem_type dest_type)
Ronald G. Minnichae631262009-04-01 10:48:39 +0000239{
Ronald G. Minnichef402092013-11-11 10:36:28 -0800240 uintptr_t entry = 0;
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700241 struct cbfs_payload_segment *cbfssegs;
Aaron Durbin899d13d2015-05-15 23:39:23 -0500242
Julius Werner965846f2021-01-11 16:07:02 -0800243 cbfssegs = &((struct cbfs_payload *)mapping)->segments;
244
245 if (check_payload_segments(cbfssegs, dest_type))
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200246 return false;
Ronald G. Minnichae631262009-04-01 10:48:39 +0000247
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700248 if (load_payload_segments(cbfssegs, &entry))
Julius Werner965846f2021-01-11 16:07:02 -0800249 return false;
Ronald G. Minnichae631262009-04-01 10:48:39 +0000250
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000251 printk(BIOS_SPEW, "Loaded segments\n");
Ronald G. Minnichae631262009-04-01 10:48:39 +0000252
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200253 /* Pass cbtables to payload if architecture desires it. */
254 prog_set_entry(payload, (void *)entry, cbmem_find(CBMEM_ID_CBTABLE));
Aaron Durbin001de1a2013-04-24 22:59:45 -0500255
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200256 return true;
Aaron Durbin001de1a2013-04-24 22:59:45 -0500257}
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700258
Ting Shen05532262019-01-28 17:22:22 +0800259bool selfload_check(struct prog *payload, enum bootmem_type dest_type)
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700260{
Julius Werner965846f2021-01-11 16:07:02 -0800261 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. Minnichc3085542018-10-24 15:46:51 -0700273}
274
275bool selfload(struct prog *payload)
276{
Julius Werner965846f2021-01-11 16:07:02 -0800277 return selfload_check(payload, BM_MEM_INVALID);
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700278}