239 lines
5.7 KiB
C
239 lines
5.7 KiB
C
/*
|
|
*
|
|
* <xcoff_load.c>
|
|
*
|
|
* XCOFF file loader
|
|
*
|
|
* Copyright (C) 2009 Laurent Vivier (Laurent@vivier.eu)
|
|
*
|
|
* from original XCOFF loader by Steven Noonan <steven@uplinklabs.net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* version 2
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "libopenbios/bindings.h"
|
|
#include "libopenbios/initprogram.h"
|
|
#include "libopenbios/xcoff_load.h"
|
|
#include "libc/diskio.h"
|
|
|
|
#include "arch/common/xcoff.h"
|
|
|
|
#ifdef CONFIG_PPC
|
|
extern void flush_icache_range( char *start, char *stop );
|
|
#endif
|
|
|
|
//#define DEBUG_XCOFF
|
|
|
|
#ifdef DEBUG_XCOFF
|
|
#define DPRINTF(fmt, args...) \
|
|
do { printk("%s: " fmt, __func__ , ##args); } while (0)
|
|
#else
|
|
#define DPRINTF(fmt, args...) \
|
|
do { } while (0)
|
|
#endif
|
|
|
|
int
|
|
is_xcoff(COFF_filehdr_t *fhdr)
|
|
{
|
|
return (fhdr->f_magic == U802WRMAGIC
|
|
|| fhdr->f_magic == U802ROMAGIC
|
|
|| fhdr->f_magic == U802TOCMAGIC
|
|
|| fhdr->f_magic == U802TOMAGIC);
|
|
}
|
|
|
|
int
|
|
xcoff_load(ihandle_t dev)
|
|
{
|
|
COFF_filehdr_t fhdr;
|
|
COFF_aouthdr_t ahdr;
|
|
COFF_scnhdr_t shdr;
|
|
uint32_t offset;
|
|
size_t total_size = 0;
|
|
int fd, i;
|
|
int retval = -1;
|
|
|
|
/* Mark the saved-program-state as invalid */
|
|
feval("0 state-valid !");
|
|
|
|
fd = open_ih(dev);
|
|
if (fd == -1) {
|
|
retval = LOADER_NOT_SUPPORT;
|
|
goto out;
|
|
}
|
|
|
|
for (offset = 0; offset < 16 * 512; offset += 512) {
|
|
seek_io(fd, offset);
|
|
if (read_io(fd, &fhdr, sizeof fhdr) != sizeof fhdr) {
|
|
DPRINTF("Can't read XCOFF header\n");
|
|
retval = LOADER_NOT_SUPPORT;
|
|
goto out;
|
|
}
|
|
|
|
if (is_xcoff(&fhdr))
|
|
break;
|
|
}
|
|
|
|
/* Is it executable ? */
|
|
if (fhdr.f_magic != 0x01DF &&
|
|
(fhdr.f_flags & COFF_F_EXEC) == 0) {
|
|
DPRINTF("Not an executable XCOFF file %02x\n", fhdr.f_flags);
|
|
return LOADER_NOT_SUPPORT;
|
|
}
|
|
|
|
/* Optional header is a.out ? */
|
|
if (fhdr.f_opthdr != sizeof(COFF_aouthdr_t)) {
|
|
DPRINTF("AOUT optional error size mismatch in XCOFF file\n");
|
|
return LOADER_NOT_SUPPORT;
|
|
}
|
|
|
|
seek_io(fd, sizeof(COFF_filehdr_t));
|
|
read_io(fd, &ahdr, sizeof(COFF_aouthdr_t));
|
|
|
|
/* check a.out magic number */
|
|
if (ahdr.magic != AOUT_MAGIC) {
|
|
DPRINTF("Invalid AOUT optional header\n");
|
|
return LOADER_NOT_SUPPORT;
|
|
}
|
|
|
|
offset = sizeof(COFF_filehdr_t) + sizeof(COFF_aouthdr_t);
|
|
|
|
DPRINTF("XCOFF file with %d sections\n", fhdr.f_nscns);
|
|
|
|
for (i = 0; i < fhdr.f_nscns; i++) {
|
|
DPRINTF("Read header at offset %0x\n", offset);
|
|
seek_io(fd, offset);
|
|
read_io(fd, &shdr, sizeof(COFF_scnhdr_t));
|
|
|
|
DPRINTF("Initializing '%s' section from %0x %0x to %0x (%0x)\n",
|
|
shdr.s_name, offset, shdr.s_scnptr,
|
|
shdr.s_vaddr, shdr.s_size);
|
|
|
|
if (strcmp(shdr.s_name, ".text") == 0) {
|
|
read_io(fd, (void *)shdr.s_vaddr, shdr.s_size);
|
|
total_size += shdr.s_size;
|
|
#ifdef CONFIG_PPC
|
|
flush_icache_range((char*)(uintptr_t)shdr.s_vaddr,
|
|
(char*)(uintptr_t)(shdr.s_vaddr + shdr.s_size));
|
|
#endif
|
|
} else if (strcmp(shdr.s_name, ".data") == 0) {
|
|
read_io(fd, (void *)shdr.s_vaddr, shdr.s_size);
|
|
total_size += shdr.s_size;
|
|
|
|
} else if (strcmp(shdr.s_name, ".bss") == 0) {
|
|
memset((void *)(uintptr_t)shdr.s_vaddr, 0, shdr.s_size);
|
|
total_size += shdr.s_size;
|
|
} else {
|
|
DPRINTF(" Skip '%s' section\n", shdr.s_name);
|
|
}
|
|
offset += sizeof(COFF_scnhdr_t);
|
|
}
|
|
|
|
DPRINTF("XCOFF entry point: %x\n", *(uint32_t*)ahdr.entry);
|
|
|
|
// Initialise load-state
|
|
PUSH(total_size);
|
|
feval("load-state >ls.file-size !");
|
|
feval("xcoff load-state >ls.file-type !");
|
|
|
|
out:
|
|
close_io(fd);
|
|
return retval;
|
|
}
|
|
|
|
void
|
|
xcoff_init_program(void)
|
|
{
|
|
char *base;
|
|
COFF_filehdr_t *fhdr;
|
|
COFF_aouthdr_t *ahdr;
|
|
COFF_scnhdr_t *shdr;
|
|
uint32_t offset;
|
|
size_t total_size = 0;
|
|
int i;
|
|
|
|
feval("load-base");
|
|
base = (char*)cell2pointer(POP());
|
|
|
|
fhdr = (COFF_filehdr_t*)base;
|
|
|
|
/* Is it an XCOFF file ? */
|
|
if (!is_xcoff(fhdr)) {
|
|
DPRINTF("Not a XCOFF file %02x\n", fhdr->f_magic);
|
|
return;
|
|
}
|
|
|
|
/* Is it executable ? */
|
|
if (fhdr->f_magic != 0x01DF &&
|
|
(fhdr->f_flags & COFF_F_EXEC) == 0) {
|
|
DPRINTF("Not an executable XCOFF file %02x\n", fhdr->f_flags);
|
|
return;
|
|
}
|
|
|
|
/* Optional header is a.out ? */
|
|
if (fhdr->f_opthdr != sizeof(COFF_aouthdr_t)) {
|
|
DPRINTF("AOUT optional error size mismatch in XCOFF file\n");
|
|
return;
|
|
}
|
|
|
|
ahdr = (COFF_aouthdr_t*)(base + sizeof(COFF_filehdr_t));
|
|
|
|
/* check a.out magic number */
|
|
if (ahdr->magic != AOUT_MAGIC) {
|
|
DPRINTF("Invalid AOUT optional header\n");
|
|
return;
|
|
}
|
|
|
|
offset = sizeof(COFF_filehdr_t) + sizeof(COFF_aouthdr_t);
|
|
|
|
DPRINTF("XCOFF file with %d sections\n", fhdr->f_nscns);
|
|
|
|
for (i = 0; i < fhdr->f_nscns; i++) {
|
|
|
|
DPRINTF("Read header at offset %0x\n", offset);
|
|
|
|
shdr = (COFF_scnhdr_t*)(base + offset);
|
|
|
|
DPRINTF("Initializing '%s' section from %0x %0x to %0x (%0x)\n",
|
|
shdr->s_name, offset, shdr->s_scnptr,
|
|
shdr->s_vaddr, shdr->s_size);
|
|
|
|
if (strcmp(shdr->s_name, ".text") == 0) {
|
|
|
|
memcpy((char*)(uintptr_t)shdr->s_vaddr, base + shdr->s_scnptr,
|
|
shdr->s_size);
|
|
total_size += shdr->s_size;
|
|
#ifdef CONFIG_PPC
|
|
flush_icache_range((char*)(uintptr_t)shdr->s_vaddr,
|
|
(char*)(uintptr_t)(shdr->s_vaddr + shdr->s_size));
|
|
#endif
|
|
} else if (strcmp(shdr->s_name, ".data") == 0) {
|
|
|
|
memcpy((char*)(uintptr_t)shdr->s_vaddr, base + shdr->s_scnptr,
|
|
shdr->s_size);
|
|
total_size += shdr->s_size;
|
|
|
|
} else if (strcmp(shdr->s_name, ".bss") == 0) {
|
|
|
|
memset((void *)(uintptr_t)shdr->s_vaddr, 0, shdr->s_size);
|
|
total_size += shdr->s_size;
|
|
} else {
|
|
DPRINTF(" Skip '%s' section\n", shdr->s_name);
|
|
}
|
|
offset += sizeof(COFF_scnhdr_t);
|
|
}
|
|
|
|
DPRINTF("XCOFF entry point: %x\n", *(uint32_t*)ahdr->entry);
|
|
|
|
// Initialise load-state
|
|
PUSH(*(uint32_t*)(uintptr_t)ahdr->entry);
|
|
feval("load-state >ls.entry !");
|
|
feval("xcoff load-state >ls.file-type !");
|
|
|
|
arch_init_program();
|
|
|
|
feval("-1 state-valid !");
|
|
}
|