Create a brand new ELF loader based upon arch/*/elfload.c and libopenbios/elfload.c combined together in libopenbios. This means

that each arch no longer needs its own ELF loader implementation.

Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>


git-svn-id: svn://coreboot.org/openbios/trunk/openbios-devel@710 f158a5a8-5612-0410-a976-696ce0be7e32
This commit is contained in:
Mark Cave-Ayland
2010-03-27 10:57:03 +00:00
committed by Mark Cave-Ayland
parent 6dd0574272
commit 1264ad95d2
22 changed files with 133 additions and 1408 deletions

View File

@@ -1,400 +0,0 @@
/* ELF Boot loader
* As we have seek, this implementation can be straightforward.
* 2003-07 by SONE Takeshi
*/
#include "config.h"
#include "kernel/kernel.h"
#include "arch/common/elf.h"
#include "asm/elf.h"
#include "arch/common/elf_boot.h"
#include "libopenbios/sys_info.h"
#include "libopenbios/ipchecksum.h"
#include "loadfs.h"
#define printf printk
#define debug printk
extern unsigned int start_elf(unsigned long entry_point, unsigned long param);
extern char _start, _end;
static char *image_name, *image_version;
static void *calloc(size_t nmemb, size_t size)
{
size_t alloc_size = nmemb * size;
void *mem;
if (alloc_size < nmemb || alloc_size < size) {
printf("calloc overflow: %u, %u\n", nmemb, size);
return 0;
}
mem = malloc(alloc_size);
memset(mem, 0, alloc_size);
return mem;
}
static int check_mem_ranges(struct sys_info *info,
Elf_phdr *phdr, int phnum)
{
int i, j;
unsigned long start, end;
unsigned long prog_start, prog_end;
struct memrange *mem;
prog_start = virt_to_phys(&_start);
prog_end = virt_to_phys(&_end);
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_LOAD)
continue;
start = phdr[i].p_paddr;
end = start + phdr[i].p_memsz;
if (start < prog_start && end > prog_start)
goto conflict;
if (start < prog_end && end > prog_end)
goto conflict;
mem=info->memrange;
for (j = 0; j < info->n_memranges; j++) {
if (mem[j].base <= start && mem[j].base + mem[j].size >= end)
break;
}
if (j >= info->n_memranges)
goto badseg;
}
return 1;
conflict:
printf("%s occupies [%#lx-%#lx]\n", program_name, prog_start, prog_end);
badseg:
printf("Segment %d [%#lx-%#lx] doesn't fit into memory\n", i, start, end-1);
return 0;
}
static unsigned long process_image_notes(Elf_phdr *phdr, int phnum,
unsigned short *sum_ptr)
{
int i;
char *buf = NULL;
int retval = 0;
unsigned long addr, end;
Elf_Nhdr *nhdr;
const char *name;
void *desc;
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_NOTE)
continue;
buf = malloc(phdr[i].p_filesz);
file_seek(phdr[i].p_offset);
if (lfile_read(buf, phdr[i].p_filesz) != phdr[i].p_filesz) {
printf("Can't read note segment\n");
goto out;
}
addr = (unsigned long) buf;
end = addr + phdr[i].p_filesz;
while (addr < end) {
nhdr = (Elf_Nhdr *) addr;
addr += sizeof(Elf_Nhdr);
name = (const char *) addr;
addr += (nhdr->n_namesz+3) & ~3;
desc = (void *) addr;
addr += (nhdr->n_descsz+3) & ~3;
if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT)
&& memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) {
if (nhdr->n_type == EIN_PROGRAM_NAME) {
image_name = calloc(1, nhdr->n_descsz + 1);
memcpy(image_name, desc, nhdr->n_descsz);
}
if (nhdr->n_type == EIN_PROGRAM_VERSION) {
image_version = calloc(1, nhdr->n_descsz + 1);
memcpy(image_version, desc, nhdr->n_descsz);
}
if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) {
*sum_ptr = *(unsigned short *) desc;
debug("Image checksum: %#04x\n", *sum_ptr);
/* Where in the file */
retval = phdr[i].p_offset
+ (unsigned long) desc - (unsigned long) buf;
}
}
}
}
out:
if (buf)
free(buf);
return retval;
}
static int load_segments(Elf_phdr *phdr, int phnum,
unsigned long checksum_offset)
{
unsigned long bytes;
//unsigned int start_time, time;
int i;
bytes = 0;
// start_time = currticks();
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_LOAD)
continue;
debug("segment %d addr:%#x file:%#x mem:%#x ",
i, phdr[i].p_paddr, phdr[i].p_filesz, phdr[i].p_memsz);
file_seek(phdr[i].p_offset);
debug("loading... ");
if (lfile_read(phys_to_virt(phdr[i].p_paddr), phdr[i].p_filesz)
!= phdr[i].p_filesz) {
printf("Can't read program segment %d\n", i);
return 0;
}
bytes += phdr[i].p_filesz;
debug("clearing... ");
memset(phys_to_virt(phdr[i].p_paddr + phdr[i].p_filesz), 0,
phdr[i].p_memsz - phdr[i].p_filesz);
if (phdr[i].p_offset <= checksum_offset
&& phdr[i].p_offset + phdr[i].p_filesz >= checksum_offset+2) {
debug("clearing checksum... ");
memset(phys_to_virt(phdr[i].p_paddr + checksum_offset
- phdr[i].p_offset), 0, 2);
}
debug("ok\n");
}
// time = currticks() - start_time;
//debug("Loaded %lu bytes in %ums (%luKB/s)\n", bytes, time,
// time? bytes/time : 0);
debug("Loaded %lu bytes \n", bytes);
return 1;
}
static int verify_image(Elf_ehdr *ehdr, Elf_phdr *phdr, int phnum,
unsigned short image_sum)
{
unsigned short sum, part_sum;
unsigned long offset;
int i;
sum = 0;
offset = 0;
part_sum = ipchksum(ehdr, sizeof *ehdr);
sum = add_ipchksums(offset, sum, part_sum);
offset += sizeof *ehdr;
part_sum = ipchksum(phdr, phnum * sizeof(*phdr));
sum = add_ipchksums(offset, sum, part_sum);
offset += phnum * sizeof(*phdr);
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_LOAD)
continue;
part_sum = ipchksum(phys_to_virt(phdr[i].p_paddr), phdr[i].p_memsz);
sum = add_ipchksums(offset, sum, part_sum);
offset += phdr[i].p_memsz;
}
if (sum != image_sum) {
printf("Verify FAILED (image:%#04x vs computed:%#04x)\n",
image_sum, sum);
return 0;
}
return 1;
}
static inline unsigned const padded(unsigned s)
{
return (s + 3) & ~3;
}
static Elf_Bhdr *add_boot_note(Elf_Bhdr *bhdr, const char *name,
unsigned type, const char *desc, unsigned descsz)
{
Elf_Nhdr nhdr;
unsigned ent_size, new_size, pad;
char *addr;
if (!bhdr)
return NULL;
nhdr.n_namesz = name? strlen(name)+1 : 0;
nhdr.n_descsz = descsz;
nhdr.n_type = type;
ent_size = sizeof(nhdr) + padded(nhdr.n_namesz) + padded(nhdr.n_descsz);
if (bhdr->b_size + ent_size > 0xffff) {
printf("Boot notes too big\n");
free(bhdr);
return NULL;
}
if (bhdr->b_size + ent_size > bhdr->b_checksum) {
do {
new_size = bhdr->b_checksum * 2;
} while (new_size < bhdr->b_size + ent_size);
if (new_size > 0xffff)
new_size = 0xffff;
debug("expanding boot note size to %u\n", new_size);
#ifdef HAVE_REALLOC
bhdr = realloc(bhdr, new_size);
bhdr->b_checksum = new_size;
#else
printf("Boot notes too big\n");
free(bhdr);
return NULL;
#endif
}
addr = (char *) bhdr;
addr += bhdr->b_size;
memcpy(addr, &nhdr, sizeof(nhdr));
addr += sizeof(nhdr);
memcpy(addr, name, nhdr.n_namesz);
addr += nhdr.n_namesz;
pad = padded(nhdr.n_namesz) - nhdr.n_namesz;
memset(addr, 0, pad);
addr += pad;
memcpy(addr, desc, nhdr.n_descsz);
addr += nhdr.n_descsz;
pad = padded(nhdr.n_descsz) - nhdr.n_descsz;
memset(addr, 0, pad);
addr += pad;
bhdr->b_size += ent_size;
bhdr->b_records++;
return bhdr;
}
static inline Elf_Bhdr *add_note_string(Elf_Bhdr *bhdr, const char *name,
unsigned type, const char *desc)
{
return add_boot_note(bhdr, name, type, desc, strlen(desc) + 1);
}
static Elf_Bhdr *build_boot_notes(struct sys_info *info, const char *cmdline)
{
Elf_Bhdr *bhdr;
bhdr = malloc(256);
bhdr->b_signature = ELF_BHDR_MAGIC;
bhdr->b_size = sizeof *bhdr;
bhdr->b_checksum = 256; /* XXX cache the current buffer size here */
bhdr->b_records = 0;
if (info->firmware)
bhdr = add_note_string(bhdr, NULL, EBN_FIRMWARE_TYPE, info->firmware);
bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_NAME, program_name);
bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_VERSION, program_version);
if (cmdline)
bhdr = add_note_string(bhdr, NULL, EBN_COMMAND_LINE, cmdline);
if (!bhdr)
return bhdr;
bhdr->b_checksum = 0;
bhdr->b_checksum = ipchksum(bhdr, bhdr->b_size);
return bhdr;
}
int elf_load(struct sys_info *info, const char *filename, const char *cmdline)
{
Elf_ehdr ehdr;
Elf_phdr *phdr = NULL;
unsigned long phdr_size;
unsigned long checksum_offset;
unsigned short checksum;
Elf_Bhdr *boot_notes = NULL;
int retval = -1;
int image_retval;
image_name = image_version = 0;
/* Mark the saved-program-state as invalid */
feval("0 state-valid !");
if (!file_open(filename))
goto out;
if (lfile_read(&ehdr, sizeof ehdr) != sizeof ehdr) {
debug("Can't read ELF header\n");
retval = LOADER_NOT_SUPPORT;
goto out;
}
if (ehdr.e_ident[EI_MAG0] != ELFMAG0
|| ehdr.e_ident[EI_MAG1] != ELFMAG1
|| ehdr.e_ident[EI_MAG2] != ELFMAG2
|| ehdr.e_ident[EI_MAG3] != ELFMAG3
|| ehdr.e_ident[EI_CLASS] != ARCH_ELF_CLASS
|| ehdr.e_ident[EI_DATA] != ARCH_ELF_DATA
|| ehdr.e_ident[EI_VERSION] != EV_CURRENT
|| ehdr.e_type != ET_EXEC
|| !ARCH_ELF_MACHINE_OK(ehdr.e_machine)
|| ehdr.e_version != EV_CURRENT
|| ehdr.e_phentsize != sizeof(Elf_phdr)) {
debug("Not a bootable ELF image\n");
retval = LOADER_NOT_SUPPORT;
goto out;
}
phdr_size = ehdr.e_phnum * sizeof *phdr;
phdr = malloc(phdr_size);
file_seek(ehdr.e_phoff);
if (lfile_read(phdr, phdr_size) != phdr_size) {
printf("Can't read program header\n");
goto out;
}
if (!check_mem_ranges(info, phdr, ehdr.e_phnum))
goto out;
checksum_offset = process_image_notes(phdr, ehdr.e_phnum, &checksum);
printf("Loading %s", image_name ? image_name : "image");
if (image_version)
printf(" version %s", image_version);
printf("...\n");
if (!load_segments(phdr, ehdr.e_phnum, checksum_offset))
goto out;
if (checksum_offset) {
if (!verify_image(&ehdr, phdr, ehdr.e_phnum, checksum))
goto out;
}
boot_notes = build_boot_notes(info, cmdline);
//debug("current time: %lu\n", currticks());
debug("entry point is %#x\n", ehdr.e_entry);
printf("Jumping to entry point...\n");
// Initialise saved-program-state
PUSH(ehdr.e_entry);
feval("saved-program-state >sps.entry !");
PUSH(file_size);
feval("saved-program-state >sps.file-size !");
feval("elf-boot saved-program-state >sps.file-type !");
feval("-1 state-valid !");
image_retval = start_elf(ehdr.e_entry, virt_to_phys(boot_notes));
// console_init(); FIXME
printf("Image returned with return value %#x\n", image_retval);
retval = 0;
out:
if (phdr)
free(phdr);
if (boot_notes)
free(boot_notes);
if (image_name)
free(image_name);
if (image_version)
free(image_version);
return retval;
}

