]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/uboot/common/main.c
Re-sync loader.mk and ficl.mk to where they should be
[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 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_info[];
66
67 extern unsigned char _etext[];
68 extern unsigned char _edata[];
69 extern unsigned char __bss_start[];
70 extern unsigned char __sbss_start[];
71 extern unsigned char __sbss_end[];
72 extern unsigned char _end[];
73
74 #ifdef LOADER_FDT_SUPPORT
75 extern int command_fdt_internal(int argc, char *argv[]);
76 #endif
77
78 static void
79 dump_sig(struct api_signature *sig)
80 {
81 #ifdef DEBUG
82         printf("signature:\n");
83         printf("  version\t= %d\n", sig->version);
84         printf("  checksum\t= 0x%08x\n", sig->checksum);
85         printf("  sc entry\t= 0x%08x\n", sig->syscall);
86 #endif
87 }
88
89 static void
90 dump_addr_info(void)
91 {
92 #ifdef DEBUG
93         printf("\naddresses info:\n");
94         printf(" _etext (sdata) = 0x%08x\n", (uint32_t)_etext);
95         printf(" _edata         = 0x%08x\n", (uint32_t)_edata);
96         printf(" __sbss_start   = 0x%08x\n", (uint32_t)__sbss_start);
97         printf(" __sbss_end     = 0x%08x\n", (uint32_t)__sbss_end);
98         printf(" __sbss_start   = 0x%08x\n", (uint32_t)__bss_start);
99         printf(" _end           = 0x%08x\n", (uint32_t)_end);
100         printf(" syscall entry  = 0x%08x\n", (uint32_t)syscall_ptr);
101 #endif
102 }
103
104 static uint64_t
105 memsize(struct sys_info *si, int flags)
106 {
107         uint64_t size;
108         int i;
109
110         size = 0;
111         for (i = 0; i < si->mr_no; i++)
112                 if (si->mr[i].flags == flags && si->mr[i].size)
113                         size += (si->mr[i].size);
114
115         return (size);
116 }
117
118 static void
119 meminfo(void)
120 {
121         uint64_t size;
122         struct sys_info *si;
123         int t[3] = { MR_ATTR_DRAM, MR_ATTR_FLASH, MR_ATTR_SRAM };
124         int i;
125
126         if ((si = ub_get_sys_info()) == NULL)
127                 panic("could not retrieve system info");
128
129         for (i = 0; i < 3; i++) {
130                 size = memsize(si, t[i]);
131                 if (size > 0)
132                         printf("%s: %juMB\n", ub_mem_type(t[i]),
133                             (uintmax_t)(size / 1024 / 1024));
134         }
135 }
136
137 static const char *
138 get_device_type(const char *devstr, int *devtype)
139 {
140         int i;
141         int namelen;
142         struct device_type *dt;
143
144         if (devstr) {
145                 for (i = 0; i < nitems(device_types); i++) {
146                         dt = &device_types[i];
147                         namelen = strlen(dt->name);
148                         if (strncmp(dt->name, devstr, namelen) == 0) {
149                                 *devtype = dt->type;
150                                 return (devstr + namelen);
151                         }
152                 }
153                 printf("Unknown device type '%s'\n", devstr);
154         }
155
156         *devtype = -1;
157         return (NULL);
158 }
159
160 static const char *
161 device_typename(int type)
162 {
163         int i;
164
165         for (i = 0; i < nitems(device_types); i++)
166                 if (device_types[i].type == type)
167                         return (device_types[i].name);
168
169         return ("<unknown>");
170 }
171
172 /*
173  * Parse a device string into type, unit, slice and partition numbers. A
174  * returned value of -1 for type indicates a search should be done for the
175  * first loadable device, otherwise a returned value of -1 for unit
176  * indicates a search should be done for the first loadable device of the
177  * given type.
178  *
179  * The returned values for slice and partition are interpreted by
180  * disk_open().
181  *
182  * Valid device strings:                     For device types:
183  *
184  * <type_name>                               DEV_TYP_STOR, DEV_TYP_NET
185  * <type_name><unit>                         DEV_TYP_STOR, DEV_TYP_NET
186  * <type_name><unit>:                        DEV_TYP_STOR, DEV_TYP_NET
187  * <type_name><unit>:<slice>                 DEV_TYP_STOR
188  * <type_name><unit>:<slice>.                DEV_TYP_STOR
189  * <type_name><unit>:<slice>.<partition>     DEV_TYP_STOR
190  *
191  * For valid type names, see the device_types array, above.
192  *
193  * Slice numbers are 1-based.  0 is a wildcard.
194  */
195 static void
196 get_load_device(int *type, int *unit, int *slice, int *partition)
197 {
198         char *devstr;
199         const char *p;
200         char *endp;
201
202         *type = -1;
203         *unit = -1;
204         *slice = 0;
205         *partition = -1;
206
207         devstr = ub_env_get("loaderdev");
208         if (devstr == NULL) {
209                 printf("U-Boot env: loaderdev not set, will probe all devices.\n");
210                 return;
211         }
212         printf("U-Boot env: loaderdev='%s'\n", devstr);
213
214         p = get_device_type(devstr, type);
215
216         /* Ignore optional spaces after the device name. */
217         while (*p == ' ')
218                 p++;
219
220         /* Unknown device name, or a known name without unit number.  */
221         if ((*type == -1) || (*p == '\0')) {
222                 return;
223         }
224
225         /* Malformed unit number. */
226         if (!isdigit(*p)) {
227                 *type = -1;
228                 return;
229         }
230
231         /* Guaranteed to extract a number from the string, as *p is a digit. */
232         *unit = strtol(p, &endp, 10);
233         p = endp;
234
235         /* Known device name with unit number and nothing else. */
236         if (*p == '\0') {
237                 return;
238         }
239
240         /* Device string is malformed beyond unit number. */
241         if (*p != ':') {
242                 *type = -1;
243                 *unit = -1;
244                 return;
245         }
246
247         p++;
248
249         /* No slice and partition specification. */
250         if ('\0' == *p )
251                 return;
252
253         /* Only DEV_TYP_STOR devices can have a slice specification. */
254         if (!(*type & DEV_TYP_STOR)) {
255                 *type = -1;
256                 *unit = -1;
257                 return;
258         }
259
260         *slice = strtoul(p, &endp, 10);
261
262         /* Malformed slice number. */
263         if (p == endp) {
264                 *type = -1;
265                 *unit = -1;
266                 *slice = 0;
267                 return;
268         }
269
270         p = endp;
271         
272         /* No partition specification. */
273         if (*p == '\0')
274                 return;
275
276         /* Device string is malformed beyond slice number. */
277         if (*p != '.') {
278                 *type = -1;
279                 *unit = -1;
280                 *slice = 0;
281                 return;
282         }
283
284         p++;
285
286         /* No partition specification. */
287         if (*p == '\0')
288                 return;
289
290         *partition = strtol(p, &endp, 10);
291         p = endp;
292
293         /*  Full, valid device string. */
294         if (*endp == '\0')
295                 return;
296
297         /* Junk beyond partition number. */
298         *type = -1;
299         *unit = -1;
300         *slice = 0;
301         *partition = -1;
302
303
304 static void
305 print_disk_probe_info()
306 {
307         char slice[32];
308         char partition[32];
309
310         if (currdev.d_disk.slice > 0)
311                 sprintf(slice, "%d", currdev.d_disk.slice);
312         else
313                 strcpy(slice, "<auto>");
314
315         if (currdev.d_disk.partition >= 0)
316                 sprintf(partition, "%d", currdev.d_disk.partition);
317         else
318                 strcpy(partition, "<auto>");
319
320         printf("  Checking unit=%d slice=%s partition=%s...",
321             currdev.d_unit, slice, partition);
322
323 }
324
325 static int
326 probe_disks(int devidx, int load_type, int load_unit, int load_slice, 
327     int load_partition)
328 {
329         int open_result, unit;
330         struct open_file f;
331
332         currdev.d_disk.slice = load_slice;
333         currdev.d_disk.partition = load_partition;
334
335         f.f_devdata = &currdev;
336         open_result = -1;
337
338         if (load_type == -1) {
339                 printf("  Probing all disk devices...\n");
340                 /* Try each disk in succession until one works.  */
341                 for (currdev.d_unit = 0; currdev.d_unit < UB_MAX_DEV;
342                      currdev.d_unit++) {
343                         print_disk_probe_info();
344                         open_result = devsw[devidx]->dv_open(&f, &currdev);
345                         if (open_result == 0) {
346                                 printf(" good.\n");
347                                 return (0);
348                         }
349                         printf("\n");
350                 }
351                 return (-1);
352         }
353
354         if (load_unit == -1) {
355                 printf("  Probing all %s devices...\n", device_typename(load_type));
356                 /* Try each disk of given type in succession until one works. */
357                 for (unit = 0; unit < UB_MAX_DEV; unit++) {
358                         currdev.d_unit = uboot_diskgetunit(load_type, unit);
359                         if (currdev.d_unit == -1)
360                                 break;
361                         print_disk_probe_info();
362                         open_result = devsw[devidx]->dv_open(&f, &currdev);
363                         if (open_result == 0) {
364                                 printf(" good.\n");
365                                 return (0);
366                         }
367                         printf("\n");
368                 }
369                 return (-1);
370         }
371
372         if ((currdev.d_unit = uboot_diskgetunit(load_type, load_unit)) != -1) {
373                 print_disk_probe_info();
374                 open_result = devsw[devidx]->dv_open(&f,&currdev);
375                 if (open_result == 0) {
376                         printf(" good.\n");
377                         return (0);
378                 }
379                 printf("\n");
380         }
381
382         printf("  Requested disk type/unit/slice/partition not found\n");
383         return (-1);
384 }
385
386 int
387 main(int argc, char **argv)
388 {
389         struct api_signature *sig = NULL;
390         int load_type, load_unit, load_slice, load_partition;
391         int i;
392         const char *ldev;
393
394         /*
395          * We first check if a command line argument was passed to us containing
396          * API's signature address. If it wasn't then we try to search for the
397          * API signature via the usual hinted address.
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_parse_cmdline_sig(argc, argv, &sig) && !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.  We are using the stack u-boot set up near the top
420          * of physical ram; hopefully there is sufficient space between the end
421          * of our bss and the bottom of the u-boot stack to avoid overlap.
422          */
423         uboot_heap_start = round_page((uintptr_t)end);
424         uboot_heap_end   = uboot_heap_start + 512 * 1024;
425         setheap((void *)uboot_heap_start, (void *)uboot_heap_end);
426
427         /*
428          * Set up console.
429          */
430         cons_probe();
431         printf("Compatible U-Boot API signature found @%p\n", sig);
432
433         printf("\n%s", bootprog_info);
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         ldev = uboot_fmtdev(&currdev);
489         env_setenv("currdev", EV_VOLATILE, ldev, uboot_setcurrdev, env_nounset);
490         env_setenv("loaddev", EV_VOLATILE, ldev, env_noset, env_nounset);
491         printf("Booting from %s\n", ldev);
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(NULL);                         /* 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 %td\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         char ldvar[128];
577         const char *val;
578         char *wrk;
579         int len;
580
581         /*
582          * On an import with the variable name formatted as ldname=ubname,
583          * import the uboot variable ubname into the loader variable ldname,
584          * otherwise the historical behavior is to import to uboot.ubname.
585          */
586         if (action == UBENV_IMPORT) { 
587                 len = strcspn(var, "=");
588                 if (len == 0) {
589                         printf("name cannot start with '=': '%s'\n", var);
590                         return;
591                 }
592                 if (var[len] == 0) {
593                         strcpy(ldvar, "uboot.");
594                         strncat(ldvar, var, sizeof(ldvar) - 7);
595                 } else {
596                         len = MIN(len, sizeof(ldvar) - 1);
597                         strncpy(ldvar, var, len);
598                         ldvar[len] = 0;
599                         var = &var[len + 1];
600                 }
601         }
602
603         /*
604          * If the user prepended "uboot." (which is how they usually see these
605          * names) strip it off as a convenience.
606          */
607         if (strncmp(var, "uboot.", 6) == 0) {
608                 var = &var[6];
609         }
610
611         /* If there is no variable name left, punt. */
612         if (var[0] == 0) {
613                 printf("empty variable name\n");
614                 return;
615         }
616
617         val = ub_env_get(var);
618         if (action == UBENV_SHOW) {
619                 if (val == NULL)
620                         printf("uboot.%s is not set\n", var);
621                 else
622                         printf("uboot.%s=%s\n", var, val);
623         } else if (action == UBENV_IMPORT) {
624                 if (val != NULL) {
625                         setenv(ldvar, val, 1);
626                 }
627         }
628 }
629
630 static int
631 command_ubenv(int argc, char *argv[])
632 {
633         enum ubenv_action action;
634         const char *var;
635         int i;
636
637         action = UBENV_UNKNOWN;
638         if (argc > 1) {
639                 if (strcasecmp(argv[1], "import") == 0)
640                         action = UBENV_IMPORT;
641                 else if (strcasecmp(argv[1], "show") == 0)
642                         action = UBENV_SHOW;
643         }
644         if (action == UBENV_UNKNOWN) {
645                 command_errmsg = "usage: 'ubenv <import|show> [var ...]";
646                 return (CMD_ERROR);
647         }
648
649         if (argc > 2) {
650                 for (i = 2; i < argc; i++)
651                         handle_uboot_env_var(action, argv[i]);
652         } else {
653                 var = NULL;
654                 for (;;) {
655                         if ((var = ub_env_enum(var)) == NULL)
656                                 break;
657                         handle_uboot_env_var(action, var);
658                 }
659         }
660
661         return (CMD_OK);
662 }
663 COMMAND_SET(ubenv, "ubenv", "show or import U-Boot env vars", command_ubenv);
664
665 #ifdef LOADER_FDT_SUPPORT
666 /*
667  * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
668  * and declaring it as extern is in contradiction with COMMAND_SET() macro
669  * (which uses static pointer), we're defining wrapper function, which
670  * calls the proper fdt handling routine.
671  */
672 static int
673 command_fdt(int argc, char *argv[])
674 {
675
676         return (command_fdt_internal(argc, argv));
677 }
678
679 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
680 #endif