Logo Search packages:      
Sourcecode: m68k-vme-tftplilo version File versions  Download package

boot.c

/*
 *  VME Linux/m68k TFTP Loader
 *
 *  (c) Copyright 1998 by Nick Holgate
 *
 *  This file is subject to the terms and conditions of the GNU General
 *  Public License.  See the file COPYING for more details.
 */

/*--------------------------------------------------------------------------*/

#include "defs.h"

/*--------------------------------------------------------------------------*/
/* startcode declarations
 */
extern char startcode_beg;
extern char startcode_end;

/*--------------------------------------------------------------------------*/
/* Compare the Bootstrap and Kernel Versions
 */

int
check_bootinfo_version
(     const char                          *memptr
)
{     const struct bootversion      *bv         = (struct bootversion *) memptr;
      unsigned long                       version     = 0;
      int                                       i;
      int                                       kernel_major;
      int                                       kernel_minor;
      int                                       boots_major;
      int                                       boots_minor;
      unsigned long                       machtype;

      if (bv->magic == BOOTINFOV_MAGIC)
      {
            machtype = get_machtype();

            for (i = 0; bv->machversions[i].machtype != 0; ++i)
            {
                  if (bv->machversions[i].machtype == machtype)
                  {
                        version = bv->machversions[i].version;
                        break;
                  }
            }

#ifdef BOOTINFO_COMPAT_1_0
            /* if no 2.1.xx version info */
            if (!version)
            {
                  machtype = get_compat_machtype();

                  /* look for 2.0.xx version info (has different machine type code) */
                  for (i = 0; bv->machversions[i].machtype != 0; ++i)
                  {
                        if (bv->machversions[i].machtype == machtype)
                        {
                              version = bv->machversions[i].version;
                        }
                  }
            }
#endif
      }

      if (!version)
      {
            printf("Kernel has no bootinfo version info, assuming 1.0\n");
            version  = get_compat_booti_version();
      }

      kernel_major = BI_VERSION_MAJOR(version);
      kernel_minor = BI_VERSION_MINOR(version);
      boots_major  = BI_VERSION_MAJOR(get_booti_version());
      boots_minor  = BI_VERSION_MINOR(get_booti_version());

      printf("Bootstrap's bootinfo version : %u.%u\n",
            boots_major, boots_minor);

      printf("Kernel's bootinfo version    : %u.%u\n",
            kernel_major, kernel_minor);

      if (kernel_major == boots_major)
      {
          if (kernel_minor > boots_minor)
            {
                  printf(
                  "Warning: Bootinfo version of bootstrap and kernel differ!\n"
                  "         Certain features may not work.\n"
                  );
          }
      }

#ifdef BOOTINFO_COMPAT_1_0
      else if (kernel_major == BI_VERSION_MAJOR(get_compat_booti_version()))
      {
          printf("(using backwards compatibility mode)\n");
      }
#endif /* BOOTINFO_COMPAT_1_0 */

      else
      {
          printf("\nThis bootstrap is too %s for this kernel!\n",
               boots_major < kernel_major ? "old" : "new");
            return 0;
    }

    return kernel_major;
}

/*--------------------------------------------------------------------------*/
/* Add a Record to the Bootinfo Structure
 */

int
add_bi_record
(     unsigned short          tag,
      unsigned short          size,
      const void              *data
)
{     struct bi_record  *record;
    unsigned short            size2;

    size2 = (sizeof(struct bi_record) + size + 3) & -4;

    if (bi_size + size2 + sizeof(bi_union.record.tag) > MAX_BI_SIZE)
      {
            printf("Can't add bootinfo record. Size exceeded MAX_BI_SIZE (%u).\n",
                                    MAX_BI_SIZE);
            return FALSE;
    }

    record       = (struct bi_record *)
                              ((unsigned long)&bi_union.record + bi_size);
    record->tag  = tag;
    record->size = size2;
    mem_move(record->data, data, size);
      bi_size     += size2;

    return TRUE;
}