View File

@@ -16,7 +16,7 @@
#include "config.h" #include "config.h"
#include "libopenbios/bindings.h" #include "libopenbios/bindings.h"
#include "libopenbios/elfload.h" #include "libopenbios/elf_load.h"
#include "arch/common/nvram.h" #include "arch/common/nvram.h"
#include "packages/nvram.h" #include "packages/nvram.h"
#include "libc/diskio.h" #include "libc/diskio.h"

View File

@@ -15,7 +15,6 @@
<object source="udiv.S"/> <object source="udiv.S"/>
<object source="linux_load.c"/> <object source="linux_load.c"/>
<object source="sys_info.c"/> <object source="sys_info.c"/>
<object source="elfload.c"/>
<object source="aoutload.c"/> <object source="aoutload.c"/>
<object source="forthload.c"/> <object source="forthload.c"/>
<object source="romvec.c"/> <object source="romvec.c"/>

View File

@@ -8,6 +8,7 @@
#include "libc/diskio.h" #include "libc/diskio.h"
#include "libc/vsprintf.h" #include "libc/vsprintf.h"
#include "libopenbios/sys_info.h" #include "libopenbios/sys_info.h"
#include "libopenbios/elf_load.h"
#include "boot.h" #include "boot.h"
struct sys_info sys_info; struct sys_info sys_info;
@@ -26,7 +27,7 @@ static int try_path(const char *path, char *param)
int image_retval = 0; int image_retval = 0;
/* ELF Boot loader */ /* ELF Boot loader */
elf_load(&sys_info, path, param); elf_load(&sys_info, path, param, &boot_notes);
feval("state-valid @"); feval("state-valid @");
valid = POP(); valid = POP();
if (valid) if (valid)

View File

@@ -9,9 +9,6 @@
// forthload.c // forthload.c
int forth_load(const char *filename); int forth_load(const char *filename);
// elfload.c
int elf_load(struct sys_info *info, const char *filename, const char *cmdline);
// aout_load.c // aout_load.c
int aout_load(struct sys_info *info, const char *filename); int aout_load(struct sys_info *info, const char *filename);

View File

@@ -14,7 +14,6 @@
<object source="switch.S"/> <object source="switch.S"/>
<object source="linux_load.c"/> <object source="linux_load.c"/>
<object source="sys_info.c"/> <object source="sys_info.c"/>
<object source="elfload.c"/>
<object source="aoutload.c"/> <object source="aoutload.c"/>
<object source="forthload.c"/> <object source="forthload.c"/>
<object source="fcodeload.c"/> <object source="fcodeload.c"/>

View File

