]> git.ozlabs.org Git - yaboot.git/blobdiff - second/yaboot.c
Certain levels of IBM firmware will allow the system to boot from an
[yaboot.git] / second / yaboot.c
index 727c72525b3d657c2bcdda0a6ef63f3ca9daedb7..bf10b01c4db8029c3eaf86b7969576cca8bee6ab 100644 (file)
@@ -1,38 +1,45 @@
-/* Yaboot - secondary boot loader for Linux on ppc.
-
-   Copyright (C) 1999 Benjamin Herrenschmidt
-
-   portions based on poof
-
-   Copyright (C) 1999 Marius Vollmer
-
-   portions based on quik
-   
-   Copyright (C) 1996 Paul Mackerras.
-
-   Because this program is derived from the corresponding file in the
-   silo-0.64 distribution, it is also
-
-   Copyright (C) 1996 Pete A. Zaitcev
-                1996 Maurizio Plaza
-                1996 David S. Miller
-                1996 Miguel de Icaza
-                1996 Jakub Jelinek
-
-   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-*/
+/*
+ *  Yaboot - secondary boot loader for Linux on PowerPC.
+ *
+ *  Copyright (C) 2001, 2002 Ethan Benson
+ *
+ *  Copyright (C) 1999, 2000, 2001 Benjamin Herrenschmidt
+ *
+ *  IBM CHRP support
+ *
+ *  Copyright (C) 2001 Peter Bergner
+ *
+ *  portions based on poof
+ *
+ *  Copyright (C) 1999 Marius Vollmer
+ *
+ *  portions based on quik
+ *
+ *  Copyright (C) 1996 Paul Mackerras.
+ *
+ *  Because this program is derived from the corresponding file in the
+ *  silo-0.64 distribution, it is also
+ *
+ *  Copyright (C) 1996 Pete A. Zaitcev
+ *                1996 Maurizio Plaza
+ *                1996 David S. Miller
+ *                1996 Miguel de Icaza
+ *                1996 Jakub Jelinek
+ *
+ *  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
 
 #include "stdarg.h"
 #include "string.h"
@@ -104,10 +111,11 @@ static void     setup_display(void);
 /* Locals & globals */
 
 int useconf = 0;
-char bootdevice[1024];
+char bootdevice[BOOTDEVSZ];
 char *password = NULL;
 struct boot_fspec_t boot;
 int _machine = _MACH_Pmac;
+int flat_vmlinux;
 
 #ifdef CONFIG_COLOR_TEXT
 
@@ -172,7 +180,7 @@ yaboot_start (unsigned long r3, unsigned long r4, unsigned long r5)
 
      /* OF seems to do it, but I'm not very confident */
      memset(&__bss_start, 0, &_end - &__bss_start);
-       
+
      /* Check for quik first stage bootloader (but I don't think we are
       * compatible with it anyway, I'll look into backporting to older OF
       * versions later
@@ -184,7 +192,7 @@ yaboot_start (unsigned long r3, unsigned long r4, unsigned long r5)
 
      /* Initialize OF interface */
      prom_init ((prom_entry) r5);
-       
+
      /* Allocate some memory for malloc'ator */
      malloc_base = prom_claim((void *)MALLOCADDR, MALLOCSIZE, 0);
      if (malloc_base == (void *)-1) {
@@ -195,7 +203,7 @@ yaboot_start (unsigned long r3, unsigned long r4, unsigned long r5)
      malloc_init(malloc_base, MALLOCSIZE);
      DEBUG_F("Malloc buffer allocated at %p (%d bytes)\n",
             malloc_base, MALLOCSIZE);
-               
+
      /* A few useless DEBUG_F's */
      DEBUG_F("reloc_offset :  %ld         (should be 0)\n", reloc_offset());
      DEBUG_F("test_bss     :  %d         (should be 0)\n", test_bss);
@@ -218,7 +226,7 @@ yaboot_start (unsigned long r3, unsigned long r4, unsigned long r5)
                    _machine = _MACH_chrp;
          }
      }
