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

ipl.c

/* 
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) Hewlett-Packard (Paul Bame) paul_bame@hp.com
 */
#include <stddef.h>
#include "bootloader.h"
#include <asm/pdc.h>
#include <asm/byteorder.h>
#include "load.h"

#undef PAGE0
#define PAGE0   ((struct zeropage *)0x00000000)

/* length of command line buffer (see crt0.S) */
#define CMDLINELEN (256+4)

int Debug = 0;
int interactive = 0;

void flush_data_cache(char *start, size_t length)
{
    char *end = start + length;

    do
    {
      asm volatile("fdc 0(%0)" : : "r" (start));
      asm volatile("fic 0(%%sr0,%0)" : : "r" (start));
      start += 16;
    } while (start < end);
    asm volatile("fdc 0(%0)" : : "r" (end));

    asm ("sync");
}

static int
parse_number(char *s, char **next)
{
    int n = 0;

    while (is_digit(*s))
    {
      n *= 10;
      n += *s++ - '0';
    }

    *next = s;

    return n;
}

static char *
parse_pfname(char *s, int *partition, char *name)
{
    char *p1, *p2 = NULL;

    if (s != NULL || *s != '\0')
    {
      /* parse the kernel partition number */
      *partition = parse_number(s, &p1);

      /* now the kernel name */
      p2 = strpbrk(p1, " \t");
      if (p2 != NULL)
      {
          *p2 = '\0';
          strcpy(name, p1);
          *p2 = ' ';
      }
      else
      {
          strcpy(name, p1);
          p2 = p1 + strlen(p1);
      }
    }

    return p2;
}

static int
chk_strcat(char *out, char *in, int len, int *ok)
{
    if (*ok)
    {
      int need = strlen(out) + strlen(in) + 1;
      if (need > len)
      {
          printf("Adding '%s' to\n'%s' exceeds length (%d)\n", in, out, len);
          *ok = 0;
      }
      else
      {
          strcat(out, in);
      }
    }
    return *ok;
}

/* return pointer to the commandline minus palo stuff */
static char *
parse(const char *cmdline, int *kpart, char *kname, int *rdpart, char *rdname)
{
    char buf[CMDLINELEN];
    static char lcmd[CMDLINELEN];
    char *suffix1, *suffix2;
    int ok = 1;

    /* need a copy to work on */
    strcpy(buf, cmdline);

    *kpart = -1;
    *rdpart = -1;

    suffix1 = parse_pfname(buf, kpart, kname);

    if (*suffix1 != '\0')
      suffix1++;
    strcpy(lcmd, suffix1);

    /* see if we have a ramdisk */
    suffix2 = suffix1;
    if ((strncmp(suffix1, "initrd=", 7) == 0) ||
             (suffix2 = strstr(suffix1, " initrd=")) != NULL)
    {
      char *suffix3;
      lcmd[suffix2 - suffix1] = '\0';
      if (*suffix2 == ' ')
          suffix2++;
      suffix3 = parse_pfname(suffix2 + 7, rdpart, rdname);
      chk_strcat(lcmd, suffix3, sizeof lcmd, &ok);
    }

    return lcmd;
}

int
load_kernel(int fd, unsigned *entryp, int *wide)
{
    struct loadable loadable;
    int i;

    if (!prepare_loadable(fd, &loadable, wide))
    {
      printf("Couldn't grok your kernel executable format\n");
      return 0;
    }

    /* need to physicalize those huge addresses */
    loadable.entry = PHYS(loadable.entry);
    loadable.first = PHYS(loadable.first);

    printf("\nEntry %08x first %08x n %d\n",
            loadable.entry, loadable.first, loadable.n);

    for (i = 0; i < loadable.n; i++)
    {
      loadable.segment[i].mem =  PHYS(loadable.segment[i].mem);
      printf("Segment %d load %08x size %d mediaptr 0x%lx\n",
            i, loadable.segment[i].mem, loadable.segment[i].length,
            loadable.segment[i].offset);
    }
    loadable.first =  PHYS(loadable.first);

    if (!load_loadable((char *)loadable.first, fd, &loadable))
    {
      printf("Fatal error loading kernel executable\n");
      return 0;
    }

    flush_data_cache((char *)loadable.first, loadable.size);

    *entryp = loadable.entry;

    return 1;
}

