acpi: Add IORT helper functions
[coreboot2.git] / src / lib / selfboot.c
blobe21c4937016e3f3c18cdae98eb0dc7fb1307f2c9
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 <string.h>
7 #include <symbols.h>
8 #include <cbfs.h>
9 #include <lib.h>
10 #include <bootmem.h>
11 #include <program_loading.h>
12 #include <timestamp.h>
13 #include <cbmem.h>
14 #include <types.h>
16 /* The type syntax for C is essentially unparsable. -- Rob Pike */
17 typedef int (*checker_t)(struct cbfs_payload_segment *cbfssegs, void *args);
19 /* Decode a serialized cbfs payload segment
20 * from memory into native endianness.
22 static void cbfs_decode_payload_segment(struct cbfs_payload_segment *segment,
23 const struct cbfs_payload_segment *src)
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);
33 static int segment_targets_type(void *dest, unsigned long memsz,
34 enum bootmem_type dest_type)
36 /* 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;
44 uintptr_t d = (uintptr_t) dest;
45 if (bootmem_region_targets_type(d, memsz, dest_type))
46 return 1;
48 if (payload_arch_usable_ram_quirk(d, memsz))
49 return 1;
51 printk(BIOS_ERR, "SELF segment doesn't target RAM: %p, %lu bytes\n", dest, memsz);
52 bootmem_dump_ranges();
53 return 0;
56 static 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)
63 unsigned char *middle, *end;
64 printk(BIOS_DEBUG, "Loading Segment: addr: %p memsz: 0x%016zx filesz: 0x%016zx\n",
65 dest, memsz, len);
67 /* Compute the boundaries of the segment */
68 end = dest + memsz;
70 /* Copy data from the initial buffer */
71 switch (compression) {
72 case CBFS_COMPRESS_LZMA: {
73 printk(BIOS_DEBUG, "using LZMA\n");
74 timestamp_add_now(TS_ULZMA_START);
75 len = ulzman(src, len, dest, memsz);
76 timestamp_add_now(TS_ULZMA_END);
77 if (!len) /* Decompression Error. */
78 return 0;
79 break;
81 case CBFS_COMPRESS_LZ4: {
82 printk(BIOS_DEBUG, "using LZ4\n");
83 timestamp_add_now(TS_ULZ4F_START);
84 len = ulz4fn(src, len, dest, memsz);
85 timestamp_add_now(TS_ULZ4F_END);
86 if (!len) /* Decompression Error. */
87 return 0;
88 break;
90 case CBFS_COMPRESS_NONE: {
91 printk(BIOS_DEBUG, "it's not compressed!\n");
92 memcpy(dest, src, len);
93 break;
95 default:
96 printk(BIOS_INFO, "CBFS: Unknown compression type %d\n", compression);
97 return 0;
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);
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",
111 (unsigned long)middle,
112 (unsigned long)(end - middle));
114 /* Zero the extra bytes */
115 memset(middle, 0, end - middle);
119 * Each architecture can perform additional operations
120 * on the loaded segment
122 prog_segment_loaded((uintptr_t)dest, memsz, flags);
124 return 1;
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 */
130 static int last_loadable_segment(struct cbfs_payload_segment *seg)
132 return read_be32(&(seg + 1)->type) == PAYLOAD_SEGMENT_ENTRY;
135 static int check_payload_segments(struct cbfs_payload_segment *cbfssegs,
136 enum bootmem_type dest_type)
138 uint8_t *dest;
139 size_t memsz;
140 struct cbfs_payload_segment *seg, segment;
142 /* dest_type == INVALID means we're not supposed to check anything. */
143 if (dest_type == BM_MEM_INVALID)
144 return 0;
146 for (seg = cbfssegs;; ++seg) {
147 printk(BIOS_DEBUG, "Checking segment from ROM address %p\n", seg);
148 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;
153 if (!segment_targets_type(dest, memsz, dest_type))
154 return -1;
156 return 0;
159 static int load_payload_segments(struct cbfs_payload_segment *cbfssegs, uintptr_t *entry)
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;
167 for (first_segment = seg = cbfssegs;; ++seg) {
168 printk(BIOS_DEBUG, "Loading segment from ROM address %p\n", seg);
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;
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,
184 " New segment dstaddr %p memsize 0x%zx srcaddr %p filesize 0x%zx\n",
185 dest, memsz, src, filesz);
187 /* Clean up the values */
188 if (filesz > memsz) {
189 filesz = memsz;
190 printk(BIOS_DEBUG, " cleaned up filesize 0x%zx\n", filesz);
192 break;
194 case PAYLOAD_SEGMENT_BSS:
195 printk(BIOS_DEBUG, " BSS %p (%d byte)\n", (void *)
196 (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;
202 case PAYLOAD_SEGMENT_ENTRY:
203 printk(BIOS_DEBUG, " Entry Point %p\n", (void *)
204 (intptr_t)segment.load_addr);
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.
211 return 0;
213 default:
214 /* We found something that we don't know about. Throw
215 * hands into the sky and run away!
217 printk(BIOS_EMERG, "Bad segment type %x\n", segment.type);
218 return -1;
220 /* 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;
229 return 1;
232 __weak int payload_arch_usable_ram_quirk(uint64_t start, uint64_t size)
234 return 0;
237 bool selfload_mapped(struct prog *payload, void *mapping,
238 enum bootmem_type dest_type)
240 uintptr_t entry = 0;
241 struct cbfs_payload_segment *cbfssegs;
243 cbfssegs = &((struct cbfs_payload *)mapping)->segments;
245 if (check_payload_segments(cbfssegs, dest_type))
246 return false;
248 if (load_payload_segments(cbfssegs, &entry))
249 return false;
251 printk(BIOS_SPEW, "Loaded segments\n");
253 /* Pass cbtables to payload if architecture desires it. */
254 prog_set_entry(payload, (void *)entry, cbmem_find(CBMEM_ID_CBTABLE));
256 return true;
259 bool selfload_check(struct prog *payload, enum bootmem_type dest_type)
261 if (prog_locate_hook(payload))
262 return false;
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;
269 bool ret = selfload_mapped(payload, mapping, dest_type);
271 cbfs_unmap(mapping);
272 return ret;
275 bool selfload(struct prog *payload)
277 return selfload_check(payload, BM_MEM_INVALID);