-       
+
      DEBUG_F("Running on _machine = %d\n", _machine);
      DEBUG_SLEEP;
 
@@ -233,9 +241,9 @@ yaboot_start (unsigned long r3, unsigned long r4, unsigned long r5)
 
      /* Return to OF */
      prom_exit();
-       
+
      return result;
-       
+
 }
 
 #ifdef CONFIG_COLOR_TEXT
@@ -252,9 +260,65 @@ check_color_text_ui(char *color)
          i++;
      }
      return -1;
-}      
+}
 #endif /* CONFIG_COLOR_TEXT */
 
+
+void print_message_file(char *filename)
+{
+     char *msg = NULL;
+     char *p, *endp;
+     char *defdev = boot.dev;
+     int defpart = boot.part;
+     char msgpath[1024];
+     int opened = 0;
+     int result = 0;
+     int n;
+     struct boot_file_t file;
+     struct boot_fspec_t msgfile;
+
+     defdev = cfg_get_strg(0, "device");
+     if (!defdev)
+         defdev = boot.dev;
+     p = cfg_get_strg(0, "partition");
+         if (p) {
+              n = simple_strtol(p, &endp, 10);
+              if (endp != p && *endp == 0)
+                   defpart = n;
+         }
+
+     strncpy(msgpath, filename, sizeof(msgpath));
+     if (!parse_device_path(msgpath, defdev, defpart, "/etc/yaboot.msg", &msgfile)) {
+         prom_printf("%s: Unable to parse\n", msgpath);
+         goto done;
+     }
+
+     result = open_file(&msgfile, &file);
+     if (result != FILE_ERR_OK) {
+         prom_printf("%s:%d,", msgfile.dev, msgfile.part);
+         prom_perror(result, msgfile.file);
+         goto done;
+     } else
+         opened = 1;
+
+     msg = malloc(2001);
+     if (!msg)
+         goto done;
+     else
+         memset(msg, 0, 2001);
+
+     if (file.fs->read(&file, 2000, msg) <= 0)
+         goto done;
+     else
+         prom_printf("%s", msg);
+
+done:
+     if (opened)
+         file.fs->close(&file);
+     if (msg)
+         free(msg);
+}
+
 /* Currently, the config file must be at the root of the filesystem.
  * todo: recognize the full path to myself and use it to load the
  * config file. Handle the "\\" (blessed system folder)
@@ -276,10 +340,10 @@ load_config_file(char *device, char* path, int partition)
      }
 
      /* Build the path to the file */
-     if (path)
-         strcpy(conf_path, path);
-     else if ( _machine == _MACH_chrp )
+     if (_machine == _MACH_chrp)
          strcpy(conf_path, "/etc/");
+     else if (path && *path)
+         strcpy(conf_path, path);
      else
          conf_path[0] = 0;
      strcat(conf_path, CONFIG_FILE_NAME);
@@ -324,7 +388,7 @@ load_config_file(char *device, char* path, int partition)
          prom_interpret(p);
 
      password = cfg_get_strg(0, "password");
-       
+
 #ifdef CONFIG_COLOR_TEXT
      p = cfg_get_strg(0, "fgcolor");
      if (p) {
@@ -343,38 +407,37 @@ load_config_file(char *device, char* path, int partition)
      }
      if (bgcolor >= 0) {
          char temp[64];
-         sprintf(temp, "%x to background-color", bgcolor); 
-         prom_interpret(temp); 
+         sprintf(temp, "%x to background-color", bgcolor);
+         prom_interpret(temp);
 #if !DEBUG
          prom_printf("\xc");
 #endif /* !DEBUG */
      }
      if (fgcolor >= 0) {
          char temp[64];
-         sprintf(temp, "%x to foreground-color", fgcolor); 
-         prom_interpret(temp); 
+         sprintf(temp, "%x to foreground-color", fgcolor);
+         prom_interpret(temp);
      }
 #endif /* CONFIG_COLOR_TEXT */
-   
+
      p = cfg_get_strg(0, "init-message");
      if (p)
          prom_printf("%s\n", p);