@@ -1,408 +0,0 @@
/* ELF Boot loader
* As we have seek, this implementation can be straightforward.
* 2003-07 by SONE Takeshi
*/
#include "config.h"
#include "kernel/kernel.h"
#include "arch/common/elf.h"
#include "asm/elf.h"
#include "arch/common/elf_boot.h"
#include "libopenbios/sys_info.h"
#include "libopenbios/ipchecksum.h"
#include "libopenbios/bindings.h"
#include "libc/diskio.h"
#include "boot.h"
#define printf printk
#define debug printk
#define addr_fixup(addr) ((addr) & 0x00ffffff)
static char *image_name, *image_version;
static int fd;
static void *calloc(size_t nmemb, size_t size)
{
size_t alloc_size = nmemb * size;
void *mem;
if (alloc_size < nmemb || alloc_size < size) {
printf("calloc overflow: %u, %u\n", nmemb, size);
return NULL;
}
mem = malloc(alloc_size);
memset(mem, 0, alloc_size);
return mem;
}
static int check_mem_ranges(struct sys_info *info,
Elf_phdr *phdr, int phnum)
{
int i, j;
unsigned long start, end;
unsigned long prog_start, prog_end;
struct memrange *mem;
prog_start = virt_to_phys(&_start);
prog_end = virt_to_phys(&_end);
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_LOAD)
continue;
start = addr_fixup(phdr[i].p_paddr);
end = start + phdr[i].p_memsz;
if (start < prog_start && end > prog_start)
goto conflict;
if (start < prog_end && end > prog_end)
goto conflict;
mem=info->memrange;
for (j = 0; j < info->n_memranges; j++) {
if (mem[j].base <= start && mem[j].base + mem[j].size >= end)
break;
}
if (j >= info->n_memranges)
goto badseg;
}
return 1;
conflict:
printf("%s occupies [%#lx-%#lx]\n", program_name, prog_start, prog_end);
badseg:
printf("Segment %d [%#lx-%#lx] doesn't fit into memory\n", i, start, end-1);
return 0;
}
static unsigned long process_image_notes(Elf_phdr *phdr, int phnum,
unsigned short *sum_ptr,
unsigned int offset)
{
int i;
char *buf = NULL;
int retval = 0;
unsigned long addr, end;
Elf_Nhdr *nhdr;
const char *name;
void *desc;
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_NOTE)
continue;
buf = malloc(phdr[i].p_filesz);
seek_io(fd, offset + phdr[i].p_offset);
if ((uint64_t)read_io(fd, buf, phdr[i].p_filesz) != phdr[i].p_filesz) {
printf("Can't read note segment\n");
goto out;
}
addr = (unsigned long) buf;
end = addr + phdr[i].p_filesz;
while (addr < end) {
nhdr = (Elf_Nhdr *) addr;
addr += sizeof(Elf_Nhdr);
name = (const char *) addr;
addr += (nhdr->n_namesz+3) & ~3;
desc = (void *) addr;
addr += (nhdr->n_descsz+3) & ~3;
if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT)
&& memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) {
if (nhdr->n_type == EIN_PROGRAM_NAME) {
image_name = calloc(1, nhdr->n_descsz + 1);
memcpy(image_name, desc, nhdr->n_descsz);
}
if (nhdr->n_type == EIN_PROGRAM_VERSION) {
image_version = calloc(1, nhdr->n_descsz + 1);
memcpy(image_version, desc, nhdr->n_descsz);
}
if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) {
*sum_ptr = *(unsigned short *) desc;
debug("Image checksum: %#04x\n", *sum_ptr);
/* Where in the file */
retval = phdr[i].p_offset
+ (unsigned long) desc - (unsigned long) buf;
}
}
}
}
out:
close_io(fd);
if (buf)
free(buf);
return retval;
}
static int load_segments(Elf_phdr *phdr, int phnum,
unsigned long checksum_offset,
unsigned int offset, unsigned long *bytes)
{
//unsigned int start_time, time;
int i;
*bytes = 0;
// start_time = currticks();
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_LOAD)
continue;
debug("segment %d addr:%#llx file:%#llx mem:%#llx ",
i, addr_fixup(phdr[i].p_paddr), phdr[i].p_filesz, phdr[i].p_memsz);
seek_io(fd, offset + phdr[i].p_offset);
debug("loading... ");
if ((uint64_t)read_io(fd, phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_filesz)
!= phdr[i].p_filesz) {
printf("Can't read program segment %d\n", i);
return 0;
}
*bytes += phdr[i].p_filesz;
debug("clearing... ");
memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + phdr[i].p_filesz), 0,
phdr[i].p_memsz - phdr[i].p_filesz);
if (phdr[i].p_offset <= checksum_offset
&& phdr[i].p_offset + phdr[i].p_filesz >= checksum_offset+2) {
debug("clearing checksum... ");
memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + checksum_offset
- phdr[i].p_offset), 0, 2);
}
debug("ok\n");
}
// time = currticks() - start_time;
//debug("Loaded %lu bytes in %ums (%luKB/s)\n", bytes, time,
// time? bytes/time : 0);
debug("Loaded %lu bytes \n", *bytes);
return 1;
}
static int verify_image(Elf_ehdr *ehdr, Elf_phdr *phdr, int phnum,
unsigned short image_sum)
{
unsigned short sum, part_sum;
unsigned long offset;
int i;
sum = 0;
offset = 0;
part_sum = ipchksum(ehdr, sizeof *ehdr);
sum = add_ipchksums(offset, sum, part_sum);
offset += sizeof *ehdr;
part_sum = ipchksum(phdr, phnum * sizeof(*phdr));
sum = add_ipchksums(offset, sum, part_sum);
offset += phnum * sizeof(*phdr);
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_LOAD)
continue;
part_sum = ipchksum(phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_memsz);
sum = add_ipchksums(offset, sum, part_sum);
offset += phdr[i].p_memsz;
}
if (sum != image_sum) {
printf("Verify FAILED (image:%#04x vs computed:%#04x)\n",
image_sum, sum);
return 0;
}
return 1;
}
static inline unsigned padded(unsigned s)
{
return (s + 3) & ~3;
}
static Elf_Bhdr *add_boot_note(Elf_Bhdr *bhdr, const char *name,
unsigned type, const char *desc, unsigned descsz)
{
Elf_Nhdr nhdr;
unsigned ent_size, new_size, pad;
char *addr;
if (!bhdr)
return NULL;
nhdr.n_namesz = name? strlen(name)+1 : 0;
nhdr.n_descsz = descsz;
nhdr.n_type = type;
ent_size = sizeof(nhdr) + padded(nhdr.n_namesz) + padded(nhdr.n_descsz);
if (bhdr->b_size + ent_size > 0xffff) {
printf("Boot notes too big\n");
free(bhdr);
return NULL;
}
if (bhdr->b_size + ent_size > bhdr->b_checksum) {
do {
new_size = bhdr->b_checksum * 2;
} while (new_size < bhdr->b_size + ent_size);
if (new_size > 0xffff)
new_size = 0xffff;
debug("expanding boot note size to %u\n", new_size);
#ifdef HAVE_REALLOC
bhdr = realloc(bhdr, new_size);
bhdr->b_checksum = new_size;
#else
printf("Boot notes too big\n");
free(bhdr);
return NULL;
#endif
}
addr = (char *) bhdr;
addr += bhdr->b_size;
memcpy(addr, &nhdr, sizeof(nhdr));
addr += sizeof(nhdr);
memcpy(addr, name, nhdr.n_namesz);
addr += nhdr.n_namesz;
pad = padded(nhdr.n_namesz) - nhdr.n_namesz;
memset(addr, 0, pad);
addr += pad;
memcpy(addr, desc, nhdr.n_descsz);
addr += nhdr.n_descsz;
pad = padded(nhdr.n_descsz) - nhdr.n_descsz;
memset(addr, 0, pad);
addr += pad;
bhdr->b_size += ent_size;
bhdr->b_records++;
return bhdr;
}
static inline Elf_Bhdr *add_note_string(Elf_Bhdr *bhdr, const char *name,
unsigned type, const char *desc)
{
return add_boot_note(bhdr, name, type, desc, strlen(desc) + 1);
}
static Elf_Bhdr *build_boot_notes(struct sys_info *info, const char *cmdline)
{
Elf_Bhdr *bhdr;
bhdr = malloc(256);
bhdr->b_signature = ELF_BHDR_MAGIC;
bhdr->b_size = sizeof *bhdr;
bhdr->b_checksum = 256; /* XXX cache the current buffer size here */
bhdr->b_records = 0;
if (info->firmware)
bhdr = add_note_string(bhdr, NULL, EBN_FIRMWARE_TYPE, info->firmware);
bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_NAME, program_name);
bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_VERSION, program_version);
if (cmdline)
bhdr = add_note_string(bhdr, NULL, EBN_COMMAND_LINE, cmdline);
if (!bhdr)
return bhdr;
bhdr->b_checksum = 0;
bhdr->b_checksum = ipchksum(bhdr, bhdr->b_size);
return bhdr;
}
int elf_load(struct sys_info *info, const char *filename, const char *cmdline)
{
Elf_ehdr ehdr;
Elf_phdr *phdr = NULL;
unsigned long phdr_size;
unsigned long checksum_offset, file_size;
unsigned short checksum = 0;
Elf_Bhdr *boot_notes = NULL;
int retval = -1;
unsigned int offset;
image_name = image_version = NULL;
/* Mark the saved-program-state as invalid */
feval("0 state-valid !");
fd = open_io(filename);
if (!fd)
goto out;
for (offset = 0; offset < 16 * 512; offset += 512) {
if ((size_t)read_io(fd, &ehdr, sizeof ehdr) != sizeof ehdr) {
debug("Can't read ELF header\n");
retval = LOADER_NOT_SUPPORT;
goto out;
}
if (ehdr.e_ident[EI_MAG0] == ELFMAG0)
break;
seek_io(fd, offset);
}
if (ehdr.e_ident[EI_MAG0] != ELFMAG0
|| ehdr.e_ident[EI_MAG1] != ELFMAG1
|| ehdr.e_ident[EI_MAG2] != ELFMAG2
|| ehdr.e_ident[EI_MAG3] != ELFMAG3
|| ehdr.e_ident[EI_CLASS] != ARCH_ELF_CLASS
|| ehdr.e_ident[EI_DATA] != ARCH_ELF_DATA
|| ehdr.e_ident[EI_VERSION] != EV_CURRENT
|| ehdr.e_type != ET_EXEC
|| !ARCH_ELF_MACHINE_OK(ehdr.e_machine)
|| ehdr.e_version != EV_CURRENT
|| ehdr.e_phentsize != sizeof(Elf_phdr)) {
debug("Not a bootable ELF image\n");
retval = LOADER_NOT_SUPPORT;
goto out;
}
phdr_size = ehdr.e_phnum * sizeof *phdr;
phdr = malloc(phdr_size);
seek_io(fd, offset + ehdr.e_phoff);
if ((unsigned long)read_io(fd, phdr, phdr_size) != phdr_size) {
printf("Can't read program header\n");
goto out;
}
if (!check_mem_ranges(info, phdr, ehdr.e_phnum))
goto out;
checksum_offset = process_image_notes(phdr, ehdr.e_phnum, &checksum, offset);
printf("Loading %s", image_name ? image_name : "image");
if (image_version)
printf(" version %s", image_version);
printf("...\n");
if (!load_segments(phdr, ehdr.e_phnum, checksum_offset, offset, &file_size))
goto out;
if (checksum_offset) {
if (!verify_image(&ehdr, phdr, ehdr.e_phnum, checksum))
goto out;
}
boot_notes = build_boot_notes(info, cmdline);
//debug("current time: %lu\n", currticks());
debug("entry point is %#llx\n", addr_fixup(ehdr.e_entry));
// Initialise saved-program-state
PUSH(addr_fixup(ehdr.e_entry));
feval("saved-program-state >sps.entry !");
PUSH(file_size);
feval("saved-program-state >sps.file-size !");
feval("elf-boot saved-program-state >sps.file-type !");
feval("-1 state-valid !");
retval = 0;
out:
close_io(fd);
if (phdr)
free(phdr);
if (boot_notes)
free(boot_notes);
if (image_name)
free(image_name);
if (image_version)
free(image_version);
return retval;
}

