mirror of
https://gitlab.com/qemu-project/openbios.git
synced 2024-02-13 08:34:06 +08:00
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:
committed by
Mark Cave-Ayland
parent
6dd0574272
commit
1264ad95d2
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"/>
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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"/>
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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"/>
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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 */
|
||||||
@@ -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"/>
|
||||||
|
|||||||
@@ -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;
|
|
||||||
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;
|
retval = LOADER_NOT_SUPPORT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
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
|
||||||
|
|
||||||
|
if (ehdr.e_phnum > MAX_HEADERS) {
|
||||||
|
printk ("elfload: too many program headers (MAX_HEADERS)\n");
|
||||||
|
retval = 0;
|
||||||
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);
|
goto out;
|
||||||
if ((uint32_t)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))
|
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)
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user