static int
load_rd(int fd, int size)
{
    extern char *rd_start, *rd_end;
    char *rd;

    if (size <= 0)
      return 0;

    /* no idea if initrd space must be aligned, but once it was before... */
    rd = malloc_aligned(size, 4096);
    printf("Loading ramdisk %d bytes @ %p...", size, rd);
    if (seekread(fd, rd, size, 0) == size)
    {
      rd_start = rd;
      rd_end = rd + size;
    }
    flush_data_cache((char *)rd, size);
    puts("\n");

    return (rd_start != 0);
}

static void
join(char *out, int argc, char *argv[], int *ok)
{
    char tmpbuf[CMDLINELEN];
    int i;

    tmpbuf[0] = '\0';
    for (i = 0; i < argc; i++)
    {
      if (i > 0)
          chk_strcat(tmpbuf, " ", sizeof tmpbuf, ok);
      chk_strcat(tmpbuf, argv[i], sizeof tmpbuf, ok);
    }
    strcpy(out, tmpbuf);
}

static struct diskpartition partition[MAXPARTS];
static int ext2 = 0;
static int bootdev;

static void
partition_transform(int *kern_part, int *rd_part)
{
    int i, palo_part = -1;

    /* if the F0 partition is the same as the requested kernel partition,
     * for now change it to zero to re-use the existing logic.  Should do
     * the reverse in the future probably.
     */
    for (i = 0; i < sizeof partition / sizeof partition[0]; i++)
    {
      if (partition[i].id == PALO_PARTITION)
      {
          palo_part = i + 1;
          if (*kern_part == palo_part)
            *kern_part = 0;

          if (rd_part && *rd_part == palo_part)
            *rd_part = 0;

          break;
      }
    }

    if (ext2 && *kern_part == 0) {
      *kern_part = palo_part;
      if(rd_part && *rd_part == 0)
          *rd_part = palo_part;
    }

}

static void
ls(char *path)
{
    char  *p, kern_dir[256];
    const char *dir;
    int fd, part, part_fd;

    parse_pfname(path, &part, kern_dir);
    if ((p = strrchr(kern_dir, '/')) != NULL)
    {
      *++p = '.';
      *++p = '\0';
    }
    else
    {
      strcpy(kern_dir, "/.");
    }

    printf("Directory listing of %s\n\n", kern_dir);

    partition_transform(&part, NULL);

    /* partition table starts from zero */
    part_fd = offset_open(bootdev, 512 * partition[part - 1].start,
                        512 * partition[part - 1].length);
    if(ext2_mount(part_fd, 0, 0) == -1) {
      printf("Failed to mount partition %d\n", part);
      return;
    }

    if((fd = ext2_open(kern_dir)) == -1) {
      printf("Failed to open directory %s\n", kern_dir);
      return;
    }

    while((dir = ext2_readdir(fd, 0)) != NULL)
      if(dir[0] != '.') /* skip hidden files and . and .. */
          printf("  %s\n", dir);
    
    printf("\n");
    ext2_close(fd);
    /* There's no umount ... since the next mount reuses
     * the current one's data structures ... */
}

static void
interact(char *commandline, int *ok)
{
    char *argv[40], *p;
    char orig[CMDLINELEN];
    const char sep[] = " \t";
    char numbuf[4];
    char fieldbuf[79];
    int i, argc, editfield;

    strcpy(orig, commandline);

    while (1)
    {
      printf("Current command line:\n%s\n", commandline);
      p = commandline;
      argc = 0;
      *ok = 1;
      do
      {
          if ((argv[argc++] = strtok(p, sep)) == NULL)
          {
            argc--;
            break;
          }
          p = NULL;
      } while (1);
      for (i = 0; i < argc; i++)
      {
          printf("%2d: %s\n", i, argv[i]);
      }
      puts("\n"
          "<#>    edit the numbered field\n"
          "'b'    boot with this command line\n"
          "'r'    restore command line\n"
          "'l'    list dir\n"
          "? ");
      numbuf[0] = '0';
      numbuf[1] = '\0';
      enter_text(numbuf, sizeof numbuf - 1);
      puts("\n");

      if (numbuf[0] == 'b')
      {
          join(commandline, argc, argv, ok);
          break;
      }

      if (numbuf[0] == 'r')
      {
          strcpy(commandline, orig);
          continue;
      }

      if (numbuf[0] == 'l')
      {
          join(commandline, argc, argv, ok);
          ls(argv[0]);
          continue;
      }

      editfield = parse_number(numbuf, &p);

      if (editfield < argc)
      {
          strcpy(fieldbuf, argv[editfield]);
          enter_text(fieldbuf, sizeof fieldbuf - 1);
          puts("\n");
          argv[editfield] = fieldbuf;
      }

      join(commandline, argc, argv, ok);
    }
}

