/* * * * * XCOFF file loader * * Copyright (C) 2009 Laurent Vivier (Laurent@vivier.eu) * * from original XCOFF loader by Steven Noonan * * 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 !"); }