-#if 0
+
      p = cfg_get_strg(0, "message");
      if (p)
          print_message_file(p);
-#endif         
 
      result = 1;
-    
+
 bail:
 
      if (opened)
          file.fs->close(&file);
-    
-     if (result != 1 && conf_file)
+
+     if (conf_file)
          free(conf_file);
-       
+
      return result;
 }
 
@@ -486,8 +549,9 @@ void check_password(char *str)
 {
      int i;
 
+     prom_printf("\n%s", str);
      for (i = 0; i < 3; i++) {
-         prom_printf ("\n%sassword: ", str);
+         prom_printf ("\nPassword: ");
          passwdbuff[0] = 0;
          cmdedit ((void (*)(void)) 0, 1);
          prom_printf ("\n");
@@ -495,18 +559,22 @@ void check_password(char *str)
          if (!strncmp (password, "$1$", 3)) {
               if (!check_md5_password(passwdbuff, password))
                    return;
-         } 
+         }
          else if (!strcmp (password, passwdbuff))
               return;
 #else /* !MD5 */
          if (!strcmp (password, passwdbuff))
               return;
 #endif /* USE_MD5_PASSWORDS */
-         if (i < 2)
-              prom_printf ("Password incorrect. Please try again...");
+         if (i < 2) {
+              prom_sleep(1);
+              prom_printf ("Incorrect password.  Try again.");
+         }
      }
-     prom_printf ("Seems like you don't know the access password.  Go away!\n");
-     prom_sleep(3);
+     prom_printf(" ___________________\n< Permission denied >\n -------------------\n"
+                "        \\   ^__^\n         \\  (oo)\\_______\n            (__)\\       )\\/\\\n"
+                "                ||----w |\n                ||     ||\n");
+     prom_sleep(4);
      prom_interpret("reset-all");
 }
 
@@ -534,7 +602,7 @@ int get_params(struct boot_param_t* params)
      params->rd.part = -1;
      params->sysmap.part = -1;
      defpart = boot.part;
