diff options
Diffstat (limited to 'sys/boot/uboot/common/main.c')
-rw-r--r-- | sys/boot/uboot/common/main.c | 398 |
1 files changed, 307 insertions, 91 deletions
diff --git a/sys/boot/uboot/common/main.c b/sys/boot/uboot/common/main.c index 52dc9d7..16cea23 100644 --- a/sys/boot/uboot/common/main.c +++ b/sys/boot/uboot/common/main.c @@ -36,10 +36,27 @@ __FBSDID("$FreeBSD$"); #include "glue.h" #include "libuboot.h" +#ifndef nitems +#define nitems(x) (sizeof((x)) / sizeof((x)[0])) +#endif + struct uboot_devdesc currdev; struct arch_switch archsw; /* MI/MD interface boundary */ int devs_no; +struct device_type { + const char *name; + int type; +} device_types[] = { + { "disk", DEV_TYP_STOR }, + { "ide", DEV_TYP_STOR | DT_STOR_IDE }, + { "mmc", DEV_TYP_STOR | DT_STOR_MMC }, + { "sata", DEV_TYP_STOR | DT_STOR_SATA }, + { "scsi", DEV_TYP_STOR | DT_STOR_SCSI }, + { "usb", DEV_TYP_STOR | DT_STOR_USB }, + { "net", DEV_TYP_NET } +}; + extern char end[]; extern char bootprog_name[]; extern char bootprog_rev[]; @@ -111,17 +128,265 @@ meminfo(void) for (i = 0; i < 3; i++) { size = memsize(si, t[i]); if (size > 0) - printf("%s:\t %lldMB\n", ub_mem_type(t[i]), + printf("%s: %lldMB\n", ub_mem_type(t[i]), size / 1024 / 1024); } } +static const char * +get_device_type(const char *devstr, int *devtype) +{ + int i; + int namelen; + struct device_type *dt; + + if (devstr) { + for (i = 0; i < nitems(device_types); i++) { + dt = &device_types[i]; + namelen = strlen(dt->name); + if (strncmp(dt->name, devstr, namelen) == 0) { + *devtype = dt->type; + return (devstr + namelen); + } + } + printf("Unknown device type '%s'\n", devstr); + } + + *devtype = -1; + return (NULL); +} + +static const char * +device_typename(int type) +{ + int i; + + for (i = 0; i < nitems(device_types); i++) + if (device_types[i].type == type) + return (device_types[i].name); + + return ("<unknown>"); +} + +/* + * Parse a device string into type, unit, slice and partition numbers. A + * returned value of -1 for type indicates a search should be done for the + * first loadable device, otherwise a returned value of -1 for unit + * indicates a search should be done for the first loadable device of the + * given type. + * + * The returned values for slice and partition are interpreted by + * disk_open(). + * + * Valid device strings: For device types: + * + * <type_name> DEV_TYP_STOR, DEV_TYP_NET + * <type_name><unit> DEV_TYP_STOR, DEV_TYP_NET + * <type_name><unit>: DEV_TYP_STOR, DEV_TYP_NET + * <type_name><unit>:<slice> DEV_TYP_STOR + * <type_name><unit>:<slice>. DEV_TYP_STOR + * <type_name><unit>:<slice>.<partition> DEV_TYP_STOR + * + * For valid type names, see the device_types array, above. + * + * Slice numbers are 1-based. 0 is a wildcard. + */ +static void +get_load_device(int *type, int *unit, int *slice, int *partition) +{ + char *devstr; + const char *p; + char *endp; + + *type = -1; + *unit = -1; + *slice = 0; + *partition = -1; + + devstr = ub_env_get("loaderdev"); + if (devstr == NULL) { + printf("U-Boot env: loaderdev not set, will probe all devices.\n"); + return; + } + printf("U-Boot env: loaderdev='%s'\n", devstr); + + p = get_device_type(devstr, type); + + /* + * Empty device string, or unknown device name, or a bare, known + * device name. + */ + if ((*type == -1) || (*p == '\0')) { + return; + } + + /* Malformed unit number. */ + if (!isdigit(*p)) { + *type = -1; + return; + } + + /* Guaranteed to extract a number from the string, as *p is a digit. */ + *unit = strtol(p, &endp, 10); + p = endp; + + /* Known device name with unit number and nothing else. */ + if (*p == '\0') { + return; + } + + /* Device string is malformed beyond unit number. */ + if (*p != ':') { + *type = -1; + *unit = -1; + return; + } + + p++; + + /* No slice and partition specification. */ + if ('\0' == *p ) + return; + + /* Only DEV_TYP_STOR devices can have a slice specification. */ + if (!(*type & DEV_TYP_STOR)) { + *type = -1; + *unit = -1; + return; + } + + *slice = strtoul(p, &endp, 10); + + /* Malformed slice number. */ + if (p == endp) { + *type = -1; + *unit = -1; + *slice = 0; + return; + } + + p = endp; + + /* No partition specification. */ + if (*p == '\0') + return; + + /* Device string is malformed beyond slice number. */ + if (*p != '.') { + *type = -1; + *unit = -1; + *slice = 0; + return; + } + + p++; + + /* No partition specification. */ + if (*p == '\0') + return; + + *partition = strtol(p, &endp, 10); + p = endp; + + /* Full, valid device string. */ + if (*endp == '\0') + return; + + /* Junk beyond partition number. */ + *type = -1; + *unit = -1; + *slice = 0; + *partition = -1; +} + +static void +print_disk_probe_info() +{ + char slice[32]; + char partition[32]; + + if (currdev.d_disk.slice > 0) + sprintf(slice, "%d", currdev.d_disk.slice); + else + strcpy(slice, "<auto>"); + + if (currdev.d_disk.partition > 0) + sprintf(partition, "%d", currdev.d_disk.partition); + else + strcpy(partition, "<auto>"); + + printf(" Checking unit=%d slice=%s partition=%s...", + currdev.d_unit, slice, partition); + +} + +static int +probe_disks(int devidx, int load_type, int load_unit, int load_slice, + int load_partition) +{ + int open_result, unit; + struct open_file f; + + currdev.d_disk.slice = load_slice; + currdev.d_disk.partition = load_partition; + + f.f_devdata = &currdev; + open_result = -1; + + if (load_type == -1) { + printf(" Probing all disk devices...\n"); + /* Try each disk in succession until one works. */ + for (currdev.d_unit = 0; currdev.d_unit < UB_MAX_DEV; + currdev.d_unit++) { + print_disk_probe_info(); + open_result = devsw[devidx]->dv_open(&f, &currdev); + if (open_result == 0) { + printf(" good.\n"); + return (0); + } + printf("\n"); + } + return (-1); + } + + if (load_unit == -1) { + printf(" Probing all %s devices...\n", device_typename(load_type)); + /* Try each disk of given type in succession until one works. */ + for (unit = 0; unit < UB_MAX_DEV; unit++) { + currdev.d_unit = uboot_diskgetunit(load_type, unit); + if (currdev.d_unit == -1) + break; + print_disk_probe_info(); + open_result = devsw[devidx]->dv_open(&f, &currdev); + if (open_result == 0) { + printf(" good.\n"); + return (0); + } + printf("\n"); + } + return (-1); + } + + if ((currdev.d_unit = uboot_diskgetunit(load_type, load_unit)) != -1) { + print_disk_probe_info(); + open_result = devsw[devidx]->dv_open(&f,&currdev); + if (open_result == 0) { + printf(" good.\n"); + return (0); + } + printf("\n"); + } + + printf(" Requested disk type/unit not found\n"); + return (-1); +} + int main(void) { struct api_signature *sig = NULL; - int diskdev, i, netdev, usedev; - struct open_file f; + int load_type, load_unit, load_slice, load_partition; + int i; const char * loaderdev; /* @@ -145,21 +410,26 @@ main(void) bzero(__bss_start, _end - __bss_start); /* - * Set up console. - */ + * Initialise the heap as early as possible. Once this is done, + * alloc() is usable. The stack is buried inside us, so this is safe. + */ + setheap((void *)end, (void *)(end + 512 * 1024)); + + /* + * Set up console. + */ cons_probe(); + printf("Compatible U-Boot API signature found @%x\n", (uint32_t)sig); - printf("Compatible API signature found @%x\n", (uint32_t)sig); + printf("\n"); + printf("%s, Revision %s\n", bootprog_name, bootprog_rev); + printf("(%s, %s)\n", bootprog_maker, bootprog_date); + printf("\n"); dump_sig(sig); dump_addr_info(); - /* - * Initialise the heap as early as possible. Once this is done, - * alloc() is usable. The stack is buried inside us, so this is - * safe. - */ - setheap((void *)end, (void *)(end + 512 * 1024)); + meminfo(); /* * Enumerate U-Boot devices @@ -168,55 +438,11 @@ main(void) panic("no U-Boot devices found"); printf("Number of U-Boot devices: %d\n", devs_no); - printf("\n"); - printf("%s, Revision %s\n", bootprog_name, bootprog_rev); - printf("(%s, %s)\n", bootprog_maker, bootprog_date); - meminfo(); + get_load_device(&load_type, &load_unit, &load_slice, &load_partition); /* - * March through the device switch probing for things -- sort of. - * - * The devsw array will have one or two items in it. If - * LOADER_DISK_SUPPORT is defined the first item will be a disk (which - * may not actually work if u-boot didn't supply one). If - * LOADER_NET_SUPPORT is defined the next item will be a network - * interface. Again it may not actually work at the u-boot level. - * - * The original logic was to always use a disk if it could be - * successfully opened, otherwise use the network interface. Now that - * logic is amended to first check whether the u-boot environment - * contains a loaderdev variable which tells us which device to use. If - * it does, we use it and skip the original (second) loop which "probes" - * for a device. We still loop over the devsw just in case it ever gets - * expanded to hold more than 2 devices (but then unit numbers, which - * don't currently exist, may come into play). If the device named by - * loaderdev isn't found, fall back using to the old "probe" loop. - * - * The original probe loop still effectively behaves as it always has: - * the first usable disk device is choosen, and a network device is used - * only if no disk device is found. The logic has been reworked so that - * it examines (and thus lists) every potential device along the way - * instead of breaking out of the loop when the first device is found. + * March through the device switch probing for things. */ - loaderdev = ub_env_get("loaderdev"); - usedev = -1; - if (loaderdev != NULL) { - for (i = 0; devsw[i] != NULL; i++) { - if (strcmp(loaderdev, devsw[i]->dv_name) == 0) { - if (devsw[i]->dv_init == NULL) - continue; - if ((devsw[i]->dv_init)() != 0) - continue; - usedev = i; - goto have_device; - } - } - printf("U-Boot env contains 'loaderdev=%s', " - "device not found.\n", loaderdev); - } - printf("Probing for bootable devices...\n"); - diskdev = -1; - netdev = -1; for (i = 0; devsw[i] != NULL; i++) { if (devsw[i]->dv_init == NULL) @@ -224,43 +450,33 @@ main(void) if ((devsw[i]->dv_init)() != 0) continue; - printf("Bootable device: %s\n", devsw[i]->dv_name); - - if (strncmp(devsw[i]->dv_name, "disk", - strlen(devsw[i]->dv_name)) == 0) { - f.f_devdata = &currdev; - currdev.d_dev = devsw[i]; - currdev.d_type = currdev.d_dev->dv_type; - currdev.d_unit = 0; - currdev.d_disk.slice = 0; - if (devsw[i]->dv_open(&f, &currdev) == 0) { - devsw[i]->dv_close(&f); - if (diskdev == -1) - diskdev = i; - } - } else if (strncmp(devsw[i]->dv_name, "net", - strlen(devsw[i]->dv_name)) == 0) { - if (netdev == -1) - netdev = i; - } - } + printf("Found U-Boot device: %s\n", devsw[i]->dv_name); - if (diskdev != -1) - usedev = diskdev; - else if (netdev != -1) - usedev = netdev; - else - panic("No bootable devices found!\n"); + currdev.d_dev = devsw[i]; + currdev.d_type = currdev.d_dev->dv_type; + currdev.d_unit = 0; -have_device: + if ((load_type == -1 || (load_type & DEV_TYP_STOR)) && + strcmp(devsw[i]->dv_name, "disk") == 0) { + if (probe_disks(i, load_type, load_unit, load_slice, + load_partition) == 0) + break; + } - currdev.d_dev = devsw[usedev]; - currdev.d_type = devsw[usedev]->dv_type; - currdev.d_unit = 0; - if (currdev.d_type == DEV_TYP_STOR) - currdev.d_disk.slice = 0; + if ((load_type == -1 || (load_type & DEV_TYP_NET)) && + strcmp(devsw[i]->dv_name, "net") == 0) + break; + } - printf("Current device: %s\n", currdev.d_dev->dv_name); + /* + * If we couldn't find a boot device, return an error to u-boot. + * U-boot may be running a boot script that can try something different + * so returning an error is better than forcing a reboot. + */ + if (devsw[i] == NULL) { + printf("No boot device found!\n"); + return (0xbadef1ce); + } env_setenv("currdev", EV_VOLATILE, uboot_fmtdev(&currdev), uboot_setcurrdev, env_nounset); |