]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/uboot/common/main.c
MFC r335869, r335933: stand: uboot behavioral fixes
[FreeBSD/FreeBSD.git] / stand / 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 #ifndef HEAP_SIZE
45 #define HEAP_SIZE       (2 * 1024 * 1024)
46 #endif
47
48 struct uboot_devdesc currdev;
49 struct arch_switch archsw;              /* MI/MD interface boundary */
50 int devs_no;
51
52 uintptr_t uboot_heap_start;
53 uintptr_t uboot_heap_end;
54
55 struct device_type { 
56         const char *name;
57         int type;
58 } device_types[] = {
59         { "disk", DEV_TYP_STOR },
60         { "ide",  DEV_TYP_STOR | DT_STOR_IDE },
61         { "mmc",  DEV_TYP_STOR | DT_STOR_MMC },
62         { "sata", DEV_TYP_STOR | DT_STOR_SATA },
63         { "scsi", DEV_TYP_STOR | DT_STOR_SCSI },
64         { "usb",  DEV_TYP_STOR | DT_STOR_USB },
65         { "net",  DEV_TYP_NET }
66 };
67
68 extern char end[];
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: %juMB\n", ub_mem_type(t[i]),
136                             (uintmax_t)(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.dd.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.dd.d_unit = 0; currdev.dd.d_unit < UB_MAX_DEV;
345                      currdev.dd.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.dd.d_unit = uboot_diskgetunit(load_type, unit);
362                         if (currdev.dd.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.dd.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/slice/partition not found\n");
386         return (-1);
387 }
388
389 int
390 main(int argc, char **argv)
391 {
392         struct api_signature *sig = NULL;
393         int load_type, load_unit, load_slice, load_partition;
394         int i;
395         const char *ldev;
396
397         /*
398          * We first check if a command line argument was passed to us containing
399          * API's signature address. If it wasn't then we try to search for the
400          * API signature via the usual hinted address.
401          * If we can't find the magic signature and related info, exit with a
402          * unique error code that U-Boot reports as "## Application terminated,
403          * rc = 0xnnbadab1". Hopefully 'badab1' looks enough like "bad api" to
404          * provide a clue. It's better than 0xffffffff anyway.
405          */
406         if (!api_parse_cmdline_sig(argc, argv, &sig) && !api_search_sig(&sig))
407                 return (0x01badab1);
408
409         syscall_ptr = sig->syscall;
410         if (syscall_ptr == NULL)
411                 return (0x02badab1);
412
413         if (sig->version > API_SIG_VERSION)
414                 return (0x03badab1);
415
416         /* Clear BSS sections */
417         bzero(__sbss_start, __sbss_end - __sbss_start);
418         bzero(__bss_start, _end - __bss_start);
419
420         /*
421          * Initialise the heap as early as possible.  Once this is done,
422          * alloc() is usable.  We are using the stack u-boot set up near the top
423          * of physical ram; hopefully there is sufficient space between the end
424          * of our bss and the bottom of the u-boot stack to avoid overlap.
425          */
426         uboot_heap_start = round_page((uintptr_t)end);
427         uboot_heap_end   = uboot_heap_start + HEAP_SIZE;
428         setheap((void *)uboot_heap_start, (void *)uboot_heap_end);
429
430         /*
431          * Set up console.
432          */
433         cons_probe();
434         printf("Compatible U-Boot API signature found @%p\n", sig);
435
436         printf("\n%s", bootprog_info);
437         printf("\n");
438
439         dump_sig(sig);
440         dump_addr_info();
441
442         meminfo();
443
444         /*
445          * Enumerate U-Boot devices
446          */
447         if ((devs_no = ub_dev_enum()) == 0) {
448                 printf("no U-Boot devices found");
449                 goto do_interact;
450         }
451         printf("Number of U-Boot devices: %d\n", devs_no);
452
453         get_load_device(&load_type, &load_unit, &load_slice, &load_partition);
454
455         /*
456          * March through the device switch probing for things.
457          */
458         for (i = 0; devsw[i] != NULL; i++) {
459
460                 if (devsw[i]->dv_init == NULL)
461                         continue;
462                 if ((devsw[i]->dv_init)() != 0)
463                         continue;
464
465                 printf("Found U-Boot device: %s\n", devsw[i]->dv_name);
466
467                 currdev.dd.d_dev = devsw[i];
468                 currdev.dd.d_unit = 0;
469
470                 if ((load_type == -1 || (load_type & DEV_TYP_STOR)) &&
471                     strcmp(devsw[i]->dv_name, "disk") == 0) {
472                         if (probe_disks(i, load_type, load_unit, load_slice, 
473                             load_partition) == 0)
474                                 break;
475                 }
476
477                 if ((load_type == -1 || (load_type & DEV_TYP_NET)) &&
478                     strcmp(devsw[i]->dv_name, "net") == 0)
479                         break;
480         }
481
482         /*
483          * If we couldn't find a boot device, return an error to u-boot.
484          * U-boot may be running a boot script that can try something different
485          * so returning an error is better than forcing a reboot.
486          */
487         if (devsw[i] == NULL) {
488                 printf("No boot device found!\n");
489                 return (0xbadef1ce);
490         }
491
492         ldev = uboot_fmtdev(&currdev);
493         env_setenv("currdev", EV_VOLATILE, ldev, uboot_setcurrdev, env_nounset);
494         env_setenv("loaddev", EV_VOLATILE, ldev, env_noset, env_nounset);
495         printf("Booting from %s\n", ldev);
496
497 do_interact:
498         setenv("LINES", "24", 1);               /* optional */
499         setenv("prompt", "loader>", 1);
500
501         archsw.arch_loadaddr = uboot_loadaddr;
502         archsw.arch_getdev = uboot_getdev;
503         archsw.arch_copyin = uboot_copyin;
504         archsw.arch_copyout = uboot_copyout;
505         archsw.arch_readin = uboot_readin;
506         archsw.arch_autoload = uboot_autoload;
507
508         interact();                             /* doesn't return */
509
510         return (0);
511 }
512
513
514 COMMAND_SET(heap, "heap", "show heap usage", command_heap);
515 static int
516 command_heap(int argc, char *argv[])
517 {
518
519         printf("heap base at %p, top at %p, used %td\n", end, sbrk(0),
520             sbrk(0) - end);
521
522         return (CMD_OK);
523 }
524
525 COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
526 static int
527 command_reboot(int argc, char *argv[])
528 {
529
530         printf("Resetting...\n");
531         ub_reset();
532
533         printf("Reset failed!\n");
534         while (1);
535         __unreachable();
536 }
537
538 COMMAND_SET(devinfo, "devinfo", "show U-Boot devices", command_devinfo);
539 static int
540 command_devinfo(int argc, char *argv[])
541 {
542         int i;
543
544         if ((devs_no = ub_dev_enum()) == 0) {
545                 command_errmsg = "no U-Boot devices found!?";
546                 return (CMD_ERROR);
547         }
548         
549         printf("U-Boot devices:\n");
550         for (i = 0; i < devs_no; i++) {
551                 ub_dump_di(i);
552                 printf("\n");
553         }
554         return (CMD_OK);
555 }
556
557 COMMAND_SET(sysinfo, "sysinfo", "show U-Boot system info", command_sysinfo);
558 static int
559 command_sysinfo(int argc, char *argv[])
560 {
561         struct sys_info *si;
562
563         if ((si = ub_get_sys_info()) == NULL) {
564                 command_errmsg = "could not retrieve U-Boot sys info!?";
565                 return (CMD_ERROR);
566         }
567
568         printf("U-Boot system info:\n");
569         ub_dump_si(si);
570         return (CMD_OK);
571 }
572
573 enum ubenv_action {
574         UBENV_UNKNOWN,
575         UBENV_SHOW,
576         UBENV_IMPORT
577 };
578
579 static void
580 handle_uboot_env_var(enum ubenv_action action, const char * var)
581 {
582         char ldvar[128];
583         const char *val;
584         char *wrk;
585         int len;
586
587         /*
588          * On an import with the variable name formatted as ldname=ubname,
589          * import the uboot variable ubname into the loader variable ldname,
590          * otherwise the historical behavior is to import to uboot.ubname.
591          */
592         if (action == UBENV_IMPORT) { 
593                 len = strcspn(var, "=");
594                 if (len == 0) {
595                         printf("name cannot start with '=': '%s'\n", var);
596                         return;
597                 }
598                 if (var[len] == 0) {
599                         strcpy(ldvar, "uboot.");
600                         strncat(ldvar, var, sizeof(ldvar) - 7);
601                 } else {
602                         len = MIN(len, sizeof(ldvar) - 1);
603                         strncpy(ldvar, var, len);
604                         ldvar[len] = 0;
605                         var = &var[len + 1];
606                 }
607         }
608
609         /*
610          * If the user prepended "uboot." (which is how they usually see these
611          * names) strip it off as a convenience.
612          */
613         if (strncmp(var, "uboot.", 6) == 0) {
614                 var = &var[6];
615         }
616
617         /* If there is no variable name left, punt. */
618         if (var[0] == 0) {
619                 printf("empty variable name\n");
620                 return;
621         }
622
623         val = ub_env_get(var);
624         if (action == UBENV_SHOW) {
625                 if (val == NULL)
626                         printf("uboot.%s is not set\n", var);
627                 else
628                         printf("uboot.%s=%s\n", var, val);
629         } else if (action == UBENV_IMPORT) {
630                 if (val != NULL) {
631                         setenv(ldvar, val, 1);
632                 }
633         }
634 }
635
636 static int
637 command_ubenv(int argc, char *argv[])
638 {
639         enum ubenv_action action;
640         const char *var;
641         int i;
642
643         action = UBENV_UNKNOWN;
644         if (argc > 1) {
645                 if (strcasecmp(argv[1], "import") == 0)
646                         action = UBENV_IMPORT;
647                 else if (strcasecmp(argv[1], "show") == 0)
648                         action = UBENV_SHOW;
649         }
650         if (action == UBENV_UNKNOWN) {
651                 command_errmsg = "usage: 'ubenv <import|show> [var ...]";
652                 return (CMD_ERROR);
653         }
654
655         if (argc > 2) {
656                 for (i = 2; i < argc; i++)
657                         handle_uboot_env_var(action, argv[i]);
658         } else {
659                 var = NULL;
660                 for (;;) {
661                         if ((var = ub_env_enum(var)) == NULL)
662                                 break;
663                         handle_uboot_env_var(action, var);
664                 }
665         }
666
667         return (CMD_OK);
668 }
669 COMMAND_SET(ubenv, "ubenv", "show or import U-Boot env vars", command_ubenv);
670
671 #ifdef LOADER_FDT_SUPPORT
672 /*
673  * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
674  * and declaring it as extern is in contradiction with COMMAND_SET() macro
675  * (which uses static pointer), we're defining wrapper function, which
676  * calls the proper fdt handling routine.
677  */
678 static int
679 command_fdt(int argc, char *argv[])
680 {
681
682         return (command_fdt_internal(argc, argv));
683 }
684
685 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
686 #endif