2 * Yaboot - secondary boot loader for Linux on PowerPC.
4 * Copyright (C) 2001, 2002 Ethan Benson
6 * Copyright (C) 1999, 2000, 2001 Benjamin Herrenschmidt
10 * Copyright (C) 2001 Peter Bergner
12 * portions based on poof
14 * Copyright (C) 1999 Marius Vollmer
16 * portions based on quik
18 * Copyright (C) 1996 Paul Mackerras.
20 * Because this program is derived from the corresponding file in the
21 * silo-0.64 distribution, it is also
23 * Copyright (C) 1996 Pete A. Zaitcev
25 * 1996 David S. Miller
26 * 1996 Miguel de Icaza
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.
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.
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.
54 #include "linux/elf.h"
58 #define CONFIG_FILE_NAME "yaboot.conf"
59 #define CONFIG_FILE_MAX 0x8000 /* 32k */
61 #ifdef USE_MD5_PASSWORDS
63 #endif /* USE_MD5_PASSWORDS */
65 /* align addr on a size boundry - adjust address up if needed -- Cort */
66 #define _ALIGN(addr,size) (((addr)+size-1)&(~(size-1)))
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
74 #define KERNEL_LINK_ADDR_PPC32 0xC0000000UL
75 #define KERNEL_LINK_ADDR_PPC64 0xC000000000000000ULL
83 unsigned long memsize;
84 unsigned long filesize;
86 unsigned long load_loc;
90 typedef void (*kernel_entry_t)( void *,
96 /* Imported functions */
97 extern unsigned long reloc_offset(void);
98 extern long flush_icache_range(unsigned long start, unsigned long stop);
100 /* Exported functions */
101 int yaboot_start(unsigned long r3, unsigned long r4, unsigned long r5);
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);
111 /* Locals & globals */
114 char bootdevice[BOOTDEVSZ];
115 char bootoncelabel[1024];
117 char bootlastlabel[BOOTLASTSZ] = {0};
118 char fw_nbr_reboots[FW_NBR_REBOOTSZ] = {0};
119 long fw_reboot_cnt = 0;
120 char *password = NULL;
121 struct boot_fspec_t boot;
122 int _machine = _MACH_Pmac;
125 #ifdef CONFIG_COLOR_TEXT
127 /* Color values for text ui */
128 static struct ansi_color_t {
132 } ansi_color_table[] = {
140 { "light-gray", 0, 37 },
141 { "dark-gray", 1, 30 },
142 { "light-blue", 1, 31 },
143 { "light-green", 1, 32 },
144 { "light-cyan", 1, 33 },
145 { "light-red", 1, 34 },
146 { "light-purple", 1, 35 },
152 /* Default colors for text ui */
155 #endif /* CONFIG_COLOR_TEXT */
157 static int pause_after;
158 static char *pause_message = "Type go<return> to continue.\n";
159 static char given_bootargs[1024];
160 static int given_bootargs_by_user = 0;
162 extern unsigned char linux_logo_red[];
163 extern unsigned char linux_logo_green[];
164 extern unsigned char linux_logo_blue[];
166 #define DEFAULT_TIMEOUT -1
168 /* Entry, currently called directly by crt0 (bss not inited) */
170 extern char* __bss_start;
174 yaboot_start (unsigned long r3, unsigned long r4, unsigned long r5)
177 void* malloc_base = NULL;
181 /* OF seems to do it, but I'm not very confident */
182 memset(&__bss_start, 0, &_end - &__bss_start);
184 /* Initialize OF interface */
185 prom_init ((prom_entry) r5);
187 /* Allocate some memory for malloc'ator */
188 for (addr = MALLOCADDR; addr <= MALLOCADDR * 16 ;addr+=0x100000) {
189 malloc_base = prom_claim((void *)addr, MALLOCSIZE, 0);
190 if (malloc_base != (void *)-1) break;
192 if (malloc_base == (void *)-1) {
193 prom_printf("Can't claim malloc buffer (%d bytes at 0x%08x)\n",
194 MALLOCSIZE, MALLOCADDR);
197 malloc_init(malloc_base, MALLOCSIZE);
198 DEBUG_F("Malloc buffer allocated at %p (%d bytes)\n",
199 malloc_base, MALLOCSIZE);
201 /* A few useless DEBUG_F's */
202 DEBUG_F("reloc_offset : %ld (should be 0)\n", reloc_offset());
203 DEBUG_F("linked at : 0x%08x\n", TEXTADDR);
205 /* ask the OF info if we're a chrp or pmac */
206 /* we need to set _machine before calling finish_device_tree */
207 root = prom_finddevice("/");
209 static char model[256];
210 if (prom_getprop(root, "CODEGEN,vendor", model, 256) > 0 &&
211 !strncmp("bplan", model, 5))
212 _machine = _MACH_bplan;
213 else if (prom_getprop(root, "device_type", model, 256 ) > 0 &&
214 !strncmp("chrp", model, 4))
215 _machine = _MACH_chrp;
217 if (prom_getprop(root, "model", model, 256 ) > 0 &&
218 !strncmp(model, "IBM", 3))
219 _machine = _MACH_chrp;
223 DEBUG_F("Running on _machine = %d\n", _machine);
227 result = yaboot_main();
229 /* Get rid of malloc pool */
231 prom_release(malloc_base, MALLOCSIZE);
232 DEBUG_F("Malloc buffer released. Exiting with code %d\n",
242 #ifdef CONFIG_COLOR_TEXT
244 * Validify color for text ui
247 check_color_text_ui(char *color)
250 while(ansi_color_table[i].name) {
251 if (!strcmp(color, ansi_color_table[i].name))
257 #endif /* CONFIG_COLOR_TEXT */
260 void print_message_file(char *filename)
264 char *defdev = boot.dev;
265 int defpart = boot.part;
270 struct boot_file_t file;
271 struct boot_fspec_t msgfile;
273 defdev = cfg_get_strg(0, "device");
276 p = cfg_get_strg(0, "partition");
278 n = simple_strtol(p, &endp, 10);
279 if (endp != p && *endp == 0)
283 strncpy(msgpath, filename, sizeof(msgpath));
284 msgfile = boot; /* Copy all the original paramters */
285 if (!parse_device_path(msgpath, defdev, defpart, "/etc/yaboot.msg", &msgfile)) {
286 prom_printf("%s: Unable to parse\n", msgpath);
290 result = open_file(&msgfile, &file);
291 if (result != FILE_ERR_OK) {
292 prom_printf("%s:%d,", msgfile.dev, msgfile.part);
293 prom_perror(result, msgfile.file);
302 memset(msg, 0, 2001);
304 if (file.fs->read(&file, 2000, msg) <= 0)
307 prom_printf("%s", msg);
311 file.fs->close(&file);
316 /* Currently, the config file must be at the root of the filesystem.
317 * todo: recognize the full path to myself and use it to load the
318 * config file. Handle the "\\" (blessed system folder)
321 load_config_file(struct boot_fspec_t *fspec)
323 char *conf_file = NULL, *p;
324 struct boot_file_t file;
325 int sz, opened = 0, result = 0;
327 /* Allocate a buffer for the config file */
328 conf_file = malloc(CONFIG_FILE_MAX);
330 prom_printf("Can't alloc config file buffer\n");
335 result = open_file(fspec, &file);
336 if (result != FILE_ERR_OK) {
337 prom_printf("%s:%d,", fspec->dev, fspec->part);
338 prom_perror(result, fspec->file);
339 prom_printf("Can't open config file\n");
345 sz = file.fs->read(&file, CONFIG_FILE_MAX, conf_file);
347 prom_printf("Error, can't read config file\n");
350 prom_printf("Config file read, %d bytes\n", sz);
354 file.fs->close(&file);
357 /* Call the parsing code in cfg.c */
358 if (cfg_parse(fspec->file, conf_file, sz) < 0) {
359 prom_printf ("Syntax error or read error config\n");
364 * set the default cf_option to label that has the same MAC addr
365 * it only works if there is a label with the MAC addr on yaboot.conf
367 if (prom_get_devtype(fspec->dev) == FILE_DEVICE_NET) {
368 /* change the variable bellow to get the MAC dinamicaly */
369 char * macaddr = NULL;
372 macaddr = prom_get_mac(prom_get_netinfo());
373 default_mac = cfg_set_default_by_mac(macaddr);
374 if (default_mac >= 1) {
375 prom_printf("Default label was changed to macaddr label.\n");
379 DEBUG_F("Config file successfully parsed, %d bytes\n", sz);
381 /* Now, we do the initialisations stored in the config file */
382 p = cfg_get_strg(0, "init-code");
386 password = cfg_get_strg(0, "password");
388 #ifdef CONFIG_COLOR_TEXT
389 p = cfg_get_strg(0, "fgcolor");
391 DEBUG_F("fgcolor=%s\n", p);
392 fgcolor = check_color_text_ui(p);
394 prom_printf("Invalid fgcolor: \"%s\".\n", p);
397 p = cfg_get_strg(0, "bgcolor");
399 DEBUG_F("bgcolor=%s\n", p);
400 bgcolor = check_color_text_ui(p);
402 prom_printf("Invalid bgcolor: \"%s\".\n", p);
406 sprintf(temp, "%x to background-color", bgcolor);
407 prom_interpret(temp);
414 sprintf(temp, "%x to foreground-color", fgcolor);
415 prom_interpret(temp);
417 #endif /* CONFIG_COLOR_TEXT */
419 p = cfg_get_strg(0, "init-message");
421 prom_printf("%s\n", p);
423 p = cfg_get_strg(0, "message");
425 print_message_file(p);
432 file.fs->close(&file);
441 * Search for config file by MAC address, then by IP address.
442 * Basically copying pxelinux's algorithm.
443 * http://syslinux.zytor.com/pxe.php#config
445 static int load_my_config_file(struct boot_fspec_t *orig_fspec)
447 struct bootp_packet *packet;
449 struct boot_fspec_t fspec = *orig_fspec;
450 char *cfgpath = (_machine == _MACH_chrp || _machine == _MACH_bplan) ? "/etc/" : "";
454 packet = prom_get_netinfo();
459 * First, try to match on mac address with the hardware type
463 /* 3 chars per byte in chaddr + 2 chars for htype + /etc/ +\0 */
464 fspec.file = malloc(packet->hlen * 3 + 2 + 6);
468 sprintf(fspec.file, "%s%02x-", cfgpath, packet->htype);
469 strcat(fspec.file, prom_get_mac(packet));
471 rc = load_config_file(&fspec);
476 * Now try to match on IP.
478 /* no need to realloc for /etc/ + 8 chars in yiaddr + \0 */
479 sprintf(fspec.file, "%s%s", cfgpath, prom_get_ip(packet));
481 for (flen = strlen(fspec.file),
482 minlen = strlen(cfgpath); flen > minlen; flen--) {
483 rc = load_config_file(&fspec);
486 /* Chop one digit off the end, try again */
487 fspec.file[flen - 1] = '\0';
491 if (rc) /* modify original only on success */
492 orig_fspec->file = fspec.file;
498 void maintabfunc (void)
502 prom_printf("boot: %s", cbuff);
507 word_split(char **linep, char **paramsp)
522 while (*p != 0 && *p != ' ')
531 make_params(char *label, char *params)
534 static char buffer[2048];
539 p = cfg_get_strg(label, "literal");
551 p = cfg_get_strg(label, "root");
558 if (cfg_get_flag(label, "read-only")) {
562 if (cfg_get_flag(label, "read-write")) {
566 p = cfg_get_strg(label, "ramdisk");
568 strcpy (q, "ramdisk=");
573 p = cfg_get_strg(label, "initrd-size");
575 strcpy (q, "ramdisk_size=");
580 if (cfg_get_flag(label, "novideo")) {
581 strcpy (q, "video=ofonly");
585 p = cfg_get_strg (label, "append");
592 pause_after = cfg_get_flag (label, "pause-after");
593 p = cfg_get_strg(label, "pause-message");
602 void check_password(char *str)
606 prom_printf("\n%s", str);
607 for (i = 0; i < 3; i++) {
608 prom_printf ("\nPassword: ");
610 cmdedit ((void (*)(void)) 0, 1);
612 #ifdef USE_MD5_PASSWORDS
613 if (!strncmp (password, "$1$", 3)) {
614 if (!check_md5_password(passwdbuff, password))
617 else if (!strcmp (password, passwdbuff))
620 if (!strcmp (password, passwdbuff))
622 #endif /* USE_MD5_PASSWORDS */
625 prom_printf ("Incorrect password. Try again.");
628 prom_printf(" ___________________\n< Permission denied >\n -------------------\n"
629 " \\ ^__^\n \\ (oo)\\_______\n (__)\\ )\\/\\\n"
630 " ||----w |\n || ||\n");
632 prom_interpret("reset-all");
635 int get_params(struct boot_param_t* params)
639 char defdevice_bak[1024];
642 char *imagename = 0, *label;
647 static int first = 1;
648 static char imagepath[1024];
649 static char initrdpath[1024];
650 static char manualinitrd[1024];
651 static int definitrd = 1, hasarg = 0;
654 memset(params, 0, sizeof(*params));
656 params->kernel.part = -1;
657 params->rd.part = -1;
662 if (first && !fw_reboot_cnt) {
664 imagename = bootargs;
665 word_split(&imagename, ¶ms->args);
666 timeout = DEFAULT_TIMEOUT;
668 prom_printf("Default supplied on the command line: %s ", imagename);
670 prom_printf("%s", params->args);
673 if (useconf && (q = cfg_get_strg(0, "timeout")) != 0 && *q != 0)
674 timeout = simple_strtol(q, NULL, 0);
677 /* If this is a reboot due to FW detecting CAS changes then
678 * set timeout to 1. The last kernel booted will be booted
679 * again automatically. It should seem seamless to the user
684 prom_printf("boot: ");
689 end = beg + 100 * timeout;
691 c = prom_nbgetchar();
692 } while (c == -1 && prom_getms() <= end);
696 else if (c != '\n' && c != '\t' && c != '\r' && c != '\b' ) {
702 if (c != -1 && c != '\n' && c != '\r') {
705 } else if (c >= ' ') {
708 if ((cfg_get_flag (cbuff, "single-key")) && useconf) {
711 prom_printf("%s\n", cbuff);
716 if (c == '\n' || c == '\r') {
718 if (bootoncelabel[0] != 0)
719 imagename = bootoncelabel;
720 else if (bootlastlabel[0] != 0)
721 imagename = bootlastlabel;
723 imagename = cfg_get_default();
726 prom_printf("%s", imagename);
728 prom_printf(" %s", params->args);
730 } else if (!singlekey) {
731 cmdedit(maintabfunc, 0);
733 strcpy(given_bootargs, cbuff);
734 given_bootargs_by_user = 1;
736 word_split(&imagename, ¶ms->args);
739 /* initrd setup via cmd console */
740 /* first, check if the user uses it with some label */
741 if (!strncmp(params->args, "initrd=", 7)) {
742 DEBUG_F("params->args: %s\n", params->args);
745 /* after, check if there is the 'initrd=' in the imagename string */
746 if (!strncmp(imagename, "initrd=", 7) || !definitrd) {
748 /* return the value of definitrd to 1 */
752 /* args = "initrd=blah" */
763 /* copy the string after the '=' to manualinitrd */
764 strcpy(manualinitrd, args+7);
766 prom_printf("New initrd file specified: %s\n", manualinitrd);
768 prom_printf("ERROR: no initrd specified!\n");
772 /* set imagename with the default values of the config file */
773 if ((prom_get_devtype(boot.dev) == FILE_DEVICE_NET) && !hasarg)
774 imagename = cfg_get_default();
776 imagename = cfg_get_default();
779 /* chrp gets this wrong, force it -- Cort */
780 if ( useconf && (!imagename || imagename[0] == 0 ))
781 imagename = cfg_get_default();
783 /* write the imagename out so it can be reused on reboot if necessary */
784 prom_set_options("boot-last-label", imagename, strlen(imagename));
787 defdevice = boot.dev;
789 strcpy(defdevice_bak,defdevice);
792 defdevice = cfg_get_strg(0, "device");
793 p = cfg_get_strg(0, "partition");
795 n = simple_strtol(p, &endp, 10);
796 if (endp != p && *endp == 0)
799 p = cfg_get_strg(0, "pause-message");
802 if (cfg_get_flag(0, "restricted"))
804 p = cfg_get_strg(imagename, "image");
808 defdevice = cfg_get_strg(label, "device");
809 if(!defdevice) defdevice=boot.dev;
810 p = cfg_get_strg(label, "partition");
812 n = simple_strtol(p, &endp, 10);
813 if (endp != p && *endp == 0)
816 if (cfg_get_flag(label, "restricted"))
819 if (params->args && password && restricted)
820 check_password ("To specify arguments for this image "
821 "you must enter the password.");
822 else if (password && !restricted)
823 check_password ("This image is restricted.");
825 params->args = make_params(label, params->args);
829 if (!strcmp (imagename, "help")) {
830 /* FIXME: defdevice shouldn't need to be reset all over the place */
831 if(!defdevice) defdevice = boot.dev;
833 "\nPress the tab key for a list of defined images.\n"
834 "The label marked with a \"*\" is is the default image, "
835 "press <return> to boot it.\n\n"
836 "To boot any other label simply type its name and press <return>.\n\n"
837 "To boot a kernel image which is not defined in the yaboot configuration \n"
838 "file, enter the kernel image name as [[device:][partno],]/path, where \n"
839 "\"device:\" is the OpenFirmware device path to the disk the image \n"
840 "resides on, and \"partno\" is the partition number the image resides on.\n"
841 "Note that the comma (,) is only required if you specify an OpenFirmware\n"
842 "device, if you only specify a filename you should not start it with a \",\"\n\n"
843 "To boot a alternative initrd file rather than specified in the yaboot\n"
844 "configuration file, use the \"initrd\" command on Yaboot's prompt: \n"
845 "\"initrd=[name.img]\". This will load the \"name.img\" file after the default\n"
846 "kernel image. You can, also, specify a different initrd file to any other\n"
847 "label of the yaboot configuration file. Just type \"label initrd=[name.img]\"\n"
848 "and the specified initrd file will be loaded.\n\n"
849 "To load an alternative config file rather than /etc/yaboot.conf, enter\n"
850 "its device, partno and path, on Open Firmware Prompt:\n"
851 "boot conf=device:partno,/path/to/configfile\n."
852 "To reload the config file or load a new one, use the \"conf\" command\n"
853 "on Yaboot's prompt:\n"
854 "conf [device=device] [partition=partno] [file=/path/to/configfile]\n\n"
855 "If you omit \"device\" and \"partno\", Yaboot will use their current\n"
856 "values. You can check them by entering \"conf\" on Yaboot's prompt.\n");
861 if (!strcmp (imagename, "halt")) {
863 check_password ("Restricted command.");
867 if (!strcmp (imagename, "bye")) {
869 check_password ("Restricted command.");
875 if (!strncmp (imagename, "conf", 4)) {
877 // imagename = "conf file=blah dev=bleh part=blih"
878 DEBUG_F("Loading user-specified config file: %s\n",imagename);
880 check_password ("Restricted command.");
884 // args= "file=blah dev=bleh part=blih"
885 char *args = params->args;
889 // set a pointer to the first space in args
890 char *space = strchr(args,' ');
894 char temp[1024] = "0";
896 // copy next argument to temp
897 strncpy(temp, args, space-args);
899 // parse temp and set boot arguments
900 if (!strncmp (temp, "file=", 5)){
901 DEBUG_F("conf file: %s\n", temp+5);
902 strcpy(boot.file, temp+5);
903 } else if (!strncmp (temp, "device=", 7)){
904 DEBUG_F("conf device: %s\n", temp+7);
905 strcpy(boot.dev, temp+7);
906 } else if (!strncmp (temp, "partition=", 10)){
907 DEBUG_F("conf partition: %s\n", temp+10);
908 boot.part=simple_strtol(temp+10,NULL,10);
912 // set the pointer to the next space in args;
913 // set the loop control variable
914 if (strlen(space)>1){
915 // Go to the next argument
919 if (strchr(args,' ') == NULL)
920 space = &args[strlen(args)];
922 space = strchr(args,' ');
929 prom_printf("Loading config file...\n");
930 useconf = load_config_file(&boot);
932 if ((q = cfg_get_strg(0, "timeout")) != 0 && *q != 0)
933 timeout = simple_strtol(q, NULL, 0);
935 prom_printf("Restoring default values.\n");
936 strcpy(boot.file,"");
937 strcpy(boot.dev, defdevice_bak);
942 prom_printf("Current configuration:\n");
943 prom_printf("device: %s\n", boot.dev);
945 prom_printf("partition: auto\n");
947 prom_printf("partition: %d\n", boot.part);
948 if (strlen(boot.file))
949 prom_printf("file: %s\n", boot.file);
951 prom_printf("file: /etc/%s\n",CONFIG_FILE_NAME);
960 if (imagename[0] == '$') {
961 /* forth command string */
963 check_password ("OpenFirmware commands are restricted.");
964 prom_interpret(imagename+1);
968 strncpy(imagepath, imagename, 1024);
970 if (!label && password)
971 check_password ("To boot a custom image you must enter the password.");
973 params->kernel = boot; /* Copy all the original paramters */
974 if (!parse_device_path(imagepath, defdevice, defpart,
975 "/vmlinux", ¶ms->kernel)) {
976 prom_printf("%s: Unable to parse\n", imagepath);
979 DEBUG_F("after parse_device_path: dev=%s part=%d file=%s\n", params->kernel.dev, params->kernel.part, params->kernel.file);
981 p = cfg_get_strg(label, "initrd");
984 /* check if user seted to use a initrd file from boot console */
985 if (!definitrd && p != manualinitrd) {
986 if (manualinitrd[0] != "/" && (prom_get_devtype(defdevice_bak) != FILE_DEVICE_NET)) {
987 strcpy(initrdpath, "/");
988 strcat(initrdpath, manualinitrd);
990 strncpy(initrdpath, manualinitrd, 1024);
992 strncpy(initrdpath, p, 1024);
994 DEBUG_F("Parsing initrd path <%s>\n", initrdpath);
995 params->rd = boot; /* Copy all the original paramters */
996 if (!parse_device_path(initrdpath, defdevice, defpart,
997 "/root.bin", ¶ms->rd)) {
998 prom_printf("%s: Unable to parse\n", imagepath);
1006 /* This is derived from quik core. To be changed to first parse the headers
1007 * doing lazy-loading, and then claim the memory before loading the kernel
1009 * We also need to add initrd support to this whole mecanism
1012 yaboot_text_ui(void)
1014 #define MAX_HEADERS 32
1016 struct boot_file_t file;
1018 static struct boot_param_t params;
1020 unsigned long initrd_size;
1021 kernel_entry_t kernel_entry;
1023 loadinfo_t loadinfo;
1024 void *initrd_more,*initrd_want;
1025 unsigned long initrd_read;
1027 loadinfo.load_loc = 0;
1033 if (get_params(¶ms))
1035 if (!params.kernel.file)
1038 prom_printf("Please wait, loading kernel...\n");
1040 memset(&file, 0, sizeof(file));
1042 if (strlen(boot.file) && !strcmp(boot.file,"\\\\") && params.kernel.file[0] != '/'
1043 && params.kernel.file[0] != '\\') {
1044 loc=(char*)malloc(strlen(params.kernel.file)+3);
1046 prom_printf ("malloc error\n");
1049 strcpy(loc,boot.file);
1050 strcat(loc,params.kernel.file);
1051 free(params.kernel.file);
1052 params.kernel.file=loc;
1054 result = open_file(¶ms.kernel, &file);
1055 if (result != FILE_ERR_OK) {
1056 prom_printf("%s:%d,", params.kernel.dev, params.kernel.part);
1057 prom_perror(result, params.kernel.file);
1061 /* Read the Elf e_ident, e_type and e_machine fields to
1062 * determine Elf file type
1064 if (file.fs->read(&file, sizeof(Elf_Ident), &loadinfo.elf) < sizeof(Elf_Ident)) {
1065 prom_printf("\nCan't read Elf e_ident/e_type/e_machine info\n");
1066 file.fs->close(&file);
1067 memset(&file, 0, sizeof(file));
1071 if (is_elf32(&loadinfo)) {
1072 if (!load_elf32(&file, &loadinfo)) {
1073 file.fs->close(&file);
1074 memset(&file, 0, sizeof(file));
1077 prom_printf(" Elf32 kernel loaded...\n");
1078 } else if (is_elf64(&loadinfo)) {
1079 if (!load_elf64(&file, &loadinfo)) {
1080 file.fs->close(&file);
1081 memset(&file, 0, sizeof(file));
1084 prom_printf(" Elf64 kernel loaded...\n");
1086 prom_printf ("%s: Not a valid ELF image\n", params.kernel.file);
1087 file.fs->close(&file);
1088 memset(&file, 0, sizeof(file));
1091 file.fs->close(&file);
1092 memset(&file, 0, sizeof(file));
1094 /* If ramdisk, load it (only if booting a vmlinux). For now, we
1095 * can't tell the size it will be so we claim an arbitrary amount
1098 if (flat_vmlinux && params.rd.file) {
1099 if(strlen(boot.file) && !strcmp(boot.file,"\\\\") && params.rd.file[0] != '/'
1100 && params.kernel.file[0] != '\\')
1103 loc=(char*)malloc(strlen(params.rd.file)+3);
1105 prom_printf ("Malloc error\n");
1108 strcpy(loc,boot.file);
1109 strcat(loc,params.rd.file);
1110 free(params.rd.file);
1113 prom_printf("Loading ramdisk...\n");
1114 result = open_file(¶ms.rd, &file);
1115 if (result != FILE_ERR_OK) {
1116 prom_printf("%s:%d,", params.rd.dev, params.rd.part);
1117 prom_perror(result, params.rd.file);
1120 #define INITRD_CHUNKSIZE 0x100000
1121 initrd_base = prom_claim(loadinfo.base+loadinfo.memsize, INITRD_CHUNKSIZE, 0);
1122 if (initrd_base == (void *)-1) {
1123 prom_printf("Claim failed for initrd memory\n");
1126 initrd_size = file.fs->read(&file, INITRD_CHUNKSIZE, initrd_base);
1127 if (initrd_size == 0)
1129 initrd_read = initrd_size;
1130 initrd_more = initrd_base;
1131 while (initrd_read == INITRD_CHUNKSIZE ) { /* need to read more? */
1132 initrd_want = (void *)((unsigned long)initrd_more+INITRD_CHUNKSIZE);
1133 initrd_more = prom_claim(initrd_want, INITRD_CHUNKSIZE, 0);
1134 if (initrd_more != initrd_want) {
1135 prom_printf("Claim failed for initrd memory at %p rc=%p\n",initrd_want,initrd_more);
1139 initrd_read = file.fs->read(&file, INITRD_CHUNKSIZE, initrd_more);
1140 DEBUG_F(" block at %p rc=%lu\n",initrd_more,initrd_read);
1141 initrd_size += initrd_read;
1144 file.fs->close(&file);
1145 memset(&file, 0, sizeof(file));
1148 prom_printf("ramdisk loaded at %p, size: %lu Kbytes\n",
1149 initrd_base, initrd_size >> 10);
1151 prom_printf("ramdisk load failed !\n");
1156 DEBUG_F("setting kernel args to: %s\n", params.args);
1157 prom_setargs(params.args);
1158 DEBUG_F("flushing icache...");
1159 flush_icache_range ((long)loadinfo.base, (long)loadinfo.base+loadinfo.memsize);
1162 /* compute the kernel's entry point. */
1163 kernel_entry = loadinfo.base + loadinfo.entry - loadinfo.load_loc;
1165 DEBUG_F("Kernel entry point = %p\n", kernel_entry);
1166 DEBUG_F("kernel: arg1 = %p,\n"
1167 " arg2 = 0x%08lx,\n"
1171 initrd_base + loadinfo.load_loc, initrd_size, prom, 0, 0);
1173 DEBUG_F("Entering kernel...\n");
1175 /* call the kernel with our stack. */
1176 kernel_entry(initrd_base + loadinfo.load_loc, initrd_size, prom, 0, 0);
1184 load_elf32(struct boot_file_t *file, loadinfo_t *loadinfo)
1187 Elf32_Ehdr *e = &(loadinfo->elf.elf32hdr);
1189 int size = sizeof(Elf32_Ehdr) - sizeof(Elf_Ident);
1190 unsigned long loadaddr;
1192 /* Read the rest of the Elf header... */
1193 if ((*(file->fs->read))(file, size, &e->e_version) < size) {
1194 prom_printf("\nCan't read Elf32 image header\n");
1198 DEBUG_F("Elf32 header:\n");
1199 DEBUG_F(" e.e_type = %d\n", (int)e->e_type);
1200 DEBUG_F(" e.e_machine = %d\n", (int)e->e_machine);
1201 DEBUG_F(" e.e_version = %d\n", (int)e->e_version);
1202 DEBUG_F(" e.e_entry = 0x%08x\n", (int)e->e_entry);
1203 DEBUG_F(" e.e_phoff = 0x%08x\n", (int)e->e_phoff);
1204 DEBUG_F(" e.e_shoff = 0x%08x\n", (int)e->e_shoff);
1205 DEBUG_F(" e.e_flags = %d\n", (int)e->e_flags);
1206 DEBUG_F(" e.e_ehsize = 0x%08x\n", (int)e->e_ehsize);
1207 DEBUG_F(" e.e_phentsize = 0x%08x\n", (int)e->e_phentsize);
1208 DEBUG_F(" e.e_phnum = %d\n", (int)e->e_phnum);
1210 loadinfo->entry = e->e_entry;
1212 if (e->e_phnum > MAX_HEADERS) {
1213 prom_printf ("Can only load kernels with one program header\n");
1217 ph = (Elf32_Phdr *)malloc(sizeof(Elf32_Phdr) * e->e_phnum);
1219 prom_printf ("Malloc error\n");
1223 /* Now, we read the section header */
1224 if ((*(file->fs->seek))(file, e->e_phoff) != FILE_ERR_OK) {
1225 prom_printf ("seek error\n");
1228 if ((*(file->fs->read))(file, sizeof(Elf32_Phdr) * e->e_phnum, ph) !=
1229 sizeof(Elf32_Phdr) * e->e_phnum) {
1230 prom_printf ("read error\n");
1234 /* Scan through the program header
1235 * HACK: We must return the _memory size of the kernel image, not the
1236 * file size (because we have to leave room before other boot
1237 * infos. This code works as a side effect of the fact that
1238 * we have one section and vaddr == p_paddr
1240 loadinfo->memsize = loadinfo->filesize = loadinfo->offset = 0;
1242 for (i = 0; i < e->e_phnum; ++i, ++p) {
1243 if (p->p_type != PT_LOAD || p->p_offset == 0)
1245 if (loadinfo->memsize == 0) {
1246 loadinfo->offset = p->p_offset;
1247 loadinfo->memsize = p->p_memsz;
1248 loadinfo->filesize = p->p_filesz;
1249 loadinfo->load_loc = p->p_vaddr;
1251 loadinfo->memsize = p->p_offset + p->p_memsz - loadinfo->offset; /* XXX Bogus */
1252 loadinfo->filesize = p->p_offset + p->p_filesz - loadinfo->offset;
1256 if (loadinfo->memsize == 0) {
1257 prom_printf("Can't find a loadable segment !\n");
1261 /* leave some room (1Mb) for boot infos */
1262 loadinfo->memsize = _ALIGN(loadinfo->memsize,(1<<20)) + 0x100000;
1263 /* Claim OF memory */
1264 DEBUG_F("Before prom_claim, mem_sz: 0x%08lx\n", loadinfo->memsize);
1266 /* Determine whether we are trying to boot a vmlinux or some
1267 * other binary image (eg, zImage). We load vmlinux's at
1268 * KERNELADDR and all other binaries at their e_entry value.
1270 if (e->e_entry == KERNEL_LINK_ADDR_PPC32) {
1272 loadaddr = KERNELADDR;
1275 loadaddr = loadinfo->load_loc;
1278 loadinfo->base = prom_claim_chunk((void *)loadaddr, loadinfo->memsize, 0);
1279 if (loadinfo->base == (void *)-1) {
1280 prom_printf("Claim error, can't allocate kernel memory\n");
1284 DEBUG_F("After ELF parsing, load base: %p, mem_sz: 0x%08lx\n",
1285 loadinfo->base, loadinfo->memsize);
1286 DEBUG_F(" wanted load base: 0x%08lx, mem_sz: 0x%08lx\n",
1287 loadaddr, loadinfo->memsize);
1289 /* Load the program segments... */
1291 for (i = 0; i < e->e_phnum; ++i, ++p) {
1292 unsigned long offset;
1293 if (p->p_type != PT_LOAD || p->p_offset == 0)
1296 /* Now, we skip to the image itself */
1297 if ((*(file->fs->seek))(file, p->p_offset) != FILE_ERR_OK) {
1298 prom_printf ("Seek error\n");
1299 prom_release(loadinfo->base, loadinfo->memsize);
1302 offset = p->p_vaddr - loadinfo->load_loc;
1303 if ((*(file->fs->read))(file, p->p_filesz, loadinfo->base+offset) != p->p_filesz) {
1304 prom_printf ("Read failed\n");
1305 prom_release(loadinfo->base, loadinfo->memsize);
1312 /* Return success at loading the Elf32 kernel */
1322 load_elf64(struct boot_file_t *file, loadinfo_t *loadinfo)
1325 Elf64_Ehdr *e = &(loadinfo->elf.elf64hdr);
1327 int size = sizeof(Elf64_Ehdr) - sizeof(Elf_Ident);
1328 unsigned long loadaddr;
1330 /* Read the rest of the Elf header... */
1331 if ((*(file->fs->read))(file, size, &e->e_version) < size) {
1332 prom_printf("\nCan't read Elf64 image header\n");
1336 DEBUG_F("Elf64 header:\n");
1337 DEBUG_F(" e.e_type = %d\n", (int)e->e_type);
1338 DEBUG_F(" e.e_machine = %d\n", (int)e->e_machine);
1339 DEBUG_F(" e.e_version = %d\n", (int)e->e_version);
1340 DEBUG_F(" e.e_entry = 0x%016lx\n", (long)e->e_entry);
1341 DEBUG_F(" e.e_phoff = 0x%016lx\n", (long)e->e_phoff);
1342 DEBUG_F(" e.e_shoff = 0x%016lx\n", (long)e->e_shoff);
1343 DEBUG_F(" e.e_flags = %d\n", (int)e->e_flags);
1344 DEBUG_F(" e.e_ehsize = 0x%08x\n", (int)e->e_ehsize);
1345 DEBUG_F(" e.e_phentsize = 0x%08x\n", (int)e->e_phentsize);
1346 DEBUG_F(" e.e_phnum = %d\n", (int)e->e_phnum);
1348 loadinfo->entry = e->e_entry;
1350 if (e->e_phnum > MAX_HEADERS) {
1351 prom_printf ("Can only load kernels with one program header\n");
1355 ph = (Elf64_Phdr *)malloc(sizeof(Elf64_Phdr) * e->e_phnum);
1357 prom_printf ("Malloc error\n");
1361 /* Now, we read the section header */
1362 if ((*(file->fs->seek))(file, e->e_phoff) != FILE_ERR_OK) {
1363 prom_printf ("Seek error\n");
1366 if ((*(file->fs->read))(file, sizeof(Elf64_Phdr) * e->e_phnum, ph) !=
1367 sizeof(Elf64_Phdr) * e->e_phnum) {
1368 prom_printf ("Read error\n");
1372 /* Scan through the program header
1373 * HACK: We must return the _memory size of the kernel image, not the
1374 * file size (because we have to leave room before other boot
1375 * infos. This code works as a side effect of the fact that
1376 * we have one section and vaddr == p_paddr
1378 loadinfo->memsize = loadinfo->filesize = loadinfo->offset = 0;
1380 for (i = 0; i < e->e_phnum; ++i, ++p) {
1381 if (p->p_type != PT_LOAD || p->p_offset == 0)
1383 if (loadinfo->memsize == 0) {
1384 loadinfo->offset = p->p_offset;
1385 loadinfo->memsize = p->p_memsz;
1386 loadinfo->filesize = p->p_filesz;
1387 loadinfo->load_loc = p->p_vaddr;
1389 loadinfo->memsize = p->p_offset + p->p_memsz - loadinfo->offset; /* XXX Bogus */
1390 loadinfo->filesize = p->p_offset + p->p_filesz - loadinfo->offset;
1394 if (loadinfo->memsize == 0) {
1395 prom_printf("Can't find a loadable segment !\n");
1399 loadinfo->memsize = _ALIGN(loadinfo->memsize,(1<<20));
1400 /* Claim OF memory */
1401 DEBUG_F("Before prom_claim, mem_sz: 0x%08lx\n", loadinfo->memsize);
1403 /* Determine whether we are trying to boot a vmlinux or some
1404 * other binary image (eg, zImage). We load vmlinux's at
1405 * KERNELADDR and all other binaries at their e_entry value.
1407 if (e->e_entry == KERNEL_LINK_ADDR_PPC64) {
1409 loadaddr = KERNELADDR;
1412 loadaddr = e->e_entry;
1415 loadinfo->base = prom_claim_chunk((void *)loadaddr, loadinfo->memsize, 0);
1416 if (loadinfo->base == (void *)-1) {
1417 prom_printf("Claim error, can't allocate kernel memory\n");
1421 DEBUG_F("After ELF parsing, load base: %p, mem_sz: 0x%08lx\n",
1422 loadinfo->base, loadinfo->memsize);
1423 DEBUG_F(" wanted load base: 0x%08lx, mem_sz: 0x%08lx\n",
1424 loadaddr, loadinfo->memsize);
1426 /* Load the program segments... */
1428 for (i = 0; i < e->e_phnum; ++i, ++p) {
1429 unsigned long offset;
1430 if (p->p_type != PT_LOAD || p->p_offset == 0)
1433 /* Now, we skip to the image itself */
1434 if ((*(file->fs->seek))(file, p->p_offset) != FILE_ERR_OK) {
1435 prom_printf ("Seek error\n");
1436 prom_release(loadinfo->base, loadinfo->memsize);
1439 offset = p->p_vaddr - loadinfo->load_loc;
1440 if ((*(file->fs->read))(file, p->p_filesz, loadinfo->base+offset) != p->p_filesz) {
1441 prom_printf ("Read failed\n");
1442 prom_release(loadinfo->base, loadinfo->memsize);
1449 /* Return success at loading the Elf64 kernel */
1459 is_elf32(loadinfo_t *loadinfo)
1461 Elf32_Ehdr *e = &(loadinfo->elf.elf32hdr);
1463 return (e->e_ident[EI_MAG0] == ELFMAG0 &&
1464 e->e_ident[EI_MAG1] == ELFMAG1 &&
1465 e->e_ident[EI_MAG2] == ELFMAG2 &&
1466 e->e_ident[EI_MAG3] == ELFMAG3 &&
1467 e->e_ident[EI_CLASS] == ELFCLASS32 &&
1468 e->e_ident[EI_DATA] == ELFDATA2MSB &&
1469 e->e_type == ET_EXEC &&
1470 e->e_machine == EM_PPC);
1474 is_elf64(loadinfo_t *loadinfo)
1476 Elf64_Ehdr *e = &(loadinfo->elf.elf64hdr);
1478 return (e->e_ident[EI_MAG0] == ELFMAG0 &&
1479 e->e_ident[EI_MAG1] == ELFMAG1 &&
1480 e->e_ident[EI_MAG2] == ELFMAG2 &&
1481 e->e_ident[EI_MAG3] == ELFMAG3 &&
1482 e->e_ident[EI_CLASS] == ELFCLASS64 &&
1483 e->e_ident[EI_DATA] == ELFDATA2MSB &&
1484 (e->e_type == ET_EXEC || e->e_type == ET_DYN) &&
1485 e->e_machine == EM_PPC64);
1491 #ifdef CONFIG_SET_COLORMAP
1492 static unsigned char default_colors[] = {
1511 prom_handle scrn = PROM_INVALID_HANDLE;
1513 /* Try Apple's mac-boot screen ihandle */
1514 result = (int)call_prom_return("interpret", 1, 2,
1515 "\" _screen-ihandle\" $find if execute else 0 then", &scrn);
1516 DEBUG_F("Trying to get screen ihandle, result: %d, scrn: %p\n", result, scrn);
1518 if (scrn == 0 || scrn == PROM_INVALID_HANDLE) {
1520 /* Hrm... check to see if stdout is a display */
1521 scrn = call_prom ("instance-to-package", 1, 1, prom_stdout);
1522 DEBUG_F("instance-to-package of stdout is: %p\n", scrn);
1523 if (prom_getprop(scrn, "device_type", type, 32) > 0 && !strncmp(type, "display", 7)) {
1524 DEBUG_F("got it ! stdout is a screen\n");
1527 /* Else, we try to open the package */
1528 scrn = (prom_handle)call_prom( "open", 1, 1, "screen" );
1529 DEBUG_F("Open screen result: %p\n", scrn);
1533 if (scrn == PROM_INVALID_HANDLE) {
1534 prom_printf("No screen device found !\n");
1538 prom_set_color(scrn, i, default_colors[i*3],
1539 default_colors[i*3+1], default_colors[i*3+2]);
1541 prom_printf("\x1b[1;37m\x1b[2;40m");
1543 for (i=0;i<16; i++) {
1544 prom_printf("\x1b[%d;%dm\x1b[1;47m%s \x1b[2;40m %s\n",
1545 ansi_color_table[i].index,
1546 ansi_color_table[i].value,
1547 ansi_color_table[i].name,
1548 ansi_color_table[i].name);
1549 prom_printf("\x1b[%d;%dm\x1b[1;37m%s \x1b[2;30m %s\n",
1550 ansi_color_table[i].index,
1551 ansi_color_table[i].value+10,
1552 ansi_color_table[i].name,
1553 ansi_color_table[i].name);
1555 prom_printf("\x1b[1;37m\x1b[2;40m");
1556 #endif /* COLOR_TEST */
1562 #endif /* CONFIG_SET_COLORMAP */
1571 char conf_path[1024];
1573 if (_machine == _MACH_Pmac)
1576 prom_get_chosen("bootargs", bootargs, sizeof(bootargs));
1577 DEBUG_F("/chosen/bootargs = %s\n", bootargs);
1578 prom_get_chosen("bootpath", bootdevice, BOOTDEVSZ);
1579 DEBUG_F("/chosen/bootpath = %s\n", bootdevice);
1580 if (prom_get_options("ibm,client-architecture-support-reboot",fw_nbr_reboots, FW_NBR_REBOOTSZ) == -1 )
1581 prom_get_options("ibm,fw-nbr-reboots",fw_nbr_reboots, FW_NBR_REBOOTSZ);
1582 fw_reboot_cnt = simple_strtol(fw_nbr_reboots,&endp,10);
1583 if (fw_reboot_cnt > 0L)
1584 prom_get_options("boot-last-label", bootlastlabel, BOOTLASTSZ);
1586 /* If conf= specified on command line, it overrides
1587 Usage: conf=device:partition,/path/to/conffile
1588 Example: On Open Firmware Prompt, type
1589 boot conf=/pci@8000000f8000000/pci@1/pci1014,028C@1/scsi@0/sd@1,0:3,/etc/yaboot.conf */
1591 if (!strncmp(bootargs, "conf=", 5)) {
1592 DEBUG_F("Using conf argument in Open Firmware\n");
1593 char *end = strchr(bootargs,' ');
1597 strcpy(bootdevice, bootargs + 5);
1599 DEBUG_F("Using conf=%s\n", bootdevice);
1601 /* Remove conf=xxx from bootargs */
1603 memmove(bootargs, end+1, strlen(end+1)+1);
1607 if (bootdevice[0] == 0) {
1608 prom_get_options("boot-device", bootdevice, BOOTDEVSZ);
1609 DEBUG_F("boot-device = %s\n", bootdevice);
1611 if (bootdevice[0] == 0) {
1612 prom_printf("Couldn't determine boot device\n");
1616 if (bootoncelabel[0] == 0) {
1617 prom_get_options("boot-once", bootoncelabel,
1618 sizeof(bootoncelabel));
1619 if (bootoncelabel[0] != 0)
1620 DEBUG_F("boot-once: [%s]\n", bootoncelabel);
1622 prom_set_options("boot-once", NULL, 0);
1624 if (!parse_device_path(bootdevice, NULL, -1, "", &boot)) {
1625 prom_printf("%s: Unable to parse\n", bootdevice);
1628 if (_machine == _MACH_bplan && !conf_given)
1630 DEBUG_F("After parse_device_path: dev=%s, part=%d, file=%s\n",
1631 boot.dev, boot.part, boot.file);
1634 if (_machine == _MACH_chrp || _machine == _MACH_bplan)
1635 boot.file = "/etc/";
1636 else if (strlen(boot.file)) {
1637 if (!strncmp(boot.file, "\\\\", 2))
1641 p = last = boot.file;
1651 if (strlen(boot.file))
1652 strcat(boot.file, "\\");
1655 strcpy(conf_path, boot.file);
1656 strcat(conf_path, CONFIG_FILE_NAME);
1657 boot.file = conf_path;
1658 DEBUG_F("After path kludgeup: dev=%s, part=%d, file=%s\n",
1659 boot.dev, boot.part, boot.file);
1663 * If we're doing a netboot, first look for one which matches our
1666 if (prom_get_devtype(boot.dev) == FILE_DEVICE_NET) {
1667 prom_printf("Try to netboot\n");
1668 useconf = load_my_config_file(&boot);
1672 useconf = load_config_file(&boot);
1674 prom_printf("Welcome to yaboot version " VERSION "\n");
1675 prom_printf("Enter \"help\" to get some basic usage information\n");
1677 /* I am fed up with lusers using the wrong partition type and
1678 mailing me *when* it breaks */
1680 if (_machine == _MACH_Pmac) {
1681 char *entry = cfg_get_strg(0, "ptypewarning");
1684 warn = strcmp(entry,
1685 "I_know_the_partition_type_is_wrong_and_will_NOT_send_mail_when_booting_breaks");
1687 ptype = get_part_type(boot.dev, boot.part);
1688 if ((ptype != NULL) && (strcmp(ptype, "Apple_Bootstrap")))
1689 prom_printf("\nWARNING: Bootstrap partition type is wrong: \"%s\"\n"
1690 " type should be: \"Apple_Bootstrap\"\n\n", ptype);
1698 prom_printf("Bye.\n");
1704 * c-file-style: "k&r"