soc/intel/tigerlake: Utilize vbt data size Kconfig option
[coreboot.git] / src / lib / selfboot.c
blobc5ad525688dc192497b7f73835f8e3ceb1207dcf
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <commonlib/bsd/compression.h>
4 #include <commonlib/endian.h>
5 #include <console/console.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <symbols.h>
10 #include <cbfs.h>
11 #include <lib.h>
12 #include <bootmem.h>
13 #include <program_loading.h>
14 #include <timestamp.h>
15 #include <cbmem.h>
17 /* The type syntax for C is essentially unparsable. -- Rob Pike */
18 typedef int (*checker_t)(struct cbfs_payload_segment *cbfssegs, void *args);
20 /* Decode a serialized cbfs payload segment
21 * from memory into native endianness.
23 static void cbfs_decode_payload_segment(struct cbfs_payload_segment *segment,
24 const struct cbfs_payload_segment *src)
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);
34 static int segment_targets_type(void *dest, unsigned long memsz,
35 enum bootmem_type dest_type)
37 uintptr_t d = (uintptr_t) dest;
38 if (bootmem_region_targets_type(d, memsz, dest_type))
39 return 1;
41 if (payload_arch_usable_ram_quirk(d, memsz))
42 return 1;
44 printk(BIOS_ERR, "SELF segment doesn't target RAM: %p, %lu bytes\n", dest, memsz);
45 bootmem_dump_ranges();
46 return 0;
49 static 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)
56 unsigned char *middle, *end;
57 printk(BIOS_DEBUG, "Loading Segment: addr: %p memsz: 0x%016zx filesz: 0x%016zx\n",
58 dest, memsz, len);
60 /* Compute the boundaries of the segment */
61 end = dest + memsz;
63 /* Copy data from the initial buffer */
64 switch (compression) {
65 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;
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;
83 case CBFS_COMPRESS_NONE: {
84 printk(BIOS_DEBUG, "it's not compressed!\n");
85 memcpy(dest, src, len);
86 break;
88 default:
89 printk(BIOS_INFO, "CBFS: Unknown compression type %d\n", compression);
90 return 0;
92 /* 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);
100 /* Zero the extra bytes between middle & end */
101 if (middle < end) {
102 printk(BIOS_DEBUG,
103 "Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n",
104 (unsigned long)middle,
105 (unsigned long)(end - middle));
107 /* Zero the extra bytes */
108 memset(middle, 0, end - middle);
112 * Each architecture can perform additional operations
113 * on the loaded segment
115 prog_segment_loaded((uintptr_t)dest, memsz, flags);
118 return 1;
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 */
124 static int last_loadable_segment(struct cbfs_payload_segment *seg)
126 return read_be32(&(seg + 1)->type) == PAYLOAD_SEGMENT_ENTRY;
129 static int check_payload_segments(struct cbfs_payload_segment *cbfssegs,
130 void *args)
132 uint8_t *dest;
133 size_t memsz;
134 struct cbfs_payload_segment *seg, segment;
135 enum bootmem_type dest_type = *(enum bootmem_type *)args;
137 for (seg = cbfssegs;; ++seg) {
138 printk(BIOS_DEBUG, "Checking segment from ROM address %p\n", seg);
139 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;
144 if (!segment_targets_type(dest, memsz, dest_type))
145 return -1;
147 return 0;
150 static int load_payload_segments(struct cbfs_payload_segment *cbfssegs, uintptr_t *entry)
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;
158 for (first_segment = seg = cbfssegs;; ++seg) {
159 printk(BIOS_DEBUG, "Loading segment from ROM address %p\n", seg);
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;
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,
175 " New segment dstaddr %p memsize 0x%zx srcaddr %p filesize 0x%zx\n",
176 dest, memsz, src, filesz);
178 /* Clean up the values */
179 if (filesz > memsz) {
180 filesz = memsz;
181 printk(BIOS_DEBUG, " cleaned up filesize 0x%zx\n", filesz);
183 break;
185 case PAYLOAD_SEGMENT_BSS:
186 printk(BIOS_DEBUG, " BSS %p (%d byte)\n", (void *)
187 (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;
193 case PAYLOAD_SEGMENT_ENTRY:
194 printk(BIOS_DEBUG, " Entry Point %p\n", (void *)
195 (intptr_t)segment.load_addr);
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.
202 return 0;
204 default:
205 /* We found something that we don't know about. Throw
206 * hands into the sky and run away!
208 printk(BIOS_EMERG, "Bad segment type %x\n", segment.type);
209 return -1;
211 /* 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;
220 return 1;
223 __weak int payload_arch_usable_ram_quirk(uint64_t start, uint64_t size)
225 return 0;
228 static void *selfprepare(struct prog *payload)
230 void *data;
231 data = rdev_mmap_full(prog_rdev(payload));
232 return data;
235 static bool _selfload(struct prog *payload, checker_t f, void *args)
237 uintptr_t entry = 0;
238 struct cbfs_payload_segment *cbfssegs;
239 void *data;
241 data = selfprepare(payload);
242 if (data == NULL)
243 return false;
245 cbfssegs = &((struct cbfs_payload *)data)->segments;
247 if (f && f(cbfssegs, args))
248 goto out;
250 if (load_payload_segments(cbfssegs, &entry))
251 goto out;
253 printk(BIOS_SPEW, "Loaded segments\n");
255 rdev_munmap(prog_rdev(payload), data);
257 /* Pass cbtables to payload if architecture desires it. */
258 prog_set_entry(payload, (void *)entry, cbmem_find(CBMEM_ID_CBTABLE));
260 return true;
261 out:
262 rdev_munmap(prog_rdev(payload), data);
263 return false;
266 bool selfload_check(struct prog *payload, enum bootmem_type dest_type)
268 return _selfload(payload, check_payload_segments, &dest_type);
271 bool selfload(struct prog *payload)
273 return _selfload(payload, NULL, 0);