/*--------------------------------------------------------------------------*/
/* Add a String Record to the Bootinfo Structure
 */

int
add_bi_string
(     unsigned short          tag,
      const unsigned char     *s
)
{
    return add_bi_record(tag, strlen(s) + 1, s);
}

/*--------------------------------------------------------------------------*/
/* Create the Bootinfo Structure
 */

int
create_bootinfo
(     void
)
{     int                           i;
    struct bi_record    *record;

    /* initialization */
    bi_size = 0;

    /* Generic tags */
    if (!add_bi_record(BI_MACHTYPE, sizeof(bi.machtype), &bi.machtype))
            return FALSE;

    if (!add_bi_record(BI_CPUTYPE, sizeof(bi.cputype), &bi.cputype))
            return FALSE;

    if (!add_bi_record(BI_FPUTYPE, sizeof(bi.fputype), &bi.fputype))
            return FALSE;

    if (!add_bi_record(BI_MMUTYPE, sizeof(bi.mmutype), &bi.mmutype))
            return FALSE;

    for (i = 0; i < bi.num_memory; i++)
            if (!add_bi_record(BI_MEMCHUNK, sizeof(bi.memory[i]), &bi.memory[i]))
                return FALSE;

    if (bi.ramdisk.size)
            if (!add_bi_record(BI_RAMDISK, sizeof(bi.ramdisk), &bi.ramdisk))
                return FALSE;

    if (!add_bi_string(BI_COMMAND_LINE, bi.command_line))
            return FALSE;

    /* add VME specific bootinfo fields */
    if (!add_vme_bootinfo())
        return FALSE;

    /* Trailer */
    record      = (struct bi_record *)
                                    ((unsigned long)&bi_union.record + bi_size);
    record->tag = BI_LAST;
      bi_size    += sizeof(bi_union.record.tag);

    return TRUE;
}

/*--------------------------------------------------------------------------*/
/*  Create the Bootinfo structure for backwards compatibility mode
 */

#ifdef BOOTINFO_COMPAT_1_0

int
create_compat_bootinfo
(     void
)
{     unsigned    i;

      compat_bootinfo.machtype = bi.machtype;

    if (bi.cputype & CPU_68020)
            compat_bootinfo.cputype = COMPAT_CPU_68020;
    else if (bi.cputype & CPU_68030)
            compat_bootinfo.cputype = COMPAT_CPU_68030;
    else if (bi.cputype & CPU_68040)
            compat_bootinfo.cputype = COMPAT_CPU_68040;
    else if (bi.cputype & CPU_68060)
            compat_bootinfo.cputype = COMPAT_CPU_68060;
    else
      {
            printf("CPU type 0x%08lx not supported by kernel.\n", bi.cputype);
            return FALSE;
    }

      if (bi.fputype & FPU_68881)
            compat_bootinfo.cputype |= COMPAT_FPU_68881;
    else if (bi.fputype & FPU_68882)
            compat_bootinfo.cputype |= COMPAT_FPU_68882;
    else if (bi.fputype & FPU_68040)
            compat_bootinfo.cputype |= COMPAT_FPU_68040;
    else if (bi.fputype & FPU_68060)
            compat_bootinfo.cputype |= COMPAT_FPU_68060;
    else if (bi.fputype)
      {
            printf("FPU type 0x%08lx not supported by kernel.\n", bi.fputype);
            return FALSE;
    }

    compat_bootinfo.num_memory = bi.num_memory;
    if (compat_bootinfo.num_memory > COMPAT_NUM_MEMINFO)
      {
            printf("Warning: only using first %u memory blocks.\n",
                         COMPAT_NUM_MEMINFO);
            compat_bootinfo.num_memory = COMPAT_NUM_MEMINFO;
    }

    for (i = 0; i < compat_bootinfo.num_memory; i++)
      {
            compat_bootinfo.memory[i].addr = bi.memory[i].addr;
            compat_bootinfo.memory[i].size = bi.memory[i].size;
    }

    if (bi.ramdisk.size)
      {
            compat_bootinfo.ramdisk_size = (bi.ramdisk.size + 1023) / 1024;
            compat_bootinfo.ramdisk_addr = bi.ramdisk.addr;
    }
      else
      {
            compat_bootinfo.ramdisk_size = 0;
            compat_bootinfo.ramdisk_addr = 0;
    }

    strncpy(compat_bootinfo.command_line,
                              bi.command_line, COMPAT_CL_SIZE);
    compat_bootinfo.command_line[COMPAT_CL_SIZE - 1] = '\0';

    return TRUE;
}
#endif /* BOOTINFO_COMPAT_1_0 */

