2 * This file is part of the coreboot project.
4 * Copyright (C) 2003 Eric W. Biederman <ebiederm@xmission.com>
5 * Copyright (C) 2009 Ron Minnich <rminnich@gmail.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
17 #include <commonlib/compression.h>
18 #include <console/console.h>
28 #include <program_loading.h>
29 #include <timestamp.h>
31 static const unsigned long lb_start
= (unsigned long)&_program
;
32 static const unsigned long lb_end
= (unsigned long)&_eprogram
;
37 unsigned long s_dstaddr
;
38 unsigned long s_srcaddr
;
39 unsigned long s_memsz
;
40 unsigned long s_filesz
;
45 * Static executables all want to share the same addresses
46 * in memory because only a few addresses are reliably present on
47 * a machine, and implementing general relocation is hard.
50 * - Allocate a buffer the size of the coreboot image plus additional
52 * - Anything that would overwrite coreboot copy into the lower part of
54 * - After loading an ELF image copy coreboot to the top of the buffer.
55 * - Then jump to the loaded image.
58 * - Nearly arbitrary standalone executables can be loaded.
59 * - Coreboot is preserved, so it can be returned to.
60 * - The implementation is still relatively simple,
61 * and much simpler than the general case implemented in kexec.
64 static unsigned long bounce_size
, bounce_buffer
;
66 static void get_bounce_buffer(unsigned long req_size
)
68 unsigned long lb_size
;
71 /* When the ramstage is relocatable there is no need for a bounce
72 * buffer. All payloads should not overlap the ramstage.
74 if (IS_ENABLED(CONFIG_RELOCATABLE_RAMSTAGE
)) {
80 lb_size
= lb_end
- lb_start
;
81 /* Plus coreboot size so I have somewhere
82 * to place a copy to return to.
84 lb_size
= req_size
+ lb_size
;
86 buffer
= bootmem_allocate_buffer(lb_size
);
88 printk(BIOS_SPEW
, "Bounce Buffer at %p, %lu bytes\n", buffer
, lb_size
);
90 bounce_buffer
= (uintptr_t)buffer
;
91 bounce_size
= req_size
;
94 static int overlaps_coreboot(struct segment
*seg
)
96 unsigned long start
, end
;
97 start
= seg
->s_dstaddr
;
98 end
= start
+ seg
->s_memsz
;
99 return !((end
<= lb_start
) || (start
>= lb_end
));
102 static int relocate_segment(unsigned long buffer
, struct segment
*seg
)
104 /* Modify all segments that want to load onto coreboot
105 * to load onto the bounce buffer instead.
107 /* ret: 1 : A new segment is inserted before the seg.
108 * 0 : A new segment is inserted after the seg, or no new one.
110 unsigned long start
, middle
, end
, ret
= 0;
112 printk(BIOS_SPEW
, "lb: [0x%016lx, 0x%016lx)\n",
115 /* I don't conflict with coreboot so get out of here */
116 if (!overlaps_coreboot(seg
))
119 if (!arch_supports_bounce_buffer())
120 die ("bounce buffer not supported");
122 start
= seg
->s_dstaddr
;
123 middle
= start
+ seg
->s_filesz
;
124 end
= start
+ seg
->s_memsz
;
126 printk(BIOS_SPEW
, "segment: [0x%016lx, 0x%016lx, 0x%016lx)\n",
129 if (seg
->compression
== CBFS_COMPRESS_NONE
) {
130 /* Slice off a piece at the beginning
131 * that doesn't conflict with coreboot.
133 if (start
< lb_start
) {
135 unsigned long len
= lb_start
- start
;
136 new = malloc(sizeof(*new));
140 seg
->s_dstaddr
+= len
;
141 seg
->s_srcaddr
+= len
;
142 if (seg
->s_filesz
> len
) {
144 seg
->s_filesz
-= len
;
149 /* Order by stream offset */
151 new->prev
= seg
->prev
;
152 seg
->prev
->next
= new;
155 /* compute the new value of start */
156 start
= seg
->s_dstaddr
;
158 printk(BIOS_SPEW
, " early: [0x%016lx, 0x%016lx, 0x%016lx)\n",
160 new->s_dstaddr
+ new->s_filesz
,
161 new->s_dstaddr
+ new->s_memsz
);
166 /* Slice off a piece at the end
167 * that doesn't conflict with coreboot
170 unsigned long len
= lb_end
- start
;
172 new = malloc(sizeof(*new));
176 new->s_dstaddr
+= len
;
177 new->s_srcaddr
+= len
;
178 if (seg
->s_filesz
> len
) {
180 new->s_filesz
-= len
;
184 /* Order by stream offset */
185 new->next
= seg
->next
;
187 seg
->next
->prev
= new;
190 printk(BIOS_SPEW
, " late: [0x%016lx, 0x%016lx, 0x%016lx)\n",
192 new->s_dstaddr
+ new->s_filesz
,
193 new->s_dstaddr
+ new->s_memsz
);
197 /* Now retarget this segment onto the bounce buffer */
198 /* sort of explanation: the buffer is a 1:1 mapping to coreboot.
199 * so you will make the dstaddr be this buffer, and it will get copied
200 * later to where coreboot lives.
202 seg
->s_dstaddr
= buffer
+ (seg
->s_dstaddr
- lb_start
);
204 printk(BIOS_SPEW
, " bounce: [0x%016lx, 0x%016lx, 0x%016lx)\n",
206 seg
->s_dstaddr
+ seg
->s_filesz
,
207 seg
->s_dstaddr
+ seg
->s_memsz
);
213 static int build_self_segment_list(
214 struct segment
*head
,
215 struct cbfs_payload
*cbfs_payload
, uintptr_t *entry
)
219 struct cbfs_payload_segment
*segment
, *first_segment
;
220 memset(head
, 0, sizeof(*head
));
221 head
->next
= head
->prev
= head
;
222 first_segment
= segment
= &cbfs_payload
->segments
;
225 printk(BIOS_DEBUG
, "Loading segment from rom address 0x%p\n", segment
);
226 switch(segment
->type
) {
227 case PAYLOAD_SEGMENT_PARAMS
:
228 printk(BIOS_DEBUG
, " parameter section (skipped)\n");
232 case PAYLOAD_SEGMENT_CODE
:
233 case PAYLOAD_SEGMENT_DATA
:
234 printk(BIOS_DEBUG
, " %s (compression=%x)\n",
235 segment
->type
== PAYLOAD_SEGMENT_CODE
? "code" : "data",
236 ntohl(segment
->compression
));
237 new = malloc(sizeof(*new));
238 new->s_dstaddr
= ntohll(segment
->load_addr
);
239 new->s_memsz
= ntohl(segment
->mem_len
);
240 new->compression
= ntohl(segment
->compression
);
242 new->s_srcaddr
= (uintptr_t)
243 ((unsigned char *)first_segment
)
244 + ntohl(segment
->offset
);
245 new->s_filesz
= ntohl(segment
->len
);
246 printk(BIOS_DEBUG
, " New segment dstaddr 0x%lx memsize 0x%lx srcaddr 0x%lx filesize 0x%lx\n",
247 new->s_dstaddr
, new->s_memsz
, new->s_srcaddr
, new->s_filesz
);
248 /* Clean up the values */
249 if (new->s_filesz
> new->s_memsz
) {
250 new->s_filesz
= new->s_memsz
;
252 " cleaned up filesize 0x%lx\n",
257 case PAYLOAD_SEGMENT_BSS
:
258 printk(BIOS_DEBUG
, " BSS 0x%p (%d byte)\n", (void *)
259 (intptr_t)ntohll(segment
->load_addr
),
260 ntohl(segment
->mem_len
));
261 new = malloc(sizeof(*new));
263 new->s_srcaddr
= (uintptr_t)
264 ((unsigned char *)first_segment
)
265 + ntohl(segment
->offset
);
266 new->s_dstaddr
= ntohll(segment
->load_addr
);
267 new->s_memsz
= ntohl(segment
->mem_len
);
270 case PAYLOAD_SEGMENT_ENTRY
:
271 printk(BIOS_DEBUG
, " Entry Point 0x%p\n",
272 (void *)(intptr_t)ntohll(segment
->load_addr
));
273 *entry
= ntohll(segment
->load_addr
);
274 /* Per definition, a payload always has the entry point
275 * as last segment. Thus, we use the occurrence of the
276 * entry point as break condition for the loop.
277 * Can we actually just look at the number of section?
282 /* We found something that we don't know about. Throw
283 * hands into the sky and run away!
285 printk(BIOS_EMERG
, "Bad segment type %x\n", segment
->type
);
289 /* We have found another CODE, DATA or BSS segment */
292 /* Find place where to insert our segment */
293 for(ptr
= head
->next
; ptr
!= head
; ptr
= ptr
->next
) {
294 if (new->s_srcaddr
< ntohll(segment
->load_addr
))
298 /* Order by stream offset */
300 new->prev
= ptr
->prev
;
301 ptr
->prev
->next
= new;
308 static int load_self_segments(
309 struct segment
*head
,
310 struct prog
*payload
)
313 struct segment
*last_non_empty
;
314 const unsigned long one_meg
= (1UL << 20);
315 unsigned long bounce_high
= lb_end
;
317 /* Determine last non-empty loaded segment. */
318 last_non_empty
= NULL
;
319 for(ptr
= head
->next
; ptr
!= head
; ptr
= ptr
->next
)
320 if (ptr
->s_filesz
!= 0)
321 last_non_empty
= ptr
;
323 for(ptr
= head
->next
; ptr
!= head
; ptr
= ptr
->next
) {
324 if (bootmem_region_targets_usable_ram(ptr
->s_dstaddr
,
328 if (ptr
->s_dstaddr
< one_meg
&&
329 (ptr
->s_dstaddr
+ ptr
->s_memsz
) <= one_meg
) {
331 "Payload being loaded below 1MiB "
332 "without region being marked as RAM usable.\n");
336 /* Payload segment not targeting RAM. */
337 printk(BIOS_ERR
, "SELF Payload doesn't target RAM:\n");
338 printk(BIOS_ERR
, "Failed Segment: 0x%lx, %lu bytes\n",
339 ptr
->s_dstaddr
, ptr
->s_memsz
);
340 bootmem_dump_ranges();
344 for(ptr
= head
->next
; ptr
!= head
; ptr
= ptr
->next
) {
346 * Add segments to bootmem memory map before a bounce buffer is
347 * allocated so that there aren't conflicts with the actual
350 bootmem_add_range(ptr
->s_dstaddr
, ptr
->s_memsz
,
353 if (!overlaps_coreboot(ptr
))
355 if (ptr
->s_dstaddr
+ ptr
->s_memsz
> bounce_high
)
356 bounce_high
= ptr
->s_dstaddr
+ ptr
->s_memsz
;
358 get_bounce_buffer(bounce_high
- lb_start
);
359 if (!bounce_buffer
) {
360 printk(BIOS_ERR
, "Could not find a bounce buffer...\n");
364 for(ptr
= head
->next
; ptr
!= head
; ptr
= ptr
->next
) {
365 unsigned char *dest
, *src
;
366 printk(BIOS_DEBUG
, "Loading Segment: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n",
367 ptr
->s_dstaddr
, ptr
->s_memsz
, ptr
->s_filesz
);
369 /* Modify the segment to load onto the bounce_buffer if necessary.
371 if (relocate_segment(bounce_buffer
, ptr
)) {
372 ptr
= (ptr
->prev
)->prev
;
376 printk(BIOS_DEBUG
, "Post relocation: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n",
377 ptr
->s_dstaddr
, ptr
->s_memsz
, ptr
->s_filesz
);
379 /* Compute the boundaries of the segment */
380 dest
= (unsigned char *)(ptr
->s_dstaddr
);
381 src
= (unsigned char *)(ptr
->s_srcaddr
);
383 /* Copy data from the initial buffer */
385 unsigned char *middle
, *end
;
386 size_t len
= ptr
->s_filesz
;
387 size_t memsz
= ptr
->s_memsz
;
388 switch(ptr
->compression
) {
389 case CBFS_COMPRESS_LZMA
: {
390 printk(BIOS_DEBUG
, "using LZMA\n");
391 timestamp_add_now(TS_START_ULZMA
);
392 len
= ulzman(src
, len
, dest
, memsz
);
393 timestamp_add_now(TS_END_ULZMA
);
394 if (!len
) /* Decompression Error. */
398 case CBFS_COMPRESS_LZ4
: {
399 printk(BIOS_DEBUG
, "using LZ4\n");
400 timestamp_add_now(TS_START_ULZ4F
);
401 len
= ulz4fn(src
, len
, dest
, memsz
);
402 timestamp_add_now(TS_END_ULZ4F
);
403 if (!len
) /* Decompression Error. */
407 case CBFS_COMPRESS_NONE
: {
408 printk(BIOS_DEBUG
, "it's not compressed!\n");
409 memcpy(dest
, src
, len
);
413 printk(BIOS_INFO
, "CBFS: Unknown compression type %d\n", ptr
->compression
);
418 printk(BIOS_SPEW
, "[ 0x%08lx, %08lx, 0x%08lx) <- %08lx\n",
420 (unsigned long)middle
,
424 /* Zero the extra bytes between middle & end */
426 printk(BIOS_DEBUG
, "Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n",
427 (unsigned long)middle
, (unsigned long)(end
- middle
));
429 /* Zero the extra bytes */
430 memset(middle
, 0, end
- middle
);
432 /* Copy the data that's outside the area that shadows ramstage */
433 printk(BIOS_DEBUG
, "dest %p, end %p, bouncebuffer %lx\n", dest
, end
, bounce_buffer
);
434 if ((unsigned long)end
> bounce_buffer
) {
435 if ((unsigned long)dest
< bounce_buffer
) {
436 unsigned char *from
= dest
;
437 unsigned char *to
= (unsigned char*)(lb_start
-(bounce_buffer
-(unsigned long)dest
));
438 unsigned long amount
= bounce_buffer
-(unsigned long)dest
;
439 printk(BIOS_DEBUG
, "move prefix around: from %p, to %p, amount: %lx\n", from
, to
, amount
);
440 memcpy(to
, from
, amount
);
442 if ((unsigned long)end
> bounce_buffer
+ (lb_end
- lb_start
)) {
443 unsigned long from
= bounce_buffer
+ (lb_end
- lb_start
);
444 unsigned long to
= lb_end
;
445 unsigned long amount
= (unsigned long)end
- from
;
446 printk(BIOS_DEBUG
, "move suffix around: from %lx, to %lx, amount: %lx\n", from
, to
, amount
);
447 memcpy((char*)to
, (char*)from
, amount
);
452 * Each architecture can perform additonal operations
453 * on the loaded segment
455 prog_segment_loaded((uintptr_t)dest
, ptr
->s_memsz
,
456 last_non_empty
== ptr
? SEG_FINAL
: 0);
463 void *selfload(struct prog
*payload
)
469 data
= rdev_mmap_full(prog_rdev(payload
));
474 /* Preprocess the self segments */
475 if (!build_self_segment_list(&head
, data
, &entry
))
478 /* Load the segments */
479 if (!load_self_segments(&head
, payload
))
482 printk(BIOS_SPEW
, "Loaded segments\n");
484 rdev_munmap(prog_rdev(payload
), data
);
486 /* Update the payload's area with the bounce buffer information. */
487 prog_set_area(payload
, (void *)(uintptr_t)bounce_buffer
, bounce_size
);
489 /* Update the payload's area with the bounce buffer information. */
490 prog_set_area(payload
, (void *)(uintptr_t)bounce_buffer
, bounce_size
);
492 return (void *)entry
;
495 rdev_munmap(prog_rdev(payload
), data
);