]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/boot/uboot/common/main.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / boot / uboot / common / main.c
1 /*-
2  * Copyright (c) 2000 Benno Rice <benno@jeamland.net>
3  * Copyright (c) 2000 Stephane Potvin <sepotvin@videotron.ca>
4  * Copyright (c) 2007-2008 Semihalf, Rafal Jaworowski <raj@semihalf.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
32
33 #include <stand.h>
34
35 #include "api_public.h"
36 #include "bootstrap.h"
37 #include "glue.h"
38 #include "libuboot.h"
39
40 #ifndef nitems
41 #define nitems(x)       (sizeof((x)) / sizeof((x)[0]))
42 #endif
43
44 struct uboot_devdesc currdev;
45 struct arch_switch archsw;              /* MI/MD interface boundary */
46 int devs_no;
47
48 uintptr_t uboot_heap_start;
49 uintptr_t uboot_heap_end;
50
51 struct device_type { 
52         const char *name;
53         int type;
54 } device_types[] = {
55         { "disk", DEV_TYP_STOR },
56         { "ide",  DEV_TYP_STOR | DT_STOR_IDE },
57         { "mmc",  DEV_TYP_STOR | DT_STOR_MMC },
58         { "sata", DEV_TYP_STOR | DT_STOR_SATA },
59         { "scsi", DEV_TYP_STOR | DT_STOR_SCSI },
60         { "usb",  DEV_TYP_STOR | DT_STOR_USB },
61         { "net",  DEV_TYP_NET }
62 };
63
64 extern char end[];
65 extern char bootprog_name[];
66 extern char bootprog_rev[];
67 extern char bootprog_date[];
68 extern char bootprog_maker[];
69
70 extern unsigned char _etext[];
71 extern unsigned char _edata[];
72 extern unsigned char __bss_start[];
73 extern unsigned char __sbss_start[];
74 extern unsigned char __sbss_end[];
75 extern unsigned char _end[];
76
77 #ifdef LOADER_FDT_SUPPORT
78 extern int command_fdt_internal(int argc, char *argv[]);
79 #endif
80
81 static void
82 dump_sig(struct api_signature *sig)
83 {
84 #ifdef DEBUG
85         printf("signature:\n");
86         printf("  version\t= %d\n", sig->version);
87         printf("  checksum\t= 0x%08x\n", sig->checksum);
88         printf("  sc entry\t= 0x%08x\n", sig->syscall);
89 #endif
90 }
91
92 static void
93 dump_addr_info(void)
94 {
95 #ifdef DEBUG
96         printf("\naddresses info:\n");
97         printf(" _etext (sdata) = 0x%08x\n", (uint32_t)_etext);
98         printf(" _edata         = 0x%08x\n", (uint32_t)_edata);
99         printf(" __sbss_start   = 0x%08x\n", (uint32_t)__sbss_start);
100         printf(" __sbss_end     = 0x%08x\n", (uint32_t)__sbss_end);
101         printf(" __sbss_start   = 0x%08x\n", (uint32_t)__bss_start);
102         printf(" _end           = 0x%08x\n", (uint32_t)_end);
103         printf(" syscall entry  = 0x%08x\n", (uint32_t)syscall_ptr);
104 #endif
105 }
106
107 static uint64_t
108 memsize(struct sys_info *si, int flags)
109 {
110         uint64_t size;
111         int i;
112
113         size = 0;
114         for (i = 0; i < si->mr_no; i++)
115                 if (si->mr[i].flags == flags && si->mr[i].size)
116                         size += (si->mr[i].size);
117
118         return (size);
119 }
120
121 static void
122 meminfo(void)
123 {
124         uint64_t size;
125         struct sys_info *si;
126         int t[3] = { MR_ATTR_DRAM, MR_ATTR_FLASH, MR_ATTR_SRAM };
127         int i;
128
129         if ((si = ub_get_sys_info()) == NULL)
130                 panic("could not retrieve system info");
131
132         for (i = 0; i < 3; i++) {
133                 size = memsize(si, t[i]);
134                 if (size > 0)
135                         printf("%s: %lldMB\n", ub_mem_type(t[i]),
136                             size / 1024 / 1024);
137         }
138 }
139
140 static const char *
141 get_device_type(const char *devstr, int *devtype)
142 {
143         int i;
144         int namelen;
145         struct device_type *dt;
146
147         if (devstr) {
148                 for (i = 0; i < nitems(device_types); i++) {
149                         dt = &device_types[i];
150                         namelen = strlen(dt->name);
151                         if (strncmp(dt->name, devstr, namelen) == 0) {
152                                 *devtype = dt->type;
153                                 return (devstr + namelen);
154                         }
155                 }
156                 printf("Unknown device type '%s'\n", devstr);
157         }
158
159         *devtype = -1;
160         return (NULL);
161 }
162
163 static const char *
164 device_typename(int type)
165 {
166         int i;
167
168         for (i = 0; i < nitems(device_types); i++)
169                 if (device_types[i].type == type)
170                         return (device_types[i].name);
171
172         return ("<unknown>");
173 }
174
175 /*
176  * Parse a device string into type, unit, slice and partition numbers. A
177  * returned value of -1 for type indicates a search should be done for the
178  * first loadable device, otherwise a returned value of -1 for unit
179  * indicates a search should be done for the first loadable device of the
180  * given type.
181  *
182  * The returned values for slice and partition are interpreted by
183  * disk_open().
184  *
185  * Valid device strings:                     For device types:
186  *
187  * <type_name>                               DEV_TYP_STOR, DEV_TYP_NET
188  * <type_name><unit>                         DEV_TYP_STOR, DEV_TYP_NET
189  * <type_name><unit>:                        DEV_TYP_STOR, DEV_TYP_NET
190  * <type_name><unit>:<slice>                 DEV_TYP_STOR
191  * <type_name><unit>:<slice>.                DEV_TYP_STOR
192  * <type_name><unit>:<slice>.<partition>     DEV_TYP_STOR
193  *
194  * For valid type names, see the device_types array, above.
195  *
196  * Slice numbers are 1-based.  0 is a wildcard.
197  */
198 static void
199 get_load_device(int *type, int *unit, int *slice, int *partition)
200 {
201         char *devstr;
202         const char *p;
203         char *endp;
204
205         *type = -1;
206         *unit = -1;
207         *slice = 0;
208         *partition = -1;
209
210         devstr = ub_env_get("loaderdev");
211         if (devstr == NULL) {
212                 printf("U-Boot env: loaderdev not set, will probe all devices.\n");
213                 return;
214         }
215         printf("U-Boot env: loaderdev='%s'\n", devstr);
216
217         p = get_device_type(devstr, type);
218
219         /* Ignore optional spaces after the device name. */
220         while (*p == ' ')
221                 p++;
222
223         /* Unknown device name, or a known name without unit number.  */
224         if ((*type == -1) || (*p == '\0')) {
225                 return;
226         }
227
228         /* Malformed unit number. */
229         if (!isdigit(*p)) {
230                 *type = -1;
231                 return;
232         }
233
234         /* Guaranteed to extract a number from the string, as *p is a digit. */
235         *unit = strtol(p, &endp, 10);
236         p = endp;
237
238         /* Known device name with unit number and nothing else. */
239         if (*p == '\0') {
240                 return;
241         }
242
243         /* Device string is malformed beyond unit number. */
244         if (*p != ':') {
245                 *type = -1;
246                 *unit = -1;
247                 return;
248         }
249
250         p++;
251
252         /* No slice and partition specification. */
253         if ('\0' == *p )
254                 return;
255
256         /* Only DEV_TYP_STOR devices can have a slice specification. */
257         if (!(*type & DEV_TYP_STOR)) {
258                 *type = -1;
259                 *unit = -1;
260                 return;
261         }
262
263         *slice = strtoul(p, &endp, 10);
264
265         /* Malformed slice number. */
266         if (p == endp) {
267                 *type = -1;
268                 *unit = -1;
269                 *slice = 0;
270                 return;
271         }
272
273         p = endp;
274         
275         /* No partition specification. */
276         if (*p == '\0')
277                 return;
278
279         /* Device string is malformed beyond slice number. */
280         if (*p != '.') {
281                 *type = -1;
282                 *unit = -1;
283                 *slice = 0;
284                 return;
285         }
286
287         p++;
288
289         /* No partition specification. */
290         if (*p == '\0')
291                 return;
292
293         *partition = strtol(p, &endp, 10);
294         p = endp;
295
296         /*  Full, valid device string. */
297         if (*endp == '\0')
298                 return;
299
300         /* Junk beyond partition number. */
301         *type = -1;
302         *unit = -1;
303         *slice = 0;
304         *partition = -1;
305
306
307 static void
308 print_disk_probe_info()
309 {
310         char slice[32];
311         char partition[32];
312
313         if (currdev.d_disk.slice > 0)
314                 sprintf(slice, "%d", currdev.d_disk.slice);
315         else
316                 strcpy(slice, "<auto>");
317
318         if (currdev.d_disk.partition > 0)
319                 sprintf(partition, "%d", currdev.d_disk.partition);
320         else
321                 strcpy(partition, "<auto>");
322
323         printf("  Checking unit=%d slice=%s partition=%s...",
324             currdev.d_unit, slice, partition);
325
326 }
327
328 static int
329 probe_disks(int devidx, int load_type, int load_unit, int load_slice, 
330     int load_partition)
331 {
332         int open_result, unit;
333         struct open_file f;
334
335         currdev.d_disk.slice = load_slice;
336         currdev.d_disk.partition = load_partition;
337
338         f.f_devdata = &currdev;
339         open_result = -1;
340
341         if (load_type == -1) {
342                 printf("  Probing all disk devices...\n");
343                 /* Try each disk in succession until one works.  */
344                 for (currdev.d_unit = 0; currdev.d_unit < UB_MAX_DEV;
345                      currdev.d_unit++) {
346                         print_disk_probe_info();
347                         open_result = devsw[devidx]->dv_open(&f, &currdev);
348                         if (open_result == 0) {
349                                 printf(" good.\n");
350                                 return (0);
351                         }
352                         printf("\n");
353                 }
354                 return (-1);
355         }
356
357         if (load_unit == -1) {
358                 printf("  Probing all %s devices...\n", device_typename(load_type));
359                 /* Try each disk of given type in succession until one works. */
360                 for (unit = 0; unit < UB_MAX_DEV; unit++) {
361                         currdev.d_unit = uboot_diskgetunit(load_type, unit);
362                         if (currdev.d_unit == -1)
363                                 break;
364                         print_disk_probe_info();
365                         open_result = devsw[devidx]->dv_open(&f, &currdev);
366                         if (open_result == 0) {
367                                 printf(" good.\n");
368                                 return (0);
369                         }
370                         printf("\n");
371                 }
372                 return (-1);
373         }
374
375         if ((currdev.d_unit = uboot_diskgetunit(load_type, load_unit)) != -1) {
376                 print_disk_probe_info();
377                 open_result = devsw[devidx]->dv_open(&f,&currdev);
378                 if (open_result == 0) {
379                         printf(" good.\n");
380                         return (0);
381                 }
382                 printf("\n");
383         }
384
385         printf("  Requested disk type/unit not found\n");
386         return (-1);
387 }
388
389 int
390 main(void)
391 {
392         struct api_signature *sig = NULL;
393         int load_type, load_unit, load_slice, load_partition;
394         int i;
395         const char * loaderdev;
396
397         /*
398          * If we can't find the magic signature and related info, exit with a
399          * unique error code that U-Boot reports as "## Application terminated,
400          * rc = 0xnnbadab1". Hopefully 'badab1' looks enough like "bad api" to
401          * provide a clue. It's better than 0xffffffff anyway.
402          */
403         if (!api_search_sig(&sig))
404                 return (0x01badab1);
405
406         syscall_ptr = sig->syscall;
407         if (syscall_ptr == NULL)
408                 return (0x02badab1);
409
410         if (sig->version > API_SIG_VERSION)
411                 return (0x03badab1);
412
413         /* Clear BSS sections */
414         bzero(__sbss_start, __sbss_end - __sbss_start);
415         bzero(__bss_start, _end - __bss_start);
416
417         /*
418          * Initialise the heap as early as possible.  Once this is done,
419          * alloc() is usable. The stack is buried inside us, so this is safe.
420          */
421         uboot_heap_start = round_page((uintptr_t)end);
422         uboot_heap_end   = uboot_heap_start + 512 * 1024;
423         setheap((void *)uboot_heap_start, (void *)uboot_heap_end);
424
425         /*
426          * Set up console.
427          */
428         cons_probe();
429         printf("Compatible U-Boot API signature found @%x\n", (uint32_t)sig);
430
431         printf("\n");
432         printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
433         printf("(%s, %s)\n", bootprog_maker, bootprog_date);
434         printf("\n");
435
436         dump_sig(sig);
437         dump_addr_info();
438
439         meminfo();
440
441         /*
442          * Enumerate U-Boot devices
443          */
444         if ((devs_no = ub_dev_enum()) == 0)
445                 panic("no U-Boot devices found");
446         printf("Number of U-Boot devices: %d\n", devs_no);
447
448         get_load_device(&load_type, &load_unit, &load_slice, &load_partition);
449
450         /*
451          * March through the device switch probing for things.
452          */
453         for (i = 0; devsw[i] != NULL; i++) {
454
455                 if (devsw[i]->dv_init == NULL)
456                         continue;
457                 if ((devsw[i]->dv_init)() != 0)
458                         continue;
459
460                 printf("Found U-Boot device: %s\n", devsw[i]->dv_name);
461
462                 currdev.d_dev = devsw[i];
463                 currdev.d_type = currdev.d_dev->dv_type;
464                 currdev.d_unit = 0;
465
466                 if ((load_type == -1 || (load_type & DEV_TYP_STOR)) &&
467                     strcmp(devsw[i]->dv_name, "disk") == 0) {
468                         if (probe_disks(i, load_type, load_unit, load_slice, 
469                             load_partition) == 0)
470                                 break;
471                 }
472
473                 if ((load_type == -1 || (load_type & DEV_TYP_NET)) &&
474                     strcmp(devsw[i]->dv_name, "net") == 0)
475                         break;
476         }
477
478         /*
479          * If we couldn't find a boot device, return an error to u-boot.
480          * U-boot may be running a boot script that can try something different
481          * so returning an error is better than forcing a reboot.
482          */
483         if (devsw[i] == NULL) {
484                 printf("No boot device found!\n");
485                 return (0xbadef1ce);
486         }
487
488         env_setenv("currdev", EV_VOLATILE, uboot_fmtdev(&currdev),
489             uboot_setcurrdev, env_nounset);
490         env_setenv("loaddev", EV_VOLATILE, uboot_fmtdev(&currdev),
491             env_noset, env_nounset);
492
493         setenv("LINES", "24", 1);               /* optional */
494         setenv("prompt", "loader>", 1);
495
496         archsw.arch_loadaddr = uboot_loadaddr;
497         archsw.arch_getdev = uboot_getdev;
498         archsw.arch_copyin = uboot_copyin;
499         archsw.arch_copyout = uboot_copyout;
500         archsw.arch_readin = uboot_readin;
501         archsw.arch_autoload = uboot_autoload;
502
503         interact();                             /* doesn't return */
504
505         return (0);
506 }
507
508
509 COMMAND_SET(heap, "heap", "show heap usage", command_heap);
510 static int
511 command_heap(int argc, char *argv[])
512 {
513
514         printf("heap base at %p, top at %p, used %d\n", end, sbrk(0),
515             sbrk(0) - end);
516
517         return (CMD_OK);
518 }
519
520 COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
521 static int
522 command_reboot(int argc, char *argv[])
523 {
524
525         printf("Resetting...\n");
526         ub_reset();
527
528         printf("Reset failed!\n");
529         while(1);
530 }
531
532 COMMAND_SET(devinfo, "devinfo", "show U-Boot devices", command_devinfo);
533 static int
534 command_devinfo(int argc, char *argv[])
535 {
536         int i;
537
538         if ((devs_no = ub_dev_enum()) == 0) {
539                 command_errmsg = "no U-Boot devices found!?";
540                 return (CMD_ERROR);
541         }
542         
543         printf("U-Boot devices:\n");
544         for (i = 0; i < devs_no; i++) {
545                 ub_dump_di(i);
546                 printf("\n");
547         }
548         return (CMD_OK);
549 }
550
551 COMMAND_SET(sysinfo, "sysinfo", "show U-Boot system info", command_sysinfo);
552 static int
553 command_sysinfo(int argc, char *argv[])
554 {
555         struct sys_info *si;
556
557         if ((si = ub_get_sys_info()) == NULL) {
558                 command_errmsg = "could not retrieve U-Boot sys info!?";
559                 return (CMD_ERROR);
560         }
561
562         printf("U-Boot system info:\n");
563         ub_dump_si(si);
564         return (CMD_OK);
565 }
566
567 enum ubenv_action {
568         UBENV_UNKNOWN,
569         UBENV_SHOW,
570         UBENV_IMPORT
571 };
572
573 static void
574 handle_uboot_env_var(enum ubenv_action action, const char * var)
575 {
576         const char * val;
577         char ubv[128];
578
579         /*
580          * If the user prepended "uboot." (which is how they usually see these
581          * names) strip it off as a convenience.
582          */
583         if (strncmp(var, "uboot.", 6) == 0) {
584                 snprintf(ubv, sizeof(ubv), "%s", &var[6]);
585                 var = ubv;
586         }
587         val = ub_env_get(var);
588         if (action == UBENV_SHOW) {
589                 if (val == NULL)
590                         printf("uboot.%s is not set\n", var);
591                 else
592                         printf("uboot.%s=%s\n", var, val);
593         } else if (action == UBENV_IMPORT) {
594                 if (val != NULL) {
595                         snprintf(ubv, sizeof(ubv), "uboot.%s", var);
596                         setenv(ubv, val, 1);
597                 }
598         }
599 }
600
601 static int
602 command_ubenv(int argc, char *argv[])
603 {
604         enum ubenv_action action;
605         const char *var;
606         int i;
607
608         action = UBENV_UNKNOWN;
609         if (argc > 1) {
610                 if (strcasecmp(argv[1], "import") == 0)
611                         action = UBENV_IMPORT;
612                 else if (strcasecmp(argv[1], "show") == 0)
613                         action = UBENV_SHOW;
614         }
615         if (action == UBENV_UNKNOWN) {
616                 command_errmsg = "usage: 'ubenv <import|show> [var ...]";
617                 return (CMD_ERROR);
618         }
619
620         if (argc > 2) {
621                 for (i = 2; i < argc; i++)
622                         handle_uboot_env_var(action, argv[i]);
623         } else {
624                 var = NULL;
625                 for (;;) {
626                         if ((var = ub_env_enum(var)) == NULL)
627                                 break;
628                         handle_uboot_env_var(action, var);
629                 }
630         }
631
632         return (CMD_OK);
633 }
634 COMMAND_SET(ubenv, "ubenv", "show or import U-Boot env vars", command_ubenv);
635
636 #ifdef LOADER_FDT_SUPPORT
637 /*
638  * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
639  * and declaring it as extern is in contradiction with COMMAND_SET() macro
640  * (which uses static pointer), we're defining wrapper function, which
641  * calls the proper fdt handling routine.
642  */
643 static int
644 command_fdt(int argc, char *argv[])
645 {
646
647         return (command_fdt_internal(argc, argv));
648 }
649
650 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
651 #endif