/*--------------------------------------------------------------------------*/
/* Call the copy-and-go-code
 */

void
start_kernel
(     unsigned long     kernel_dest_addr,
      char              *kernel_load_addr,
      unsigned long     kernel_mem_size,
      unsigned long     ramdisk_dest_addr,
      char              *ramdisk_load_addr,
      unsigned long     ramdisk_mem_size,
      int                     calldbg
)
{     register void (*a0)() __asm("a0") = startcode_entry;
      register long   a1    __asm("a1") = kernel_dest_addr;
      register char  *a2    __asm("a2") = kernel_load_addr;
      register long   a3    __asm("a3") = ramdisk_dest_addr;
      register char  *a4    __asm("a4") = ramdisk_load_addr;
      register long   d0    __asm("d0") = kernel_mem_size;
      register long   d1    __asm("d1") = ramdisk_mem_size;
      register long   d2    __asm("d2") = calldbg;

      __asm __volatile ("jmp (%%a0)"
                                : /* no outputs */
                                : "r" (a0), "r" (a1), "r" (a2), "r" (a3),
                                    "r" (a4), "r" (d0), "r" (d1), "r" (d2)
      );

      /* fake a noreturn */
      for (;;);
}

/*--------------------------------------------------------------------------*/

char *
load_kernel
(     char              *file_name,
      unsigned long     mem_start,
      unsigned long     *kernel_size,
      unsigned long     bootinfo_size
)
{     Elf32_Phdr        *kernel_phdrs     = NULL;
      char              *kernel_load_addr = NULL;
      int                     opened            = FALSE;
      Elf32_Ehdr        kexec_elf;
      void              *file_data;
      unsigned long     file_size;
      unsigned long     min_addr;
      unsigned long     max_addr;
      int                     i;

      printf("Loading kernel '%s' ...\n", file_name);

      if ((file_data = tftp(file_name, &file_size)) == NULL)
      {
            printf("\nCan't TFTP kernel image '%s'.\n", file_name);
            return NULL;
      }

    stream_init();
    stream_push( &memory_mod );
    stream_push( &gunzip_mod );

      /* open kernel executable and read exec header */
      if (sopen(file_data, file_size) < 0)
      {
            printf("\nUnable to open kernel file `%s'.\n", file_name);
            goto fail;
      }
      opened = TRUE;

      if (sread(&kexec_elf, sizeof(kexec_elf)) != sizeof(kexec_elf))
      {
            printf("\nCannot read ELF header of kernel image `%s'.\n",
                        file_name);
            goto fail;
      }

      /* Allow images starting '0x00001000 0x00000800' as they are
       * probably valid kernels on which 16xcfg has been run.
       */
      if (mem_cmp(&kexec_elf.e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0 &&
            mem_cmp(&kexec_elf.e_ident[EI_MAG0],
                  "\000\000\020\000\000\000\010\000", 8) != 0)
      {
            printf("\nKernel image '%s' is not an ELF executable.\n",
                        file_name);
            goto fail;
      }

      /* A few plausibility checks */
      if ((kexec_elf.e_type    != ET_EXEC   )
      ||    (kexec_elf.e_machine != EM_68K    )
      ||    (kexec_elf.e_version != EV_CURRENT))
      {
            printf("\nInvalid ELF header contents in kernel '%s'.\n",
                        file_name);
            goto fail;
      }

      /* allocate memory for program headers */
      if ((kernel_phdrs = (Elf32_Phdr *)
            malloc(kexec_elf.e_phnum * sizeof(Elf32_Phdr))) == NULL)
      {
            goto no_mem;
      }

      /* Load the program headers */
      sseek(kexec_elf.e_phoff, SEEK_SET);
      if (sread(kernel_phdrs,
                  kexec_elf.e_phnum * sizeof(*kernel_phdrs)) !=
                        kexec_elf.e_phnum * sizeof(*kernel_phdrs))
      {
            printf("\nUnable to read program headers from kernel file '%s'.\n",
                        file_name);
            goto fail;
      }

      /* calculate the total required amount of memory */
      min_addr = 0xffffffff;
      max_addr = 0;

      for (i = 0; i < kexec_elf.e_phnum; i++)
      {
            if (min_addr > kernel_phdrs[i].p_vaddr)
            {
                  min_addr = kernel_phdrs[i].p_vaddr;
            }
            if (max_addr < kernel_phdrs[i].p_vaddr + kernel_phdrs[i].p_memsz)
            {
                  max_addr = kernel_phdrs[i].p_vaddr + kernel_phdrs[i].p_memsz;
            }
      }

      /* This is needed for newer linkers that include the header in
       * the first segment.
       */
      if (min_addr == 0)
      {
            min_addr                  = PAGE_SIZE;
            kernel_phdrs[0].p_vaddr  += PAGE_SIZE;
            kernel_phdrs[0].p_offset += PAGE_SIZE;
            kernel_phdrs[0].p_filesz -= PAGE_SIZE;
            kernel_phdrs[0].p_memsz  -= PAGE_SIZE;
      }

      /* get size of kernel */
      *kernel_size = max_addr - min_addr;

      /* allocate memory for kernel and bootinfo
       * as low in memory as possible
       */
      if ((kernel_load_addr = malloc_low(*kernel_size + bootinfo_size)) == NULL)
      {
            goto no_mem;
      }

      /* zero initialise kernel memory */
      mem_clear(kernel_load_addr, *kernel_size);

      /* read the text and data segments from the kernel image */
      for (i = 0; i < kexec_elf.e_phnum; i++)
      {
            if (debug_mode)
            {
                  printf("Kernel segment %u at 0x%08lx, size %u, align %u\n",
                        i,
                        mem_start + kernel_phdrs[i].p_vaddr - PAGE_SIZE,
                        kernel_phdrs[i].p_memsz,
                        kernel_phdrs[i].p_align
                  );
            }

            if (sseek(kernel_phdrs[i].p_offset, SEEK_SET) == -1)
            {
                  printf("\nFailed to seek to kernel segment %u.\n", i);
                  goto fail;
            }

            if (sread(kernel_load_addr + kernel_phdrs[i].p_vaddr - PAGE_SIZE,
                         kernel_phdrs[i].p_filesz) != kernel_phdrs[i].p_filesz)
            {
                  printf("\nFailed to read kernel segment %u.\n", i);
                  goto fail;
            }
      }

      if (debug_mode)
      {
            printf("Kernel entry is 0x%08x.\n", kexec_elf.e_entry);
      }

      sclose();

      free(file_data   );
      free(kernel_phdrs);

      return kernel_load_addr;

no_mem:
      put_str("\nNot enough memory to boot!\n");

fail:
      if (opened)
      {
            sclose();
      }

      free(file_data       );
      free(kernel_phdrs    );
      free(kernel_load_addr);
      return NULL;
}

/*--------------------------------------------------------------------------*/

void
boot_linux
(     BOOT                    *boot,
      const char              *more,
      int                           autoboot
)
{     int                           i;
      unsigned long           mem_start;
      unsigned long           mem_size;
      unsigned long           mem_end;
    unsigned long       bootinfo_mem_size;
    unsigned long       kernel_size;
      unsigned long           kernel_major;
      char                    *kernel_load_addr  = NULL;
      char                    *ramdisk_load_addr = NULL;
    void                      *bi_ptr;

    mem_clear(&bi, sizeof(bi));

      /* get default (2.1.xx) machine type */
      bi.machtype = get_machtype();

      /* detect and configure cpu/fpu type */
      switch (cpu_type)
      {
            case 40:
            {
                  bi.cputype = CPU_68040;
                  break;
            }

            case 60:
            {
                  bi.cputype = CPU_68060;
                  break;
            }
      }
      bi.mmutype = bi.cputype;
      if (fpu_type)
            bi.fputype = bi.cputype;
      else
            bi.fputype = 0;

      if (debug_mode)
      {
            put_char('\n');
            print_model();
            printf(" CPU:680%u with%s FPU\n",
                  cpu_type, fpu_type ? " internal" : "out");
      }

      /* if no ram size has been specified */
      if ((mem_size = boot->memsize) == 0)
      {
            /* use detected memory size */
            mem_size = detected_mem_size;
      }

      /* if too much ram has been specified */
      if (mem_size > detected_mem_size)
      {
            /* use detected memory size */
            mem_size = detected_mem_size;

            if (debug_mode)
            {
                  printf("Specified memory size greater than detected size.\n"
                           "Using detected memory size %luK.\n",
                              mem_size >> 10
                  );
            }
      }

      else if (debug_mode)
      {
            printf("Using %s memory size %luK.\n",
                        boot->memsize ? "specified" : "detected",
                        mem_size >> 10
            );
      }

      bi.num_memory     = 1;
      bi.memory[0].addr = 0x00000000;
      bi.memory[0].size = mem_size;

      if (debug_mode)
      {
            printf("Found %u block%s of memory.\n", bi.num_memory,
                  bi.num_memory > 1 ? "s" : "");

            for (i = 0; i < bi.num_memory; i++)
            {
                  printf(" Block %u: 0x%08lx to 0x%08lx (%luK).\n", i,
                              bi.memory[i].addr,
                              bi.memory[i].addr + bi.memory[i].size - 1,
                              bi.memory[i].size >> 10
                  );
            }
      }

      mem_start = bi.memory[0].addr;
      mem_size  = bi.memory[0].size;
      mem_end   = mem_start + mem_size;

      /* don't allow ramdisk to be moved over the startup code */
      if (mem_end > (unsigned long) startcode_entry)
            mem_end = (unsigned long) startcode_entry;

      /* Load the kernel at one page after start of memory */
      mem_start += PAGE_SIZE;

#ifdef BOOTINFO_COMPAT_1_0
      /* make sure we have enough room for old bootinfo */
    if (sizeof(compat_bootinfo) > sizeof(bi_union))
            bootinfo_mem_size = sizeof(compat_bootinfo);
      else
#endif /* BOOTINFO_COMPAT_1_0 */
            bootinfo_mem_size = sizeof(bi_union);

      /* load the kernel */
      if ((kernel_load_addr = load_kernel(boot->kernel, mem_start,
                                    &kernel_size, bootinfo_mem_size)) == NULL)
      {
            return;
      }

      /* support for ramdisk */
      if (boot->ramdisk)
      {
            printf("Loading ramdisk '%s' ...\n", boot->ramdisk);
            /* load the ramdisk image */
            if ((ramdisk_load_addr = tftp(boot->ramdisk, &bi.ramdisk.size))
                        == NULL)
            {
                  printf("\nCan't TFTP ramdisk image '%s'.\n", boot->ramdisk);
                  goto fail;
            }

            /* make sure ramdisk will sit on a 1K boundry at end of memory */
            bi.ramdisk.addr = mem_end - ((bi.ramdisk.size + 1023) & ~1023);
      }
      else
      {
            bi.ramdisk.size   = 0;
            bi.ramdisk.addr   = 0;
            ramdisk_load_addr = NULL;
      }

    /* Check kernel's bootinfo version */
    kernel_major = check_bootinfo_version(kernel_load_addr);

      /* build kernel command line
       */

      /* environment variable indicating loaded boot record */
      strncpy(bi.command_line, "BOOT_IMAGE=", CL_SIZE);
      strcatn(bi.command_line, boot->label,   CL_SIZE);

      /* add 'auto' flag if we didn't ask for input */
      if (autoboot)
      {
            strcatn(bi.command_line, " auto", CL_SIZE);
      }

      /* append root device */
      if (boot->root)
      {
            strcatn(bi.command_line, " root=",   CL_SIZE);
            strcatn(bi.command_line, boot->root, CL_SIZE);
      }

      /* append boot record read-only/read-write flag */
      if (boot->read_only == TRUE || boot->read_only == FALSE)
      {
            strcatn(bi.command_line, boot->read_only ? " ro" : " rw", CL_SIZE);
      }

      /* append boot record console device, for kernels later than 2.0.xx */
      if (boot->console && (kernel_major > 1))
      {
            strcatn(bi.command_line, " console=",   CL_SIZE);
            strcatn(bi.command_line, boot->console, CL_SIZE);
      }

      /* boot record command line */
      if (boot->cmdline)
      {
            strcatn(bi.command_line, " ",           CL_SIZE);
            substip(bi.command_line, boot->cmdline, CL_SIZE);
      }

      /* append user specified input */
      if (more[0])
      {
            strcatn(bi.command_line, " ",  CL_SIZE);
            substip(bi.command_line, more, CL_SIZE);
      }

      /* any additional parameters to append */
      if (boot->append)
      {
            strcatn(bi.command_line, " ",          CL_SIZE);
            substip(bi.command_line, boot->append, CL_SIZE);
      }

      if (debug_mode)
      {
            printf("Using command line `%s'.\n", bi.command_line);
      }

      if (kernel_major == BI_VERSION_MAJOR(get_booti_version()))
      {
          /* create the bootinfo structure */
          if (!create_bootinfo())
            {
                  printf("\nCouldn't create bootinfo.\n");
                  goto fail;
            }

            bi_ptr = &bi_union.record;
      }

#ifdef BOOTINFO_COMPAT_1_0
      else if (kernel_major == BI_VERSION_MAJOR(get_compat_booti_version()))
      {
            /* fix machine type for 2.0.xx kernels */
            bi.machtype = get_compat_machtype();

            if (!create_compat_bootinfo())
            {
            printf("\nCouldn't create compatable bootinfo.\n");
            goto fail;
      }

            bi_ptr  = &compat_bootinfo;
            bi_size = sizeof(compat_bootinfo);
      }
#endif /* BOOTINFO_COMPAT_1_0 */

      else
      {
            printf("Kernel has unsupported bootinfo version.\n");
            goto fail;
      }

      /* copy the bootinfo to the end of the kernel image */
      mem_move(kernel_load_addr + kernel_size, bi_ptr, bi_size);

      if (debug_mode)
      {
            if (bi.ramdisk.size)
            {
                  printf("RAM disk at 0x%08lx, size is %lu bytes.\n",
                              (unsigned long) ramdisk_load_addr, bi.ramdisk.size
                  );
            }

            printf("Boot info at 0x%08lx.\n", mem_start + kernel_size);

            if (!boot->calldbg)
            {
                  put_str("\nPress a key to continue booting... ");
                  get_char(NO_TIMEOUT);
                  put_char('\n');
            }
      }

      /* move the copy-and-go code to it's proper place */
      mem_move((void *)startcode_entry, &startcode_beg,
                                                &startcode_end - &startcode_beg);
      invalidate_icache();

      /* execute the copy-and-go code (**never returns**) */
      start_kernel(     mem_start,       kernel_load_addr,  kernel_size + bi_size,
                              bi.ramdisk.addr, ramdisk_load_addr, bi.ramdisk.size,
                              boot->calldbg);

fail:
      free(ramdisk_load_addr);
      free(kernel_load_addr);
}

/*-----------------------------< end of file >------------------------------*/

Generated by  Doxygen 1.6.0   Back to index