diff --git a/sys/boot/uboot/common/main.c b/sys/boot/uboot/common/main.c index 52dc9d7a5130..531d72857294 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[]; @@ -116,12 +133,242 @@ meminfo(void) } } +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 (""); +} + +/* + * 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: + * + * DEV_TYP_STOR, DEV_TYP_NET + * DEV_TYP_STOR, DEV_TYP_NET + * : DEV_TYP_STOR, DEV_TYP_NET + * : DEV_TYP_STOR + * :. DEV_TYP_STOR + * :. 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; + + devstr = ub_env_get("loaderdev"); + if (devstr == NULL) + devstr = ""; + else + printf("U-Boot setting: loaderdev=%s\n", devstr); + + p = get_device_type(devstr, type); + + *unit = -1; + *slice = 0; + *partition = -1; + + /* + * 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 int +probe_disks(int load_type, int load_unit, int load_slice, int load_partition) +{ + int i, 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 storage devices...\n"); + /* Try each disk in succession until one works. */ + for (currdev.d_unit = 0; currdev.d_unit < UB_MAX_DEV; + currdev.d_unit++) { + printf("Checking unit=%d slice=%d partition=%d...", + currdev.d_unit, currdev.d_disk.slice, + currdev.d_disk.partition); + open_result = devsw[i]->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; + printf("Checking unit=%d slice=%d partition=%d...", + currdev.d_unit, currdev.d_disk.slice, + currdev.d_disk.partition); + open_result = devsw[i]->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) { + printf("Checking unit=%d slice=%d partition=%d...", + currdev.d_unit, currdev.d_disk.slice, + currdev.d_disk.partition); + open_result = devsw[i]->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; /* @@ -173,50 +420,11 @@ main(void) 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 +432,33 @@ main(void) if ((devsw[i]->dv_init)() != 0) continue; - printf("Bootable device: %s\n", devsw[i]->dv_name); + printf("Found U-Boot 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; + currdev.d_dev = devsw[i]; + currdev.d_type = currdev.d_dev->dv_type; + currdev.d_unit = 0; + + if ((load_type == -1 || (load_type & DEV_TYP_STOR)) && + strcmp(devsw[i]->dv_name, "disk") == 0) { + if (probe_disks(load_type, load_unit, load_slice, + load_partition) == 0) + break; } + + if (load_type == -1 || ((load_type & DEV_TYP_NET) && + strcmp(devsw[i]->dv_name, "net") == 0)) + break; } - if (diskdev != -1) - usedev = diskdev; - else if (netdev != -1) - usedev = netdev; - else - panic("No bootable devices found!\n"); - -have_device: - - 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; - - 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); diff --git a/sys/boot/uboot/lib/api_public.h b/sys/boot/uboot/lib/api_public.h index 953747972502..f393de88933b 100644 --- a/sys/boot/uboot/lib/api_public.h +++ b/sys/boot/uboot/lib/api_public.h @@ -132,7 +132,7 @@ typedef unsigned long lbastart_t; #define DT_STOR_SCSI 0x0020 #define DT_STOR_USB 0x0040 #define DT_STOR_MMC 0x0080 -#define DT_STOR_NAND 0x0100 +#define DT_STOR_SATA 0x0100 #define DEV_STA_CLOSED 0x0000 /* invalid, closed */ #define DEV_STA_OPEN 0x0001 /* open i.e. active */ diff --git a/sys/boot/uboot/lib/disk.c b/sys/boot/uboot/lib/disk.c index 62b31328f011..4681e1bf0a79 100644 --- a/sys/boot/uboot/lib/disk.c +++ b/sys/boot/uboot/lib/disk.c @@ -278,3 +278,26 @@ stor_ioctl(struct open_file *f, u_long cmd, void *data) return (0); } + +/* + * Return the device unit number for the given type and type-relative unit + * number. + */ +int +uboot_diskgetunit(int type, int type_unit) +{ + int local_type_unit; + int i; + + local_type_unit = 0; + for (i = 0; i < stor_info_no; i++) { + if ((stor_info[i].type & type) == type) { + if (local_type_unit == type_unit) { + return (i); + } + local_type_unit++; + } + } + + return (-1); +} diff --git a/sys/boot/uboot/lib/glue.c b/sys/boot/uboot/lib/glue.c index f389f1128ba6..d01e33b167ae 100644 --- a/sys/boot/uboot/lib/glue.c +++ b/sys/boot/uboot/lib/glue.c @@ -404,8 +404,8 @@ ub_stor_type(int type) if (type & DT_STOR_MMC) return ("MMC"); - if (type & DT_STOR_NAND) - return ("NAND"); + if (type & DT_STOR_SATA) + return ("SATA"); return ("Unknown"); } diff --git a/sys/boot/uboot/lib/libuboot.h b/sys/boot/uboot/lib/libuboot.h index bf07ad380a6d..82410a08a720 100644 --- a/sys/boot/uboot/lib/libuboot.h +++ b/sys/boot/uboot/lib/libuboot.h @@ -32,9 +32,9 @@ struct uboot_devdesc struct devsw *d_dev; int d_type; int d_unit; + void *d_opendata; union { struct { - void *data; int slice; int partition; off_t offset; @@ -70,6 +70,8 @@ extern struct file_format uboot_elf; void reboot(void); +int uboot_diskgetunit(int type, int type_unit); + #if defined(LOADER_FDT_SUPPORT) extern int fdt_setup_fdtp(); extern int fdt_copy(vm_offset_t);