View File

@@ -4,7 +4,7 @@
#undef BOOTSTRAP #undef BOOTSTRAP
#include "config.h" #include "config.h"
#include "libopenbios/bindings.h" #include "libopenbios/bindings.h"
#include "libopenbios/elfload.h" #include "libopenbios/elf_load.h"
#include "arch/common/nvram.h" #include "arch/common/nvram.h"
#include "libc/diskio.h" #include "libc/diskio.h"

View File

@@ -14,7 +14,6 @@
<object source="linux_load.c"/> <object source="linux_load.c"/>
<object source="segment.c"/> <object source="segment.c"/>
<object source="sys_info.c"/> <object source="sys_info.c"/>
<object source="elfload.c"/>
<object source="forthload.c"/> <object source="forthload.c"/>
<object source="entry.S"/> <object source="entry.S"/>
<object source="xbox/console.c" condition="XBOX"/> <object source="xbox/console.c" condition="XBOX"/>

View File

@@ -1,401 +0,0 @@
/* ELF Boot loader
* As we have seek, this implementation can be straightforward.
* 2003-07 by SONE Takeshi
*/
#include "config.h"
#include "kernel/kernel.h"
#include "arch/common/elf.h"
#include "asm/elf.h"
#include "arch/common/elf_boot.h"
#include "libopenbios/sys_info.h"
#include "libopenbios/ipchecksum.h"
#include "libopenbios/bindings.h"
#include "libc/diskio.h"
#include "boot.h"
#define debug printk
/* FreeBSD and possibly others mask the high 8 bits */
#define ADDRMASK 0x00ffffff
/* #define ADDRMASK 0xffffffff // old behavior */
extern char _start, _end;
static char *image_name, *image_version;
static int fd;
static void *calloc(size_t nmemb, size_t size)
{
size_t alloc_size = nmemb * size;
void *mem;
if (alloc_size < nmemb || alloc_size < size) {
printk("calloc overflow: %u, %u\n", nmemb, size);
return NULL;
}
mem = malloc(alloc_size);
memset(mem, 0, alloc_size);
return mem;
}
static int check_mem_ranges(struct sys_info *info,
Elf_phdr *phdr, int phnum)
{
int i, j;
unsigned long start, end;
unsigned long prog_start, prog_end;
struct memrange *mem;
prog_start = virt_to_phys(&_start);
prog_end = virt_to_phys(&_end);
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_LOAD)
continue;
start = phdr[i].p_paddr;
end = start + phdr[i].p_memsz;
if ((start & ADDRMASK ) < prog_start && (end & ADDRMASK) > prog_start)
goto conflict;
if ((start & ADDRMASK) < prog_end && (end & ADDRMASK) > prog_end)
goto conflict;
mem=info->memrange;
for (j = 0; j < info->n_memranges; j++) {
if (mem[j].base <= (start & ADDRMASK) &&
mem[j].base + mem[j].size >= (end & ADDRMASK) )
break;
}
if (j >= info->n_memranges)
goto badseg;
}
return 1;
conflict:
printk("%s occupies [%#lx-%#lx]\n", program_name, prog_start, prog_end);
badseg:
printk("Segment %d [%#lx-%#lx] doesn't fit into memory\n", i, start, end-1);
return 0;
}
static unsigned long process_image_notes(Elf_phdr *phdr, int phnum,
unsigned short *sum_ptr)
{
int i;
char *buf = NULL;
int retval = 0;
unsigned long addr, end;
Elf_Nhdr *nhdr;
const char *name;
void *desc;
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_NOTE)
continue;
buf = malloc(phdr[i].p_filesz);
seek_io(fd, phdr[i].p_offset);
if (read_io(fd, buf, phdr[i].p_filesz) != phdr[i].p_filesz) {
printk("Can't read note segment\n");
goto out;
}
addr = (unsigned long) buf;
end = addr + phdr[i].p_filesz;
while (addr < end) {
nhdr = (Elf_Nhdr *) addr;
addr += sizeof(Elf_Nhdr);
name = (const char *) addr;
addr += (nhdr->n_namesz+3) & ~3;
desc = (void *) addr;
addr += (nhdr->n_descsz+3) & ~3;
if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT)
&& memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) {
if (nhdr->n_type == EIN_PROGRAM_NAME) {
image_name = calloc(1, nhdr->n_descsz + 1);
memcpy(image_name, desc, nhdr->n_descsz);
}
if (nhdr->n_type == EIN_PROGRAM_VERSION) {
image_version = calloc(1, nhdr->n_descsz + 1);
memcpy(image_version, desc, nhdr->n_descsz);
}
if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) {
*sum_ptr = *(unsigned short *) desc;
debug("Image checksum: %#04x\n", *sum_ptr);
/* Where in the file */
retval = phdr[i].p_offset
+ (unsigned long) desc - (unsigned long) buf;
}
}
}
}
out:
if (buf)
free(buf);
return retval;
}
static int load_segments(Elf_phdr *phdr, int phnum,
unsigned long checksum_offset, unsigned long *bytes)
{
//unsigned int start_time, time;
int i;
*bytes = 0;
// start_time = currticks();
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_LOAD)
continue;
debug("segment %d addr:%#x file:%#x mem:%#x ",
i, phdr[i].p_paddr, phdr[i].p_filesz, phdr[i].p_memsz);
seek_io(fd, phdr[i].p_offset);
debug("loading... ");
if (read_io(fd, phys_to_virt(phdr[i].p_paddr & ADDRMASK), phdr[i].p_filesz)
!= phdr[i].p_filesz) {
printk("Can't read program segment %d\n", i);
return 0;
}
*bytes += phdr[i].p_filesz;
debug("clearing... ");
memset(phys_to_virt(phdr[i].p_paddr + phdr[i].p_filesz), 0,
phdr[i].p_memsz - phdr[i].p_filesz);
if (phdr[i].p_offset <= checksum_offset
&& phdr[i].p_offset + phdr[i].p_filesz >= checksum_offset+2) {
debug("clearing checksum... ");
memset(phys_to_virt((phdr[i].p_paddr & ADDRMASK) + checksum_offset
- phdr[i].p_offset), 0, 2);
}
debug("ok\n");
}
// time = currticks() - start_time;
//debug("Loaded %lu bytes in %ums (%luKB/s)\n", bytes, time,
// time? bytes/time : 0);
debug("Loaded %lu bytes \n", *bytes);
return 1;
}
static int verify_image(Elf_ehdr *ehdr, Elf_phdr *phdr, int phnum,
unsigned short image_sum)
{
unsigned short sum, part_sum;
unsigned long offset;
int i;
sum = 0;
offset = 0;
part_sum = ipchksum(ehdr, sizeof *ehdr);
sum = add_ipchksums(offset, sum, part_sum);
offset += sizeof *ehdr;
part_sum = ipchksum(phdr, phnum * sizeof(*phdr));
sum = add_ipchksums(offset, sum, part_sum);
offset += phnum * sizeof(*phdr);
for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_LOAD)
continue;
part_sum = ipchksum(phys_to_virt(phdr[i].p_paddr), phdr[i].p_memsz);
sum = add_ipchksums(offset, sum, part_sum);
offset += phdr[i].p_memsz;
}
if (sum != image_sum) {
printk("Verify FAILED (image:%#04x vs computed:%#04x)\n",
image_sum, sum);
return 0;
}
return 1;
}
static inline unsigned padded(unsigned s)
{
return (s + 3) & ~3;
}
static Elf_Bhdr *add_boot_note(Elf_Bhdr *bhdr, const char *name,
unsigned type, const char *desc, unsigned descsz)
{
Elf_Nhdr nhdr;
unsigned ent_size, new_size, pad;
char *addr;
if (!bhdr)
return NULL;
nhdr.n_namesz = name? strlen(name)+1 : 0;
nhdr.n_descsz = descsz;
nhdr.n_type = type;
ent_size = sizeof(nhdr) + padded(nhdr.n_namesz) + padded(nhdr.n_descsz);
if (bhdr->b_size + ent_size > 0xffff) {
printk("Boot notes too big\n");
free(bhdr);
return NULL;
}
if (bhdr->b_size + ent_size > bhdr->b_checksum) {
do {
new_size = bhdr->b_checksum * 2;
} while (new_size < bhdr->b_size + ent_size);
if (new_size > 0xffff)
new_size = 0xffff;
debug("expanding boot note size to %u\n", new_size);
#ifdef HAVE_REALLOC
bhdr = realloc(bhdr, new_size);
bhdr->b_checksum = new_size;
#else
printk("Boot notes too big\n");
free(bhdr);
return NULL;
#endif
}
addr = (char *) bhdr;
addr += bhdr->b_size;
memcpy(addr, &nhdr, sizeof(nhdr));
addr += sizeof(nhdr);
memcpy(addr, name, nhdr.n_namesz);
addr += nhdr.n_namesz;
pad = padded(nhdr.n_namesz) - nhdr.n_namesz;
memset(addr, 0, pad);
addr += pad;
memcpy(addr, desc, nhdr.n_descsz);
addr += nhdr.n_descsz;
pad = padded(nhdr.n_descsz) - nhdr.n_descsz;
memset(addr, 0, pad);
addr += pad;
bhdr->b_size += ent_size;
bhdr->b_records++;
return bhdr;
}
static inline Elf_Bhdr *add_note_string(Elf_Bhdr *bhdr, const char *name,
unsigned type, const char *desc)
{
return add_boot_note(bhdr, name, type, desc, strlen(desc) + 1);
}
static Elf_Bhdr *build_boot_notes(struct sys_info *info, const char *cmdline)
{
Elf_Bhdr *bhdr;
bhdr = malloc(256);
bhdr->b_signature = ELF_BHDR_MAGIC;
bhdr->b_size = sizeof *bhdr;
bhdr->b_checksum = 256; /* XXX cache the current buffer size here */
bhdr->b_records = 0;
if (info->firmware)
bhdr = add_note_string(bhdr, NULL, EBN_FIRMWARE_TYPE, info->firmware);
bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_NAME, program_name);
bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_VERSION, program_version);
if (cmdline)
bhdr = add_note_string(bhdr, NULL, EBN_COMMAND_LINE, cmdline);
if (!bhdr)
return bhdr;
bhdr->b_checksum = 0;
bhdr->b_checksum = ipchksum(bhdr, bhdr->b_size);
return bhdr;
}
int elf_load(struct sys_info *info, const char *filename, const char *cmdline)
{
Elf_ehdr ehdr;
Elf_phdr *phdr = NULL;
unsigned long phdr_size;
unsigned long checksum_offset, file_size;
unsigned short checksum = 0;
Elf_Bhdr *boot_notes = NULL;
int retval = -1;
image_name = image_version = NULL;
/* Mark the saved-program-state as invalid */
feval("0 state-valid !");
fd = open_io(filename);
if (!fd)
goto out;
if (read_io(fd, &ehdr, sizeof ehdr) != sizeof ehdr) {
debug("Can't read ELF header\n");
retval = LOADER_NOT_SUPPORT;
goto out;
}
if (ehdr.e_ident[EI_MAG0] != ELFMAG0
|| ehdr.e_ident[EI_MAG1] != ELFMAG1
|| ehdr.e_ident[EI_MAG2] != ELFMAG2
|| ehdr.e_ident[EI_MAG3] != ELFMAG3
|| ehdr.e_ident[EI_CLASS] != ARCH_ELF_CLASS
|| ehdr.e_ident[EI_DATA] != ARCH_ELF_DATA
|| ehdr.e_ident[EI_VERSION] != EV_CURRENT
|| ehdr.e_type != ET_EXEC
|| !ARCH_ELF_MACHINE_OK(ehdr.e_machine)
|| ehdr.e_version != EV_CURRENT
|| ehdr.e_phentsize != sizeof(Elf_phdr)) {
debug("Not a bootable ELF image\n");
retval = LOADER_NOT_SUPPORT;
goto out;
}
phdr_size = ehdr.e_phnum * sizeof *phdr;
phdr = malloc(phdr_size);
seek_io(fd, ehdr.e_phoff);
if (read_io(fd, phdr, phdr_size) != phdr_size) {
printk("Can't read program header\n");
goto out;
}
if (!check_mem_ranges(info, phdr, ehdr.e_phnum))
goto out;
checksum_offset = process_image_notes(phdr, ehdr.e_phnum, &checksum);
printk("Loading %s", image_name ? image_name : "image");
if (image_version)
printk(" version %s", image_version);
printk("...\n");
if (!load_segments(phdr, ehdr.e_phnum, checksum_offset, &file_size))
goto out;
if (checksum_offset) {
if (!verify_image(&ehdr, phdr, ehdr.e_phnum, checksum))
goto out;
}
boot_notes = build_boot_notes(info, cmdline);
//debug("current time: %lu\n", currticks());
debug("entry point is %#x\n", ehdr.e_entry);
// Initialise saved-program-state
PUSH(ehdr.e_entry & ADDRMASK);
feval("saved-program-state >sps.entry !");
PUSH(file_size);
feval("saved-program-state >sps.file-size !");
feval("elf-boot saved-program-state >sps.file-type !");
feval("-1 state-valid !");
retval = 0;
out:
if (phdr)
free(phdr);
if (boot_notes)
free(boot_notes);
if (image_name)
free(image_name);
if (image_version)
free(image_version);
return retval;
}

