xref: /aosp_15_r20/external/coreboot/src/lib/selfboot.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
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>
15 
16 /* The type syntax for C is essentially unparsable. -- Rob Pike */
17 typedef int (*checker_t)(struct cbfs_payload_segment *cbfssegs, void *args);
18 
19 /* Decode a serialized cbfs payload segment
20  * from memory into native endianness.
21  */
cbfs_decode_payload_segment(struct cbfs_payload_segment * segment,const struct cbfs_payload_segment * src)22 static 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 }
32 
segment_targets_type(void * dest,unsigned long memsz,enum bootmem_type dest_type)33 static int segment_targets_type(void *dest, unsigned long memsz,
34 		enum bootmem_type dest_type)
35 {
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;
42 	}
43 
44 	uintptr_t d = (uintptr_t) dest;
45 	if (bootmem_region_targets_type(d, memsz, dest_type))
46 		return 1;
47 
48 	if (payload_arch_usable_ram_quirk(d, memsz))
49 		return 1;
50 
51 	printk(BIOS_ERR, "SELF segment doesn't target RAM: %p, %lu bytes\n", dest, memsz);
52 	bootmem_dump_ranges();
53 	return 0;
54 }
55 
load_one_segment(uint8_t * dest,uint8_t * src,size_t len,size_t memsz,uint32_t compression,int flags)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)
62 {
63 	unsigned char *middle, *end;
64 	printk(BIOS_DEBUG, "Loading Segment: addr: %p memsz: 0x%016zx filesz: 0x%016zx\n",
65 	       dest, memsz, len);
66 
67 	/* Compute the boundaries of the segment */
68 	end = dest + memsz;
69 
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;
80 	}
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;
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:
96 		printk(BIOS_INFO, "CBFS:  Unknown compression type %d\n", compression);
97 		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",
111 			(unsigned long)middle,
112 			(unsigned long)(end - middle));
113 
114 		/* Zero the extra bytes */
115 		memset(middle, 0, end - middle);
116 	}
117 
118 	/*
119 	 * Each architecture can perform additional operations
120 	 * on the loaded segment
121 	 */
122 	prog_segment_loaded((uintptr_t)dest, memsz, flags);
123 
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 */
last_loadable_segment(struct cbfs_payload_segment * seg)130 static int last_loadable_segment(struct cbfs_payload_segment *seg)
131 {
132 	return read_be32(&(seg + 1)->type) == PAYLOAD_SEGMENT_ENTRY;
133 }
134 
check_payload_segments(struct cbfs_payload_segment * cbfssegs,enum bootmem_type dest_type)135 static int check_payload_segments(struct cbfs_payload_segment *cbfssegs,
136 				  enum bootmem_type dest_type)
137 {
138 	uint8_t *dest;
139 	size_t memsz;
140 	struct cbfs_payload_segment *seg, segment;
141 
142 	/* dest_type == INVALID means we're not supposed to check anything. */
143 	if (dest_type == BM_MEM_INVALID)
144 		return 0;
145 
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;
155 	}
156 	return 0;
157 }
158 
load_payload_segments(struct cbfs_payload_segment * cbfssegs,uintptr_t * entry)159 static int load_payload_segments(struct cbfs_payload_segment *cbfssegs, uintptr_t *entry)
160 {
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) {
168 		printk(BIOS_DEBUG, "Loading segment from ROM address %p\n", seg);
169 
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,
184 				"  New segment dstaddr %p memsize 0x%zx srcaddr %p filesize 0x%zx\n",
185 			       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:
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;
201 
202 		case PAYLOAD_SEGMENT_ENTRY:
203 			printk(BIOS_DEBUG, "  Entry Point %p\n", (void *)
204 				(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 		}
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;
227 	}
228 
229 	return 1;
230 }
231 
payload_arch_usable_ram_quirk(uint64_t start,uint64_t size)232 __weak int payload_arch_usable_ram_quirk(uint64_t start, uint64_t size)
233 {
234 	return 0;
235 }
236 
selfload_mapped(struct prog * payload,void * mapping,enum bootmem_type dest_type)237 bool selfload_mapped(struct prog *payload, void *mapping,
238 		     enum bootmem_type dest_type)
239 {
240 	uintptr_t entry = 0;
241 	struct cbfs_payload_segment *cbfssegs;
242 
243 	cbfssegs = &((struct cbfs_payload *)mapping)->segments;
244 
245 	if (check_payload_segments(cbfssegs, dest_type))
246 		return false;
247 
248 	if (load_payload_segments(cbfssegs, &entry))
249 		return false;
250 
251 	printk(BIOS_SPEW, "Loaded segments\n");
252 
253 	/* Pass cbtables to payload if architecture desires it. */
254 	prog_set_entry(payload, (void *)entry, cbmem_find(CBMEM_ID_CBTABLE));
255 
256 	return true;
257 }
258 
selfload_check(struct prog * payload,enum bootmem_type dest_type)259 bool selfload_check(struct prog *payload, enum bootmem_type dest_type)
260 {
261 	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;
273 }
274 
selfload(struct prog * payload)275 bool selfload(struct prog *payload)
276 {
277 	return selfload_check(payload, BM_MEM_INVALID);
278 }
279