]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/uboot/common/main.c
Use DEV_TYP_NONE instead of -1 to indicate no device was specified.
[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 = DEV_TYP_NONE;
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 = DEV_TYP_NONE;
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.d_slice > 0)
314                 sprintf(slice, "%d", currdev.d_disk.d_slice);
315         else
316                 strcpy(slice, "<auto>");
317
318         if (currdev.d_disk.d_partition >= 0)
319                 sprintf(partition, "%d", currdev.d_disk.d_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.d_slice = load_slice;
336         currdev.d_disk.d_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 == DEV_TYP_NONE || (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 == DEV_TYP_NONE || (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 #ifdef __powerpc__
501         setenv("usefdt", "1", 1);
502 #endif
503
504         archsw.arch_loadaddr = uboot_loadaddr;
505         archsw.arch_getdev = uboot_getdev;
506         archsw.arch_copyin = uboot_copyin;
507         archsw.arch_copyout = uboot_copyout;
508         archsw.arch_readin = uboot_readin;
509         archsw.arch_autoload = uboot_autoload;
510
511         interact();                             /* doesn't return */
512
513         return (0);
514 }
515
516
517 COMMAND_SET(heap, "heap", "show heap usage", command_heap);
518 static int
519 command_heap(int argc, char *argv[])
520 {
521
522         printf("heap base at %p, top at %p, used %td\n", end, sbrk(0),
523             sbrk(0) - end);
524
525         return (CMD_OK);
526 }
527
528 COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
529 static int
530 command_reboot(int argc, char *argv[])
531 {
532
533         printf("Resetting...\n");
534         ub_reset();
535
536         printf("Reset failed!\n");
537         while (1);
538         __unreachable();
539 }
540
541 COMMAND_SET(devinfo, "devinfo", "show U-Boot devices", command_devinfo);
542 static int
543 command_devinfo(int argc, char *argv[])
544 {
545         int i;
546
547         if ((devs_no = ub_dev_enum()) == 0) {
548                 command_errmsg = "no U-Boot devices found!?";
549                 return (CMD_ERROR);
550         }
551         
552         printf("U-Boot devices:\n");
553         for (i = 0; i < devs_no; i++) {
554                 ub_dump_di(i);
555                 printf("\n");
556         }
557         return (CMD_OK);
558 }
559
560 COMMAND_SET(sysinfo, "sysinfo", "show U-Boot system info", command_sysinfo);
561 static int
562 command_sysinfo(int argc, char *argv[])
563 {
564         struct sys_info *si;
565
566         if ((si = ub_get_sys_info()) == NULL) {
567                 command_errmsg = "could not retrieve U-Boot sys info!?";
568                 return (CMD_ERROR);
569         }
570
571         printf("U-Boot system info:\n");
572         ub_dump_si(si);
573         return (CMD_OK);
574 }
575
576 enum ubenv_action {
577         UBENV_UNKNOWN,
578         UBENV_SHOW,
579         UBENV_IMPORT
580 };
581
582 static void
583 handle_uboot_env_var(enum ubenv_action action, const char * var)
584 {
585         char ldvar[128];
586         const char *val;
587         char *wrk;
588         int len;
589
590         /*
591          * On an import with the variable name formatted as ldname=ubname,
592          * import the uboot variable ubname into the loader variable ldname,
593          * otherwise the historical behavior is to import to uboot.ubname.
594          */
595         if (action == UBENV_IMPORT) { 
596                 len = strcspn(var, "=");
597                 if (len == 0) {
598                         printf("name cannot start with '=': '%s'\n", var);
599                         return;
600                 }
601                 if (var[len] == 0) {
602                         strcpy(ldvar, "uboot.");
603                         strncat(ldvar, var, sizeof(ldvar) - 7);
604                 } else {
605                         len = MIN(len, sizeof(ldvar) - 1);
606                         strncpy(ldvar, var, len);
607                         ldvar[len] = 0;
608                         var = &var[len + 1];
609                 }
610         }
611
612         /*
613          * If the user prepended "uboot." (which is how they usually see these
614          * names) strip it off as a convenience.
615          */
616         if (strncmp(var, "uboot.", 6) == 0) {
617                 var = &var[6];
618         }
619
620         /* If there is no variable name left, punt. */
621         if (var[0] == 0) {
622                 printf("empty variable name\n");
623                 return;
624         }
625
626         val = ub_env_get(var);
627         if (action == UBENV_SHOW) {
628                 if (val == NULL)
629                         printf("uboot.%s is not set\n", var);
630                 else
631                         printf("uboot.%s=%s\n", var, val);
632         } else if (action == UBENV_IMPORT) {
633                 if (val != NULL) {
634                         setenv(ldvar, val, 1);
635                 }
636         }
637 }
638
639 static int
640 command_ubenv(int argc, char *argv[])
641 {
642         enum ubenv_action action;
643         const char *var;
644         int i;
645
646         action = UBENV_UNKNOWN;
647         if (argc > 1) {
648                 if (strcasecmp(argv[1], "import") == 0)
649                         action = UBENV_IMPORT;
650                 else if (strcasecmp(argv[1], "show") == 0)
651                         action = UBENV_SHOW;
652         }
653         if (action == UBENV_UNKNOWN) {
654                 command_errmsg = "usage: 'ubenv <import|show> [var ...]";
655                 return (CMD_ERROR);
656         }
657
658         if (argc > 2) {
659                 for (i = 2; i < argc; i++)
660                         handle_uboot_env_var(action, argv[i]);
661         } else {
662                 var = NULL;
663                 for (;;) {
664                         if ((var = ub_env_enum(var)) == NULL)
665                                 break;
666                         handle_uboot_env_var(action, var);
667                 }
668         }
669
670         return (CMD_OK);
671 }
672 COMMAND_SET(ubenv, "ubenv", "show or import U-Boot env vars", command_ubenv);
673
674 #ifdef LOADER_FDT_SUPPORT
675 /*
676  * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
677  * and declaring it as extern is in contradiction with COMMAND_SET() macro
678  * (which uses static pointer), we're defining wrapper function, which
679  * calls the proper fdt handling routine.
680  */
681 static int
682 command_fdt(int argc, char *argv[])
683 {
684
685         return (command_fdt_internal(argc, argv));
686 }
687
688 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
689 #endif