/* * 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" static char argv_rd_start[32]; static char argv_rd_size[32]; unsigned long max_page_size = 0; static int is64=0; typedef union { unsigned char e_ident[EI_NIDENT]; Elf32_Ehdr header32; Elf64_Ehdr header64; } Elf_Ehdr; 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(max_page_size == 0) { max_page_size = segment->p_offset; } 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); prom_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); prom_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; unsigned long size; is64=1; printf("Loading 64-bit executable\n\r"); for (idx = 0; idx < header->header64.e_phnum; idx++) { if(max_page_size == 0) { max_page_size = segment->p_offset; } 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); prom_fatal("Cannot seek to program segment\n\r"); } arc_do_progress = 1; status = ext2fs_file_read(file, (void *) (KSEG0ADDR((unsigned long) segment->p_vaddr)), segment->p_filesz, NULL); arc_do_progress = 0; if (status != 0) { print_ext2fs_error(status); prom_fatal("Cannot read program segment\n\r"); } size = (unsigned long)segment->p_memsz - (unsigned long)segment->p_filesz; if (size > 0) { printf ("Zeroing memory at 0x%lx, size = 0x%lx\n\r", (KSEG0ADDR((unsigned long)segment->p_vaddr + (unsigned long)segment->p_filesz)), size); memset((void *) (KSEG0ADDR((unsigned long)segment->p_vaddr + (unsigned long)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 loaded = 0; 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) prom_fatal("No program segments\n\r"); segments = malloc(size); if (segments == NULL) prom_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); prom_fatal("Cannot seek to program segment headers\n\r"); } status = ext2fs_file_read(file, segments, size, NULL); if (status != 0) { print_ext2fs_error(status); prom_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) prom_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); prom_fatal("Can't read file header\n\r"); } if (memcmp(&(header.e_ident[EI_MAG0]), ELFMAG, SELFMAG) != 0) prom_fatal("Not an ELF file\n\r"); if (header.e_ident[EI_CLASS] != ELFCLASS32 && header.e_ident[EI_CLASS] != ELFCLASS64) prom_fatal("Not a 32-bit or 64-bit file\n\r"); if (header.e_ident[EI_DATA] != ELFDATA2MSB) prom_fatal("Not a big-endian file\n\r"); if (header.e_ident[EI_VERSION] != EV_CURRENT) prom_fatal("Wrong ELF version\n\r"); if (header.e_ident[EI_CLASS]==ELFCLASS32) { if (header.header32.e_type != ET_EXEC) prom_fatal("Not an executable file\n\r"); if (header.header32.e_machine != EM_MIPS) prom_fatal("Unsupported machine type\n\r"); if (header.header32.e_version != EV_CURRENT) prom_fatal("Wrong ELF version\n\r"); entry = (Elf64_Addr) header.header32.e_entry; } else { if (header.header64.e_type != ET_EXEC) prom_fatal("Not an executable file\n\r"); if (header.header64.e_machine != EM_MIPS) prom_fatal("Unsupported machine type\n\r"); if (header.header64.e_version != EV_CURRENT) prom_fatal("Wrong ELF version\n\r"); entry = header.header64.e_entry; } LoadProgramSegments(file, &header); return entry; } int 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 0; } status = ext2fs_namei_follow (fs, EXT2_ROOT_INO, EXT2_ROOT_INO, filename, &file_inode); if (status != 0) { print_ext2fs_error(status); return 0; } status = ext2fs_file_open(fs, file_inode, 0, file); if (status != 0) { print_ext2fs_error(status); return 0; } return 1; } Elf64_Addr LoadKernel(const char *partition, const char *filename) { ext2_file_t file; if(!OpenFile( partition, filename, &file )) prom_fatal("Can't load kernel!\n\r"); return LoadKernelFile(file); } void LoadInitrd(const char *partition, const char *filename) { ext2_file_t file; unsigned long initrd_addr, initrd_sz; int status; if (!OpenFile(partition, filename, &file)) prom_fatal("Can't load initrd!\n\r"); initrd_sz = ext2fs_file_get_size(file); initrd_addr = (unsigned long)malloc(initrd_sz + max_page_size); if (initrd_addr == 0) { prom_fatal("Cannot allocate memory for initrd\n\r"); } initrd_addr = (initrd_addr + max_page_size) & ~(max_page_size - 1); printf("Loading initrd at 0x%lx, %lu bytes...\n\r", initrd_addr, initrd_sz); arc_do_progress = 1; status = ext2fs_file_read(file, (void*) initrd_addr, initrd_sz, NULL); arc_do_progress = 0; if (status != 0) { print_ext2fs_error(status); prom_fatal("Cannot read initrd\n\r"); } /* Add rd_start=, rd_size= */ sprintf(argv_rd_start, "rd_start=0x%lx", initrd_addr); sprintf(argv_rd_size, "rd_size=0x%lx", initrd_sz); prom_add_arg(argv_rd_start); prom_add_arg(argv_rd_size); } 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(%0)\n" "\tmove $4, %1\n" "\tmove $5, %2\n" "\tjr $1\n" "\t nop\n" "\t.set pop": : "r" (addr), "r" (argc), "r" (argv) : "$4", "$5"); } void _start(int argc, char *argv[], char *envp[]) { char **nargv; char **params; char *bootpartition, *label, *kernelfile=NULL, *initrdfile=NULL; int nargc, nopt; Elf32_Addr kernel_entry32; Elf64_Addr kernel_entry64; prom_init(); prom_init_malloc(); /* Print identification */ printf(ANSI_CLEAR "\n\rarcsboot: ARCS Linux ext2fs loader " __ARCSBOOT_VERSION__ "\n\n\r"); prom_parse_args(argc, argv); label=prom_get_label(); bootpartition=prom_get_partition(); if (!bootpartition) prom_fatal("No partition to boot\n\r"); #if 1 printf("label: %s\n\r", label); printf("bootpartition: %s\n\r", bootpartition); #endif params=ReadConfFile(&bootpartition, CONF_FILE, label); if(!params) { printf("Couldn't find label: %s in %s.\n\r", label, CONF_FILE); printf("Will try to boot %s%s.\n\r", bootpartition, label); kernelfile=label; } else { int i; initrdfile=params[1]; kernelfile=params[2]; /* Add contents of config file to kernel arg line */ for(i=3;params[i];i++) { prom_add_arg(params[i]); } } printf("Loading %s from %s\n\r",kernelfile, bootpartition); kernel_entry64 = LoadKernel(bootpartition, kernelfile); kernel_entry32 = (Elf32_Addr) kernel_entry64; if(initrdfile) { printf("Loading initrd %s from %s\n\r", initrdfile, bootpartition); LoadInitrd(bootpartition, initrdfile); } if(kernel_entry64) { int kargc; char **kargv; prom_get_karg(&kargc, &kargv); if(is64==0) { printf("Starting ELF32 kernel\n\r"); prom_flush_cache_all(); ((void (*)(int argc, char *argv[], char *envp[])) kernel_entry32)(kargc ,kargv, envp); } else { printf("Starting ELF64 kernel\n\r"); prom_flush_cache_all(); _start64(kargc, kargv, 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 */ prom_wait("\n\r--- Press to restart ---"); prom_restart(); }