View File

@@ -460,7 +460,6 @@ static int load_initrd(struct linux_header *hdr, struct sys_info *info,
uint32_t max; uint32_t max;
uint32_t start, end, size; uint32_t start, end, size;
uint64_t forced; uint64_t forced;
extern char _start[], _end[];
fd = open_io(initrd_file); fd = open_io(initrd_file);
if (!fd) { if (!fd) {

View File

@@ -40,7 +40,6 @@ struct segment_desc gdt[NUM_SEG] = {
{0xffff, 0, 0, 0x93, 0xcf, 0}, {0xffff, 0, 0, 0x93, 0xcf, 0},
}; };
extern char _start[], _end[];
void relocate(struct sys_info *info) void relocate(struct sys_info *info)
{ {

View File

@@ -1,6 +1,7 @@
#ifndef _ASM_IO_H #ifndef _ASM_IO_H
#define _ASM_IO_H #define _ASM_IO_H
extern char _start, _end;
extern unsigned long virt_offset; extern unsigned long virt_offset;
#define phys_to_virt(phys) ((void *) ((unsigned long) (phys) - virt_offset)) #define phys_to_virt(phys) ((void *) ((unsigned long) (phys) - virt_offset))

View File

@@ -23,6 +23,8 @@ typedef unsigned long long ucell;
typedef __int128_t dcell; typedef __int128_t dcell;
typedef __uint128_t ducell; typedef __uint128_t ducell;
#define FMT_elf "%#x"
#define bitspercell (sizeof(cell)<<3) #define bitspercell (sizeof(cell)<<3)
#define bitsperdcell (sizeof(dcell)<<3) #define bitsperdcell (sizeof(dcell)<<3)

View File

@@ -40,6 +40,8 @@ typedef unsigned long long ducell;
#define FMT_ucellx "%08x" #define FMT_ucellx "%08x"
#define FMT_ucellX "%08X" #define FMT_ucellX "%08X"
#define FMT_elf "%#x"
#define bitspercell (sizeof(cell)<<3) #define bitspercell (sizeof(cell)<<3)
#define bitsperdcell (sizeof(dcell)<<3) #define bitsperdcell (sizeof(dcell)<<3)

View File

@@ -37,6 +37,10 @@ typedef unsigned long long ucell;
#define FMT_ucellx "%016llx" #define FMT_ucellx "%016llx"
#define FMT_ucellX "%016llX" #define FMT_ucellX "%016llX"
#define FMT_sizet "%lx"
#define FMT_elf "%#llx"
#define FMT_aout_ehdr "%x"
#ifdef NEED_FAKE_INT128_T #ifdef NEED_FAKE_INT128_T
typedef struct { typedef struct {
uint64_t hi; uint64_t hi;

View File

@@ -3,6 +3,7 @@
#include "asm/types.h" #include "asm/types.h"
extern char _start, _end;
extern unsigned long virt_offset; extern unsigned long virt_offset;
#define phys_to_virt(phys) ((void *) ((unsigned long) (phys) - virt_offset)) #define phys_to_virt(phys) ((void *) ((unsigned long) (phys) - virt_offset))

View File

@@ -29,6 +29,8 @@ typedef uint64_t ducell;
#define FMT_ucellx "%08x" #define FMT_ucellx "%08x"
#define FMT_ucellX "%08X" #define FMT_ucellX "%08X"
#define FMT_elf "%#x"
#define bitspercell (sizeof(cell)<<3) #define bitspercell (sizeof(cell)<<3)
#define bitsperdcell (sizeof(dcell)<<3) #define bitsperdcell (sizeof(dcell)<<3)

View File

@@ -19,11 +19,12 @@
#include "arch/common/elf.h" #include "arch/common/elf.h"
#include "asm/elf.h" #include "asm/elf.h"
#include "libopenbios/sys_info.h"
extern int is_elf( int fd, int offs ); extern int elf_load(struct sys_info *info, const char *filename, const char *cmdline, void **boot_notes);
extern int find_elf( int fd ); extern int is_elf(Elf_ehdr *ehdr);
extern int find_elf(Elf_ehdr *ehdr);
extern Elf32_Phdr * elf_readhdrs( int fd, int offs, Elf32_Ehdr *e );
extern Elf_phdr * elf_readhdrs(int offset, Elf_ehdr *ehdr);
#endif /* _H_ELFLOAD */ #endif /* _H_ELFLOAD */

View File

@@ -9,6 +9,10 @@
<object source="elf_info.c" condition="SPARC32"/> <object source="elf_info.c" condition="SPARC32"/>
<object source="elf_info.c" condition="SPARC64"/> <object source="elf_info.c" condition="SPARC64"/>
<object source="elf_info.c" condition="PPC"/> <object source="elf_info.c" condition="PPC"/>
<object source="elf_load.c" condition="X86"/>
<object source="elf_load.c" condition="SPARC32"/>
<object source="elf_load.c" condition="SPARC64"/>
<object source="elf_load.c" condition="AMD64"/>
<object source="font_8x8.c" condition="FONT_8X8"/> <object source="font_8x8.c" condition="FONT_8X8"/>
<object source="font_8x16.c" condition="FONT_8X16"/> <object source="font_8x16.c" condition="FONT_8X16"/>
<object source="ipchecksum.c"/> <object source="ipchecksum.c"/>

View File

@@ -5,23 +5,28 @@
#include "config.h" #include "config.h"
#include "kernel/kernel.h" #include "kernel/kernel.h"
#include "arch/common/elf.h" #include "libc/diskio.h"
#include "asm/elf.h"
#include "arch/common/elf_boot.h" #include "arch/common/elf_boot.h"
#include "libopenbios/elf_load.h"
#include "libopenbios/sys_info.h" #include "libopenbios/sys_info.h"
#include "libopenbios/ipchecksum.h" #include "libopenbios/ipchecksum.h"
#include "libopenbios/bindings.h" #include "libopenbios/bindings.h"
#include "libc/diskio.h"
#include "boot.h"
#define printf printk #define printf printk
#define debug printk #define debug printk
#define DEBUG 0
#define MAX_HEADERS 0x20
#define BS 0x100 /* smallest step used when looking for the ELF header */
/* FreeBSD and possibly others mask the high 8 bits */
#define addr_fixup(addr) ((addr) & 0x00ffffff) #define addr_fixup(addr) ((addr) & 0x00ffffff)
static char *image_name, *image_version; static char *image_name, *image_version;
static int fd; static int fd;
static void *calloc(size_t nmemb, size_t size) /* Note: avoid name collision with platforms which have their own version of calloc() */
static void *ob_calloc(size_t nmemb, size_t size)
{ {
size_t alloc_size = nmemb * size; size_t alloc_size = nmemb * size;
void *mem; void *mem;
@@ -37,7 +42,6 @@ static void *calloc(size_t nmemb, size_t size)
return mem; return mem;
} }
static int check_mem_ranges(struct sys_info *info, static int check_mem_ranges(struct sys_info *info,
Elf_phdr *phdr, int phnum) Elf_phdr *phdr, int phnum)
{ {
@@ -93,7 +97,7 @@ static unsigned long process_image_notes(Elf_phdr *phdr, int phnum,
continue; continue;
buf = malloc(phdr[i].p_filesz); buf = malloc(phdr[i].p_filesz);
seek_io(fd, offset + phdr[i].p_offset); seek_io(fd, offset + phdr[i].p_offset);
if ((uint32_t)read_io(fd, buf, phdr[i].p_filesz) != phdr[i].p_filesz) { if ((size_t)read_io(fd, buf, phdr[i].p_filesz) != phdr[i].p_filesz) {
printf("Can't read note segment\n"); printf("Can't read note segment\n");
goto out; goto out;
} }
@@ -110,11 +114,11 @@ static unsigned long process_image_notes(Elf_phdr *phdr, int phnum,
if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT) if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT)
&& memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) { && memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) {
if (nhdr->n_type == EIN_PROGRAM_NAME) { if (nhdr->n_type == EIN_PROGRAM_NAME) {
image_name = calloc(1, nhdr->n_descsz + 1); image_name = ob_calloc(1, nhdr->n_descsz + 1);
memcpy(image_name, desc, nhdr->n_descsz); memcpy(image_name, desc, nhdr->n_descsz);
} }
if (nhdr->n_type == EIN_PROGRAM_VERSION) { if (nhdr->n_type == EIN_PROGRAM_VERSION) {
image_version = calloc(1, nhdr->n_descsz + 1); image_version = ob_calloc(1, nhdr->n_descsz + 1);
memcpy(image_version, desc, nhdr->n_descsz); memcpy(image_version, desc, nhdr->n_descsz);
} }
if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) { if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) {
@@ -146,16 +150,16 @@ static int load_segments(Elf_phdr *phdr, int phnum,
for (i = 0; i < phnum; i++) { for (i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_LOAD) if (phdr[i].p_type != PT_LOAD)
continue; continue;
debug("segment %d addr:%#x file:%#x mem:%#x ", debug("segment %d addr:" FMT_elf " file:" FMT_elf " mem:" FMT_elf " ",
i, addr_fixup(phdr[i].p_paddr), phdr[i].p_filesz, phdr[i].p_memsz); i, addr_fixup(phdr[i].p_paddr), phdr[i].p_filesz, phdr[i].p_memsz);
seek_io(fd, offset + phdr[i].p_offset); seek_io(fd, offset + phdr[i].p_offset);
debug("loading... "); debug("loading... ");
if ((uint32_t)read_io(fd, phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_filesz) if ((size_t)read_io(fd, phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_filesz)
!= phdr[i].p_filesz) { != phdr[i].p_filesz) {
printf("Can't read program segment %d\n", i); printf("Can't read program segment %d\n", i);
return 0; return 0;
} }
*bytes += phdr[i].p_filesz; bytes += phdr[i].p_filesz;
debug("clearing... "); debug("clearing... ");
memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + phdr[i].p_filesz), 0, memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + phdr[i].p_filesz), 0,
phdr[i].p_memsz - phdr[i].p_filesz); phdr[i].p_memsz - phdr[i].p_filesz);
@@ -302,15 +306,68 @@ static Elf_Bhdr *build_boot_notes(struct sys_info *info, const char *cmdline)
return bhdr; return bhdr;
} }
int elf_load(struct sys_info *info, const char *filename, const char *cmdline, int
const void *romvec) is_elf(Elf_ehdr *ehdr)
{
return (ehdr->e_ident[EI_MAG0] == ELFMAG0
&& ehdr->e_ident[EI_MAG1] == ELFMAG1
&& ehdr->e_ident[EI_MAG2] == ELFMAG2
&& ehdr->e_ident[EI_MAG3] == ELFMAG3
&& ehdr->e_ident[EI_CLASS] == ARCH_ELF_CLASS
&& ehdr->e_ident[EI_DATA] == ARCH_ELF_DATA
&& ehdr->e_ident[EI_VERSION] == EV_CURRENT
&& ehdr->e_type == ET_EXEC
&& ARCH_ELF_MACHINE_OK(ehdr->e_machine)
&& ehdr->e_version == EV_CURRENT
&& ehdr->e_phentsize == sizeof(Elf_phdr));
}
int
find_elf(Elf_ehdr *ehdr)
{
int offset;
for (offset = 0; offset < MAX_HEADERS * BS; offset += BS) {
if ((size_t)read_io(fd, ehdr, sizeof ehdr) != sizeof ehdr) {
debug("Can't read ELF header\n");
return 0;
}
if (is_elf(ehdr)) {
debug("Found ELF header at offset %d\n", offset);
return offset;
}
seek_io(fd, offset);
}
debug("Not a bootable ELF image\n");
return 0;
}
Elf_phdr *
elf_readhdrs(int offset, Elf_ehdr *ehdr)
{
unsigned long phdr_size;
Elf_phdr *phdr;
phdr_size = ehdr->e_phnum * sizeof(Elf_phdr);
phdr = malloc(phdr_size);
seek_io(fd, offset + ehdr->e_phoff);
if ((size_t)read_io(fd, phdr, phdr_size) != phdr_size) {
printf("Can't read program header\n");
return NULL;
}
return phdr;
}
int elf_load(struct sys_info *info, const char *filename, const char *cmdline, void **boot_notes)
{ {
Elf_ehdr ehdr; Elf_ehdr ehdr;
Elf_phdr *phdr = NULL; Elf_phdr *phdr = NULL;
unsigned long phdr_size;
unsigned long checksum_offset, file_size; unsigned long checksum_offset, file_size;
unsigned short checksum = 0; unsigned short checksum = 0;
Elf_Bhdr *boot_notes = NULL;
int retval = -1; int retval = -1;
unsigned int offset; unsigned int offset;
@@ -323,42 +380,35 @@ int elf_load(struct sys_info *info, const char *filename, const char *cmdline,
if (!fd) if (!fd)
goto out; goto out;
for (offset = 0; offset < 16 * 512; offset += 512) { offset = find_elf(&ehdr);
if (read_io(fd, &ehdr, sizeof ehdr) != sizeof ehdr) { if (!offset) {
debug("Can't read ELF header\n");
retval = LOADER_NOT_SUPPORT; retval = LOADER_NOT_SUPPORT;
goto out; goto out;
} }
if (ehdr.e_ident[EI_MAG0] == ELFMAG0) #if DEBUG
break; printk("ELF header:\n");
printk(" ehdr.e_type = %d\n", (int)ehdr.e_type);
printk(" ehdr.e_machine = %d\n", (int)ehdr.e_machine);
printk(" ehdr.e_version = %d\n", (int)ehdr.e_version);
printk(" ehdr.e_entry = 0x%08x\n", (int)ehdr.e_entry);
printk(" ehdr.e_phoff = 0x%08x\n", (int)ehdr.e_phoff);
printk(" ehdr.e_shoff = 0x%08x\n", (int)ehdr.e_shoff);
printk(" ehdr.e_flags = %d\n", (int)ehdr.e_flags);
printk(" ehdr.e_ehsize = 0x%08x\n", (int)ehdr.e_ehsize);
printk(" ehdr.e_phentsize = 0x%08x\n", (int)ehdr.e_phentsize);
printk(" ehdr.e_phnum = %d\n", (int)ehdr.e_phnum);
#endif
seek_io(fd, offset); if (ehdr.e_phnum > MAX_HEADERS) {
} printk ("elfload: too many program headers (MAX_HEADERS)\n");
retval = 0;
if (ehdr.e_ident[EI_MAG0] != ELFMAG0
|| ehdr.e_ident[EI_MAG1] != ELFMAG1
|| ehdr.e_ident[EI_MAG2] != ELFMAG2
|| ehdr.e_ident[EI_MAG3] != ELFMAG3
|| ehdr.e_ident[EI_CLASS] != ARCH_ELF_CLASS
|| ehdr.e_ident[EI_DATA] != ARCH_ELF_DATA
|| ehdr.e_ident[EI_VERSION] != EV_CURRENT
|| ehdr.e_type != ET_EXEC
|| !ARCH_ELF_MACHINE_OK(ehdr.e_machine)
|| ehdr.e_version != EV_CURRENT
|| ehdr.e_phentsize != sizeof(Elf_phdr)) {
debug("Not a bootable ELF image\n");
retval = LOADER_NOT_SUPPORT;
goto out; goto out;
} }
phdr_size = ehdr.e_phnum * sizeof *phdr; phdr = elf_readhdrs(offset, &ehdr);
phdr = malloc(phdr_size); if (!phdr)
seek_io(fd, offset + ehdr.e_phoff);
if ((uint32_t)read_io(fd, phdr, phdr_size) != phdr_size) {
printf("Can't read program header\n");
goto out; goto out;
}
if (!check_mem_ranges(info, phdr, ehdr.e_phnum)) if (!check_mem_ranges(info, phdr, ehdr.e_phnum))
goto out; goto out;
@@ -378,29 +428,32 @@ int elf_load(struct sys_info *info, const char *filename, const char *cmdline,
goto out; goto out;
} }
boot_notes = build_boot_notes(info, cmdline); /* If we are attempting an ELF boot image, we pass a non-NULL pointer
into boot_notes and mark the image as elf-boot rather than standard
ELF */
if (boot_notes) {
*boot_notes = (void *)virt_to_phys(build_boot_notes(info, cmdline));
feval("elf-boot saved-program-state >sps.file-type !");
} else {
feval("elf saved-program-state >sps.file-type !");
}
//debug("current time: %lu\n", currticks()); //debug("current time: %lu\n", currticks());
debug("entry point is %#x\n", addr_fixup(ehdr.e_entry)); debug("entry point is " FMT_elf "\n", addr_fixup(ehdr.e_entry));
// Initialise saved-program-state // Initialise saved-program-state
PUSH(addr_fixup(ehdr.e_entry)); PUSH(addr_fixup(ehdr.e_entry));
feval("saved-program-state >sps.entry !"); feval("saved-program-state >sps.entry !");
PUSH(file_size); PUSH(file_size);
feval("saved-program-state >sps.file-size !"); feval("saved-program-state >sps.file-size !");
feval("elf-boot saved-program-state >sps.file-type !");
feval("-1 state-valid !"); feval("-1 state-valid !");
retval = 0;
out: out:
close_io(fd); close_io(fd);
if (phdr) if (phdr)
free(phdr); free(phdr);
if (boot_notes)
free(boot_notes);
if (image_name) if (image_name)
free(image_name); free(image_name);
if (image_version) if (image_version)

View File

@@ -1,129 +0,0 @@
/* Mac-on-Linux ELF loader
Copyright (C) 2001-2003 Samuel Rydh
adapted from yaboot
Copyright (C) 1999 Benjamin Herrenschmidt
portions based on poof
Copyright (C) 1999 Marius Vollmer
portions based on quik
Copyright (C) 1996 Paul Mackerras.
Because this program is derived from the corresponding file in the
silo-0.64 distribution, it is also
Copyright (C) 1996 Pete A. Zaitcev
1996 Maurizio Plaza
1996 David S. Miller
1996 Miguel de Icaza
1996 Jakub Jelinek
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#include "config.h"
#include "libopenbios/bindings.h"
#include "libopenbios/elfload.h"
#include "libc/diskio.h"
#include "arch/common/elf.h"
#define DEBUG 0
#define MAX_HEADERS 32
#define BS 0x100 /* smallest step used when looking for the ELF header */
int
find_elf( int fd )
{
int size, offs;
seek_io( fd, -1 );
size = tell( fd );
if( size > 0x10000 )
size = 0x10000;
for( offs=0; offs < size; offs+= BS )
if( is_elf(fd, offs) )
return offs;
return -1;
}
int
is_elf( int fd, int offs )
{
Elf_ehdr e;
seek_io( fd, offs );
if( read_io(fd, &e, sizeof(e)) != sizeof(e) ) {
printk("\nCan't read ELF image header\n");
return 0;
}
return (e.e_ident[EI_MAG0] == ELFMAG0 &&
e.e_ident[EI_MAG1] == ELFMAG1 &&
e.e_ident[EI_MAG2] == ELFMAG2 &&
e.e_ident[EI_MAG3] == ELFMAG3 &&
e.e_ident[EI_CLASS] == ARCH_ELF_CLASS &&
e.e_ident[EI_DATA] == ARCH_ELF_DATA &&
e.e_type == ET_EXEC &&
ARCH_ELF_MACHINE_OK(e.e_machine));
}
Elf_phdr *
elf_readhdrs( int fd, int offs, Elf_ehdr *e )
{
int size;
Elf_phdr *ph;
if( !is_elf(fd, offs) ) {
printk("Not an ELF image\n");
return NULL;
}
seek_io( fd, offs );
if( read_io(fd, e, sizeof(*e)) != sizeof(*e) ) {
printk("\nCan't read ELF image header\n");
return NULL;
}
#if DEBUG
printk("ELF header:\n");
printk(" e.e_type = %d\n", (int)e->e_type);
printk(" e.e_machine = %d\n", (int)e->e_machine);
printk(" e.e_version = %d\n", (int)e->e_version);
printk(" e.e_entry = 0x%08x\n", (int)e->e_entry);
printk(" e.e_phoff = 0x%08x\n", (int)e->e_phoff);
printk(" e.e_shoff = 0x%08x\n", (int)e->e_shoff);
printk(" e.e_flags = %d\n", (int)e->e_flags);
printk(" e.e_ehsize = 0x%08x\n", (int)e->e_ehsize);
printk(" e.e_phentsize = 0x%08x\n", (int)e->e_phentsize);
printk(" e.e_phnum = %d\n", (int)e->e_phnum);
#endif
if (e->e_phnum > MAX_HEADERS) {
printk ("elfload: too many program headers (MAX_HEADERS)\n");
return NULL;
}
size = sizeof(Elf_phdr) * e->e_phnum;
if( !(ph=(Elf_phdr *)malloc(size)) ) {
printk("malloc error\n");
return NULL;
}
/* Now, we read the section header */
seek_io( fd, offs+e->e_phoff );
if( read_io(fd, (char*)ph, size) != size ) {
printk("read error");
free( ph );
return NULL;
}
return ph;
}