blob: 7def7b164af265f337d75d5cb5cc93d17c0e28ba [file] [log] [blame]
Angel Pons118a9c72020-04-02 23:48:34 +02001/* SPDX-License-Identifier: GPL-2.0-only */
2/* This file is part of the coreboot project. */
ebiedermc7798892009-04-01 11:03:32 +00003
Julius Werner98eeb962019-12-11 15:47:42 -08004#include <commonlib/bsd/compression.h>
George Trudeauc1b98b92016-04-04 00:19:02 -04005#include <commonlib/endian.h>
Ronald G. Minnichae631262009-04-01 10:48:39 +00006#include <console/console.h>
Ronald G. Minnichae631262009-04-01 10:48:39 +00007#include <stdint.h>
8#include <stdlib.h>
9#include <string.h>
Julius Wernerec5e5e02014-08-20 15:29:56 -070010#include <symbols.h>
Peter Stuge483b7bb2009-04-14 07:40:01 +000011#include <cbfs.h>
Myles Watson58170782009-10-28 16:13:28 +000012#include <lib.h>
Aaron Durbinceebc052014-02-25 00:21:10 -060013#include <bootmem.h>
Aaron Durbin04654a22015-03-17 11:43:44 -050014#include <program_loading.h>
Julius Werner09f29212015-09-29 13:51:35 -070015#include <timestamp.h>
Patrick Rudolph59b8f272018-04-26 09:53:16 +020016#include <cbmem.h>
Ronald G. Minnichae631262009-04-01 10:48:39 +000017
Ronald G. Minnichc3085542018-10-24 15:46:51 -070018/* The type syntax for C is essentially unparsable. -- Rob Pike */
Ting Shen05532262019-01-28 17:22:22 +080019typedef int (*checker_t)(struct cbfs_payload_segment *cbfssegs, void *args);
Ronald G. Minnichc3085542018-10-24 15:46:51 -070020
George Trudeauc1b98b92016-04-04 00:19:02 -040021/* Decode a serialized cbfs payload segment
22 * from memory into native endianness.
23 */
24static 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. Minnichae631262009-04-01 10:48:39 +000034
Ting Shen05532262019-01-28 17:22:22 +080035static int segment_targets_type(void *dest, unsigned long memsz,
36 enum bootmem_type dest_type)
Ronald G. Minnichae631262009-04-01 10:48:39 +000037{
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070038 uintptr_t d = (uintptr_t) dest;
Ting Shen05532262019-01-28 17:22:22 +080039 if (bootmem_region_targets_type(d, memsz, dest_type))
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070040 return 1;
George Trudeauc1b98b92016-04-04 00:19:02 -040041
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070042 if (payload_arch_usable_ram_quirk(d, memsz))
43 return 1;
Ronald G. Minnichae631262009-04-01 10:48:39 +000044
Julius Werner540a9802019-12-09 13:03:29 -080045 printk(BIOS_ERR, "SELF segment doesn't target RAM: %p, %lu bytes\n", dest, memsz);
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070046 bootmem_dump_ranges();
Kyösti Mälkkia48433d2018-06-07 06:31:43 +030047 return 0;
48}
49
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070050static 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. Minnichae631262009-04-01 10:48:39 +000056{
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070057 unsigned char *middle, *end;
Julius Werner540a9802019-12-09 13:03:29 -080058 printk(BIOS_DEBUG, "Loading Segment: addr: %p memsz: 0x%016zx filesz: 0x%016zx\n",
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070059 dest, memsz, len);
Patrick Georgi5eceb322009-05-13 16:27:25 +000060
Ronald G. Minnichae631262009-04-01 10:48:39 +000061 /* Compute the boundaries of the segment */
Aaron Durbin044fb532016-07-11 15:36:50 -050062 end = dest + memsz;
Zheng Baoe6ad7fa2009-11-05 10:02:59 +000063
Ronald G. Minnichae631262009-04-01 10:48:39 +000064 /* Copy data from the initial buffer */
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -070065 switch (compression) {
Lee Leahye20a3192017-03-09 16:21:34 -080066 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. Minnich83bd46e2018-09-16 09:59:54 -070090 printk(BIOS_INFO, "CBFS: Unknown compression type %d\n", compression);
91 return 0;
Ronald G. Minnichae631262009-04-01 10:48:39 +000092 }
Aaron Durbin044fb532016-07-11 15:36:50 -050093 /* 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 Leahy73402172017-03-10 15:23:24 -0800103 printk(BIOS_DEBUG,
104 "Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n",
105 (unsigned long)middle,
106 (unsigned long)(end - middle));
Aaron Durbin044fb532016-07-11 15:36:50 -0500107
108 /* Zero the extra bytes */
109 memset(middle, 0, end - middle);
110 }
111
Aaron Durbin044fb532016-07-11 15:36:50 -0500112 /*
Elyes HAOUAS0b133972018-08-15 11:17:39 +0200113 * Each architecture can perform additional operations
Aaron Durbin044fb532016-07-11 15:36:50 -0500114 * on the loaded segment
115 */
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700116 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 */
125static int last_loadable_segment(struct cbfs_payload_segment *seg)
126{
127 return read_be32(&(seg + 1)->type) == PAYLOAD_SEGMENT_ENTRY;
128}
129
Ting Shen05532262019-01-28 17:22:22 +0800130static int check_payload_segments(struct cbfs_payload_segment *cbfssegs,
131 void *args)
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700132{
133 uint8_t *dest;
134 size_t memsz;
Elyes HAOUASd84e20b2019-04-23 22:17:04 +0200135 struct cbfs_payload_segment *seg, segment;
Ting Shen05532262019-01-28 17:22:22 +0800136 enum bootmem_type dest_type = *(enum bootmem_type *)args;
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700137
Elyes HAOUASd84e20b2019-04-23 22:17:04 +0200138 for (seg = cbfssegs;; ++seg) {
Julius Werner540a9802019-12-09 13:03:29 -0800139 printk(BIOS_DEBUG, "Checking segment from ROM address %p\n", seg);
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700140 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 Shen05532262019-01-28 17:22:22 +0800145 if (!segment_targets_type(dest, memsz, dest_type))
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700146 return -1;
147 }
148 return 0;
149}
150
151static int load_payload_segments(struct cbfs_payload_segment *cbfssegs, uintptr_t *entry)
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700152{
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 Werner540a9802019-12-09 13:03:29 -0800160 printk(BIOS_DEBUG, "Loading segment from ROM address %p\n", seg);
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700161
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 Werner540a9802019-12-09 13:03:29 -0800176 " New segment dstaddr %p memsize 0x%zx srcaddr %p filesize 0x%zx\n",
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700177 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 Werner540a9802019-12-09 13:03:29 -0800187 printk(BIOS_DEBUG, " BSS %p (%d byte)\n", (void *)
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700188 (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 Werner540a9802019-12-09 13:03:29 -0800195 printk(BIOS_DEBUG, " Entry Point %p\n", (void *)
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700196 (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. Minnich83bd46e2018-09-16 09:59:54 -0700212 /* 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. Minnichae631262009-04-01 10:48:39 +0000219 }
Ionela Voinescu00903e52015-01-09 13:14:20 +0000220
Ronald G. Minnichae631262009-04-01 10:48:39 +0000221 return 1;
Ronald G. Minnichae631262009-04-01 10:48:39 +0000222}
223
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700224__weak int payload_arch_usable_ram_quirk(uint64_t start, uint64_t size)
225{
226 return 0;
227}
228
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700229static void *selfprepare(struct prog *payload)
230{
231 void *data;
232 data = rdev_mmap_full(prog_rdev(payload));
233 return data;
234}
235
Ting Shen05532262019-01-28 17:22:22 +0800236static bool _selfload(struct prog *payload, checker_t f, void *args)
Ronald G. Minnichae631262009-04-01 10:48:39 +0000237{
Ronald G. Minnichef402092013-11-11 10:36:28 -0800238 uintptr_t entry = 0;
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700239 struct cbfs_payload_segment *cbfssegs;
Aaron Durbin899d13d2015-05-15 23:39:23 -0500240 void *data;
241
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700242 data = selfprepare(payload);
Aaron Durbin899d13d2015-05-15 23:39:23 -0500243 if (data == NULL)
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200244 return false;
Ronald G. Minnichae631262009-04-01 10:48:39 +0000245
Ronald G. Minnich83bd46e2018-09-16 09:59:54 -0700246 cbfssegs = &((struct cbfs_payload *)data)->segments;
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700247
Ting Shen05532262019-01-28 17:22:22 +0800248 if (f && f(cbfssegs, args))
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700249 goto out;
250
251 if (load_payload_segments(cbfssegs, &entry))
Ronald G. Minnichae631262009-04-01 10:48:39 +0000252 goto out;
253
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000254 printk(BIOS_SPEW, "Loaded segments\n");
Ronald G. Minnichae631262009-04-01 10:48:39 +0000255
Aaron Durbinac12c66c2015-05-20 12:08:55 -0500256 rdev_munmap(prog_rdev(payload), data);
257
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200258 /* Pass cbtables to payload if architecture desires it. */
259 prog_set_entry(payload, (void *)entry, cbmem_find(CBMEM_ID_CBTABLE));
Aaron Durbin001de1a2013-04-24 22:59:45 -0500260
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200261 return true;
Aaron Durbin001de1a2013-04-24 22:59:45 -0500262out:
Aaron Durbinac12c66c2015-05-20 12:08:55 -0500263 rdev_munmap(prog_rdev(payload), data);
Patrick Rudolph59b8f272018-04-26 09:53:16 +0200264 return false;
Aaron Durbin001de1a2013-04-24 22:59:45 -0500265}
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700266
Ting Shen05532262019-01-28 17:22:22 +0800267bool selfload_check(struct prog *payload, enum bootmem_type dest_type)
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700268{
Ting Shen05532262019-01-28 17:22:22 +0800269 return _selfload(payload, check_payload_segments, &dest_type);
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700270}
271
272bool selfload(struct prog *payload)
273{
Ting Shen05532262019-01-28 17:22:22 +0800274 return _selfload(payload, NULL, 0);
Ronald G. Minnichc3085542018-10-24 15:46:51 -0700275}