From d1e063beb43e595680c65e3804d1f8ddff53373b Mon Sep 17 00:00:00 2001 From: Guido Guenther Date: Sat, 18 Nov 2006 23:48:07 +0100 Subject: Imported Debian version 0.3.8.8 --- ext2load/loader.c | 564 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 564 insertions(+) create mode 100644 ext2load/loader.c (limited to 'ext2load/loader.c') diff --git a/ext2load/loader.c b/ext2load/loader.c new file mode 100644 index 0000000..45d1988 --- /dev/null +++ b/ext2load/loader.c @@ -0,0 +1,564 @@ +/* + * Copyright 1999, 2001 Silicon Graphics, Inc. + * Copyright 2001 Ralf Baechle + * 2001-04 Guido Guenther + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "arcboot.h" + +#include + +#define KSEG0ADDR(addr) (((addr) & 0x1fffffff) | 0x80000000) + +#define ANSI_CLEAR "\033[2J" +#define CONF_FILE "/etc/arcboot.conf" + +CHAR *OSLoadPartition = NULL; +CHAR *OSLoadFilename = NULL; +CHAR *OSLoadOptions = NULL; +static int is64=0; + + +typedef union { + unsigned char e_ident[EI_NIDENT]; + Elf32_Ehdr header32; + Elf64_Ehdr header64; +} Elf_Ehdr; + +static void Wait(const char *prompt) +{ + int ch; + + if (prompt != NULL) + puts(prompt); + + do { + ch = getchar(); + } while ((ch != EOF) && (((char) ch) != ' ')); +} + + +void Fatal(const CHAR * message, ...) +{ + va_list ap; + + if (message != NULL) { + printf("FATAL ERROR: "); + va_start(ap, message); + vprintf(message, ap); + va_end(ap); + } + + Wait("\n\r--- Press to enter ARC interactive mode ---"); + ArcEnterInteractiveMode(); +} + + +void InitMalloc(void) +{ + MEMORYDESCRIPTOR *current = NULL; + ULONG stack = (ULONG) ¤t; +#ifdef DEBUG + printf("stack starts at: 0x%lx\n\r", stack); +#endif + + current = ArcGetMemoryDescriptor(current); + if(! current ) { + Fatal("Can't find any valid memory descriptors!\n\r"); + } + while (current != NULL) { + /* + * The spec says we should have an adjacent FreeContiguous + * memory area that includes our stack. It would be much + * easier to just look for that and give it to malloc, but + * the Indy only shows FreeMemory areas, no FreeContiguous. + * Oh well. + */ + if (current->Type == FreeMemory) { + ULONG start = KSEG0ADDR(current->BasePage * PAGE_SIZE); + ULONG end = + start + (current->PageCount * PAGE_SIZE); +#if DEBUG + printf("Free Memory(%u) segment found at (0x%lx,0x%lx).\n\r", + current->Type, start, end); +#endif + + /* Leave some space for our stack */ + if ((stack >= start) && (stack < end)) + end = + (stack - + (STACK_PAGES * + PAGE_SIZE)) & ~(PAGE_SIZE - 1); + /* Don't use memory from reserved region */ + if ((start >= kernel_load[SUBARCH].base ) + && (start < (kernel_load[SUBARCH].base + + kernel_load[SUBARCH].reserved ))) + start = kernel_load[SUBARCH].base + + kernel_load[SUBARCH].reserved; + if ((end > kernel_load[SUBARCH].base) + && (end <= (kernel_load[SUBARCH].base + + kernel_load[SUBARCH].reserved ))) + end = kernel_load[SUBARCH].base; + if (end > start) { +#ifdef DEBUG + printf("Adding %lu bytes at 0x%lx to the list of available memory\n\r", + end-start, start); +#endif + arclib_malloc_add(start, end - start); + } + } + current = ArcGetMemoryDescriptor(current); + } +} + +int isEnvVar(const char* arg) +{ + unsigned int i; + + for (i = 0; i < NENTS(env_vars); i++) { + if(strncmp( env_vars[i], arg, strlen(env_vars[i]) ) == 0) + return 1; + } + return 0; +} + +int ProcessArguments(LONG argc, CHAR * argv[]) +{ + LONG arg; + CHAR *equals; + size_t len; + + /* save some things we need later */ + for (arg = 1; arg < argc; arg++) { + equals = strchr(argv[arg], '='); + if (equals != NULL) { + len = equals - argv[arg]; + if (strncmp(argv[arg], "OSLoadPartition", len) == 0) + OSLoadPartition = equals + 1; + if (strncmp(argv[arg], "OSLoadFilename", len) == 0) + OSLoadFilename = equals + 1; + if (strncmp(argv[arg], "OSLoadOptions", len) == 0) { + /* Copy options to local memory to avoid overwrite later */ + OSLoadOptions = strdup(equals + 1); + if (OSLoadOptions == NULL) + Fatal ("Cannot allocate memory for options string\n\r"); + } + } + } + /* + * in case the user typed "boot a b c" the argv looks like: + * scsi(0)..(8)/arcboot a b c OSLoadPartition=.. SystemPartition=.. + * argv: `0 `-1`-2`-3`-4 `-5 + * we're interested in a,b,c so scan the command line and check for + * each argument if it is an environment variable. We're using a fixed + * list instead of ArcGetEnvironmentVariable since e.g. the prom sets + * "OSLoadOptions=auto" on reboot but + * EnvironmentVariable("OSLoadOptions") == NULL + */ + for( arg = 1; arg < argc; arg++ ) { + if( isEnvVar(argv[arg])) { + return (arg-1); +#ifdef DEBUG + } else { + printf("%s is not an envVar\n\r", argv[arg]); +#endif + } + } + return 0; +} + +int LoadProgramSegments32(ext2_file_t file, Elf_Ehdr * header, void *segments) +{ + int idx; + int loaded = 0; + Elf32_Phdr* segment=(Elf32_Phdr*)segments; + errcode_t status; + size_t size; + + printf("Loading 32-bit executable\n\r"); + + for (idx = 0; idx < header->header32.e_phnum; idx++) { + if (segment->p_type == PT_LOAD) { + printf + ("Loading program segment %u at 0x%x, offset=0x%x, size = 0x%x\n\r", + idx + 1, KSEG0ADDR(segment->p_vaddr), + segment->p_offset, segment->p_filesz); + + status = + ext2fs_file_lseek(file, segment->p_offset, + EXT2_SEEK_SET, NULL); + if (status != 0) { + print_ext2fs_error(status); + Fatal("Cannot seek to program segment\n\r"); + } + + arc_do_progress = 1; + status = ext2fs_file_read(file, + (void *) (KSEG0ADDR( + segment->p_vaddr)), + segment->p_filesz, NULL); + printf("\n\n\r"); /* Clear progress */ + arc_do_progress = 0; + if (status != 0) { + print_ext2fs_error(status); + Fatal("Cannot read program segment\n\r"); + } + + size = segment->p_memsz - segment->p_filesz; + if (size > 0) { + printf + ("Zeroing memory at 0x%x, size = 0x%x\n\r", + (KSEG0ADDR(segment->p_vaddr + + segment->p_filesz)), size); + memset((void *) + (KSEG0ADDR(segment-> + p_vaddr + segment->p_filesz)), 0, size); + } + + loaded = 1; + } + + segment = + (Elf32_Phdr *) (((char *) segment) + + header->header32.e_phentsize); + } + + return loaded; +} + +int LoadProgramSegments64(ext2_file_t file, Elf_Ehdr * header, void *segments) +{ + int idx; + int loaded = 0; + Elf64_Phdr* segment=(Elf64_Phdr*)segments; + errcode_t status; + size_t size; + + is64=1; + printf("Loading 64-bit executable\n\r"); + + for (idx = 0; idx < header->header64.e_phnum; idx++) { + if (segment->p_type == PT_LOAD) { + printf ("Loading program segment %u at 0x%x, " + "offset=0x%lx %lx, size = 0x%lx %lx\n\r", + idx + 1, + (int)KSEG0ADDR(segment->p_vaddr), + (long)(segment->p_offset>>32), + (long)(segment->p_offset&0xffffffff), + (long)(segment->p_filesz>>32), + (long)(segment->p_filesz&0xffffffff)); + + status = + ext2fs_file_lseek(file, segment->p_offset, + EXT2_SEEK_SET, NULL); + if (status != 0) { + print_ext2fs_error(status); + Fatal("Cannot seek to program segment\n\r"); + } + + arc_do_progress = 1; + status = ext2fs_file_read(file, + (void *) (KSEG0ADDR( + segment->p_vaddr)), + segment->p_filesz, NULL); + arc_do_progress = 0; + if (status != 0) { + print_ext2fs_error(status); + Fatal("Cannot read program segment\n\r"); + } + + size = segment->p_memsz - segment->p_filesz; + if (size > 0) { + printf + ("Zeroing memory at 0x%lx, size = 0x%lx\n\r", + (KSEG0ADDR(segment->p_vaddr + + segment->p_filesz)), size); + memset((void *) + (KSEG0ADDR(segment-> + p_vaddr + segment->p_filesz)), 0, size); + } + + loaded = 1; + } + + segment = (Elf64_Phdr *) (((char *) segment) + + header->header64.e_phentsize); + } + + return loaded; +} + +void LoadProgramSegments(ext2_file_t file, Elf_Ehdr * header) +{ + int idx; + Boolean loaded = False; + void *segments; + size_t size; + errcode_t status; + + if (header->e_ident[EI_CLASS] == ELFCLASS32) + size = (size_t) (header->header32.e_phentsize * + header->header32.e_phnum); + else + size = (size_t) (header->header64.e_phentsize * + header->header64.e_phnum); + + if (size <= 0) + Fatal("No program segments\n\r"); + + segments = malloc(size); + if (segments == NULL) + Fatal("Cannot allocate memory for segment headers\n\r"); + else + printf("Allocated 0x%x bytes for segments\n\r",size); + + if (header->e_ident[EI_CLASS] == ELFCLASS32) { + status = ext2fs_file_lseek(file, + (ext2_off_t) header->header32.e_phoff, + EXT2_SEEK_SET, NULL); + } else { + status = ext2fs_file_lseek(file, + (ext2_off_t) header->header64.e_phoff, + EXT2_SEEK_SET, NULL); + } + if (status != 0) { + print_ext2fs_error(status); + Fatal("Cannot seek to program segment headers\n\r"); + } + + status = ext2fs_file_read(file, segments, size, NULL); + if (status != 0) { + print_ext2fs_error(status); + Fatal("Cannot read program segment headers\n\r"); + } + + if(header->e_ident[EI_CLASS] == ELFCLASS32) + loaded = LoadProgramSegments32(file, header, segments); + else + loaded = LoadProgramSegments64(file, header, segments); + + if (!loaded) + Fatal("No loadable program segments found\n\r"); + + free(segments); +} + + +Elf64_Addr LoadKernelFile(ext2_file_t file) +{ + Elf_Ehdr header; + Elf64_Addr entry; + errcode_t status; + + status = + ext2fs_file_read(file, (void *) &header, sizeof(header), NULL); + if (status != 0) { + print_ext2fs_error(status); + Fatal("Can't read file header\n\r"); + } + + if (memcmp(&(header.e_ident[EI_MAG0]), ELFMAG, SELFMAG) != 0) + Fatal("Not an ELF file\n\r"); + + if (header.e_ident[EI_CLASS] != ELFCLASS32 && + header.e_ident[EI_CLASS] != ELFCLASS64) + Fatal("Not a 32-bit or 64-bit file\n\r"); + + if (header.e_ident[EI_DATA] != ELFDATA2MSB) + Fatal("Not a big-endian file\n\r"); + + if (header.e_ident[EI_VERSION] != EV_CURRENT) + Fatal("Wrong ELF version\n\r"); + + if (header.e_ident[EI_CLASS]==ELFCLASS32) { + if (header.header32.e_type != ET_EXEC) + Fatal("Not an executable file\n\r"); + + if (header.header32.e_machine != EM_MIPS) + Fatal("Unsupported machine type\n\r"); + + if (header.header32.e_version != EV_CURRENT) + Fatal("Wrong ELF version\n\r"); + + entry = (Elf64_Addr) header.header32.e_entry; + } else { + if (header.header64.e_type != ET_EXEC) + Fatal("Not an executable file\n\r"); + + if (header.header64.e_machine != EM_MIPS) + Fatal("Unsupported machine type\n\r"); + + if (header.header64.e_version != EV_CURRENT) + Fatal("Wrong ELF version\n\r"); + + entry = header.header64.e_entry; + } + + LoadProgramSegments(file, &header); + + return entry; +} + + +Boolean OpenFile(const char *partition, const char *filename, ext2_file_t* file) +{ + extern io_manager arc_io_manager; + ext2_filsys fs; + ext2_ino_t file_inode; + errcode_t status; + + initialize_ext2_error_table(); + + status = ext2fs_open(partition, 0, 0, 0, arc_io_manager, &fs); + if (status != 0) { + print_ext2fs_error(status); + return False; + } + + status = ext2fs_namei_follow + (fs, EXT2_ROOT_INO, EXT2_ROOT_INO, filename, &file_inode); + if (status != 0) { + print_ext2fs_error(status); + return False; + } + + status = ext2fs_file_open(fs, file_inode, 0, file); + if (status != 0) { + print_ext2fs_error(status); + return False; + } + return True; +} + +Elf64_Addr LoadKernel(const char *partition, const char *filename) +{ + ext2_file_t file; + + if(!OpenFile( partition, filename, &file )) + Fatal("Can't load kernel!\n\r"); + return LoadKernelFile(file); +} + +void printCmdLine(int argc, CHAR *argv[]) +{ + int i; + + for(i = 0; i < argc; i++ ) + printf("%u: %s\n\r", i, argv[i]); +} + +void _start64(LONG argc, CHAR * argv[], CHAR * envp[], + unsigned long long *addr) +{ + __asm__ __volatile__( + ".set push\n" + "\t.set mips3\n" + "\t.set noreorder\n" + "\t.set noat\n" + "\tld $1, 0($7)\n" + "\tjr $1\n" + "\t nop\n" + "\t.set pop"); +} + +void _start(LONG argc, CHAR *argv[], CHAR *envp[]) +{ + CHAR** nargv; + CHAR** params; + int nargc, nopt; + + Elf32_Addr kernel_entry32; + Elf64_Addr kernel_entry64; + + /* Print identification */ + printf(ANSI_CLEAR "\n\rarcsboot: ARCS Linux ext2fs loader " + __ARCSBOOT_VERSION__ "\n\n\r"); + + InitMalloc(); + + nopt = ProcessArguments(argc, argv); + +#if DEBUG + printf("Command line: \n\r"); + printCmdLine(argc, argv); +#endif + if (nopt) { /* the user typed s.th. on the commandline */ + OSLoadFilename = argv[1]; + } + + /* Fall back to "Linux" as default name. */ + if (OSLoadFilename == NULL) + OSLoadFilename = "Linux"; + + if (OSLoadPartition == NULL) + Fatal("Invalid load partition\n\r"); +#if DEBUG + printf("OSLoadPartition: %s\n\r", OSLoadPartition); + printf("OSLoadFilename: %s\n\r", OSLoadFilename); +#endif + /* + * XXX: let's play stupid for now: assume /etc/arcboot.conf + * is on OSLoadPartition + */ + if( !(params = ReadConfFile(&OSLoadPartition, CONF_FILE, OSLoadFilename))) { + printf("Couldn't find label: %s in %s.\n\r", OSLoadFilename, CONF_FILE); + printf("Will try to boot %s%s.\n\r", OSLoadPartition, OSLoadFilename); + nargc = argc; + nargv = argv; + } else { + int i; + OSLoadFilename = params[1]; + nargv = ¶ms[1]; /* nargv[0] is the labels name */ + for( nargc=0; nargv[nargc]; nargc++); /* count nargv argumnts */ + if(OSLoadOptions != NULL) { /* append OSLoadOptions if present */ + nargv[nargc] = OSLoadOptions; + nargc++; + } + /* append command line arguments */ + for(i = 2; i <= nopt; i++) { + nargv[nargc] = argv[i]; + nargc++; + } + } + printf("Loading %s from %s\n\r",(params) ? params[0] : OSLoadFilename, OSLoadPartition); + kernel_entry64 = LoadKernel(OSLoadPartition, OSLoadFilename); + kernel_entry32 = (Elf32_Addr) kernel_entry64; +#if DEBUG + printf("Command line after config file: \n\r"); + printCmdLine(nargc, nargv); + printf("Kernel entry: 0x%lx %lx\n\r", + (long)(kernel_entry64>>32),(long)(kernel_entry64&0xffffffff)); + Wait("\n\r--- Debug: press to boot kernel ---"); +#endif + if( kernel_entry64 ) { + if(is64==0){ + printf("Starting ELF32 kernel\n\r"); + ArcFlushAllCaches(); + ((void (*)(int argc, CHAR * argv[], CHAR * envp[])) + kernel_entry32)(nargc ,nargv, envp); + } else { + printf("Starting ELF64 kernel\n\r"); + ArcFlushAllCaches(); + _start64(nargc, nargv, envp, &kernel_entry64); + } + } else { + printf("Invalid kernel entry NULL\n\r"); + } + + /* Not likely to get back here in a functional state, but what the heck */ + Wait("\n\r--- Press to restart ---"); + ArcRestart(); +} -- cgit v1.2.3