unsigned
iplmain(int is_interactive, char *initialstackptr, int started_wide)
{
    extern char _end, _edata;
    int partitioned;
    unsigned entry;
    struct firstblock f;
    extern char commandline[];
    int blocked_bootdev;
    int wide;
    int kern_part, rd_part;
    char kern_name[128], rd_name[128];
    char kern_fullname[128];
    int ok = 1;

    /* BSS clear */
    bzero(&_edata, &_end - &_edata);

    /* heap grows down from initial stack pointer */
    malloc_init(initialstackptr);

    firmware_init(started_wide);
    putchar('p'); /* if you get this p and no more, string storage */
                  /* in $GLOBAL$ is wrong or %dp is wrong */
    puts("alo ipl " PALOVERSION " ");
    puts(bld_info);
    puts("\n");
    interactive = is_interactive;
    if (Debug) printf("iplmain(%d, started %s)\n", interactive,
      started_wide ? "wide" : "narrow");
    if (Debug) printf("initial-sp %p\n", initialstackptr);

 restart:

    blocked_bootdev = pdc_bootdev_open();
    bootdev = byteio_open(blocked_bootdev);

    STRUCTREAD(bootdev, f, 0);
    if (strncmp(f.palomagic, PALOMAGIC, 4) != 0)
    {
      printf("ERROR: bad palo magic on boot device\n");
      while(1);
    }

    memset(&partition, 0, sizeof partition);
    partitioned = load_partitions(bootdev,
            partition, sizeof partition / sizeof partition[0]);

    if (partitioned)
    {
      printf("\n");
      print_ptab_pretty(partition, sizeof partition / sizeof partition[0]);
    }

    printf("\n%s contains:\n",
      partitioned ? "PALO(F0) partition" : "Boot image");

    if(partitioned && f.version >= 4 && (f.flags & PFLAG_EXT2)) {
      printf("PALO is formatted EXT2/3\n");
      ext2 = 1;
    }

    if (f.version < 3 && f.kern32_sz > 0)
    {
      printf("    0/vmlinux %d bytes @ 0x%x\n", f.kern32_sz, f.kern32_offset);
    }
    else
    {
        if (f.kern32_sz > 0)
          printf("    0/vmlinux32 %d bytes @ 0x%x\n", f.kern32_sz, f.kern32_offset);
        if (f.kern64_sz > 0)
          printf("    0/vmlinux64 %d bytes @ 0x%x\n", f.kern64_sz, f.kern64_offset);
    }

    if (f.rd_sz > 0)
      printf("    0/ramdisk %d bytes @ 0x%x\n", f.rd_sz, f.rd_offset);

    if (f.cmdline[0] == '\0') /* no command line specified */
    {
      die("ERROR: No command line on boot media -- faking one\n");
      strcpy(f.cmdline, "0/vmlinux root=???");
      interactive = 1;
    }

    if (strlen(f.cmdline) >= sizeof f.cmdline)
      printf("WARNING: stored command line is longer than allowed\n");
    strcpy(commandline, f.cmdline);

    /* add the right console= if there isn't one yet */
    if (strstr(commandline, " console=") == 0)
    {
      printf("\nInformation: No console specified on kernel command line."
            " This is normal.\nPALO will choose the console currently"
            " used by firmware ");

        chk_strcat(commandline, " console=", CMDLINELEN, &ok);
      if (pdc_cons_duplex())
      {
            int is_mux;

          printf("(serial).\n");

            if(pdc_cons_mux(&is_mux) != PDC_OK)
                printf("Information: The PDC calls to query the console device failed. Assuming console=ttyS0\n");

            if(is_mux)
              chk_strcat(commandline, "ttyB0", CMDLINELEN, &ok);
            else
              chk_strcat(commandline, "ttyS0", CMDLINELEN, &ok);

          if (strstr(commandline, " TERM=") == 0)
              chk_strcat(commandline, " TERM=vt102", CMDLINELEN, &ok);
      }
      else
      {
          printf("(graphics).\n");
          chk_strcat(commandline, "tty0", CMDLINELEN, &ok);
          if (strstr(commandline, " sti=") == 0)
          {
            struct {
                unsigned char flags;
                unsigned char bc[6];
                unsigned char mod;
            } cons;
            int i;
            
            chk_strcat(commandline, " sti=", CMDLINELEN, &ok);
            if (pdc_read_conspath((unsigned char *)&cons) > 0)
            {
                char pathcomp[4];
                
                for (i = 0; i < 6; i++)
                {
                  if (cons.bc[i] < 64)
                  {
                      sprintf(pathcomp, "%d/", cons.bc[i]);
                      chk_strcat(commandline, pathcomp, CMDLINELEN, &ok);
                  }
                }
                sprintf(pathcomp, "%d", cons.mod);
                chk_strcat(commandline, pathcomp, CMDLINELEN, &ok);
            }
            else
                chk_strcat (commandline, "0", CMDLINELEN, &ok);
          }
          if (strstr(commandline, " sti_font=") == 0)
              chk_strcat(commandline, " sti_font=VGA8x16", CMDLINELEN, &ok);
          if (strstr(commandline, " TERM=") == 0)
              chk_strcat(commandline, " TERM=linux", CMDLINELEN, &ok);
      }
    }

    if (interactive)
      interact(commandline, &ok);

    /* If we have any failures after this, be sure we're interactive
     * for the re-start */
    interactive = 1;

    strcpy(commandline,
          parse(commandline, &kern_part, kern_name, &rd_part, rd_name));

    sprintf(kern_fullname, "%d%s", kern_part, kern_name);
    chk_strcat(commandline, " palo_kernel=", CMDLINELEN, &ok);
    chk_strcat(commandline, kern_fullname, CMDLINELEN, &ok);

    printf("\nCommand line for kernel: '%s'\n", commandline);

    printf("Selected kernel: %s from partition %d\n", kern_name, kern_part);

    if (rd_part != -1)
      printf("Selected ramdisk: %s from partition %d\n", rd_name, rd_part);

    partition_transform(&kern_part, &rd_part);

    if (kern_part > 0 && !partitioned)
    {
      printf("ERROR: Requesting kernel from partition %d "
                "on unpartitioned media!\n", kern_part);
      goto restart;
    }

    if (rd_part != -1 && rd_part != kern_part)
    {
      die("ERROR:: palo does not support ramdisk on different"
            " partition than kernel\n");
      goto restart;
    }

    if (kern_part == 0)
    {
      int kernfd;
      const char *wname;
      int rdfd;

      wname = kern_name + strlen(kern_name) - 2;
      if (wname >= kern_name && streq(wname, "32"))
      {
          if (f.kern32_sz == 0)
          {
              die("Error: can't find a 32-bit kernel here");
            goto restart;
          }
          kernfd = offset_open(bootdev, f.kern32_offset, f.kern32_sz);
      }
      else if (wname >= kern_name && streq(wname, "64"))
      {
          if (f.kern64_sz == 0)
          {
              die("Error: can't find a 64-bit kernel here");
            goto restart;
          }
          kernfd = offset_open(bootdev, f.kern64_offset, f.kern64_sz);
      }
      else
      {
          if (f.version > 2)
            printf("Warning: kernel name doesn't end with 32 or 64 -- Guessing... ");

          kernfd = -1;

          if ((pdc_os_bits() & (OS_32|OS_64)) == (OS_32|OS_64))
          {
            printf("\nThis box can boot either 32 or 64-bit kernels...\n");
            if (f.kern32_offset == 0 && f.kern64_offset != 0)
            {
                printf("Only see a 64-bit kernel, using that\n");
                kernfd = offset_open(bootdev, f.kern64_offset, f.kern64_sz);
            }
            else if (f.kern32_offset != 0 && f.kern64_offset == 0)
            {
                printf("Only see a 32-bit kernel, using that\n");
                kernfd = offset_open(bootdev, f.kern32_offset, f.kern32_sz);
            }
            else if (f.kern32_offset != 0 && f.kern64_offset != 0)
            {
                printf("Both kernels available, choosing 32-bit kernel\n");
                kernfd = offset_open(bootdev, f.kern32_offset, f.kern32_sz);
            }
            else
            {
                die("No kernels found.");
                goto restart;
            }
          }

          if (kernfd == -1 && (pdc_os_bits() & OS_32))
          {
            printf("Choosing 32-bit kernel\n");
            kernfd = offset_open(bootdev, f.kern32_offset, f.kern32_sz);
          }
          else if (kernfd == -1 && (pdc_os_bits() & OS_64))
          {
            printf("Choosing 64-bit kernel\n");
            kernfd = offset_open(bootdev, f.kern64_offset, f.kern64_sz);
          }
      }

      /* FIXME!!! This *could* overwrite us -- probably should check */
      if (!load_kernel(kernfd, &entry, &wide))
      {
          die("ERROR: failed to load kernel\n");
          goto restart;
      }

      if (rd_part != -1)
      {
          rdfd = offset_open(bootdev, f.rd_offset, f.rd_sz);
          if (!load_rd(rdfd, f.rd_sz))
          {
            printf("ERROR: failed to load ramdisk - proceeding anyway\n");
          }
      }
    }
    else /* kern_part > 0 && we're partitioned */
    {
      int kern_fd;
      int bkern_fd;
      int rd_fd, brd_fd;
      int part_fd;
      int mount_fd;
      struct diskpartition *pp;

      if (kern_part >= MAXPARTS || 
          (partition[kern_part - 1].id != LINUX_EXT2_PARTITION &&
           partition[kern_part - 1].id != LINUX_RAID_PARTITION &&
           partition[kern_part - 1].id != PALO_PARTITION) )
      {
          printf("ERROR: Partition %d must be ext2\n", kern_part);
          goto restart;
      }

      pp = &partition[kern_part - 1];
    
      part_fd = offset_open(bootdev, 512 * pp->start, 512 * pp->length);

      mount_fd = ext2_mount(part_fd, 0, 0);
      if (0) printf("ext2_mount(partition %d) returns %d\n",
          kern_part, mount_fd);

      kern_fd = ext2_open(kern_name);
      if (0) printf("ext2_open(%s) = %d\n", kern_name, kern_fd);
      if (kern_fd < 0)
      {
          printf("ERROR: open %s from partition %d failed\n",
            kern_name, kern_part);
          goto restart;
      }

      bkern_fd = byteio_open(kern_fd);
      if (!load_kernel(bkern_fd, &entry, &wide))
      {
          die("ERROR: failed to load kernel\n");
          goto restart;
      }

      if (rd_part != -1)
      {
          rd_fd = ext2_open(rd_name);
          if(rd_fd >= 0) {
            brd_fd = byteio_open(rd_fd);

            if (!load_rd(brd_fd, ext2_filesize(rd_fd)))
            {
                printf("ERROR: failed to load ramdisk - proceeding anyway\n");
            }
          } else {
            printf("ERROR: failed to open ramdisk %s\n", rd_name);
          }
      }
    }

    /* FIXME!!! need to pass command line to kernel */
    /* could theoretically use a function pointer, but they're ugly on PA */
    if(pdc_default_width(wide))
          goto restart;
    printf("Branching to kernel entry point 0x%08x.  If this is the last\n"
         "message you see, you may need to switch your console.  This is\n"
         "a common symptom -- search the FAQ and mailing list at parisc-linux.org\n\n",
         entry);
    return entry;
}
/* $Id: ipl.c,v 1.18 2001/06/14 19:39:56 bame Exp $ */

Generated by  Doxygen 1.6.0   Back to index