30abbd6b181ab6a79c9d317e9d6159ae2bb7767a
[yaboot.git] / second / yaboot.c
1 /*
2  *  Yaboot - secondary boot loader for Linux on PowerPC.
3  *
4  *  Copyright (C) 2001, 2002 Ethan Benson
5  *
6  *  Copyright (C) 1999, 2000, 2001 Benjamin Herrenschmidt
7  *
8  *  IBM CHRP support
9  *
10  *  Copyright (C) 2001 Peter Bergner
11  *
12  *  portions based on poof
13  *
14  *  Copyright (C) 1999 Marius Vollmer
15  *
16  *  portions based on quik
17  *
18  *  Copyright (C) 1996 Paul Mackerras.
19  *
20  *  Because this program is derived from the corresponding file in the
21  *  silo-0.64 distribution, it is also
22  *
23  *  Copyright (C) 1996 Pete A. Zaitcev
24  *                1996 Maurizio Plaza
25  *                1996 David S. Miller
26  *                1996 Miguel de Icaza
27  *                1996 Jakub Jelinek
28  *
29  *  This program is free software; you can redistribute it and/or modify
30  *  it under the terms of the GNU General Public License as published by
31  *  the Free Software Foundation; either version 2 of the License, or
32  *  (at your option) any later version.
33  *
34  *  This program is distributed in the hope that it will be useful,
35  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
36  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37  *  GNU General Public License for more details.
38  *
39  *  You should have received a copy of the GNU General Public License
40  *  along with this program; if not, write to the Free Software
41  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
42  */
43
44 #include "stdarg.h"
45 #include "string.h"
46 #include "ctype.h"
47 #include "stdlib.h"
48 #include "prom.h"
49 #include "file.h"
50 #include "errors.h"
51 #include "cfg.h"
52 #include "cmdline.h"
53 #include "yaboot.h"
54 #include "linux/elf.h"
55 #include "bootinfo.h"
56 #include "debug.h"
57
58 #define CONFIG_FILE_NAME        "yaboot.conf"
59 #define CONFIG_FILE_MAX         0x8000          /* 32k */
60
61 #ifdef USE_MD5_PASSWORDS
62 #include "md5.h"
63 #endif /* USE_MD5_PASSWORDS */
64
65 /* align addr on a size boundry - adjust address up if needed -- Cort */
66 #define _ALIGN(addr,size)       (((addr)+size-1)&(~(size-1)))
67
68 /* Addresses where the PPC32 and PPC64 vmlinux kernels are linked at.
69  * These are used to determine whether we are booting a vmlinux, in
70  * which case, it will be loaded at KERNELADDR.  Otherwise (eg zImage),
71  * we load the binary where it was linked at (ie, e_entry field in
72  * the ELF header).
73  */
74 #define KERNEL_LINK_ADDR_PPC32  0xC0000000UL
75 #define KERNEL_LINK_ADDR_PPC64  0xC000000000000000ULL
76
77 typedef struct {
78      union {
79           Elf32_Ehdr  elf32hdr;
80           Elf64_Ehdr  elf64hdr;
81      } elf;
82      void*          base;
83      unsigned long   memsize;
84      unsigned long   filesize;
85      unsigned long   offset;
86      unsigned long   load_loc;
87      unsigned long   entry;
88 } loadinfo_t;
89
90 typedef void (*kernel_entry_t)( void *,
91                                 unsigned long,
92                                 prom_entry,
93                                 unsigned long,
94                                 unsigned long );
95
96 /* Imported functions */
97 extern unsigned long reloc_offset(void);
98 extern long flush_icache_range(unsigned long start, unsigned long stop);
99
100 /* Exported functions */
101 int     yaboot_start(unsigned long r3, unsigned long r4, unsigned long r5);
102
103 /* Local functions */
104 static int      yaboot_main(void);
105 static int      is_elf32(loadinfo_t *loadinfo);
106 static int      is_elf64(loadinfo_t *loadinfo);
107 static int      load_elf32(struct boot_file_t *file, loadinfo_t *loadinfo);
108 static int      load_elf64(struct boot_file_t *file, loadinfo_t *loadinfo);
109 static void     setup_display(void);
110
111 /* Locals & globals */
112
113 int useconf = 0;
114 char bootdevice[BOOTDEVSZ];
115 char bootoncelabel[1024];
116 char bootargs[1024];
117 char *password = NULL;
118 struct boot_fspec_t boot;
119 int _machine = _MACH_Pmac;
120 int flat_vmlinux;
121
122 #ifdef CONFIG_COLOR_TEXT
123
124 /* Color values for text ui */
125 static struct ansi_color_t {
126      char*      name;
127      int        index;
128      int        value;
129 } ansi_color_table[] = {
130      { "black",         2, 30 },
131      { "blue",          0, 31 },
132      { "green",         0, 32 },
133      { "cyan",          0, 33 },
134      { "red",           0, 34 },
135      { "purple",                0, 35 },
136      { "brown",         0, 36 },
137      { "light-gray",    0, 37 },
138      { "dark-gray",             1, 30 },
139      { "light-blue",            1, 31 },
140      { "light-green",   1, 32 },
141      { "light-cyan",            1, 33 },
142      { "light-red",             1, 34 },
143      { "light-purple",  1, 35 },
144      { "yellow",                1, 36 },
145      { "white",         1, 37 },
146      { NULL,                    0, 0 },
147 };
148
149 /* Default colors for text ui */
150 int fgcolor = 15;
151 int bgcolor = 0;
152 #endif /* CONFIG_COLOR_TEXT */
153
154 #if DEBUG
155 static int test_bss;
156 static int test_data = 0;
157 #endif
158 static int pause_after;
159 static char *pause_message = "Type go<return> to continue.\n";
160 static char given_bootargs[1024];
161 static int given_bootargs_by_user = 0;
162
163 extern unsigned char linux_logo_red[];
164 extern unsigned char linux_logo_green[];
165 extern unsigned char linux_logo_blue[];
166
167 #define DEFAULT_TIMEOUT         -1
168
169 /* Entry, currently called directly by crt0 (bss not inited) */
170
171 extern char* __bss_start;
172 extern char* _end;
173
174 static struct first_info *quik_fip = NULL;
175
176 int
177 yaboot_start (unsigned long r3, unsigned long r4, unsigned long r5)
178 {
179      int result;
180      void* malloc_base = NULL;
181      prom_handle root;
182
183      /* OF seems to do it, but I'm not very confident */
184      memset(&__bss_start, 0, &_end - &__bss_start);
185
186      /* Check for quik first stage bootloader (but I don't think we are
187       * compatible with it anyway, I'll look into backporting to older OF
188       * versions later
189       */
190      if (r5 == 0xdeadbeef) {
191           r5 = r3;
192           quik_fip = (struct first_info *)r4;
193      }
194
195      /* Initialize OF interface */
196      prom_init ((prom_entry) r5);
197
198      /* Allocate some memory for malloc'ator */
199      malloc_base = prom_claim((void *)MALLOCADDR, MALLOCSIZE, 0);
200      if (malloc_base == (void *)-1) {
201           prom_printf("Can't claim malloc buffer (%d bytes at 0x%08x)\n",
202                       MALLOCSIZE, MALLOCADDR);
203           return -1;
204      }
205      malloc_init(malloc_base, MALLOCSIZE);
206      DEBUG_F("Malloc buffer allocated at %p (%d bytes)\n",
207              malloc_base, MALLOCSIZE);
208
209      /* A few useless DEBUG_F's */
210      DEBUG_F("reloc_offset :  %ld         (should be 0)\n", reloc_offset());
211      DEBUG_F("test_bss     :  %d         (should be 0)\n", test_bss);
212      DEBUG_F("test_data    :  %d         (should be 0)\n", test_data);
213      DEBUG_F("&test_data   :  %p\n", &test_data);
214      DEBUG_F("&test_bss    :  %p\n", &test_bss);
215      DEBUG_F("linked at    :  0x%08x\n", TEXTADDR);
216
217      /* ask the OF info if we're a chrp or pmac */
218      /* we need to set _machine before calling finish_device_tree */
219      root = prom_finddevice("/");
220      if (root != 0) {
221           static char model[256];
222           if (prom_getprop(root, "device_type", model, 256 ) > 0 &&
223               !strncmp("chrp", model, 4))
224                _machine = _MACH_chrp;
225           else {
226                if (prom_getprop(root, "model", model, 256 ) > 0 &&
227                    !strncmp(model, "IBM", 3))
228                     _machine = _MACH_chrp;
229           }
230      }
231
232      DEBUG_F("Running on _machine = %d\n", _machine);
233      DEBUG_SLEEP;
234
235      /* Call out main */
236      result = yaboot_main();
237
238      /* Get rid of malloc pool */
239      malloc_dispose();
240      prom_release(malloc_base, MALLOCSIZE);
241      DEBUG_F("Malloc buffer released. Exiting with code %d\n",
242              result);
243
244      /* Return to OF */
245      prom_exit();
246
247      return result;
248
249 }
250
251 #ifdef CONFIG_COLOR_TEXT
252 /*
253  * Validify color for text ui
254  */
255 static int
256 check_color_text_ui(char *color)
257 {
258      int i = 0;
259      while(ansi_color_table[i].name) {
260           if (!strcmp(color, ansi_color_table[i].name))
261                return i;
262           i++;
263      }
264      return -1;
265 }
266 #endif /* CONFIG_COLOR_TEXT */
267
268
269 void print_message_file(char *filename)
270 {
271      char *msg = NULL;
272      char *p, *endp;
273      char *defdev = boot.dev;
274      int defpart = boot.part;
275      char msgpath[1024];
276      int opened = 0;
277      int result = 0;
278      int n;
279      struct boot_file_t file;
280      struct boot_fspec_t msgfile;
281
282      defdev = cfg_get_strg(0, "device");
283      if (!defdev)
284           defdev = boot.dev;
285      p = cfg_get_strg(0, "partition");
286           if (p) {
287                n = simple_strtol(p, &endp, 10);
288                if (endp != p && *endp == 0)
289                     defpart = n;
290           }
291
292      strncpy(msgpath, filename, sizeof(msgpath));
293      if (!parse_device_path(msgpath, defdev, defpart, "/etc/yaboot.msg", &msgfile)) {
294           prom_printf("%s: Unable to parse\n", msgpath);
295           goto done;
296      }
297
298      result = open_file(&msgfile, &file);
299      if (result != FILE_ERR_OK) {
300           prom_printf("%s:%d,", msgfile.dev, msgfile.part);
301           prom_perror(result, msgfile.file);
302           goto done;
303      } else
304           opened = 1;
305
306      msg = malloc(2001);
307      if (!msg)
308           goto done;
309      else
310           memset(msg, 0, 2001);
311
312      if (file.fs->read(&file, 2000, msg) <= 0)
313           goto done;
314      else
315           prom_printf("%s", msg);
316
317 done:
318      if (opened)
319           file.fs->close(&file);
320      if (msg)
321           free(msg);
322 }
323
324 /* Currently, the config file must be at the root of the filesystem.
325  * todo: recognize the full path to myself and use it to load the
326  * config file. Handle the "\\" (blessed system folder)
327  */
328 static int
329 load_config_file(struct boot_fspec_t *fspec)
330 {
331      char *conf_file = NULL, *p;
332      struct boot_file_t file;
333      int sz, opened = 0, result = 0;
334
335      /* Allocate a buffer for the config file */
336      conf_file = malloc(CONFIG_FILE_MAX);
337      if (!conf_file) {
338           prom_printf("Can't alloc config file buffer\n");
339           goto bail;
340      }
341
342      /* Open it */
343      result = open_file(fspec, &file);
344      if (result != FILE_ERR_OK) {
345           prom_printf("%s:%d,", fspec->dev, fspec->part);
346           prom_perror(result, fspec->file);
347           prom_printf("Can't open config file\n");
348           goto bail;
349      }
350      opened = 1;
351
352      /* Read it */
353      sz = file.fs->read(&file, CONFIG_FILE_MAX, conf_file);
354      if (sz <= 0) {
355           prom_printf("Error, can't read config file\n");
356           goto bail;
357      }
358      prom_printf("Config file read, %d bytes\n", sz);
359
360      /* Close the file */
361      if (opened)
362           file.fs->close(&file);
363      opened = 0;
364
365      /* Call the parsing code in cfg.c */
366      if (cfg_parse(fspec->file, conf_file, sz) < 0) {
367           prom_printf ("Syntax error or read error config\n");
368           goto bail;
369      }
370
371      DEBUG_F("Config file successfully parsed, %d bytes\n", sz);
372
373      /* Now, we do the initialisations stored in the config file */
374      p = cfg_get_strg(0, "init-code");
375      if (p)
376           prom_interpret(p);
377
378      password = cfg_get_strg(0, "password");
379
380 #ifdef CONFIG_COLOR_TEXT
381      p = cfg_get_strg(0, "fgcolor");
382      if (p) {
383           DEBUG_F("fgcolor=%s\n", p);
384           fgcolor = check_color_text_ui(p);
385           if (fgcolor == -1) {
386                prom_printf("Invalid fgcolor: \"%s\".\n", p);
387           }
388      }
389      p = cfg_get_strg(0, "bgcolor");
390      if (p) {
391           DEBUG_F("bgcolor=%s\n", p);
392           bgcolor = check_color_text_ui(p);
393           if (bgcolor == -1)
394                prom_printf("Invalid bgcolor: \"%s\".\n", p);
395      }
396      if (bgcolor >= 0) {
397           char temp[64];
398           sprintf(temp, "%x to background-color", bgcolor);
399           prom_interpret(temp);
400 #if !DEBUG
401           prom_printf("\xc");
402 #endif /* !DEBUG */
403      }
404      if (fgcolor >= 0) {
405           char temp[64];
406           sprintf(temp, "%x to foreground-color", fgcolor);
407           prom_interpret(temp);
408      }
409 #endif /* CONFIG_COLOR_TEXT */
410
411      p = cfg_get_strg(0, "init-message");
412      if (p)
413           prom_printf("%s\n", p);
414
415      p = cfg_get_strg(0, "message");
416      if (p)
417           print_message_file(p);
418
419      result = 1;
420
421 bail:
422
423      if (opened)
424           file.fs->close(&file);
425
426      if (conf_file)
427           free(conf_file);
428
429      return result;
430 }
431
432 /*
433  * Search for config file by MAC address, then by IP address.
434  * Basically copying pxelinux's algorithm.
435  * http://syslinux.zytor.com/pxe.php#config
436  */
437 static int load_my_config_file(struct boot_fspec_t *orig_fspec)
438 {
439      struct bootp_packet *packet;
440      int rc = 0;
441      struct boot_fspec_t fspec = *orig_fspec;
442 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
443
444      packet = prom_get_netinfo();
445
446      /*
447       * First, try to match on mac address with the hardware type
448       * prepended.
449       */
450
451      /* 3 chars per byte in chaddr + 2 chars for htype + /etc/ +\0 */
452      fspec.file = malloc(packet->hlen * 3 + 2 + 6);
453      if (!fspec.file)
454           goto out;
455
456      if (_machine == _MACH_chrp)
457          sprintf(fspec.file, "/etc/%02x-", packet->htype);
458      else
459          sprintf(fspec.file, "%02x-", packet->htype);
460      strcat(fspec.file, prom_get_mac(packet));
461
462      rc = load_config_file(&fspec);
463      if (rc)
464           goto out;
465
466
467      /*
468       * Now try to match on IP.
469       */
470      free(fspec.file);
471      /* 8 chars in yiaddr + \0 */
472      fspec.file = malloc(9);
473      if (!fspec.file)
474          goto out;
475
476      strcat(fspec.file, prom_get_ip(packet));
477
478      while (strlen(fspec.file)) {
479           rc = load_config_file(&fspec);
480           if (rc)
481                goto out;
482           /* Chop one digit off the end, try again */
483           fspec.file[strlen(fspec.file) - 1] = '\0';
484      }
485
486  out:
487      if (rc) /* modify original only on success */
488           orig_fspec->file = fspec.file;
489      else
490           free(fspec.file);
491      return rc;
492 }
493
494 void maintabfunc (void)
495 {
496      if (useconf) {
497           cfg_print_images();
498           prom_printf("boot: %s", cbuff);
499      }
500 }
501
502 void
503 word_split(char **linep, char **paramsp)
504 {
505      char *p;
506
507      *paramsp = 0;
508      p = *linep;
509      if (p == 0)
510           return;
511      while (*p == ' ')
512           ++p;
513      if (*p == 0) {
514           *linep = 0;
515           return;
516      }
517      *linep = p;
518      while (*p != 0 && *p != ' ')
519           ++p;
520      while (*p == ' ')
521           *p++ = 0;
522      if (*p != 0)
523           *paramsp = p;
524 }
525
526 char *
527 make_params(char *label, char *params)
528 {
529      char *p, *q;
530      static char buffer[2048];
531
532      q = buffer;
533      *q = 0;
534
535      p = cfg_get_strg(label, "literal");
536      if (p) {
537           strcpy(q, p);
538           q = strchr(q, 0);
539           if (params) {
540                if (*p)
541                     *q++ = ' ';
542                strcpy(q, params);
543           }
544           return buffer;
545      }
546
547      p = cfg_get_strg(label, "root");
548      if (p) {
549           strcpy (q, "root=");
550           strcpy (q + 5, p);
551           q = strchr (q, 0);
552           *q++ = ' ';
553      }
554      if (cfg_get_flag(label, "read-only")) {
555           strcpy (q, "ro ");
556           q += 3;
557      }
558      if (cfg_get_flag(label, "read-write")) {
559           strcpy (q, "rw ");
560           q += 3;
561      }
562      p = cfg_get_strg(label, "ramdisk");
563      if (p) {
564           strcpy (q, "ramdisk=");
565           strcpy (q + 8, p);
566           q = strchr (q, 0);
567           *q++ = ' ';
568      }
569      p = cfg_get_strg(label, "initrd-size");
570      if (p) {
571           strcpy (q, "ramdisk_size=");
572           strcpy (q + 13, p);
573           q = strchr (q, 0);
574           *q++ = ' ';
575      }
576      if (cfg_get_flag(label, "novideo")) {
577           strcpy (q, "video=ofonly");
578           q = strchr (q, 0);
579           *q++ = ' ';
580      }
581      p = cfg_get_strg (label, "append");
582      if (p) {
583           strcpy (q, p);
584           q = strchr (q, 0);
585           *q++ = ' ';
586      }
587      *q = 0;
588      pause_after = cfg_get_flag (label, "pause-after");
589      p = cfg_get_strg(label, "pause-message");
590      if (p)
591           pause_message = p;
592      if (params)
593           strcpy(q, params);
594
595      return buffer;
596 }
597
598 void check_password(char *str)
599 {
600      int i;
601
602      prom_printf("\n%s", str);
603      for (i = 0; i < 3; i++) {
604           prom_printf ("\nPassword: ");
605           passwdbuff[0] = 0;
606           cmdedit ((void (*)(void)) 0, 1);
607           prom_printf ("\n");
608 #ifdef USE_MD5_PASSWORDS
609           if (!strncmp (password, "$1$", 3)) {
610                if (!check_md5_password(passwdbuff, password))
611                     return;
612           }
613           else if (!strcmp (password, passwdbuff))
614                return;
615 #else /* !MD5 */
616           if (!strcmp (password, passwdbuff))
617                return;
618 #endif /* USE_MD5_PASSWORDS */
619           if (i < 2) {
620                prom_sleep(1);
621                prom_printf ("Incorrect password.  Try again.");
622           }
623      }
624      prom_printf(" ___________________\n< Permission denied >\n -------------------\n"
625                  "        \\   ^__^\n         \\  (oo)\\_______\n            (__)\\       )\\/\\\n"
626                  "                ||----w |\n                ||     ||\n");
627      prom_sleep(4);
628      prom_interpret("reset-all");
629 }
630
631 int get_params(struct boot_param_t* params)
632 {
633      int defpart;
634      char *defdevice = 0;
635      char defdevice_bak[1024];
636      char *p, *q, *endp;
637      int c, n;
638      char *imagename = 0, *label;
639      int timeout = -1;
640      int beg = 0, end;
641      int singlekey = 0;
642      int restricted = 0;
643      static int first = 1;
644      static char imagepath[1024];
645      static char initrdpath[1024];
646      static char sysmappath[1024];
647
648      pause_after = 0;
649      memset(params, 0, sizeof(*params));
650      params->args = "";
651      params->kernel.part = -1;
652      params->rd.part = -1;
653      params->sysmap.part = -1;
654      defpart = boot.part;
655
656      cmdinit();
657
658      if (first) {
659           first = 0;
660           imagename = bootargs;
661           word_split(&imagename, &params->args);
662           timeout = DEFAULT_TIMEOUT;
663           if (imagename) {
664                prom_printf("Default supplied on the command line: %s ", imagename);
665                if (params->args)
666                     prom_printf("%s", params->args);
667                prom_printf("\n");
668           }
669           if (useconf && (q = cfg_get_strg(0, "timeout")) != 0 && *q != 0)
670                timeout = simple_strtol(q, NULL, 0);
671      }
672
673      prom_printf("boot: ");
674      c = -1;
675      if (timeout != -1) {
676           beg = prom_getms();
677           if (timeout > 0) {
678                end = beg + 100 * timeout;
679                do {
680                     c = prom_nbgetchar();
681                } while (c == -1 && prom_getms() <= end);
682           }
683           if (c == -1)
684                c = '\n';
685           else if (c != '\n' && c != '\t' && c != '\r' && c != '\b' ) {
686                cbuff[0] = c;
687                cbuff[1] = 0;
688           }
689      }
690
691      if (c != -1 && c != '\n' && c != '\r') {
692           if (c == '\t') {
693                maintabfunc ();
694           }  else if (c >= ' ') {
695                cbuff[0] = c;
696                cbuff[1] = 0;
697                if ((cfg_get_flag (cbuff, "single-key")) && useconf) {
698                     imagename = cbuff;
699                     singlekey = 1;
700                     prom_printf("%s\n", cbuff);
701                }
702           }
703      }
704
705      if (c == '\n' || c == '\r') {
706           if (!imagename) {
707                if (bootoncelabel[0] != 0)
708                     imagename = bootoncelabel;
709                else
710                     imagename = cfg_get_default();
711           }
712           if (imagename)
713                prom_printf("%s", imagename);
714           if (params->args)
715                prom_printf(" %s", params->args);
716           prom_printf("\n");
717      } else if (!singlekey) {
718           cmdedit(maintabfunc, 0);
719           prom_printf("\n");
720           strcpy(given_bootargs, cbuff);
721           given_bootargs_by_user = 1;
722           imagename = cbuff;
723           word_split(&imagename, &params->args);
724      }
725
726      /* chrp gets this wrong, force it -- Cort */
727      if ( useconf && (!imagename || imagename[0] == 0 ))
728           imagename = cfg_get_default();
729
730      label = 0;
731      defdevice = boot.dev;
732
733      strcpy(defdevice_bak,defdevice);
734
735      if (useconf) {
736           defdevice = cfg_get_strg(0, "device");
737           p = cfg_get_strg(0, "partition");
738           if (p) {
739                n = simple_strtol(p, &endp, 10);
740                if (endp != p && *endp == 0)
741                     defpart = n;
742           }
743           p = cfg_get_strg(0, "pause-message");
744           if (p)
745                pause_message = p;
746           if (cfg_get_flag(0, "restricted"))
747                restricted = 1;
748           p = cfg_get_strg(imagename, "image");
749           if (p && *p) {
750                label = imagename;
751                imagename = p;
752                defdevice = cfg_get_strg(label, "device");
753                if(!defdevice) defdevice=boot.dev;
754                p = cfg_get_strg(label, "partition");
755                if (p) {
756                     n = simple_strtol(p, &endp, 10);
757                     if (endp != p && *endp == 0)
758                          defpart = n;
759                }
760                if (cfg_get_flag(label, "restricted"))
761                     restricted = 1;
762                if (label) {
763                     if (params->args && password && restricted)
764                          check_password ("To specify arguments for this image "
765                                          "you must enter the password.");
766                     else if (password && !restricted)
767                          check_password ("This image is restricted.");
768                }
769                params->args = make_params(label, params->args);
770           }
771      }
772
773      if (!strcmp (imagename, "help")) {
774           /* FIXME: defdevice shouldn't need to be reset all over the place */
775           if(!defdevice) defdevice = boot.dev;
776           prom_printf(
777                "\nPress the tab key for a list of defined images.\n"
778                "The label marked with a \"*\" is is the default image, "
779                "press <return> to boot it.\n\n"
780                "To boot any other label simply type its name and press <return>.\n\n"
781                "To boot a kernel image which is not defined in the yaboot configuration \n"
782                "file, enter the kernel image name as [[device:][partno],]/path, where \n"
783                "\"device:\" is the OpenFirmware device path to the disk the image \n"
784                "resides on, and \"partno\" is the partition number the image resides on.\n"
785                "Note that the comma (,) is only required if you specify an OpenFirmware\n"
786                "device, if you only specify a filename you should not start it with a \",\"\n\n"
787                "To load an alternative config file rather than /etc/yaboot.conf, enter\n"
788                "its device, partno and path, on Open Firmware Prompt:\n"
789                "boot conf=device:partno,/path/to/configfile\n."
790                "To reload the config file or load a new one, use the \"conf\" command\n"
791                "on Yaboot's prompt:\n"
792                "conf [device=device] [partition=partno] [file=/path/to/configfile]\n\n"
793                "If you omit \"device\" and \"partno\", Yaboot will use their current\n"
794                "values. You can check them by entering \"conf\" on Yaboot's prompt.\n");
795
796           return 0;
797      }
798
799      if (!strcmp (imagename, "halt")) {
800           if (password)
801                check_password ("Restricted command.");
802           prom_pause();
803           return 0;
804      }
805      if (!strcmp (imagename, "bye")) {
806           if (password) {
807                check_password ("Restricted command.");
808                return 1;
809           }
810           return 1;
811      }
812
813      if (!strncmp (imagename, "conf", 4)) {
814
815          // imagename = "conf file=blah dev=bleh part=blih"
816          DEBUG_F("Loading user-specified config file: %s\n",imagename);
817          if (password) {
818              check_password ("Restricted command.");
819              return 1;
820          }
821
822          // args= "file=blah dev=bleh part=blih"
823          char *args = params->args;
824
825          if (strlen(args)){
826
827             // set a pointer to the first space in args
828             char *space = strchr(args,' ');
829
830             int loop = 3;
831             while (loop > 0){
832                 char temp[1024] = "0";
833
834                 // copy next argument to temp
835                 strncpy(temp, args, space-args);
836
837                 // parse temp and set boot arguments
838                 if (!strncmp (temp, "file=", 5)){
839                    DEBUG_F("conf file: %s\n", temp+5);
840                    strcpy(boot.file, temp+5);
841                 } else if (!strncmp (temp, "device=", 7)){
842                    DEBUG_F("conf device: %s\n", temp+7);
843                    strcpy(boot.dev, temp+7);
844                 } else if (!strncmp (temp, "partition=", 10)){
845                    DEBUG_F("conf partition: %s\n", temp+10);
846                    boot.part=simple_strtol(temp+10,NULL,10);
847                 } else
848                    space = NULL;
849
850                 // set the pointer to the next space in args;
851                 // set the loop control variable
852                 if (strlen(space)>1){
853                     // Go to the next argument
854                     args = space+1;
855
856                     loop--;
857                     if (strchr(args,' ') == NULL)
858                         space = &args[strlen(args)];
859                     else
860                         space = strchr(args,' ');
861                 } else {
862                     loop = -1;
863                     space = NULL;
864                 }
865             }
866
867             prom_printf("Loading config file...\n");
868             useconf = load_config_file(&boot);
869             if (useconf > 0){
870                 if ((q = cfg_get_strg(0, "timeout")) != 0 && *q != 0)
871                    timeout = simple_strtol(q, NULL, 0);
872             } else {
873                prom_printf("Restoring default values.\n");
874                strcpy(boot.file,"");
875                strcpy(boot.dev, defdevice_bak);
876                boot.part = defpart;
877             }
878
879          } else {
880              prom_printf("Current configuration:\n");
881              prom_printf("device: %s\n", boot.dev);
882              if (boot.part < 0)
883                  prom_printf("partition: auto\n");
884              else
885                  prom_printf("partition: %d\n", boot.part);
886              if (strlen(boot.file))
887                  prom_printf("file: %s\n", boot.file);
888              else
889                  prom_printf("file: /etc/%s\n",CONFIG_FILE_NAME);
890          }
891
892          imagename = "\0";
893          params->args = "\0";
894
895          return 0;
896      }
897
898      if (imagename[0] == '$') {
899           /* forth command string */
900           if (password)
901                check_password ("OpenFirmware commands are restricted.");
902           prom_interpret(imagename+1);
903           return 0;
904      }
905
906      strncpy(imagepath, imagename, 1024);
907
908      if (!label && password)
909           check_password ("To boot a custom image you must enter the password.");
910
911      if (!parse_device_path(imagepath, defdevice, defpart,
912                             "/vmlinux", &params->kernel)) {
913           prom_printf("%s: Unable to parse\n", imagepath);
914           return 0;
915      }
916      DEBUG_F("after parse_device_path: dev=%s part=%d file=%s\n", params->kernel.dev,
917              params->kernel.part, params->kernel.file);
918
919      if (useconf) {
920           p = cfg_get_strg(label, "initrd");
921           if (p && *p) {
922                DEBUG_F("Parsing initrd path <%s>\n", p);
923                strncpy(initrdpath, p, 1024);
924                if (!parse_device_path(initrdpath, defdevice, defpart,
925                                       "/root.bin", &params->rd)) {
926                     prom_printf("%s: Unable to parse\n", imagepath);
927                     return 0;
928                }
929           }
930           p = cfg_get_strg(label, "sysmap");
931           if (p && *p) {
932                DEBUG_F("Parsing sysmap path <%s>\n", p);
933                strncpy(sysmappath, p, 1024);
934                if (!parse_device_path(sysmappath, defdevice, defpart,
935                                       "/boot/System.map", &params->sysmap)) {
936                     prom_printf("%s: Unable to parse\n", imagepath);
937                     return 0;
938                }
939           }
940      }
941      return 0;
942 }
943
944 /* This is derived from quik core. To be changed to first parse the headers
945  * doing lazy-loading, and then claim the memory before loading the kernel
946  * to it
947  * We also need to add initrd support to this whole mecanism
948  */
949 void
950 yaboot_text_ui(void)
951 {
952 #define MAX_HEADERS     32
953
954      struct boot_file_t file;
955      int                        result;
956      static struct boot_param_t params;
957      void               *initrd_base;
958      unsigned long      initrd_size;
959      void                *sysmap_base;
960      unsigned long      sysmap_size;
961      kernel_entry_t      kernel_entry;
962      struct bi_record*  birec;
963      char*               loc=NULL;
964      loadinfo_t          loadinfo;
965      void                *initrd_more,*initrd_want;
966      unsigned long       initrd_read;
967
968      loadinfo.load_loc = 0;
969
970      for (;;) {
971           initrd_size = 0;
972           initrd_base = 0;
973           sysmap_base = 0;
974           sysmap_size = 0;
975
976           if (get_params(&params))
977                return;
978           if (!params.kernel.file)
979                continue;
980
981           prom_printf("Please wait, loading kernel...\n");
982
983           memset(&file, 0, sizeof(file));
984
985           if (strlen(boot.file) && !strcmp(boot.file,"\\\\") && params.kernel.file[0] != '/'
986               && params.kernel.file[0] != '\\') {
987                loc=(char*)malloc(strlen(params.kernel.file)+3);
988                if (!loc) {
989                     prom_printf ("malloc error\n");
990                     goto next;
991                }
992                strcpy(loc,boot.file);
993                strcat(loc,params.kernel.file);
994                free(params.kernel.file);
995                params.kernel.file=loc;
996           }
997           result = open_file(&params.kernel, &file);
998           if (result != FILE_ERR_OK) {
999                prom_printf("%s:%d,", params.kernel.dev, params.kernel.part);
1000                prom_perror(result, params.kernel.file);
1001                goto next;
1002           }
1003
1004           /* Read the Elf e_ident, e_type and e_machine fields to
1005            * determine Elf file type
1006            */
1007           if (file.fs->read(&file, sizeof(Elf_Ident), &loadinfo.elf) < sizeof(Elf_Ident)) {
1008                prom_printf("\nCan't read Elf e_ident/e_type/e_machine info\n");
1009                file.fs->close(&file);
1010                memset(&file, 0, sizeof(file));
1011                goto next;
1012           }
1013
1014           if (is_elf32(&loadinfo)) {
1015                if (!load_elf32(&file, &loadinfo)) {
1016                     file.fs->close(&file);
1017                     memset(&file, 0, sizeof(file));
1018                     goto next;
1019                }
1020                prom_printf("   Elf32 kernel loaded...\n");
1021           } else if (is_elf64(&loadinfo)) {
1022                if (!load_elf64(&file, &loadinfo)) {
1023                     file.fs->close(&file);
1024                     memset(&file, 0, sizeof(file));
1025                     goto next;
1026                }
1027                prom_printf("   Elf64 kernel loaded...\n");
1028           } else {
1029                prom_printf ("%s: Not a valid ELF image\n", params.kernel.file);
1030                file.fs->close(&file);
1031                memset(&file, 0, sizeof(file));
1032                goto next;
1033           }
1034           file.fs->close(&file);
1035           memset(&file, 0, sizeof(file));
1036
1037           /* If sysmap, load it (only if booting a vmlinux).
1038            */
1039           if (flat_vmlinux && params.sysmap.file) {
1040                prom_printf("Loading System.map ...\n");
1041                if(strlen(boot.file) && !strcmp(boot.file,"\\\\") && params.sysmap.file[0] != '/'
1042                   && params.sysmap.file[0] != '\\') {
1043                     if (loc) free(loc);
1044                     loc=(char*)malloc(strlen(params.sysmap.file)+3);
1045                     if (!loc) {
1046                          prom_printf ("malloc error\n");
1047                          goto next;
1048                     }
1049                     strcpy(loc,boot.file);
1050                     strcat(loc,params.sysmap.file);
1051                     free(params.sysmap.file);
1052                     params.sysmap.file=loc;
1053                }
1054
1055                result = open_file(&params.sysmap, &file);
1056                if (result != FILE_ERR_OK) {
1057                     prom_printf("%s:%d,", params.sysmap.dev, params.sysmap.part);
1058                     prom_perror(result, params.sysmap.file);
1059                }
1060                else {
1061                     sysmap_base = prom_claim(loadinfo.base+loadinfo.memsize, 0x100000, 0);
1062                     if (sysmap_base == (void *)-1) {
1063                          prom_printf("Claim failed for sysmap memory\n");
1064                          prom_pause();
1065                          sysmap_base = 0;
1066                     } else {
1067                          sysmap_size = file.fs->read(&file, 0xfffff, sysmap_base);
1068                          if (sysmap_size == 0)
1069                               sysmap_base = 0;
1070                          else
1071                               ((char *)sysmap_base)[sysmap_size++] = 0;
1072                     }
1073                     file.fs->close(&file);
1074                     memset(&file, 0, sizeof(file));
1075                }
1076                if (sysmap_base) {
1077                     prom_printf("System.map loaded at %p, size: %lu Kbytes\n",
1078                                 sysmap_base, sysmap_size >> 10);
1079                     loadinfo.memsize += _ALIGN(0x100000, 0x1000);
1080                } else {
1081                     prom_printf("System.map load failed !\n");
1082                     prom_pause();
1083                }
1084           }
1085
1086           /* If ramdisk, load it (only if booting a vmlinux).  For now, we
1087            * can't tell the size it will be so we claim an arbitrary amount
1088            * of 4Mb.
1089            */
1090           if (flat_vmlinux && params.rd.file) {
1091                if(strlen(boot.file) && !strcmp(boot.file,"\\\\") && params.rd.file[0] != '/'
1092                   && params.kernel.file[0] != '\\')
1093                {
1094                     if (loc) free(loc);
1095                     loc=(char*)malloc(strlen(params.rd.file)+3);
1096                     if (!loc) {
1097                          prom_printf ("Malloc error\n");
1098                          goto next;
1099                     }
1100                     strcpy(loc,boot.file);
1101                     strcat(loc,params.rd.file);
1102                     free(params.rd.file);
1103                     params.rd.file=loc;
1104                }
1105                prom_printf("Loading ramdisk...\n");
1106                result = open_file(&params.rd, &file);
1107                if (result != FILE_ERR_OK) {
1108                     prom_printf("%s:%d,", params.rd.dev, params.rd.part);
1109                     prom_perror(result, params.rd.file);
1110                }
1111                else {
1112 #define INITRD_CHUNKSIZE 0x100000
1113                     initrd_base = prom_claim(loadinfo.base+loadinfo.memsize, INITRD_CHUNKSIZE, 0);
1114                     if (initrd_base == (void *)-1) {
1115                          prom_printf("Claim failed for initrd memory\n");
1116                          initrd_base = 0;
1117                     } else {
1118                          initrd_size = file.fs->read(&file, INITRD_CHUNKSIZE, initrd_base);
1119                          if (initrd_size == 0)
1120                               initrd_base = 0;
1121                          initrd_read = initrd_size;
1122                          initrd_more = initrd_base;
1123                          while (initrd_read == INITRD_CHUNKSIZE ) { /* need to read more? */
1124                               initrd_want = (void *)((unsigned long)initrd_more+INITRD_CHUNKSIZE);
1125                               initrd_more = prom_claim(initrd_want, INITRD_CHUNKSIZE, 0);
1126                               if (initrd_more != initrd_want) {
1127                                    prom_printf("Claim failed for initrd memory at %p rc=%p\n",initrd_want,initrd_more);
1128                                    prom_pause();
1129                                    break;
1130                               }
1131                               initrd_read = file.fs->read(&file, INITRD_CHUNKSIZE, initrd_more);
1132                               DEBUG_F("  block at %p rc=%lu\n",initrd_more,initrd_read);
1133                               initrd_size += initrd_read;
1134                          }
1135                     }
1136                     file.fs->close(&file);
1137                     memset(&file, 0, sizeof(file));
1138                }
1139                if (initrd_base)
1140                     prom_printf("ramdisk loaded at %p, size: %lu Kbytes\n",
1141                                 initrd_base, initrd_size >> 10);
1142                else {
1143                     prom_printf("ramdisk load failed !\n");
1144                     prom_pause();
1145                }
1146           }
1147
1148           DEBUG_F("setting kernel args to: %s\n", params.args);
1149           prom_setargs(params.args);
1150           DEBUG_F("flushing icache...");
1151           flush_icache_range ((long)loadinfo.base, (long)loadinfo.base+loadinfo.memsize);
1152           DEBUG_F(" done\n");
1153
1154           if (flat_vmlinux) {
1155                /*
1156                 * Fill new boot infos (only if booting a vmlinux).
1157                 *
1158                 * The birec is low on memory, probably inside the malloc pool,
1159                 * so we don't write it earlier. At this point, we should not
1160                 * use anything coming from the malloc pool.
1161                 */
1162                birec = (struct bi_record *)_ALIGN(loadinfo.filesize+(1<<20)-1,(1<<20));
1163
1164                /* We make sure it's mapped. We map only 64k for now, it's
1165                 * plenty enough we don't claim since this precise memory
1166                 * range may already be claimed by the malloc pool.
1167                 */
1168                prom_map (birec, birec, 0x10000);
1169                DEBUG_F("birec at %p\n", birec);
1170                DEBUG_SLEEP;
1171
1172                birec->tag = BI_FIRST;
1173                birec->size = sizeof(struct bi_record);
1174                birec = (struct bi_record *)((ulong)birec + birec->size);
1175
1176                birec->tag = BI_BOOTLOADER_ID;
1177                sprintf( (char *)birec->data, "yaboot");
1178                birec->size = sizeof(struct bi_record) + strlen("yaboot") + 1;
1179                birec = (struct bi_record *)((ulong)birec + birec->size);
1180
1181                birec->tag = BI_MACHTYPE;
1182                birec->data[0] = _machine;
1183                birec->size = sizeof(struct bi_record) + sizeof(ulong);
1184                birec = (struct bi_record *)((ulong)birec + birec->size);
1185
1186                if (sysmap_base) {
1187                     birec->tag = BI_SYSMAP;
1188                     birec->data[0] = (ulong)sysmap_base;
1189                     birec->data[1] = sysmap_size;
1190                     birec->size = sizeof(struct bi_record) + sizeof(ulong)*2;
1191                     birec = (struct bi_record *)((ulong)birec + birec->size);
1192                }
1193                birec->tag = BI_LAST;
1194                birec->size = sizeof(struct bi_record);
1195                birec = (struct bi_record *)((ulong)birec + birec->size);
1196           }
1197
1198           /* compute the kernel's entry point. */
1199           kernel_entry = loadinfo.base + loadinfo.entry - loadinfo.load_loc;
1200
1201           DEBUG_F("Kernel entry point = %p\n", kernel_entry);
1202           DEBUG_F("kernel: arg1 = %p,\n"
1203                   "        arg2 = 0x%08lx,\n"
1204                   "        prom = %p,\n"
1205                   "        arg4 = %d,\n"
1206                   "        arg5 = %d\n\n",
1207                   initrd_base + loadinfo.load_loc, initrd_size, prom, 0, 0);
1208
1209           DEBUG_F("Entering kernel...\n");
1210
1211           /* call the kernel with our stack. */
1212           kernel_entry(initrd_base + loadinfo.load_loc, initrd_size, prom, 0, 0);
1213           continue;
1214      next:
1215           ; /* do nothing */
1216      }
1217 }
1218
1219 static int
1220 load_elf32(struct boot_file_t *file, loadinfo_t *loadinfo)
1221 {
1222      int                        i;
1223      Elf32_Ehdr         *e = &(loadinfo->elf.elf32hdr);
1224      Elf32_Phdr         *p, *ph;
1225      int                        size = sizeof(Elf32_Ehdr) - sizeof(Elf_Ident);
1226      unsigned long      addr, loadaddr;
1227
1228      /* Read the rest of the Elf header... */
1229      if ((*(file->fs->read))(file, size, &e->e_version) < size) {
1230           prom_printf("\nCan't read Elf32 image header\n");
1231           goto bail;
1232      }
1233
1234      DEBUG_F("Elf32 header:\n");
1235      DEBUG_F(" e.e_type      = %d\n", (int)e->e_type);
1236      DEBUG_F(" e.e_machine   = %d\n", (int)e->e_machine);
1237      DEBUG_F(" e.e_version   = %d\n", (int)e->e_version);
1238      DEBUG_F(" e.e_entry     = 0x%08x\n", (int)e->e_entry);
1239      DEBUG_F(" e.e_phoff     = 0x%08x\n", (int)e->e_phoff);
1240      DEBUG_F(" e.e_shoff     = 0x%08x\n", (int)e->e_shoff);
1241      DEBUG_F(" e.e_flags     = %d\n", (int)e->e_flags);
1242      DEBUG_F(" e.e_ehsize    = 0x%08x\n", (int)e->e_ehsize);
1243      DEBUG_F(" e.e_phentsize = 0x%08x\n", (int)e->e_phentsize);
1244      DEBUG_F(" e.e_phnum     = %d\n", (int)e->e_phnum);
1245
1246      loadinfo->entry = e->e_entry;
1247
1248      if (e->e_phnum > MAX_HEADERS) {
1249           prom_printf ("Can only load kernels with one program header\n");
1250           goto bail;
1251      }
1252
1253      ph = (Elf32_Phdr *)malloc(sizeof(Elf32_Phdr) * e->e_phnum);
1254      if (!ph) {
1255           prom_printf ("Malloc error\n");
1256           goto bail;
1257      }
1258
1259      /* Now, we read the section header */
1260      if ((*(file->fs->seek))(file, e->e_phoff) != FILE_ERR_OK) {
1261           prom_printf ("seek error\n");
1262           goto bail;
1263      }
1264      if ((*(file->fs->read))(file, sizeof(Elf32_Phdr) * e->e_phnum, ph) !=
1265          sizeof(Elf32_Phdr) * e->e_phnum) {
1266           prom_printf ("read error\n");
1267           goto bail;
1268      }
1269
1270      /* Scan through the program header
1271       * HACK:  We must return the _memory size of the kernel image, not the
1272       *        file size (because we have to leave room before other boot
1273       *   infos. This code works as a side effect of the fact that
1274       *   we have one section and vaddr == p_paddr
1275       */
1276      loadinfo->memsize = loadinfo->filesize = loadinfo->offset = 0;
1277      p = ph;
1278      for (i = 0; i < e->e_phnum; ++i, ++p) {
1279           if (p->p_type != PT_LOAD || p->p_offset == 0)
1280                continue;
1281           if (loadinfo->memsize == 0) {
1282                loadinfo->offset = p->p_offset;
1283                loadinfo->memsize = p->p_memsz;
1284                loadinfo->filesize = p->p_filesz;
1285                loadinfo->load_loc = p->p_vaddr;
1286           } else {
1287                loadinfo->memsize = p->p_offset + p->p_memsz - loadinfo->offset; /* XXX Bogus */
1288                loadinfo->filesize = p->p_offset + p->p_filesz - loadinfo->offset;
1289           }
1290      }
1291
1292      if (loadinfo->memsize == 0) {
1293           prom_printf("Can't find a loadable segment !\n");
1294           goto bail;
1295      }
1296
1297      /* leave some room (1Mb) for boot infos */
1298      loadinfo->memsize = _ALIGN(loadinfo->memsize,(1<<20)) + 0x100000;
1299      /* Claim OF memory */
1300      DEBUG_F("Before prom_claim, mem_sz: 0x%08lx\n", loadinfo->memsize);
1301
1302      /* Determine whether we are trying to boot a vmlinux or some
1303       * other binary image (eg, zImage).  We load vmlinux's at
1304       * KERNELADDR and all other binaries at their e_entry value.
1305       */
1306      if (e->e_entry == KERNEL_LINK_ADDR_PPC32) {
1307           flat_vmlinux = 1;
1308           loadaddr = KERNELADDR;
1309      } else {
1310           flat_vmlinux = 0;
1311           loadaddr = loadinfo->load_loc;
1312      }
1313
1314      /* On some systems, loadaddr may already be claimed, so try some
1315       * other nearby addresses before giving up.
1316       */
1317      for(addr=loadaddr; addr <= loadaddr * 8 ;addr+=0x100000) {
1318           loadinfo->base = prom_claim((void *)addr, loadinfo->memsize, 0);
1319           if (loadinfo->base != (void *)-1) break;
1320      }
1321      if (loadinfo->base == (void *)-1) {
1322           prom_printf("Claim error, can't allocate kernel memory\n");
1323           goto bail;
1324      }
1325
1326      DEBUG_F("After ELF parsing, load base: %p, mem_sz: 0x%08lx\n",
1327              loadinfo->base, loadinfo->memsize);
1328      DEBUG_F("    wanted load base: 0x%08lx, mem_sz: 0x%08lx\n",
1329              loadaddr, loadinfo->memsize);
1330
1331      /* Load the program segments... */
1332      p = ph;
1333      for (i = 0; i < e->e_phnum; ++i, ++p) {
1334           unsigned long offset;
1335           if (p->p_type != PT_LOAD || p->p_offset == 0)
1336                continue;
1337
1338           /* Now, we skip to the image itself */
1339           if ((*(file->fs->seek))(file, p->p_offset) != FILE_ERR_OK) {
1340                prom_printf ("Seek error\n");
1341                prom_release(loadinfo->base, loadinfo->memsize);
1342                goto bail;
1343           }
1344           offset = p->p_vaddr - loadinfo->load_loc;
1345           if ((*(file->fs->read))(file, p->p_filesz, loadinfo->base+offset) != p->p_filesz) {
1346                prom_printf ("Read failed\n");
1347                prom_release(loadinfo->base, loadinfo->memsize);
1348                goto bail;
1349           }
1350      }
1351
1352      free(ph);
1353
1354      /* Return success at loading the Elf32 kernel */
1355      return 1;
1356
1357 bail:
1358      if (ph)
1359        free(ph);
1360      return 0;
1361 }
1362
1363 static int
1364 load_elf64(struct boot_file_t *file, loadinfo_t *loadinfo)
1365 {
1366      int                        i;
1367      Elf64_Ehdr         *e = &(loadinfo->elf.elf64hdr);
1368      Elf64_Phdr         *p, *ph;
1369      int                        size = sizeof(Elf64_Ehdr) - sizeof(Elf_Ident);
1370      unsigned long      addr, loadaddr;
1371
1372      /* Read the rest of the Elf header... */
1373      if ((*(file->fs->read))(file, size, &e->e_version) < size) {
1374           prom_printf("\nCan't read Elf64 image header\n");
1375           goto bail;
1376      }
1377
1378      DEBUG_F("Elf64 header:\n");
1379      DEBUG_F(" e.e_type      = %d\n", (int)e->e_type);
1380      DEBUG_F(" e.e_machine   = %d\n", (int)e->e_machine);
1381      DEBUG_F(" e.e_version   = %d\n", (int)e->e_version);
1382      DEBUG_F(" e.e_entry     = 0x%016lx\n", (long)e->e_entry);
1383      DEBUG_F(" e.e_phoff     = 0x%016lx\n", (long)e->e_phoff);
1384      DEBUG_F(" e.e_shoff     = 0x%016lx\n", (long)e->e_shoff);
1385      DEBUG_F(" e.e_flags     = %d\n", (int)e->e_flags);
1386      DEBUG_F(" e.e_ehsize    = 0x%08x\n", (int)e->e_ehsize);
1387      DEBUG_F(" e.e_phentsize = 0x%08x\n", (int)e->e_phentsize);
1388      DEBUG_F(" e.e_phnum     = %d\n", (int)e->e_phnum);
1389
1390      loadinfo->entry = e->e_entry;
1391
1392      if (e->e_phnum > MAX_HEADERS) {
1393           prom_printf ("Can only load kernels with one program header\n");
1394           goto bail;
1395      }
1396
1397      ph = (Elf64_Phdr *)malloc(sizeof(Elf64_Phdr) * e->e_phnum);
1398      if (!ph) {
1399           prom_printf ("Malloc error\n");
1400           goto bail;
1401      }
1402
1403      /* Now, we read the section header */
1404      if ((*(file->fs->seek))(file, e->e_phoff) != FILE_ERR_OK) {
1405           prom_printf ("Seek error\n");
1406           goto bail;
1407      }
1408      if ((*(file->fs->read))(file, sizeof(Elf64_Phdr) * e->e_phnum, ph) !=
1409          sizeof(Elf64_Phdr) * e->e_phnum) {
1410           prom_printf ("Read error\n");
1411           goto bail;
1412      }
1413
1414      /* Scan through the program header
1415       * HACK:  We must return the _memory size of the kernel image, not the
1416       *        file size (because we have to leave room before other boot
1417       *   infos. This code works as a side effect of the fact that
1418       *   we have one section and vaddr == p_paddr
1419       */
1420      loadinfo->memsize = loadinfo->filesize = loadinfo->offset = 0;
1421      p = ph;
1422      for (i = 0; i < e->e_phnum; ++i, ++p) {
1423           if (p->p_type != PT_LOAD || p->p_offset == 0)
1424                continue;
1425           if (loadinfo->memsize == 0) {
1426                loadinfo->offset = p->p_offset;
1427                loadinfo->memsize = p->p_memsz;
1428                loadinfo->filesize = p->p_filesz;
1429                loadinfo->load_loc = p->p_vaddr;
1430           } else {
1431                loadinfo->memsize = p->p_offset + p->p_memsz - loadinfo->offset; /* XXX Bogus */
1432                loadinfo->filesize = p->p_offset + p->p_filesz - loadinfo->offset;
1433           }
1434      }
1435
1436      if (loadinfo->memsize == 0) {
1437           prom_printf("Can't find a loadable segment !\n");
1438           goto bail;
1439      }
1440
1441      /* leave some room (1Mb) for boot infos */
1442      loadinfo->memsize = _ALIGN(loadinfo->memsize,(1<<20)) + 0x100000;
1443      /* Claim OF memory */
1444      DEBUG_F("Before prom_claim, mem_sz: 0x%08lx\n", loadinfo->memsize);
1445
1446      /* Determine whether we are trying to boot a vmlinux or some
1447       * other binary image (eg, zImage).  We load vmlinux's at
1448       * KERNELADDR and all other binaries at their e_entry value.
1449       */
1450      if (e->e_entry == KERNEL_LINK_ADDR_PPC64) {
1451           flat_vmlinux = 1;
1452           loadaddr = KERNELADDR;
1453      } else {
1454           flat_vmlinux = 0;
1455           loadaddr = e->e_entry;
1456      }
1457
1458      /* On some systems, loadaddr may already be claimed, so try some
1459       * other nearby addresses before giving up.
1460       */
1461      for(addr=loadaddr; addr <= loadaddr * 8 ;addr+=0x100000) {
1462           loadinfo->base = prom_claim((void *)addr, loadinfo->memsize, 0);
1463           if (loadinfo->base != (void *)-1) break;
1464      }
1465      if (loadinfo->base == (void *)-1) {
1466           prom_printf("Claim error, can't allocate kernel memory\n");
1467           goto bail;
1468      }
1469
1470      DEBUG_F("After ELF parsing, load base: %p, mem_sz: 0x%08lx\n",
1471              loadinfo->base, loadinfo->memsize);
1472      DEBUG_F("    wanted load base: 0x%08lx, mem_sz: 0x%08lx\n",
1473              loadaddr, loadinfo->memsize);
1474
1475      /* Load the program segments... */
1476      p = ph;
1477      for (i = 0; i < e->e_phnum; ++i, ++p) {
1478           unsigned long offset;
1479           if (p->p_type != PT_LOAD || p->p_offset == 0)
1480                continue;
1481
1482           /* Now, we skip to the image itself */
1483           if ((*(file->fs->seek))(file, p->p_offset) != FILE_ERR_OK) {
1484                prom_printf ("Seek error\n");
1485                prom_release(loadinfo->base, loadinfo->memsize);
1486                goto bail;
1487           }
1488           offset = p->p_vaddr - loadinfo->load_loc;
1489           if ((*(file->fs->read))(file, p->p_filesz, loadinfo->base+offset) != p->p_filesz) {
1490                prom_printf ("Read failed\n");
1491                prom_release(loadinfo->base, loadinfo->memsize);
1492                goto bail;
1493           }
1494      }
1495
1496      free(ph);
1497
1498      /* Return success at loading the Elf64 kernel */
1499      return 1;
1500
1501 bail:
1502      if (ph)
1503        free(ph);
1504      return 0;
1505 }
1506
1507 static int
1508 is_elf32(loadinfo_t *loadinfo)
1509 {
1510      Elf32_Ehdr *e = &(loadinfo->elf.elf32hdr);
1511
1512      return (e->e_ident[EI_MAG0]  == ELFMAG0        &&
1513              e->e_ident[EI_MAG1]  == ELFMAG1        &&
1514              e->e_ident[EI_MAG2]  == ELFMAG2        &&
1515              e->e_ident[EI_MAG3]  == ELFMAG3        &&
1516              e->e_ident[EI_CLASS] == ELFCLASS32  &&
1517              e->e_ident[EI_DATA]  == ELFDATA2MSB &&
1518              e->e_type            == ET_EXEC        &&
1519              e->e_machine         == EM_PPC);
1520 }
1521
1522 static int
1523 is_elf64(loadinfo_t *loadinfo)
1524 {
1525      Elf64_Ehdr *e = &(loadinfo->elf.elf64hdr);
1526
1527      return (e->e_ident[EI_MAG0]  == ELFMAG0        &&
1528              e->e_ident[EI_MAG1]  == ELFMAG1        &&
1529              e->e_ident[EI_MAG2]  == ELFMAG2        &&
1530              e->e_ident[EI_MAG3]  == ELFMAG3        &&
1531              e->e_ident[EI_CLASS] == ELFCLASS64  &&
1532              e->e_ident[EI_DATA]  == ELFDATA2MSB &&
1533              e->e_type            == ET_EXEC        &&
1534              e->e_machine         == EM_PPC64);
1535 }
1536
1537 static void
1538 setup_display(void)
1539 {
1540 #ifdef CONFIG_SET_COLORMAP
1541      static unsigned char default_colors[] = {
1542           0x00, 0x00, 0x00,
1543           0x00, 0x00, 0xaa,
1544           0x00, 0xaa, 0x00,
1545           0x00, 0xaa, 0xaa,
1546           0xaa, 0x00, 0x00,
1547           0xaa, 0x00, 0xaa,
1548           0xaa, 0x55, 0x00,
1549           0xaa, 0xaa, 0xaa,
1550           0x55, 0x55, 0x55,
1551           0x55, 0x55, 0xff,
1552           0x55, 0xff, 0x55,
1553           0x55, 0xff, 0xff,
1554           0xff, 0x55, 0x55,
1555           0xff, 0x55, 0xff,
1556           0xff, 0xff, 0x55,
1557           0xff, 0xff, 0xff
1558      };
1559      int i, result;
1560      prom_handle scrn = PROM_INVALID_HANDLE;
1561
1562      /* Try Apple's mac-boot screen ihandle */
1563      result = (int)call_prom_return("interpret", 1, 2,
1564                                     "\" _screen-ihandle\" $find if execute else 0 then", &scrn);
1565      DEBUG_F("Trying to get screen ihandle, result: %d, scrn: %p\n", result, scrn);
1566
1567      if (scrn == 0 || scrn == PROM_INVALID_HANDLE) {
1568           char type[32];
1569           /* Hrm... check to see if stdout is a display */
1570           scrn = call_prom ("instance-to-package", 1, 1, prom_stdout);
1571           DEBUG_F("instance-to-package of stdout is: %p\n", scrn);
1572           if (prom_getprop(scrn, "device_type", type, 32) > 0 && !strncmp(type, "display", 7)) {
1573                DEBUG_F("got it ! stdout is a screen\n");
1574                scrn = prom_stdout;
1575           } else {
1576                /* Else, we try to open the package */
1577                scrn = (prom_handle)call_prom( "open", 1, 1, "screen" );
1578                DEBUG_F("Open screen result: %p\n", scrn);
1579           }
1580      }
1581
1582      if (scrn == PROM_INVALID_HANDLE) {
1583           prom_printf("No screen device found !/n");
1584           return;
1585      }
1586      for(i=0;i<16;i++) {
1587           prom_set_color(scrn, i, default_colors[i*3],
1588                          default_colors[i*3+1], default_colors[i*3+2]);
1589      }
1590      prom_printf("\x1b[1;37m\x1b[2;40m");
1591 #ifdef COLOR_TEST
1592      for (i=0;i<16; i++) {
1593           prom_printf("\x1b[%d;%dm\x1b[1;47m%s \x1b[2;40m %s\n",
1594                       ansi_color_table[i].index,
1595                       ansi_color_table[i].value,
1596                       ansi_color_table[i].name,
1597                       ansi_color_table[i].name);
1598           prom_printf("\x1b[%d;%dm\x1b[1;37m%s \x1b[2;30m %s\n",
1599                       ansi_color_table[i].index,
1600                       ansi_color_table[i].value+10,
1601                       ansi_color_table[i].name,
1602                       ansi_color_table[i].name);
1603      }
1604      prom_printf("\x1b[1;37m\x1b[2;40m");
1605 #endif /* COLOR_TEST */
1606
1607 #if !DEBUG
1608      prom_printf("\xc");
1609 #endif /* !DEBUG */
1610
1611 #endif /* CONFIG_SET_COLORMAP */
1612 }
1613
1614 int
1615 yaboot_main(void)
1616 {
1617      char *ptype;
1618      int conf_given = 0;
1619      char conf_path[1024];
1620
1621      if (_machine == _MACH_Pmac)
1622           setup_display();
1623
1624      prom_get_chosen("bootargs", bootargs, sizeof(bootargs));
1625      DEBUG_F("/chosen/bootargs = %s\n", bootargs);
1626      prom_get_chosen("bootpath", bootdevice, BOOTDEVSZ);
1627      DEBUG_F("/chosen/bootpath = %s\n", bootdevice);
1628
1629      /* If conf= specified on command line, it overrides
1630         Usage: conf=device:partition,/path/to/conffile
1631         Example: On Open Firmware Prompt, type
1632                  boot conf=/pci@8000000f8000000/pci@1/pci1014,028C@1/scsi@0/sd@1,0:3,/etc/yaboot.conf */
1633
1634      if (!strncmp(bootargs, "conf=", 5)) {
1635         DEBUG_F("Using conf argument in Open Firmware\n");
1636         char *end = strchr(bootargs,' ');
1637         if (end)
1638             *end = 0;
1639
1640         strcpy(bootdevice, bootargs + 5);
1641         conf_given = 1;
1642         DEBUG_F("Using conf=%s\n", bootdevice);
1643
1644         /* Remove conf=xxx from bootargs */
1645         if (end)
1646             memmove(bootargs, end+1, strlen(end+1)+1);
1647         else
1648             bootargs[0] = 0;
1649      }
1650      if (bootdevice[0] == 0) {
1651           prom_get_options("boot-device", bootdevice, BOOTDEVSZ);
1652           DEBUG_F("boot-device = %s\n", bootdevice);
1653      }
1654      if (bootdevice[0] == 0) {
1655           prom_printf("Couldn't determine boot device\n");
1656           return -1;
1657      }
1658
1659      if (bootoncelabel[0] == 0) {
1660           prom_get_options("boot-once", bootoncelabel, 
1661                            sizeof(bootoncelabel));
1662           if (bootoncelabel[0] != 0)
1663                 DEBUG_F("boot-once: [%s]\n", bootoncelabel);
1664      }
1665      prom_set_options("boot-once", NULL, 0);
1666
1667      if (!parse_device_path(bootdevice, NULL, -1, "", &boot)) {
1668           prom_printf("%s: Unable to parse\n", bootdevice);
1669           return -1;
1670      }
1671      DEBUG_F("After parse_device_path: dev=%s, part=%d, file=%s\n",
1672              boot.dev, boot.part, boot.file);
1673
1674      if (!conf_given) {
1675          if (_machine == _MACH_chrp)
1676              boot.file = "/etc/";
1677          else if (strlen(boot.file)) {
1678              if (!strncmp(boot.file, "\\\\", 2))
1679                  boot.file = "\\\\";
1680              else {
1681                  char *p, *last;
1682                  p = last = boot.file;
1683                  while(*p) {
1684                      if (*p == '\\')
1685                          last = p;
1686                      p++;
1687                  }
1688                  if (p)
1689                      *(last) = 0;
1690                  else
1691                      boot.file = "";
1692                  if (strlen(boot.file))
1693                      strcat(boot.file, "\\");
1694              }
1695          }
1696          strcpy(conf_path, boot.file);
1697          strcat(conf_path, CONFIG_FILE_NAME);
1698          boot.file = conf_path;
1699          DEBUG_F("After path kludgeup: dev=%s, part=%d, file=%s\n",
1700             boot.dev, boot.part, boot.file);
1701      }
1702
1703      /*
1704       * If we're doing a netboot, first look for one which matches our
1705       * MAC address.
1706       */
1707      if (prom_get_devtype(boot.dev) == FILE_DEVICE_NET) {
1708           prom_printf("Try to netboot\n");
1709           useconf = load_my_config_file(&boot);
1710      }
1711
1712      if (!useconf)
1713          useconf = load_config_file(&boot);
1714
1715      prom_printf("Welcome to yaboot version " VERSION "\n");
1716      prom_printf("Enter \"help\" to get some basic usage information\n");
1717
1718      /* I am fed up with lusers using the wrong partition type and
1719         mailing me *when* it breaks */
1720
1721      if (_machine == _MACH_Pmac) {
1722           char *entry = cfg_get_strg(0, "ptypewarning");
1723           int warn = 1;
1724           if (entry)
1725                warn = strcmp(entry,
1726                              "I_know_the_partition_type_is_wrong_and_will_NOT_send_mail_when_booting_breaks");
1727           if (warn) {
1728                ptype = get_part_type(boot.dev, boot.part);
1729                if ((ptype != NULL) && (strcmp(ptype, "Apple_Bootstrap")))
1730                     prom_printf("\nWARNING: Bootstrap partition type is wrong: \"%s\"\n"
1731                                 "         type should be: \"Apple_Bootstrap\"\n\n", ptype);
1732                if (ptype)
1733                     free(ptype);
1734           }
1735      }
1736
1737      yaboot_text_ui();
1738
1739      prom_printf("Bye.\n");
1740      return 0;
1741 }
1742
1743 /*
1744  * Local variables:
1745  * c-file-style: "k&r"
1746  * c-basic-offset: 5
1747  * End:
1748  */