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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
21 #include <arch/byteorder.h>
22 #include <console/console.h>
30 #include <payload_loader.h>
32 /* from ramstage.ld: */
33 extern unsigned char _ram_seg
;
34 extern unsigned char _eram_seg
;
36 static const unsigned long lb_start
= (unsigned long)&_ram_seg
;
37 static const unsigned long lb_end
= (unsigned long)&_eram_seg
;
42 unsigned long s_dstaddr
;
43 unsigned long s_srcaddr
;
44 unsigned long s_memsz
;
45 unsigned long s_filesz
;
50 * Static executables all want to share the same addresses
51 * in memory because only a few addresses are reliably present on
52 * a machine, and implementing general relocation is hard.
55 * - Allocate a buffer the size of the coreboot image plus additional
57 * - Anything that would overwrite coreboot copy into the lower part of
59 * - After loading an ELF image copy coreboot to the top of the buffer.
60 * - Then jump to the loaded image.
63 * - Nearly arbitrary standalone executables can be loaded.
64 * - Coreboot is preserved, so it can be returned to.
65 * - The implementation is still relatively simple,
66 * and much simpler than the general case implemented in kexec.
69 static unsigned long bounce_size
, bounce_buffer
;
71 static void get_bounce_buffer(unsigned long req_size
)
73 unsigned long lb_size
;
76 /* When the ramstage is relocatable there is no need for a bounce
77 * buffer. All payloads should not overlap the ramstage.
79 if (IS_ENABLED(CONFIG_RELOCATABLE_RAMSTAGE
)) {
85 lb_size
= lb_end
- lb_start
;
86 /* Plus coreboot size so I have somewhere
87 * to place a copy to return to.
89 lb_size
= req_size
+ lb_size
;
91 buffer
= bootmem_allocate_buffer(lb_size
);
93 printk(BIOS_SPEW
, "Bounce Buffer at %p, %lu bytes\n", buffer
, lb_size
);
95 bounce_buffer
= (uintptr_t)buffer
;
96 bounce_size
= req_size
;
99 static int overlaps_coreboot(struct segment
*seg
)
101 unsigned long start
, end
;
102 start
= seg
->s_dstaddr
;
103 end
= start
+ seg
->s_memsz
;
104 return !((end
<= lb_start
) || (start
>= lb_end
));
107 static int relocate_segment(unsigned long buffer
, struct segment
*seg
)
109 /* Modify all segments that want to load onto coreboot
110 * to load onto the bounce buffer instead.
112 /* ret: 1 : A new segment is inserted before the seg.
113 * 0 : A new segment is inserted after the seg, or no new one.
115 unsigned long start
, middle
, end
, ret
= 0;
117 printk(BIOS_SPEW
, "lb: [0x%016lx, 0x%016lx)\n",
120 /* I don't conflict with coreboot so get out of here */
121 if (!overlaps_coreboot(seg
))
124 start
= seg
->s_dstaddr
;
125 middle
= start
+ seg
->s_filesz
;
126 end
= start
+ seg
->s_memsz
;
128 printk(BIOS_SPEW
, "segment: [0x%016lx, 0x%016lx, 0x%016lx)\n",
131 if (seg
->compression
== CBFS_COMPRESS_NONE
) {
132 /* Slice off a piece at the beginning
133 * that doesn't conflict with coreboot.
135 if (start
< lb_start
) {
137 unsigned long len
= lb_start
- start
;
138 new = malloc(sizeof(*new));
142 seg
->s_dstaddr
+= len
;
143 seg
->s_srcaddr
+= len
;
144 if (seg
->s_filesz
> len
) {
146 seg
->s_filesz
-= len
;
151 /* Order by stream offset */
153 new->prev
= seg
->prev
;
154 seg
->prev
->next
= new;
157 /* compute the new value of start */
158 start
= seg
->s_dstaddr
;
160 printk(BIOS_SPEW
, " early: [0x%016lx, 0x%016lx, 0x%016lx)\n",
162 new->s_dstaddr
+ new->s_filesz
,
163 new->s_dstaddr
+ new->s_memsz
);
168 /* Slice off a piece at the end
169 * that doesn't conflict with coreboot
172 unsigned long len
= lb_end
- start
;
174 new = malloc(sizeof(*new));
178 new->s_dstaddr
+= len
;
179 new->s_srcaddr
+= len
;
180 if (seg
->s_filesz
> len
) {
182 new->s_filesz
-= len
;
186 /* Order by stream offset */
187 new->next
= seg
->next
;
189 seg
->next
->prev
= new;
192 printk(BIOS_SPEW
, " late: [0x%016lx, 0x%016lx, 0x%016lx)\n",
194 new->s_dstaddr
+ new->s_filesz
,
195 new->s_dstaddr
+ new->s_memsz
);
199 /* Now retarget this segment onto the bounce buffer */
200 /* sort of explanation: the buffer is a 1:1 mapping to coreboot.
201 * so you will make the dstaddr be this buffer, and it will get copied
202 * later to where coreboot lives.
204 seg
->s_dstaddr
= buffer
+ (seg
->s_dstaddr
- lb_start
);
206 printk(BIOS_SPEW
, " bounce: [0x%016lx, 0x%016lx, 0x%016lx)\n",
208 seg
->s_dstaddr
+ seg
->s_filesz
,
209 seg
->s_dstaddr
+ seg
->s_memsz
);
215 static int build_self_segment_list(
216 struct segment
*head
,
217 struct payload
*payload
, uintptr_t *entry
)
221 struct cbfs_payload_segment
*segment
, *first_segment
;
222 struct cbfs_payload
*cbfs_payload
;
223 cbfs_payload
= payload
->backing_store
.data
;
224 memset(head
, 0, sizeof(*head
));
225 head
->next
= head
->prev
= head
;
226 first_segment
= segment
= &cbfs_payload
->segments
;
229 printk(BIOS_DEBUG
, "Loading segment from rom address 0x%p\n", segment
);
230 switch(segment
->type
) {
231 case PAYLOAD_SEGMENT_PARAMS
:
232 printk(BIOS_DEBUG
, " parameter section (skipped)\n");
236 case PAYLOAD_SEGMENT_CODE
:
237 case PAYLOAD_SEGMENT_DATA
:
238 printk(BIOS_DEBUG
, " %s (compression=%x)\n",
239 segment
->type
== PAYLOAD_SEGMENT_CODE
? "code" : "data",
240 ntohl(segment
->compression
));
241 new = malloc(sizeof(*new));
242 new->s_dstaddr
= ntohll(segment
->load_addr
);
243 new->s_memsz
= ntohl(segment
->mem_len
);
244 new->compression
= ntohl(segment
->compression
);
246 new->s_srcaddr
= (uintptr_t)
247 ((unsigned char *)first_segment
)
248 + ntohl(segment
->offset
);
249 new->s_filesz
= ntohl(segment
->len
);
250 printk(BIOS_DEBUG
, " New segment dstaddr 0x%lx memsize 0x%lx srcaddr 0x%lx filesize 0x%lx\n",
251 new->s_dstaddr
, new->s_memsz
, new->s_srcaddr
, new->s_filesz
);
252 /* Clean up the values */
253 if (new->s_filesz
> new->s_memsz
) {
254 new->s_filesz
= new->s_memsz
;
256 " cleaned up filesize 0x%lx\n",
261 case PAYLOAD_SEGMENT_BSS
:
262 printk(BIOS_DEBUG
, " BSS 0x%p (%d byte)\n", (void *)
263 (intptr_t)ntohll(segment
->load_addr
),
264 ntohl(segment
->mem_len
));
265 new = malloc(sizeof(*new));
267 new->s_srcaddr
= (uintptr_t)
268 ((unsigned char *)first_segment
)
269 + ntohl(segment
->offset
);
270 new->s_dstaddr
= ntohll(segment
->load_addr
);
271 new->s_memsz
= ntohl(segment
->mem_len
);
274 case PAYLOAD_SEGMENT_ENTRY
:
275 printk(BIOS_DEBUG
, " Entry Point 0x%p\n",
276 (void *)(intptr_t)ntohll(segment
->load_addr
));
277 *entry
= ntohll(segment
->load_addr
);
278 /* Per definition, a payload always has the entry point
279 * as last segment. Thus, we use the occurrence of the
280 * entry point as break condition for the loop.
281 * Can we actually just look at the number of section?
286 /* We found something that we don't know about. Throw
287 * hands into the sky and run away!
289 printk(BIOS_EMERG
, "Bad segment type %x\n", segment
->type
);
293 /* We have found another CODE, DATA or BSS segment */
296 /* Find place where to insert our segment */
297 for(ptr
= head
->next
; ptr
!= head
; ptr
= ptr
->next
) {
298 if (new->s_srcaddr
< ntohll(segment
->load_addr
))
302 /* Order by stream offset */
304 new->prev
= ptr
->prev
;
305 ptr
->prev
->next
= new;
312 static int load_self_segments(
313 struct segment
*head
,
314 struct payload
*payload
)
317 const unsigned long one_meg
= (1UL << 20);
318 unsigned long bounce_high
= lb_end
;
320 for(ptr
= head
->next
; ptr
!= head
; ptr
= ptr
->next
) {
321 if (bootmem_region_targets_usable_ram(ptr
->s_dstaddr
,
325 if (ptr
->s_dstaddr
< one_meg
&&
326 (ptr
->s_dstaddr
+ ptr
->s_memsz
) <= one_meg
) {
328 "Payload being loaded below 1MiB "
329 "without region being marked as RAM usable.\n");
333 /* Payload segment not targeting RAM. */
334 printk(BIOS_ERR
, "SELF Payload doesn't target RAM:\n");
335 printk(BIOS_ERR
, "Failed Segment: 0x%lx, %lu bytes\n",
336 ptr
->s_dstaddr
, ptr
->s_memsz
);
337 bootmem_dump_ranges();
341 for(ptr
= head
->next
; ptr
!= head
; ptr
= ptr
->next
) {
343 * Add segments to bootmem memory map before a bounce buffer is
344 * allocated so that there aren't conflicts with the actual
347 bootmem_add_range(ptr
->s_dstaddr
, ptr
->s_memsz
,
350 if (!overlaps_coreboot(ptr
))
352 if (ptr
->s_dstaddr
+ ptr
->s_memsz
> bounce_high
)
353 bounce_high
= ptr
->s_dstaddr
+ ptr
->s_memsz
;
355 get_bounce_buffer(bounce_high
- lb_start
);
356 if (!bounce_buffer
) {
357 printk(BIOS_ERR
, "Could not find a bounce buffer...\n");
361 /* Update the payload's bounce buffer data used when loading. */
362 payload
->bounce
.data
= (void *)(uintptr_t)bounce_buffer
;
363 payload
->bounce
.size
= bounce_size
;
365 for(ptr
= head
->next
; ptr
!= head
; ptr
= ptr
->next
) {
366 unsigned char *dest
, *src
;
367 printk(BIOS_DEBUG
, "Loading Segment: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n",
368 ptr
->s_dstaddr
, ptr
->s_memsz
, ptr
->s_filesz
);
370 /* Modify the segment to load onto the bounce_buffer if necessary.
372 if (relocate_segment(bounce_buffer
, ptr
)) {
373 ptr
= (ptr
->prev
)->prev
;
377 printk(BIOS_DEBUG
, "Post relocation: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n",
378 ptr
->s_dstaddr
, ptr
->s_memsz
, ptr
->s_filesz
);
380 /* Compute the boundaries of the segment */
381 dest
= (unsigned char *)(ptr
->s_dstaddr
);
382 src
= (unsigned char *)(ptr
->s_srcaddr
);
384 /* Copy data from the initial buffer */
386 unsigned char *middle
, *end
;
389 switch(ptr
->compression
) {
390 case CBFS_COMPRESS_LZMA
: {
391 printk(BIOS_DEBUG
, "using LZMA\n");
392 len
= ulzma(src
, dest
);
393 if (!len
) /* Decompression Error. */
397 case CBFS_COMPRESS_NONE
: {
398 printk(BIOS_DEBUG
, "it's not compressed!\n");
399 memcpy(dest
, src
, len
);
403 printk(BIOS_INFO
, "CBFS: Unknown compression type %d\n", ptr
->compression
);
406 end
= dest
+ ptr
->s_memsz
;
408 printk(BIOS_SPEW
, "[ 0x%08lx, %08lx, 0x%08lx) <- %08lx\n",
410 (unsigned long)middle
,
414 /* Zero the extra bytes between middle & end */
416 printk(BIOS_DEBUG
, "Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n",
417 (unsigned long)middle
, (unsigned long)(end
- middle
));
419 /* Zero the extra bytes */
420 memset(middle
, 0, end
- middle
);
422 /* Copy the data that's outside the area that shadows ramstage */
423 printk(BIOS_DEBUG
, "dest %p, end %p, bouncebuffer %lx\n", dest
, end
, bounce_buffer
);
424 if ((unsigned long)end
> bounce_buffer
) {
425 if ((unsigned long)dest
< bounce_buffer
) {
426 unsigned char *from
= dest
;
427 unsigned char *to
= (unsigned char*)(lb_start
-(bounce_buffer
-(unsigned long)dest
));
428 unsigned long amount
= bounce_buffer
-(unsigned long)dest
;
429 printk(BIOS_DEBUG
, "move prefix around: from %p, to %p, amount: %lx\n", from
, to
, amount
);
430 memcpy(to
, from
, amount
);
432 if ((unsigned long)end
> bounce_buffer
+ (lb_end
- lb_start
)) {
433 unsigned long from
= bounce_buffer
+ (lb_end
- lb_start
);
434 unsigned long to
= lb_end
;
435 unsigned long amount
= (unsigned long)end
- from
;
436 printk(BIOS_DEBUG
, "move suffix around: from %lx, to %lx, amount: %lx\n", from
, to
, amount
);
437 memcpy((char*)to
, (char*)from
, amount
);
445 void *selfload(struct payload
*payload
)
450 /* Preprocess the self segments */
451 if (!build_self_segment_list(&head
, payload
, &entry
))
454 /* Load the segments */
455 if (!load_self_segments(&head
, payload
))
458 printk(BIOS_SPEW
, "Loaded segments\n");
460 return (void *)entry
;