1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <commonlib/bsd/compression.h>
4 #include <commonlib/endian.h>
5 #include <console/console.h>
11 #include <program_loading.h>
12 #include <timestamp.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
40 "Callers not supposed to call selfload_check() in romstage");
44 uintptr_t d
= (uintptr_t) dest
;
45 if (bootmem_region_targets_type(d
, memsz
, dest_type
))
48 if (payload_arch_usable_ram_quirk(d
, memsz
))
51 printk(BIOS_ERR
, "SELF segment doesn't target RAM: %p, %lu bytes\n", dest
, memsz
);
52 bootmem_dump_ranges();
56 static int load_one_segment(uint8_t *dest
,
63 unsigned char *middle
, *end
;
64 printk(BIOS_DEBUG
, "Loading Segment: addr: %p memsz: 0x%016zx filesz: 0x%016zx\n",
67 /* Compute the boundaries of the segment */
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. */
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. */
90 case CBFS_COMPRESS_NONE
: {
91 printk(BIOS_DEBUG
, "it's not compressed!\n");
92 memcpy(dest
, src
, len
);
96 printk(BIOS_INFO
, "CBFS: Unknown compression type %d\n", compression
);
99 /* Calculate middle after any changes to len. */
101 printk(BIOS_SPEW
, "[ 0x%08lx, %08lx, 0x%08lx) <- %08lx\n",
103 (unsigned long)middle
,
107 /* Zero the extra bytes between middle & end */
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
);
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
)
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
)
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
)
153 if (!segment_targets_type(dest
, memsz
, dest_type
))
159 static int load_payload_segments(struct cbfs_payload_segment
*cbfssegs
, uintptr_t *entry
)
162 size_t filesz
, memsz
;
163 uint32_t compression
;
164 struct cbfs_payload_segment
*first_segment
, *seg
, segment
;
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
;
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
) {
190 printk(BIOS_DEBUG
, " cleaned up filesize 0x%zx\n", filesz
);
194 case PAYLOAD_SEGMENT_BSS
:
195 printk(BIOS_DEBUG
, " BSS %p (%d byte)\n", (void *)
196 (intptr_t)segment
.load_addr
, segment
.mem_len
);
198 src
= ((uint8_t *)first_segment
) + segment
.offset
;
199 compression
= CBFS_COMPRESS_NONE
;
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.
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
);
220 /* Note that the 'seg + 1' is safe as we only call this
221 * function on "not the last" * items, since entry
223 if (last_loadable_segment(seg
))
225 if (!load_one_segment(dest
, src
, filesz
, memsz
, compression
, flags
))
232 __weak
int payload_arch_usable_ram_quirk(uint64_t start
, uint64_t size
)
237 bool selfload_mapped(struct prog
*payload
, void *mapping
,
238 enum bootmem_type dest_type
)
241 struct cbfs_payload_segment
*cbfssegs
;
243 cbfssegs
= &((struct cbfs_payload
*)mapping
)->segments
;
245 if (check_payload_segments(cbfssegs
, dest_type
))
248 if (load_payload_segments(cbfssegs
, &entry
))
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
));
259 bool selfload_check(struct prog
*payload
, enum bootmem_type dest_type
)
261 if (prog_locate_hook(payload
))
264 payload
->cbfs_type
= CBFS_TYPE_SELF
;
265 void *mapping
= cbfs_type_map(prog_name(payload
), NULL
, &payload
->cbfs_type
);
269 bool ret
= selfload_mapped(payload
, mapping
, dest_type
);
275 bool selfload(struct prog
*payload
)
277 return selfload_check(payload
, BM_MEM_INVALID
);