Logo Search packages:      
Sourcecode: aboot version File versions  Download package

aboot.c

/*
 * 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/elf.h>
#include <linux/kernel.h>
#include <linux/version.h>

#include <asm/console.h>
#include <asm/hwrpb.h>
#include <asm/system.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"

#ifndef elf_check_arch
# define aboot_elf_check_arch(e)    1
#else
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
#  define aboot_elf_check_arch(e)        elf_check_arch(e)
# else
#  define aboot_elf_check_arch(e)        elf_check_arch(e->e_machine)
# endif
#endif

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)
{
      struct elfhdr *elf;
      struct elf_phdr *phdrs;

      elf  = (struct elfhdr *) buf;
      
      if (elf->e_ident[0] == 0x7f
          && strncmp(elf->e_ident + 1, "ELF", 3) == 0)
      {
            int i;
            /* looks like an ELF binary: */
            if (elf->e_type != ET_EXEC) {
                  printf("aboot: not an executable ELF file\n");
                  return -1;
            }
            if (!aboot_elf_check_arch(elf)) {
                  printf("aboot: ELF executable not for this machine\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);
            nchunks = 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",
                   nchunks, start_addr, entry_addr);
#endif
            for (i = 0; i < elf->e_phnum; ++i) {
                  int status;

                  chunks[i].addr   = phdrs[i].p_vaddr;
                  chunks[i].offset = phdrs[i].p_offset;
                  chunks[i].size   = phdrs[i].p_filesz;
#ifdef DEBUG
                  printf("aboot: segment %d vaddr %#lx offset %#lx size %#lx\n",
                         i, chunks[i].addr, chunks[i].offset, chunks[i].size);
#endif

#ifndef TESTING
                  status = check_memory(chunks[i].addr, chunks[i].size);
                  if (status) {
                        printf("aboot: Can't load kernel.\n"
                               "  Memory at %lx - %lx (chunk %i) "
                                 "is %s\n",
                               chunks[i].addr,
                               chunks[i].addr + chunks[i].size - 1,
                               i,
                               (status == -ENOMEM) ?
                                  "Not Found" :
                                "Busy (Reserved)");
                        return -1;
                  }
#endif
            }
            bss_start = (char *) (phdrs[elf->e_phnum - 1].p_vaddr +
                              phdrs[elf->e_phnum - 1].p_filesz);
            bss_size = (phdrs[elf->e_phnum - 1].p_memsz -
                      phdrs[elf->e_phnum - 1].p_filesz);
#ifdef DEBUG
            printf("aboot: bss at 0x%p, size %#lx\n", bss_start, bss_size);
#endif
      } else {
            /* Fail silently, it might be a compressed file */
            return -1;
      }

      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 */

Generated by  Doxygen 1.6.0   Back to index