- Fix small syntax error in reset16.lds

- small bug fixes to the elf bootloader
- factor the elf bootloader into focused smal subroutines that are easier to comprehend
  and maintain
- Implement support for PT_NOTE headers and parsing ELF notes if we need special functionality
  An ELF Image should never need an ELF loader to interpret an ELF to work correctly, making
  interpretation of all ELF notes optional.
- Implement support for notes giving PROGRAM_NAME, PROGRAM_VERSION, and PROGRAM_CHECKSUM
This commit is contained in:
Eric W. Biederman 2002-01-22 17:51:31 +00:00
parent 811a430c87
commit bf85f95212
7 changed files with 500 additions and 234 deletions

View file

@ -90,6 +90,7 @@ void jmp_to_elf_entry(void *entry, unsigned long buffer)
printk_spew("lb_size = 0x%08lx\n", lb_size);
printk_spew("adjust = 0x%08lx\n", adjust);
printk_spew("buffer = 0x%08lx\n", buffer);
printk_spew(" elf_boot_notes = 0x%08lx\n", (unsigned long)&elf_boot_notes);
printk_spew("adjusted_boot_notes = 0x%08lx\n", adjusted_boot_notes);
/* Jump to kernel */
@ -138,8 +139,8 @@ void jmp_to_elf_entry(void *entry, unsigned long buffer)
" rep movsl\n\t"
/* Now jump to the loaded image */
" movl 0(%%esp), %%eax\n\t"
" movl $0x0E1FB007, %%ebx\n\t"
" movl $0x0E1FB007, %%eax\n\t"
" movl 0(%%esp), %%ebx\n\t"
" call *4(%%esp)\n\t"
/* The loaded image returned? */
@ -174,7 +175,7 @@ void jmp_to_elf_entry(void *entry, unsigned long buffer)
::
"g" (lb_start), "g" (buffer), "g" (lb_size),
"g" (entry), "g"(&adjusted_boot_notes)
"g" (entry), "g"(adjusted_boot_notes)
);
}

View file