-    
+
      cmdinit();
 
      if (first) {
@@ -638,22 +706,25 @@ int get_params(struct boot_param_t* params)
                    restricted = 1;
               if (label) {
                    if (params->args && password && restricted)
-                        check_password ("To specify image arguments you must enter the p");
+                        check_password ("To specify arguments for this image "
+                                        "you must enter the password.");
                    else if (password && !restricted)
-                        check_password ("P");
+                        check_password ("This image is restricted.");
               }
               params->args = make_params(label, params->args);
          }
      }
 
      if (!strcmp (imagename, "help")) {
+          /* FIXME: defdevice shouldn't need to be reset all over the place */
+         if(!defdevice) defdevice = boot.dev;
          prom_printf(
               "\nPress the tab key for a list of defined images.\n"
               "The label marked with a \"*\" is is the default image, "
               "press <return> to boot it.\n\n"
               "To boot any other label simply type its name and press <return>.\n\n"
               "To boot a kernel image which is not defined in the yaboot configuration \n"
-              "file, enter the kernel image name as [device:][partno],/path, where \n"
+              "file, enter the kernel image name as [[device:][partno],]/path, where \n"
               "\"device:\" is the OpenFirmware device path to the disk the image \n"
               "resides on, and \"partno\" is the partition number the image resides on.\n"
               "Note that the comma (,) is only required if you specify an OpenFirmware\n"
@@ -667,22 +738,22 @@ int get_params(struct boot_param_t* params)
 
      if (!strcmp (imagename, "halt")) {
          if (password)
-              check_password ("P");
+              check_password ("Restricted command.");
          prom_pause();
          return 0;
      }
      if (!strcmp (imagename, "bye")) {
          if (password) {
-              check_password ("P");
+              check_password ("Restricted command.");
               return 1;
          }
-         return 1; 
+         return 1;
      }
 
      if (imagename[0] == '$') {
          /* forth command string */
          if (password)
-              check_password ("P");
+              check_password ("OpenFirmware commands are restricted.");
          prom_interpret(imagename+1);
          return 0;
      }
@@ -690,7 +761,7 @@ int get_params(struct boot_param_t* params)
      strncpy(imagepath, imagename, 1024);
 
      if (!label && password)
-         check_password ("To boot a custom image you must enter the p");
+         check_password ("To boot a custom image you must enter the password.");
 
      if (!parse_device_path(imagepath, defdevice, defpart,
                            "/vmlinux", &params->kernel)) {
@@ -748,7 +819,7 @@ yaboot_text_ui(void)
      loadinfo_t          loadinfo;
      void                *initrd_more,*initrd_want;
      unsigned long       initrd_read;
-    
+
      loadinfo.load_loc = 0;
 
      for (;;) {
@@ -756,12 +827,12 @@ yaboot_text_ui(void)
          initrd_base = 0;
          sysmap_base = 0;
          sysmap_size = 0;
-       
+
          if (get_params(&params))
               return;
          if (!params.kernel.file)
               continue;
-       
+
          prom_printf("Please wait, loading kernel...\n");
 
          memset(&file, 0, sizeof(file));
@@ -818,9 +889,9 @@ yaboot_text_ui(void)
          file.fs->close(&file);
          memset(&file, 0, sizeof(file));
 
-         /* If sysmap, load it
+         /* If sysmap, load it (only if booting a vmlinux).
           */
-         if (params.sysmap.file) {
+         if (flat_vmlinux && params.sysmap.file) {
               prom_printf("Loading System.map ...\n");
               if(strlen(boot.file) && !strcmp(boot.file,"\\\\") && params.sysmap.file[0] != '/'
                  && params.sysmap.file[0] != '\\') {
@@ -835,7 +906,7 @@ yaboot_text_ui(void)
                    free(params.sysmap.file);
                    params.sysmap.file=loc;
               }
-            
+
               result = open_file(&params.sysmap, &file);
               if (result != FILE_ERR_OK) {
                    prom_printf("%s:%d,", params.sysmap.dev, params.sysmap.part);
@@ -845,6 +916,7 @@ yaboot_text_ui(void)
                    sysmap_base = prom_claim(loadinfo.base+loadinfo.memsize, 0x100000, 0);
                    if (sysmap_base == (void *)-1) {
                         prom_printf("Claim failed for sysmap memory\n");
+                        prom_pause();
                         sysmap_base = 0;
                    } else {
                         sysmap_size = file.fs->read(&file, 0xfffff, sysmap_base);
@@ -866,10 +938,11 @@ yaboot_text_ui(void)
               }
          }
 
-         /* If ramdisk, load it. For now, we can't tell the size it will be
-          * so we claim an arbitrary amount of 4Mb
+         /* If ramdisk, load it (only if booting a vmlinux).  For now, we
+          * can't tell the size it will be so we claim an arbitrary amount
+          * of 4Mb.
           */
-         if (params.rd.file) {
+         if (flat_vmlinux && params.rd.file) {
               if(strlen(boot.file) && !strcmp(boot.file,"\\\\") && params.rd.file[0] != '/'
                  && params.kernel.file[0] != '\\')
               {
@@ -891,7 +964,7 @@ yaboot_text_ui(void)
                    prom_perror(result, params.rd.file);
               }
               else {
-#define INITRD_CHUNKSIZE 0x400000
+#define INITRD_CHUNKSIZE 0x100000
                    initrd_base = prom_claim(loadinfo.base+loadinfo.memsize, INITRD_CHUNKSIZE, 0);
                    if (initrd_base == (void *)-1) {
                         prom_printf("Claim failed for initrd memory\n");
@@ -907,6 +980,7 @@ yaboot_text_ui(void)
                              initrd_more = prom_claim(initrd_want, INITRD_CHUNKSIZE, 0);
                              if (initrd_more != initrd_want) {
                                   prom_printf("Claim failed for initrd memory at %p rc=%p\n",initrd_want,initrd_more);
+                                  prom_pause();
                                   break;
                              }
                              initrd_read = file.fs->read(&file, INITRD_CHUNKSIZE, initrd_more);
@@ -932,47 +1006,49 @@ yaboot_text_ui(void)
          flush_icache_range ((long)loadinfo.base, (long)loadinfo.base+loadinfo.memsize);
          DEBUG_F(" done\n");
 
-/* 
- * Fill mew boot infos
- *
- * The birec is low on memory, probably inside the malloc pool, so
- * we don't write it earlier. At this point, we should not use anything
- * coming from the malloc pool
- */
-         birec = (struct bi_record *)_ALIGN(loadinfo.filesize+(1<<20)-1,(1<<20));
+         if (flat_vmlinux) {
+              /*
+               * Fill new boot infos (only if booting a vmlinux).
+               *
+               * The birec is low on memory, probably inside the malloc pool,
+               * so we don't write it earlier. At this point, we should not
+               * use anything coming from the malloc pool.
+               */
+              birec = (struct bi_record *)_ALIGN(loadinfo.filesize+(1<<20)-1,(1<<20));
+
+              /* We make sure it's mapped. We map only 64k for now, it's
+               * plenty enough we don't claim since this precise memory
+               * range may already be claimed by the malloc pool.
+               */
+              prom_map (birec, birec, 0x10000);
+              DEBUG_F("birec at %p\n", birec);
+              DEBUG_SLEEP;
+
+              birec->tag = BI_FIRST;
+              birec->size = sizeof(struct bi_record);
+              birec = (struct bi_record *)((ulong)birec + birec->size);
+
+              birec->tag = BI_BOOTLOADER_ID;
+              sprintf( (char *)birec->data, "yaboot");
+              birec->size = sizeof(struct bi_record) + strlen("yaboot") + 1;
+              birec = (struct bi_record *)((ulong)birec + birec->size);
+
+              birec->tag = BI_MACHTYPE;
+              birec->data[0] = _machine;
+              birec->size = sizeof(struct bi_record) + sizeof(ulong);
+              birec = (struct bi_record *)((ulong)birec + birec->size);
 
-/* We make sure it's mapped. We map only 64k for now, it's plenty enough
- * we don't claim since this precise memory range may already be claimed
- * by the malloc pool
- */
-         prom_map (birec, birec, 0x10000);
-         DEBUG_F("birec at %p\n", birec);
-         DEBUG_SLEEP;
-
-         birec->tag = BI_FIRST;
-         birec->size = sizeof(struct bi_record);
-         birec = (struct bi_record *)((unsigned long)birec + birec->size);
-       
-         birec->tag = BI_BOOTLOADER_ID;
-         sprintf( (char *)birec->data, "yaboot");
-         birec->size = sizeof(struct bi_record) + strlen("yaboot") + 1;
-         birec = (struct bi_record *)((unsigned long)birec + birec->size);
-       
-         birec->tag = BI_MACHTYPE;
-         birec->data[0] = _machine;
-         birec->size = sizeof(struct bi_record) + sizeof(unsigned long);
-         birec = (struct bi_record *)((unsigned long)birec + birec->size);
-
-         if (sysmap_base) {
-              birec->tag = BI_SYSMAP;
-              birec->data[0] = (unsigned long)sysmap_base;
-              birec->data[1] = sysmap_size;
-              birec->size = sizeof(struct bi_record) + sizeof(unsigned long)*2;
-              birec = (struct bi_record *)((unsigned long)birec + birec->size);
-         }
-         birec->tag = BI_LAST;
-         birec->size = sizeof(struct bi_record);
-         birec = (struct bi_record *)((unsigned long)birec + birec->size);
+              if (sysmap_base) {
+                   birec->tag = BI_SYSMAP;
+                   birec->data[0] = (ulong)sysmap_base;
+                   birec->data[1] = sysmap_size;
+                   birec->size = sizeof(struct bi_record) + sizeof(ulong)*2;
+                   birec = (struct bi_record *)((ulong)birec + birec->size);
+              }
+              birec->tag = BI_LAST;
+              birec->size = sizeof(struct bi_record);
+              birec = (struct bi_record *)((ulong)birec + birec->size);
+          }
 
           /* compute the kernel's entry point. */
          kernel_entry = loadinfo.base + loadinfo.entry - loadinfo.load_loc;
@@ -1007,7 +1083,7 @@ load_elf32(struct boot_file_t *file, loadinfo_t *loadinfo)
      /* Read the rest of the Elf header... */
      if ((*(file->fs->read))(file, size, &e->e_version) < size) {
          prom_printf("\nCan't read Elf32 image header\n");
-         return 0;
+         goto bail;
      }
 
      DEBUG_F("Elf32 header:\n");
@@ -1026,24 +1102,24 @@ load_elf32(struct boot_file_t *file, loadinfo_t *loadinfo)
 
      if (e->e_phnum > MAX_HEADERS) {
          prom_printf ("Can only load kernels with one program header\n");
-         return 0;
+         goto bail;
      }
 
      ph = (Elf32_Phdr *)malloc(sizeof(Elf32_Phdr) * e->e_phnum);
      if (!ph) {
          prom_printf ("Malloc error\n");
-         return 0;
+         goto bail;
      }
 
      /* Now, we read the section header */
      if ((*(file->fs->seek))(file, e->e_phoff) != FILE_ERR_OK) {
          prom_printf ("seek error\n");
-         return 0;
+         goto bail;
      }
      if ((*(file->fs->read))(file, sizeof(Elf32_Phdr) * e->e_phnum, ph) !=
         sizeof(Elf32_Phdr) * e->e_phnum) {
          prom_printf ("read error\n");
-         return 0;
+         goto bail;
      }
 
      /* Scan through the program header
@@ -1070,7 +1146,7 @@ load_elf32(struct boot_file_t *file, loadinfo_t *loadinfo)
 
      if (loadinfo->memsize == 0) {
          prom_printf("Can't find a loadable segment !\n");
-         return 0;
+         goto bail;
      }
 
      /* leave some room (1Mb) for boot infos */
@@ -1078,19 +1154,29 @@ load_elf32(struct boot_file_t *file, loadinfo_t *loadinfo)
      /* Claim OF memory */
      DEBUG_F("Before prom_claim, mem_sz: 0x%08lx\n", loadinfo->memsize);
 
+     /* Determine whether we are trying to boot a vmlinux or some
+      * other binary image (eg, zImage).  We load vmlinux's at
+      * KERNELADDR and all other binaries at their e_entry value.
+      */
+     if (e->e_entry == KERNEL_LINK_ADDR_PPC32) {
+          flat_vmlinux = 1;
+          loadaddr = KERNELADDR;
+     } else {
+          flat_vmlinux = 0;
+          loadaddr = loadinfo->load_loc;
+     }
+
      /* On some systems, loadaddr may already be claimed, so try some
       * other nearby addresses before giving up.
       */
-     loadaddr = (e->e_entry == KERNEL_LINK_ADDR_PPC32 ||
-                e->e_entry == 0) ? KERNELADDR : e->e_entry;
      for(addr=loadaddr; addr <= loadaddr * 8 ;addr+=0x100000) {
          loadinfo->base = prom_claim((void *)addr, loadinfo->memsize, 0);
          if (loadinfo->base != (void *)-1) break;
      }
      if (loadinfo->base == (void *)-1) {
          prom_printf("Claim error, can't allocate kernel memory\n");
-         return 0;
-     } 
+         goto bail;
+     }
 
      DEBUG_F("After ELF parsing, load base: %p, mem_sz: 0x%08lx\n",
             loadinfo->base, loadinfo->memsize);
@@ -1108,20 +1194,25 @@ load_elf32(struct boot_file_t *file, loadinfo_t *loadinfo)
          if ((*(file->fs->seek))(file, p->p_offset) != FILE_ERR_OK) {
               prom_printf ("Seek error\n");
               prom_release(loadinfo->base, loadinfo->memsize);
-              return 0;
+              goto bail;
          }
          offset = p->p_vaddr - loadinfo->load_loc;
          if ((*(file->fs->read))(file, p->p_filesz, loadinfo->base+offset) != p->p_filesz) {
               prom_printf ("Read failed\n");
               prom_release(loadinfo->base, loadinfo->memsize);
-              return 0;
+              goto bail;
          }
      }
 
      free(ph);
-    
+
      /* Return success at loading the Elf32 kernel */
      return 1;
+
+bail:
+     if (ph)
+       free(ph);
+     return 0;
 }
 
 static int
@@ -1136,7 +1227,7 @@ load_elf64(struct boot_file_t *file, loadinfo_t *loadinfo)
      /* Read the rest of the Elf header... */
      if ((*(file->fs->read))(file, size, &e->e_version) < size) {
          prom_printf("\nCan't read Elf64 image header\n");
-         return 0;
+         goto bail;
      }
 
      DEBUG_F("Elf64 header:\n");
@@ -1155,24 +1246,24 @@ load_elf64(struct boot_file_t *file, loadinfo_t *loadinfo)
 
      if (e->e_phnum > MAX_HEADERS) {
          prom_printf ("Can only load kernels with one program header\n");
-         return 0;
+         goto bail;
      }
 
      ph = (Elf64_Phdr *)malloc(sizeof(Elf64_Phdr) * e->e_phnum);
      if (!ph) {
          prom_printf ("Malloc error\n");
-         return 0;
+         goto bail;
      }
 
      /* Now, we read the section header */
      if ((*(file->fs->seek))(file, e->e_phoff) != FILE_ERR_OK) {
          prom_printf ("Seek error\n");
-         return 0;
+         goto bail;
      }
      if ((*(file->fs->read))(file, sizeof(Elf64_Phdr) * e->e_phnum, ph) !=
         sizeof(Elf64_Phdr) * e->e_phnum) {
          prom_printf ("Read error\n");
-         return 0;
+         goto bail;
      }
 
      /* Scan through the program header
@@ -1199,7 +1290,7 @@ load_elf64(struct boot_file_t *file, loadinfo_t *loadinfo)
 
      if (loadinfo->memsize == 0) {
          prom_printf("Can't find a loadable segment !\n");
-         return 0;
+         goto bail;
      }
 
      /* leave some room (1Mb) for boot infos */
@@ -1207,18 +1298,29 @@ load_elf64(struct boot_file_t *file, loadinfo_t *loadinfo)
      /* Claim OF memory */
      DEBUG_F("Before prom_claim, mem_sz: 0x%08lx\n", loadinfo->memsize);
 
+     /* Determine whether we are trying to boot a vmlinux or some
+      * other binary image (eg, zImage).  We load vmlinux's at
+      * KERNELADDR and all other binaries at their e_entry value.
+      */
+     if (e->e_entry == KERNEL_LINK_ADDR_PPC64) {
+          flat_vmlinux = 1;
+          loadaddr = KERNELADDR;
+     } else {
+          flat_vmlinux = 0;
+          loadaddr = e->e_entry;
+     }
+
      /* On some systems, loadaddr may already be claimed, so try some
       * other nearby addresses before giving up.
       */
-     loadaddr = (e->e_entry == KERNEL_LINK_ADDR_PPC64) ? KERNELADDR : e->e_entry;
      for(addr=loadaddr; addr <= loadaddr * 8 ;addr+=0x100000) {
          loadinfo->base = prom_claim((void *)addr, loadinfo->memsize, 0);
          if (loadinfo->base != (void *)-1) break;
      }
      if (loadinfo->base == (void *)-1) {
          prom_printf("Claim error, can't allocate kernel memory\n");
-         return 0;
-     } 
+         goto bail;
+     }
 
      DEBUG_F("After ELF parsing, load base: %p, mem_sz: 0x%08lx\n",
             loadinfo->base, loadinfo->memsize);
@@ -1236,20 +1338,25 @@ load_elf64(struct boot_file_t *file, loadinfo_t *loadinfo)
          if ((*(file->fs->seek))(file, p->p_offset) != FILE_ERR_OK) {
               prom_printf ("Seek error\n");
               prom_release(loadinfo->base, loadinfo->memsize);
-              return 0;
+              goto bail;
          }
          offset = p->p_vaddr - loadinfo->load_loc;
          if ((*(file->fs->read))(file, p->p_filesz, loadinfo->base+offset) != p->p_filesz) {
               prom_printf ("Read failed\n");
               prom_release(loadinfo->base, loadinfo->memsize);
-              return 0;
+              goto bail;
          }
      }
 
      free(ph);
-    
+
      /* Return success at loading the Elf64 kernel */
      return 1;
+
+bail:
+     if (ph)
+       free(ph);
+     return 0;
 }
 
 static int
@@ -1326,7 +1433,7 @@ setup_display(void)
               DEBUG_F("Open screen result: %p\n", scrn);
          }
      }
-       
+
      if (scrn == PROM_INVALID_HANDLE) {
          prom_printf("No screen device found !/n");
          return;
@@ -1335,7 +1442,7 @@ setup_display(void)
          prom_set_color(scrn, i, default_colors[i*3],
                         default_colors[i*3+1], default_colors[i*3+2]);
      }
-     prom_printf("\x1b[1;37m\x1b[2;40m");      
+     prom_printf("\x1b[1;37m\x1b[2;40m");
 #ifdef COLOR_TEST
      for (i=0;i<16; i++) {
          prom_printf("\x1b[%d;%dm\x1b[1;47m%s \x1b[2;40m %s\n",
@@ -1349,7 +1456,7 @@ setup_display(void)
                      ansi_color_table[i].name,
                      ansi_color_table[i].name);
      }
-     prom_printf("\x1b[1;37m\x1b[2;40m");      
+     prom_printf("\x1b[1;37m\x1b[2;40m");
 #endif /* COLOR_TEST */
 
 #if !DEBUG
@@ -1366,18 +1473,19 @@ yaboot_main(void)
 
      if (_machine == _MACH_Pmac)
          setup_display();
-       
-     prom_get_chosen("bootpath", bootdevice, sizeof(bootdevice));
+
+     prom_get_chosen("bootpath", bootdevice, BOOTDEVSZ);
      DEBUG_F("/chosen/bootpath = %s\n", bootdevice);
-     if (bootdevice[0] == 0)
-         prom_get_options("boot-device", bootdevice, sizeof(bootdevice));
+     if (bootdevice[0] == 0) {
+         prom_get_options("boot-device", bootdevice, BOOTDEVSZ);
+         DEBUG_F("boot-device = %s\n", bootdevice);
+     }
      if (bootdevice[0] == 0) {
          prom_printf("Couldn't determine boot device\n");
          return -1;
      }
 
-     if (!parse_device_path(bootdevice, (_machine == _MACH_Pmac) ? "hd" : "disc",
-                           -1, "", &boot)) {
+     if (!parse_device_path(bootdevice, NULL, -1, "", &boot)) {
          prom_printf("%s: Unable to parse\n", bootdevice);
          return -1;
      }
@@ -1403,7 +1511,7 @@ yaboot_main(void)
                    strcat(boot.file, "\\");
          }
      }
-     DEBUG_F("After path fixup: dev=%s, part=%d, file=%s\n",
+     DEBUG_F("After pmac path kludgeup: dev=%s, part=%d, file=%s\n",
             boot.dev, boot.part, boot.file);
 
      useconf = load_config_file(boot.dev, boot.file, boot.part);
@@ -1425,18 +1533,20 @@ yaboot_main(void)
               if ((ptype != NULL) && (strcmp(ptype, "Apple_Bootstrap")))
                    prom_printf("\nWARNING: Bootstrap partition type is wrong: \"%s\"\n"
                                "         type should be: \"Apple_Bootstrap\"\n\n", ptype);
+              if (ptype)
+                   free(ptype);
          }
      }
 
      yaboot_text_ui();
-       
+
      prom_printf("Bye.\n");
      return 0;
 }
 
-/* 
+/*
  * Local variables:
- * c-file-style: "K&R"
+ * c-file-style: "k&r"
  * c-basic-offset: 5
  * End:
  */