1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <commonlib/bsd/compression.h>
4 #include <commonlib/endian.h>
5 #include <console/console.h>
13 #include <program_loading.h>
14 #include <timestamp.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
))
41 if (payload_arch_usable_ram_quirk(d
, memsz
))
44 printk(BIOS_ERR
, "SELF segment doesn't target RAM: %p, %lu bytes\n", dest
, memsz
);
45 bootmem_dump_ranges();
49 static int load_one_segment(uint8_t *dest
,
56 unsigned char *middle
, *end
;
57 printk(BIOS_DEBUG
, "Loading Segment: addr: %p memsz: 0x%016zx filesz: 0x%016zx\n",
60 /* Compute the boundaries of the segment */
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. */
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. */
83 case CBFS_COMPRESS_NONE
: {
84 printk(BIOS_DEBUG
, "it's not compressed!\n");
85 memcpy(dest
, src
, len
);
89 printk(BIOS_INFO
, "CBFS: Unknown compression type %d\n", compression
);
92 /* Calculate middle after any changes to len. */
94 printk(BIOS_SPEW
, "[ 0x%08lx, %08lx, 0x%08lx) <- %08lx\n",
96 (unsigned long)middle
,
100 /* Zero the extra bytes between middle & end */
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
);
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
,
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
)
144 if (!segment_targets_type(dest
, memsz
, dest_type
))
150 static int load_payload_segments(struct cbfs_payload_segment
*cbfssegs
, uintptr_t *entry
)
153 size_t filesz
, memsz
;
154 uint32_t compression
;
155 struct cbfs_payload_segment
*first_segment
, *seg
, segment
;
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
;
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
) {
181 printk(BIOS_DEBUG
, " cleaned up filesize 0x%zx\n", filesz
);
185 case PAYLOAD_SEGMENT_BSS
:
186 printk(BIOS_DEBUG
, " BSS %p (%d byte)\n", (void *)
187 (intptr_t)segment
.load_addr
, segment
.mem_len
);
189 src
= ((uint8_t *)first_segment
) + segment
.offset
;
190 compression
= CBFS_COMPRESS_NONE
;
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.
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
);
211 /* Note that the 'seg + 1' is safe as we only call this
212 * function on "not the last" * items, since entry
214 if (last_loadable_segment(seg
))
216 if (!load_one_segment(dest
, src
, filesz
, memsz
, compression
, flags
))
223 __weak
int payload_arch_usable_ram_quirk(uint64_t start
, uint64_t size
)
228 static void *selfprepare(struct prog
*payload
)
231 data
= rdev_mmap_full(prog_rdev(payload
));
235 static bool _selfload(struct prog
*payload
, checker_t f
, void *args
)
238 struct cbfs_payload_segment
*cbfssegs
;
241 data
= selfprepare(payload
);
245 cbfssegs
= &((struct cbfs_payload
*)data
)->segments
;
247 if (f
&& f(cbfssegs
, args
))
250 if (load_payload_segments(cbfssegs
, &entry
))
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
));
262 rdev_munmap(prog_rdev(payload
), data
);
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);