@ -6,7 +6,7 @@
SECTIONS {
_ROMTOP = (_ROMBASE >= 0xffff0000)? 0xfffffff0 : 0xffff0;
. = _ROMTOP;
.reset (.): {
.reset . : {
*(.reset)
. = 15 ;
BYTE(0x00);

View file

@ -393,10 +393,12 @@ extern int elf_check_arch(Elf_ehdr *ehdr);
extern void jmp_to_elf_entry(void *entry, unsigned long buffer);
struct stream;
struct lb_memory;
extern int elfload(struct stream *stream, struct lb_memory *mem,
unsigned char *header, unsigned long header_size);
extern int elfboot(struct stream *stream, struct lb_memory *mem);
#define FIRMWARE_TYPE "LinuxBIOS"
#define BOOTLOADER "elfboot"
#define BOOTLOADER_VERSION "1.1"
#define BOOTLOADER_VERSION "1.2"
#endif /* elf.h */

View file

@ -2,5 +2,6 @@
#define IP_CHECKSUM_H
unsigned long compute_ip_checksum(void *addr, unsigned long length);
unsigned long add_ip_checksums(unsigned long offset, unsigned long sum, unsigned long new);
#endif /* IP_CHECKSUM_H */

View file

@ -11,7 +11,7 @@ object memset.o
object memcpy.o
object memcmp.o
object malloc.o
object elfboot.o
object elfboot.o USE_ELF_BOOT
object do_inflate.o
object floppy_subr.o
object delay.o

View file

@ -1,11 +1,14 @@
#include <stdint.h>
#include <ip_checksum.h>
unsigned long compute_ip_checksum(void *addr, unsigned long length)
{
unsigned short *ptr;
uint16_t *ptr;
unsigned long sum;
unsigned long len;
/* Assumes len is a multiple of two, and addr is 2 byte aligned. */
/* FIXME this assumes addr is 2 byte aligned.
* This isn't fatal on x86 but it can be on other platforms..
*/
/* compute an ip style checksum */
sum = 0;
len = length >> 1;
@ -15,6 +18,40 @@ unsigned long compute_ip_checksum(void *addr, unsigned long length)
if (sum > 0xFFFF)
sum -= 0xFFFF;
}
if (length & 1) {
uint16_t buffer;
unsigned char *ptr;
/* copy the last byte into a 2 byte buffer.
* This way automatically handles the endian question
* of which byte (low or high) the last byte goes in.
*/
buffer = 0;
ptr = addr;
ptr += length - 1;
memcpy(&buffer, ptr, 1);
sum += buffer;
if (sum > 0xFFFF)
sum -= 0xFFFF;
}
return (~sum) & 0xFFFF;
}
unsigned long add_ip_checksums(unsigned long offset, unsigned long sum, unsigned long new)
{
unsigned long checksum;
sum = ~sum & 0xFFFF;
new = ~new & 0xFFFF;
if (offset & 1) {
/* byte swap the sum if it came from an odd offset
* since the computation is endian independant this
* works.
*/
new = ((new >> 8) & 0xff) | ((new << 8) & 0xff00);
}
checksum = sum + new;
if (checksum > 0xFFFF) {
checksum -= 0xFFFF;
}
return (~checksum) & 0xFFFF;
}

View file

@ -1,9 +1,9 @@
#if USE_ELF_BOOT
#include <printk.h>
#include <part/fallback_boot.h>
#include <boot/elf.h>
#include <boot/elf_boot.h>
#include <boot/linuxbios_tables.h>
#include <ip_checksum.h>
#include <rom/read_bytes.h>
#include <string.h>
#include <subr.h>
@ -22,12 +22,66 @@ extern unsigned char _eram_seg;
struct segment {
struct segment *next;
struct segment *prev;
struct segment *phdr_next;
struct segment *phdr_prev;
unsigned long s_addr;
unsigned long s_memsz;
unsigned long s_offset;
unsigned long s_filesz;
};
struct verify_callback {
struct verify_callback *next;
int (*callback)(struct verify_callback *vcb,
Elf_ehdr *ehdr, Elf_phdr *phdr, struct segment *head);
unsigned long desc_offset;
unsigned long desc_addr;
};
struct ip_checksum_vcb {
struct verify_callback data;
unsigned short ip_checksum;
};
int verify_ip_checksum(
struct verify_callback *vcb,
Elf_ehdr *ehdr, Elf_phdr *phdr, struct segment *head)
{
struct ip_checksum_vcb *cb;
struct segment *ptr;
unsigned long bytes;
unsigned long checksum;
unsigned char buff[2], *n_desc;
cb = (struct ip_checksum_vcb *)vcb;
/* zero the checksum so it's value won't
* get in the way of verifying the checksum.
*/
n_desc = 0;
if (vcb->desc_addr) {
n_desc = (unsigned char *)(vcb->desc_addr);
memcpy(buff, n_desc, 2);
memset(n_desc, 0, 2);
}
bytes = 0;
checksum = compute_ip_checksum(ehdr, sizeof(*ehdr));
bytes += sizeof(*ehdr);
checksum = add_ip_checksums(bytes, checksum,
compute_ip_checksum(phdr, ehdr->e_phnum*sizeof(*phdr)));
bytes += sizeof(*phdr);
for(ptr = head->phdr_next; ptr != head; ptr = ptr->phdr_next) {
checksum = add_ip_checksums(bytes, checksum,
compute_ip_checksum((void *)ptr->s_addr, ptr->s_memsz));
bytes += ptr->s_memsz;
}
if (n_desc != 0) {
memcpy(n_desc, buff, 2);
}
if (checksum != cb->ip_checksum) {
printk_err("Image checksum: %04x != computed checksum: %04x\n",
cb->ip_checksum, checksum);
}
return checksum == cb->ip_checksum;
}
/* The problem:
* Static executables all want to share the same addresses
@ -84,7 +138,69 @@ static unsigned long get_bounce_buffer(struct lb_memory *mem)
return buffer;
}
static int safe_range(struct lb_memory *mem, unsigned long buffer,
static struct verify_callback *process_elf_notes(
unsigned char *header,
unsigned long offset, unsigned long length)
{
struct verify_callback *cb_chain;
unsigned char *note, *end;
char *program, *version;
cb_chain = 0;
note = header + offset;
end = note + length;
program = version = 0;
while(note < end) {
Elf_Nhdr *hdr;
unsigned char *n_name, *n_desc, *next;
hdr = (Elf_Nhdr *)note;
n_name = note + sizeof(*hdr);
n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
next = n_desc + ((hdr->n_descsz + 3) & ~3);
if (next > end) {
break;
}
if ((hdr->n_namesz == sizeof(ELF_NOTE_BOOT)) &&
(memcmp(n_name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT)) == 0)) {
switch(hdr->n_type) {
case EIN_PROGRAM_NAME:
if (n_desc[hdr->n_descsz -1] == 0) {
program = n_desc;
}
break;
case EIN_PROGRAM_VERSION:
if (n_desc[hdr->n_descsz -1] == 0) {
version = n_desc;
}
break;
case EIN_PROGRAM_CHECKSUM:
{
struct ip_checksum_vcb *cb;
cb = malloc(sizeof(*cb));
cb->ip_checksum = *((uint16_t *)n_desc);
cb->data.callback = verify_ip_checksum;
cb->data.next = cb_chain;
cb->data.desc_offset = n_desc - header;
cb_chain = &cb->data;
break;
}
}
}
printk_spew("n_type: %08x n_name(%d): %-*.*s n_desc(%d): %-*.*s\n",
hdr->n_type,
hdr->n_namesz, hdr->n_namesz, hdr->n_namesz, n_name,
hdr->n_descsz,hdr->n_descsz, hdr->n_descsz, n_desc);
note = next;
}
if (program && version) {
printk_info("Loading %s version: %s\n",
program, version);
}
return cb_chain;
}
static int valid_area(struct lb_memory *mem, unsigned long buffer,
unsigned long start, unsigned long len)
{
/* Check through all of the memory segments and ensure
@ -134,119 +250,293 @@ static int safe_range(struct lb_memory *mem, unsigned long buffer,
return 1;
}
static void bounce_segments(unsigned long buffer, struct segment *head)
static void relocate_segment(unsigned long buffer, struct segment *seg)
{
/* Modify all segments that want to load onto linuxBIOS
* to load onto the bounce buffer instead.
*/
unsigned long lb_start = (unsigned long)&_ram_seg;
unsigned long lb_end = (unsigned long)&_eram_seg;
struct segment *ptr;
unsigned long start, middle, end;
printk_spew("lb: [0x%016lx, 0x%016lx)\n",
lb_start, lb_end);
for(ptr = head->next; ptr != head; ptr = ptr->next) {
unsigned long start, middle, end;
start = ptr->s_addr;
middle = start + ptr->s_filesz;
end = start + ptr->s_memsz;
/* I don't conflict with linuxBIOS so get out of here */
if ((end <= lb_start) || (start >= lb_end))
continue;
start = seg->s_addr;
middle = start + seg->s_filesz;
end = start + seg->s_memsz;
/* I don't conflict with linuxBIOS so get out of here */
if ((end <= lb_start) || (start >= lb_end))
return;
printk_spew("segment: [0x%016lx, 0x%016lx, 0x%016lx)\n",
start, middle, end);
printk_spew("segment: [0x%016lx, 0x%016lx, 0x%016lx)\n",
start, middle, end);
/* Slice off a piece at the beginning
* that doesn't conflict with linuxBIOS.
*/
if (start < lb_start) {
struct segment *new;
unsigned long len = lb_start - start;
new = malloc(sizeof(*new));
*new = *ptr;
new->s_memsz = len;
ptr->s_memsz -= len;
ptr->s_addr += len;
ptr->s_offset += len;
if (ptr->s_filesz > len) {
new->s_filesz = len;
ptr->s_filesz -= len;
} else {
ptr->s_filesz = 0;
}
new->next = ptr;
ptr->prev = new;
start = ptr->s_addr;
printk_spew(" early: [0x%016lx, 0x%016lx, 0x%016lx)\n",
new->s_addr,
new->s_addr + new->s_filesz,
new->s_addr + new->s_memsz);
/* Slice off a piece at the beginning
* that doesn't conflict with linuxBIOS.
*/
if (start < lb_start) {
struct segment *new;
unsigned long len = lb_start - start;
new = malloc(sizeof(*new));
*new = *seg;
new->s_memsz = len;
seg->s_memsz -= len;
seg->s_addr += len;
seg->s_offset += len;
if (seg->s_filesz > len) {
new->s_filesz = len;
seg->s_filesz -= len;
} else {
seg->s_filesz = 0;
}
/* Slice off a piece at the end
* that doesn't conflict with linuxBIOS
*/
if (end > lb_end) {
struct segment *new;
unsigned long len = end - lb_end;
new = malloc(sizeof(*new));
*new = *ptr;
ptr->s_memsz = len;
new->s_memsz -= len;
new->s_addr += len;
new->s_offset += len;
if (ptr->s_filesz > len) {
ptr->s_filesz = len;
new->s_filesz -= len;
} else {
new->s_filesz = 0;
}
ptr->next = new;
new->prev = ptr;
end = start + len;
/* Order by stream offset */
new->next = seg;
new->prev = seg->prev;
seg->prev->next = new;
seg->prev = new;
/* Order by original program header order */
new->phdr_next = seg;
new->phdr_prev = seg->phdr_prev;
seg->phdr_prev->phdr_next = new;
seg->phdr_prev = new;
printk_spew(" late: [0x%016lx, 0x%016lx, 0x%016lx)\n",
new->s_addr,
new->s_addr + new->s_filesz,
new->s_addr + new->s_memsz);
}
/* Now retarget this segment onto the bounce buffer */
ptr->s_addr = buffer + (ptr->s_addr - lb_start);
printk_spew(" bounce: [0x%016lx, 0x%016lx, 0x%016lx)\n",
ptr->s_addr,
ptr->s_addr + ptr->s_filesz,
ptr->s_addr + ptr->s_memsz);
/* compute the new value of start */
start = seg->s_addr;
printk_spew(" early: [0x%016lx, 0x%016lx, 0x%016lx)\n",
new->s_addr,
new->s_addr + new->s_filesz,
new->s_addr + new->s_memsz);
}
/* Slice off a piece at the end
* that doesn't conflict with linuxBIOS
*/
if (end > lb_end) {
unsigned long len = lb_end - start;
struct segment *new;
new = malloc(sizeof(*new));
*new = *seg;
seg->s_memsz = len;
new->s_memsz -= len;
new->s_addr += len;
new->s_offset += len;
if (seg->s_filesz > len) {
seg->s_filesz = len;
new->s_filesz -= len;
} else {
new->s_filesz = 0;
}
/* Order by stream offset */
new->next = seg->next;
new->prev = seg;
seg->next->prev = new;
seg->next = new;
/* Order by original program header order */
new->phdr_next = seg->phdr_next;
new->phdr_prev = seg;
seg->phdr_next->phdr_prev = new;
seg->phdr_next = new;
/* compute the new value of end */
end = start + len;
printk_spew(" late: [0x%016lx, 0x%016lx, 0x%016lx)\n",
new->s_addr,
new->s_addr + new->s_filesz,
new->s_addr + new->s_memsz);
}
/* Now retarget this segment onto the bounce buffer */
seg->s_addr = buffer + (seg->s_addr - lb_start);
printk_spew(" bounce: [0x%016lx, 0x%016lx, 0x%016lx)\n",
seg->s_addr,
seg->s_addr + seg->s_filesz,
seg->s_addr + seg->s_memsz);
}
int elfboot(struct stream *stream, struct lb_memory *mem)
static int build_elf_segment_list(
struct segment *head,
unsigned long bounce_buffer, struct lb_memory *mem,
Elf_phdr *phdr, int headers)
{
struct segment *ptr;
int i;
memset(head, 0, sizeof(*head));
head->next = head->prev = head;
for(i = 0; i < headers; i++) {
struct segment *new;
/* Ignore data that I don't need to handle */
if (phdr[i].p_type != PT_LOAD) {
printk_debug("Dropping non PT_LOAD segment\n");
continue;
}
if (phdr[i].p_memsz == 0) {
printk_debug("Dropping empty segment\n");
continue;
}
new = malloc(sizeof(*new));
new->s_addr = phdr[i].p_paddr;
new->s_memsz = phdr[i].p_memsz;
new->s_offset = phdr[i].p_offset;
new->s_filesz = phdr[i].p_filesz;
/* Clean up the values */
if (new->s_filesz > new->s_memsz) {
new->s_filesz = new->s_memsz;
}
for(ptr = head->next; ptr != head; ptr = ptr->next) {
if (new->s_offset < ptr->s_offset)
break;
}
/* Order by stream offset */
new->next = ptr;
new->prev = ptr->prev;
ptr->prev->next = new;
ptr->prev = new;
/* Order by original program header order */
new->phdr_next = head;
new->phdr_prev = head->phdr_prev;
head->phdr_prev->phdr_next = new;
head->phdr_prev = new;
/* Verify the memory addresses in the segment are valid */
if (!valid_area(mem, bounce_buffer, new->s_addr, new->s_memsz))
goto out;
/* Modify the segment to load onto the bounce_buffer if necessary.
*/
relocate_segment(bounce_buffer, new);
}
return 1;
out:
return 0;
}
static int load_elf_segments(
struct segment *head, struct stream *stream,
unsigned char *header, unsigned long header_size)
{
static unsigned char header[ELF_HEAD_SIZE];
unsigned long offset;
struct segment *ptr;
offset = 0;
for(ptr = head->next; ptr != head; ptr = ptr->next) {
unsigned long start_offset;
unsigned long skip_bytes, read_bytes;
unsigned char *dest, *middle, *end;
byte_offset_t result;
printk_debug("Loading Segment: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n",
ptr->s_addr, ptr->s_memsz, ptr->s_filesz);
/* Compute the boundaries of the segment */
dest = (unsigned char *)(ptr->s_addr);
end = dest + ptr->s_memsz;
middle = dest + ptr->s_filesz;
start_offset = ptr->s_offset;
printk_spew("[ 0x%016lx, %016lx, 0x%016lx) <- %016lx\n",
(unsigned long)dest,
(unsigned long)middle,
(unsigned long)end,
(unsigned long)start_offset);
/* Skip intial buffer unused bytes */
if (offset < header_size) {
if (start_offset < header_size) {
offset = start_offset;
} else {
offset = header_size;
}
}
/* Skip the unused bytes */
skip_bytes = start_offset - offset;
if (skip_bytes &&
((result = stream->skip(skip_bytes)) != skip_bytes)) {
printk_err("ERROR: Skip of %ld bytes skiped %ld bytes\n",
skip_bytes, result);
goto out;
}
offset = start_offset;
/* Copy data from the initial buffer */
if (offset < header_size) {
size_t len;
if ((ptr->s_filesz + start_offset) > header_size) {
len = header_size - start_offset;
}
else {
len = ptr->s_filesz;
}
memcpy(dest, &header[start_offset], len);
dest += len;
}
/* Read the segment into memory */
read_bytes = middle - dest;
if (read_bytes &&
((result = stream->read(dest, read_bytes)) != read_bytes)) {
printk_err("ERROR: Read of %ld bytes read %ld bytes...\n",
read_bytes, result);
goto out;
}
offset += ptr->s_filesz;
/* Zero the extra bytes between middle & end */
if (middle < end) {
printk_debug("Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n",
(unsigned long)middle, end - middle);
/* Zero the extra bytes */
memset(middle, 0, end - middle);
}
}
return 1;
out:
return 0;
}
static int verify_loaded_image(
struct verify_callback *vcb,
Elf_ehdr *ehdr, Elf_phdr *phdr,
struct segment *head
)
{
struct segment *ptr;
int ok;
ok = 1;
for(; ok && vcb ; vcb = vcb->next) {
/* Find where the note is loaded */
/* The whole note must be loaded intact
* so an address of 0 for the descriptor is impossible
*/
vcb->desc_addr = 0;
for(ptr = head->next; ptr != head; ptr = ptr->next) {
unsigned long desc_addr;
desc_addr = ptr->s_addr + vcb->desc_offset - ptr->s_offset;
if ((desc_addr >= ptr->s_addr) &&
(desc_addr < (ptr->s_addr + ptr->s_filesz))) {
vcb->desc_addr = desc_addr;
}
}
ok = vcb->callback(vcb, ehdr, phdr, head);
}
return ok;
}
int elfload(struct stream *stream, struct lb_memory *mem,
unsigned char *header, unsigned long header_size)
{
Elf_ehdr *ehdr;
Elf_phdr *phdr;
int header_offset;
unsigned long bounce_buffer;
struct segment dummy;
struct segment *head, *ptr;
void *entry;
int i;
byte_offset_t amtread;
printk_info("\n");
printk_info("Welcome to %s, the open sourced starter.\n", BOOTLOADER);
printk_info("January 2002, Eric Biederman.\n");
printk_info("Version %s\n", BOOTLOADER_VERSION);
printk_info("\n");
if (stream->init() < 0) {
printk_err("Could not initialize driver...\n");
goto out;
}
struct segment head;
struct verify_callback *cb_chain;
unsigned long bounce_buffer;
/* Find a bounce buffer so I can load to linuxBIOS's current location */
bounce_buffer = get_bounce_buffer(mem);
@ -255,8 +545,68 @@ int elfboot(struct stream *stream, struct lb_memory *mem)
goto out;
}
ehdr = (Elf_ehdr *)header;
entry = (void *)(ehdr->e_entry);
phdr = (Elf_phdr *)(&header[ehdr->e_phoff]);
/* Digest elf note information... */
cb_chain = 0;
if ((phdr[0].p_type == PT_NOTE) &&
((phdr[0].p_offset + phdr[0].p_filesz) < header_size)) {
cb_chain = process_elf_notes(header,
phdr[0].p_offset, phdr[0].p_filesz);
}
/* Preprocess the elf segments */
if (!build_elf_segment_list(&head,
bounce_buffer, mem, phdr, ehdr->e_phnum))
goto out;
/* Load the segments */
if (!load_elf_segments(&head, stream, header, header_size))
goto out;
/* Verify the loaded image */
if (!verify_loaded_image(cb_chain, ehdr, phdr, &head))
goto out;
/* Shutdown the stream device */
stream->fini();
/* Reset to booting from this image as late as possible */
boot_successful();
printk_debug("Jumping to boot code\n");
post_code(0xfe);
/* Jump to kernel */
jmp_to_elf_entry(entry, bounce_buffer);
return 1;
out:
return 0;
}
int elfboot(struct stream *stream, struct lb_memory *mem)
{
Elf_ehdr *ehdr;
static unsigned char header[ELF_HEAD_SIZE];
int header_offset;
int i, result;
result = 0;
printk_info("\n");
printk_info("Welcome to %s, the open sourced starter.\n", BOOTLOADER);
printk_info("January 2002, Eric Biederman.\n");
printk_info("Version %s\n", BOOTLOADER_VERSION);
printk_info("\n");
post_code(0xf8);
if (stream->init() < 0) {
printk_err("Could not initialize driver...\n");
goto out;
}
/* Read in the initial ELF_HEAD_SIZE bytes */
if (stream->read(header, ELF_HEAD_SIZE) != ELF_HEAD_SIZE) {
printk_err("Read failed...\n");
@ -288,143 +638,18 @@ int elfboot(struct stream *stream, struct lb_memory *mem)
if (header_offset == -1) {
goto out;
}
entry = (void *)(ehdr->e_entry);
phdr = (Elf_phdr *)&header[ehdr->e_phoff + header_offset];
/* Create an ordered table of the segments */
memset(&dummy, 0, sizeof(dummy));
head = &dummy;
head->next = head->prev = head;
for(i = 0; i < ehdr->e_phnum; i++) {
struct segment *new;
/* Ignore data that I don't need to handle */
if (phdr[i].p_type != PT_LOAD)
continue;
if (phdr[i].p_memsz == 0)
continue;
new = malloc(sizeof(*new));
new->s_addr = phdr[i].p_paddr;
new->s_memsz = phdr[i].p_memsz;
new->s_offset = phdr[i].p_offset;
new->s_filesz = phdr[i].p_filesz;
/* Clean up the values */
if (new->s_filesz > new->s_memsz) {
new->s_filesz = new->s_memsz;
}
for(ptr = head->next; ptr != head; ptr = ptr->next) {
if (new->s_offset < ptr->s_offset)
break;
}
new->next = ptr;
new->prev = ptr->prev;
ptr->prev->next = new;
ptr->prev = new;
}
/* Sanity check the segments */
for(ptr = head->next; ptr != head; ptr = ptr->next) {
if (!safe_range(mem, bounce_buffer, ptr->s_addr, ptr->s_memsz)) {
goto out;
}
}
/* Modify all segments that want to load onto linuxBIOS
* to load onto the bounce buffer instead.
*/
bounce_segments(bounce_buffer, head);
/* Load the segments */
offset = 0;
for(ptr = head->next; ptr != head; ptr = ptr->next) {
unsigned long start_offset;
unsigned long skip_bytes, read_bytes;
unsigned char *dest, *middle, *end;
byte_offset_t result;
printk_debug("Loading Segment: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n",
ptr->s_addr, ptr->s_memsz, ptr->s_filesz);
/* Compute the boundaries of the segment */
dest = (unsigned char *)(ptr->s_addr);
end = dest + ptr->s_memsz;
middle = dest + ptr->s_filesz;
start_offset = ptr->s_offset;
printk_spew("[ 0x%016lx, %016lx, 0x%016lx) <- %016lx\n",
(unsigned long)dest,
(unsigned long)middle,
(unsigned long)end,
(unsigned long)start_offset);
/* Skip intial buffer unused bytes */
if (offset < (ELF_HEAD_SIZE - header_offset)) {
if (start_offset < (ELF_HEAD_SIZE - header_offset)) {
offset = start_offset;
} else {
offset = (ELF_HEAD_SIZE - header_offset);
}
}
/* Skip the unused bytes */
skip_bytes = start_offset - offset;
if (skip_bytes &&
((result = stream->skip(skip_bytes)) != skip_bytes)) {
printk_err("ERROR: Skip of %ld bytes skiped %ld bytes\n",
skip_bytes, result);
goto out;
}
offset = start_offset;
/* Copy data from the initial buffer */
if (offset < (ELF_HEAD_SIZE - header_offset)) {
size_t len;
if ((ptr->s_filesz + start_offset) > ELF_HEAD_SIZE) {
len = ELF_HEAD_SIZE - start_offset;
}
else {
len = ptr->s_filesz;
}
memcpy(dest, &header[header_offset + start_offset], len);
dest += len;
}
/* Read the segment into memory */
read_bytes = middle - dest;
if (read_bytes &&
((result = stream->read(dest, read_bytes)) != read_bytes)) {
printk_err("ERROR: Read of %ld bytes read %ld bytes...\n",
read_bytes, result);
goto out;
}
offset += ptr->s_filesz;
/* Zero the extra bytes between middle & end */
if (middle < end) {
printk_debug("Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n",
(unsigned long)middle, end - middle);
/* Zero the extra bytes */
memset(middle, 0, end - middle);
}
}
/* Reset to booting from this image as late as possible */
stream->fini();
boot_successful();
printk_debug("Jumping to boot code\n");
post_code(0xfe);
/* Jump to kernel */
jmp_to_elf_entry(entry, bounce_buffer);
return 1;
result = elfload(stream, mem,
header + header_offset , ELF_HEAD_SIZE - header_offset);
out:
printk_err("Cannot Load ELF Image\n");
if (!result) {
/* Shutdown the stream device */
stream->fini();
/* Shutdown the stream device */
stream->fini();
printk_err("Cannot Load ELF Image\n");
post_code(0xff);
}
return 0;
}
#endif /* USE_ELF_BOOT */