diff options
author | Waldemar Brodkorb <wbx@openadk.org> | 2017-12-31 18:47:16 +0100 |
---|---|---|
committer | Waldemar Brodkorb <wbx@openadk.org> | 2017-12-31 18:47:25 +0100 |
commit | 3a96085b999220c4da0c5ef7d1f7ba26b9ddfb98 (patch) | |
tree | 77f1445aae2e6be5135594e95986b3278bbc061c /package/aboot/src/aboot.c | |
parent | cc28479164b8dc8afd4310716da32f16022f5974 (diff) |
dec-multia: make netboot possible, add aboot bootloader
Diffstat (limited to 'package/aboot/src/aboot.c')
-rw-r--r-- | package/aboot/src/aboot.c | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/package/aboot/src/aboot.c b/package/aboot/src/aboot.c new file mode 100644 index 000000000..90614a72c --- /dev/null +++ b/package/aboot/src/aboot.c @@ -0,0 +1,289 @@ +/* + * aboot.c + * + * This file is part of aboot, the SRM bootloader for Linux/Alpha + * Copyright (C) 1996 Linus Torvalds, David Mosberger, and Michael Schwingen. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/version.h> +#include <asm/console.h> +#include "hwrpb.h" +#include "system.h" + +#include <elf.h> +#include <alloca.h> +#include <errno.h> + +#include "aboot.h" +#include "config.h" +#include "cons.h" +#include "setjmp.h" +#include "utils.h" +#include "string.h" + +struct bootfs * bfs = 0; /* filesystem to boot from */ +char * dest_addr = 0; +jmp_buf jump_buffer; + +unsigned long start_addr = START_ADDR; /* default only */ +struct segment* chunks; +int nchunks; + +char boot_file[256] = ""; +char initrd_file[256] = ""; +char kernel_args[256] = ""; +char * bss_start = 0; +long bss_size = 0; /* size of bss */ +unsigned long initrd_start = 0; +unsigned long initrd_size = 0; + +/* Offsets from start_addr (yes, they are negative, because start_addr + is actually the load address plus the text offset) */ +#define PARAM_OFFSET -0x6000 /* load address + 0xa000 */ +#define STACK_OFFSET -0xe000 /* load address + 0x2000 */ + +/* eventually we may have to deal with 44-bit physical addressing and + also possibly larger pages */ +unsigned long page_offset = 0xfffffc0000000000; +unsigned long page_shift = 13; + +static unsigned long entry_addr = START_ADDR; + +/* + * The decompression code calls this function after decompressing the + * first block of the object file. The first block must contain all + * the relevant header information. + */ +long +first_block (const char *buf, long blocksize) +{ + Elf64_Ehdr *elf; + Elf64_Phdr *phdrs; + int i, j; + + elf = (Elf64_Ehdr *) buf; + + if (elf->e_ident[0] != 0x7f + || elf->e_ident[1] != 'E' + || elf->e_ident[2] != 'L' + || elf->e_ident[3] != 'F') + { + /* Fail silently, it might be a compressed file */ + return -1; + } + if (elf->e_ident[EI_CLASS] != ELFCLASS64 + || elf->e_ident[EI_DATA] != ELFDATA2LSB + || elf->e_machine != EM_ALPHA) + { + printf("aboot: ELF executable not for this machine\n"); + return -1; + } + + /* Looks like an ELF binary. */ + if (elf->e_type != ET_EXEC) { + printf("aboot: not an executable ELF file\n"); + return -1; + } + + if (elf->e_phoff + elf->e_phnum * sizeof(*phdrs) > (unsigned) blocksize) + { + printf("aboot: " + "ELF program headers not in first block (%ld)\n", + (long) elf->e_phoff); + return -1; + } + + phdrs = (struct elf_phdr *) (buf + elf->e_phoff); + chunks = malloc(sizeof(struct segment) * elf->e_phnum); + start_addr = phdrs[0].p_vaddr; /* assume they are sorted */ + entry_addr = elf->e_entry; + +#ifdef DEBUG + printf("aboot: %d program headers, start address %#lx, entry %#lx\n", + elf->e_phnum, start_addr, entry_addr); +#endif + + for (i = j = 0; i < elf->e_phnum; ++i) { + int status; + + if (phdrs[i].p_type != PT_LOAD) + continue; + + chunks[j].addr = phdrs[i].p_vaddr; + chunks[j].offset = phdrs[i].p_offset; + chunks[j].size = phdrs[i].p_filesz; + +#ifdef DEBUG + printf("aboot: PHDR %d vaddr %#lx offset %#lx size %#lx\n", + i, chunks[j].addr, chunks[j].offset, chunks[j].size); +#endif + +#ifndef TESTING + status = check_memory(chunks[j].addr, chunks[j].size); + if (status) { + printf("aboot: Can't load kernel.\n" + " Memory at %lx - %lx (PHDR %i) " + "is %s\n", + chunks[j].addr, + chunks[j].addr + chunks[j].size - 1, + i, + (status == -ENOMEM) ? + "Not Found" : + "Busy (Reserved)"); + return -1; + } +#endif + + if (phdrs[i].p_memsz > phdrs[i].p_filesz) { + if (bss_size > 0) { + printf("aboot: Can't load kernel.\n" + " Multiple BSS segments" + " (PHDR %d)\n", i); + return -1; + } + bss_start = (char *) (phdrs[i].p_vaddr + + phdrs[i].p_filesz); + bss_size = (phdrs[i].p_memsz - phdrs[i].p_filesz); + } + + j++; + } + nchunks = j; +#ifdef DEBUG + printf("aboot: bss at 0x%p, size %#lx\n", bss_start, bss_size); +#endif + + return 0; +} + +static void +get_boot_args(void) +{ + long result; + + /* get boot command line: */ +#ifdef TESTING + const char *e; + if ((e = getenv("BOOTED_FILE"))) { + strncpy(boot_file, e, sizeof(boot_file)-1); + boot_file[sizeof(boot_file)-1] = 0; + } else { + strcpy(boot_file, "vmlinux.gz"); + } + if ((e = getenv("BOOTED_OSFLAGS"))) { + strncpy(kernel_args, e, sizeof(kernel_args)-1); + kernel_args[sizeof(kernel_args)-1] = 0; + } else { + strcpy(kernel_args, "i"); + } +#else + result = cons_getenv(ENV_BOOTED_FILE, boot_file, sizeof(boot_file)); + if (result < 0) { + printf("aboot: warning: can't get ENV_BOOTED_FILE " + "(result=%lx)!\n", result); + strcpy(boot_file, "vmlinux.gz"); + } + result = cons_getenv(ENV_BOOTED_OSFLAGS, + kernel_args, sizeof(kernel_args)); + if (result < 0) { + printf("aboot: warning: can't get ENV_BOOTED_OSFLAGS " + "(result=%lx)!\n", result); + strcpy(kernel_args, "i"); + } +#endif /* TESTING */ +} + +#ifdef TESTING +long config_file_partition = 1; +void halt() +{ + exit(0); +} + +void unzip_error(char *x) +{ + printf("unzip: %s\n", x); +} + + +int main() +{ + extern long load_kernel(); + long result; + + get_boot_args(); + result = load_kernel(); + if (result < 0) { + printf("aboot: kernel load failed (%ld)\n", result); + return 0; + } + printf("aboot: starting kernel %s with arguments %s\n", + boot_file, kernel_args); + return 0; +} +#else /* not TESTING */ +/* + * Head transfers control to this function. Don't call it main() to avoid + * gcc doing magic initialization things that we don't want. + */ +void +main_ (void) +{ + extern long load_kernel (void); + long i, result; + + cons_init(); + + printf("aboot: Linux/Alpha SRM bootloader version "ABOOT_VERSION"\n"); + + /* don't know how to deal with this yet */ + if (INIT_HWRPB->pagesize != 8192) { + printf("aboot: expected 8kB pages, got %ldkB\n", + INIT_HWRPB->pagesize >> 10); + + cons_close_console(); + return; + } + + pal_init(); + get_boot_args(); + result = load_kernel(); + if (result < 0) { + printf("aboot: kernel load failed (%ld)\n", result); + cons_close_console(); + return; + } + printf("aboot: starting kernel %s with arguments %s\n", + boot_file, kernel_args); + strcpy((char*)start_addr + PARAM_OFFSET, kernel_args); + *(unsigned long *)(start_addr + PARAM_OFFSET + 0x100) + = initrd_start; + *(unsigned long *)(start_addr + PARAM_OFFSET + 0x108) + = initrd_size; + + cons_close_console(); + run_kernel(entry_addr, start_addr + STACK_OFFSET); + + cons_open_console(); + printf("aboot: kernel returned unexpectedly. Halting slowly...\n"); + for (i = 0 ; i < 0x100000000 ; i++) + /* nothing */; + cons_close_console(); + halt(); +} +#endif /* TESTING */ |