blob: c5ad525688dc192497b7f73835f8e3ceb1207dcf [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{
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070037 uintptr_t d = (uintptr_t) dest;
Ting Shen05532262019-01-28 17:22:22 +080038 if (bootmem_region_targets_type(d, memsz, dest_type))
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070039 return 1;
George Trudeauc1b98b92016-04-04 00:19:02 -040040
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070041 if (payload_arch_usable_ram_quirk(d, memsz))
42 return 1;
Ronald G. Minnichae631262009-04-01 10:48:39 +000043
Julius Werner540a9802019-12-09 13:03:29 -080044 printk(BIOS_ERR, "SELF segment doesn't target RAM: %p, %lu bytes\n", dest, memsz);
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070045 bootmem_dump_ranges();
Kyösti Mälkkia48433d2018-06-07 06:31:43 +030046 return 0;
47}
48
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070049static int load_one_segment(uint8_t *dest,
50 uint8_t *src,
51 size_t len,
52 size_t memsz,
53 uint32_t compression,
54 int flags)
Ronald G. Minnichae631262009-04-01 10:48:39 +000055{
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070056 unsigned char *middle, *end;
Julius Werner540a9802019-12-09 13:03:29 -080057 printk(BIOS_DEBUG, "Loading Segment: addr: %p memsz: 0x%016zx filesz: 0x%016zx\n",
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070058 dest, memsz, len);
Patrick Georgi5eceb322009-05-13 16:27:25 +000059
Ronald G. Minnichae631262009-04-01 10:48:39 +000060 /* Compute the boundaries of the segment */
Aaron Durbin044fb532016-07-11 15:36:50 -050061 end = dest + memsz;
Zheng Baoe6ad7fa2009-11-05 10:02:59 +000062
Ronald G. Minnichae631262009-04-01 10:48:39 +000063 /* Copy data from the initial buffer */
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070064 switch (compression) {
Lee Leahye20a3192017-03-09 16:21:34 -080065 case CBFS_COMPRESS_LZMA: {
66 printk(BIOS_DEBUG, "using LZMA\n");
67 timestamp_add_now(TS_START_ULZMA);
68 len = ulzman(src, len, dest, memsz);
69 timestamp_add_now(TS_END_ULZMA);
70 if (!len) /* Decompression Error. */
71 return 0;
72 break;
73 }
74 case CBFS_COMPRESS_LZ4: {
75 printk(BIOS_DEBUG, "using LZ4\n");
76 timestamp_add_now(TS_START_ULZ4F);
77 len = ulz4fn(src, len, dest, memsz);
78 timestamp_add_now(TS_END_ULZ4F);
79 if (!len) /* Decompression Error. */
80 return 0;
81 break;
82 }
83 case CBFS_COMPRESS_NONE: {
84 printk(BIOS_DEBUG, "it's not compressed!\n");
85 memcpy(dest, src, len);
86 break;
87 }
88 default:
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070089 printk(BIOS_INFO, "CBFS: Unknown compression type %d\n", compression);
90 return 0;
Ronald G. Minnichae631262009-04-01 10:48:39 +000091 }
Aaron Durbin044fb532016-07-11 15:36:50 -050092 /* Calculate middle after any changes to len. */
93 middle = dest + len;
94 printk(BIOS_SPEW, "[ 0x%08lx, %08lx, 0x%08lx) <- %08lx\n",
95 (unsigned long)dest,
96 (unsigned long)middle,
97 (unsigned long)end,
98 (unsigned long)src);
99
100 /* Zero the extra bytes between middle & end */
101 if (middle < end) {
Lee Leahy73402172017-03-10 15:23:24 -0800102 printk(BIOS_DEBUG,
103 "Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n",
104 (unsigned long)middle,
105 (unsigned long)(end - middle));
Aaron Durbin044fb532016-07-11 15:36:50 -0500106
107 /* Zero the extra bytes */
108 memset(middle, 0, end - middle);
109 }
110
Aaron Durbin044fb532016-07-11 15:36:50 -0500111 /*
Elyes HAOUAS0b133972018-08-15 11:17:39 +0200112 * Each architecture can perform additional operations
Aaron Durbin044fb532016-07-11 15:36:50 -0500113 * on the loaded segment
114 */
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700115 prog_segment_loaded((uintptr_t)dest, memsz, flags);
116
117
118 return 1;
119}
120
121/* Note: this function is a bit dangerous so is not exported.
122 * It assumes you're smart enough not to call it with the very
123 * last segment, since it uses seg + 1 */
124static int last_loadable_segment(struct cbfs_payload_segment *seg)
125{
126 return read_be32(&(seg + 1)->type) == PAYLOAD_SEGMENT_ENTRY;
127}
128
Ting Shen05532262019-01-28 17:22:22 +0800129static int check_payload_segments(struct cbfs_payload_segment *cbfssegs,
130 void *args)
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700131{
132 uint8_t *dest;
133 size_t memsz;
Elyes HAOUASd84e20b2019-04-23 22:17:04 +0200134 struct cbfs_payload_segment *seg, segment;
Ting Shen05532262019-01-28 17:22:22 +0800135 enum bootmem_type dest_type = *(enum bootmem_type *)args;
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700136
Elyes HAOUASd84e20b2019-04-23 22:17:04 +0200137 for (seg = cbfssegs;; ++seg) {
Julius Werner540a9802019-12-09 13:03:29 -0800138 printk(BIOS_DEBUG, "Checking segment from ROM address %p\n", seg);
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700139 cbfs_decode_payload_segment(&segment, seg);
140 dest = (uint8_t *)(uintptr_t)segment.load_addr;
141 memsz = segment.mem_len;
142 if (segment.type == PAYLOAD_SEGMENT_ENTRY)
143 break;
Ting Shen05532262019-01-28 17:22:22 +0800144 if (!segment_targets_type(dest, memsz, dest_type))
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700145 return -1;
146 }
147 return 0;
148}
149
150static int load_payload_segments(struct cbfs_payload_segment *cbfssegs, uintptr_t *entry)
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700151{
152 uint8_t *dest, *src;
153 size_t filesz, memsz;
154 uint32_t compression;
155 struct cbfs_payload_segment *first_segment, *seg, segment;
156 int flags = 0;
157
158 for (first_segment = seg = cbfssegs;; ++seg) {
Julius Werner540a9802019-12-09 13:03:29 -0800159 printk(BIOS_DEBUG, "Loading segment from ROM address %p\n", seg);
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700160
161 cbfs_decode_payload_segment(&segment, seg);
162 dest = (uint8_t *)(uintptr_t)segment.load_addr;
163 memsz = segment.mem_len;
164 compression = segment.compression;
165 filesz = segment.len;
166
167 switch (segment.type) {
168 case PAYLOAD_SEGMENT_CODE:
169 case PAYLOAD_SEGMENT_DATA:
170 printk(BIOS_DEBUG, " %s (compression=%x)\n",
171 segment.type == PAYLOAD_SEGMENT_CODE
172 ? "code" : "data", segment.compression);
173 src = ((uint8_t *)first_segment) + segment.offset;
174 printk(BIOS_DEBUG,
Julius Werner540a9802019-12-09 13:03:29 -0800175 " New segment dstaddr %p memsize 0x%zx srcaddr %p filesize 0x%zx\n",
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700176 dest, memsz, src, filesz);
177
178 /* Clean up the values */
179 if (filesz > memsz) {
180 filesz = memsz;
181 printk(BIOS_DEBUG, " cleaned up filesize 0x%zx\n", filesz);
182 }
183 break;
184
185 case PAYLOAD_SEGMENT_BSS:
Julius Werner540a9802019-12-09 13:03:29 -0800186 printk(BIOS_DEBUG, " BSS %p (%d byte)\n", (void *)
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700187 (intptr_t)segment.load_addr, segment.mem_len);
188 filesz = 0;
189 src = ((uint8_t *)first_segment) + segment.offset;
190 compression = CBFS_COMPRESS_NONE;
191 break;
192
193 case PAYLOAD_SEGMENT_ENTRY:
Julius Werner540a9802019-12-09 13:03:29 -0800194 printk(BIOS_DEBUG, " Entry Point %p\n", (void *)
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700195 (intptr_t)segment.load_addr);
196
197 *entry = segment.load_addr;
198 /* Per definition, a payload always has the entry point
199 * as last segment. Thus, we use the occurrence of the
200 * entry point as break condition for the loop.
201 */
202 return 0;
203
204 default:
205 /* We found something that we don't know about. Throw
206 * hands into the sky and run away!
207 */
208 printk(BIOS_EMERG, "Bad segment type %x\n", segment.type);
209 return -1;
210 }
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700211 /* Note that the 'seg + 1' is safe as we only call this
212 * function on "not the last" * items, since entry
213 * is always last. */
214 if (last_loadable_segment(seg))
215 flags = SEG_FINAL;
216 if (!load_one_segment(dest, src, filesz, memsz, compression, flags))
217 return -1;
Ronald G. Minnichae631262009-04-01 10:48:39 +0000218 }
Ionela Voinescu00903e52015-01-09 13:14:20 +0000219
Ronald G. Minnichae631262009-04-01 10:48:39 +0000220 return 1;
Ronald G. Minnichae631262009-04-01 10:48:39 +0000221}
222
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700223__weak int payload_arch_usable_ram_quirk(uint64_t start, uint64_t size)
224{
225 return 0;
226}
227
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700228static void *selfprepare(struct prog *payload)
229{
230 void *data;
231 data = rdev_mmap_full(prog_rdev(payload));
232 return data;
233}
234
Ting Shen05532262019-01-28 17:22:22 +0800235static bool _selfload(struct prog *payload, checker_t f, void *args)
Ronald G. Minnichae631262009-04-01 10:48:39 +0000236{
Ronald G. Minnichef402092013-11-11 10:36:28 -0800237 uintptr_t entry = 0;
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700238 struct cbfs_payload_segment *cbfssegs;
Aaron Durbin899d13d2015-05-15 23:39:23 -0500239 void *data;
240
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700241 data = selfprepare(payload);
Aaron Durbin899d13d2015-05-15 23:39:23 -0500242 if (data == NULL)
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200243 return false;
Ronald G. Minnichae631262009-04-01 10:48:39 +0000244
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700245 cbfssegs = &((struct cbfs_payload *)data)->segments;
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700246
Ting Shen05532262019-01-28 17:22:22 +0800247 if (f && f(cbfssegs, args))
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700248 goto out;
249
250 if (load_payload_segments(cbfssegs, &entry))
Ronald G. Minnichae631262009-04-01 10:48:39 +0000251 goto out;
252
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000253 printk(BIOS_SPEW, "Loaded segments\n");
Ronald G. Minnichae631262009-04-01 10:48:39 +0000254
Aaron Durbinac12c66c2015-05-20 12:08:55 -0500255 rdev_munmap(prog_rdev(payload), data);
256
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200257 /* Pass cbtables to payload if architecture desires it. */
258 prog_set_entry(payload, (void *)entry, cbmem_find(CBMEM_ID_CBTABLE));
Aaron Durbin001de1a2013-04-24 22:59:45 -0500259
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200260 return true;
Aaron Durbin001de1a2013-04-24 22:59:45 -0500261out:
Aaron Durbinac12c66c2015-05-20 12:08:55 -0500262 rdev_munmap(prog_rdev(payload), data);
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200263 return false;
Aaron Durbin001de1a2013-04-24 22:59:45 -0500264}
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700265
Ting Shen05532262019-01-28 17:22:22 +0800266bool selfload_check(struct prog *payload, enum bootmem_type dest_type)
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700267{
Ting Shen05532262019-01-28 17:22:22 +0800268 return _selfload(payload, check_payload_segments, &dest_type);
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700269}
270
271bool selfload(struct prog *payload)
272{
Ting Shen05532262019-01-28 17:22:22 +0800273 return _selfload(payload, NULL, 0);
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700274}