summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSuriyan Ramasami <suriyan@Stealth.ibm.com>2013-10-21 22:40:39 +0400
committerMauro Ribeiro <git@mdrjr.net>2013-10-21 22:40:39 +0400
commite8da0148b8c8927296db1ec65b28abe5b133f3b4 (patch)
tree28a1a9eef496f9fb6a6142ede8f923e0414f34d4
parent29d4c3b97c8e30504a951105fb6452210c56528a (diff)
downloadu-boot-e8da0148b8c8927296db1ec65b28abe5b133f3b4.tar.xz
Bug fix and boot menu
Fix netconsole bug and usb keyboard related slowness. Add bootscan to present user with menu
-rw-r--r--common/Makefile2
-rw-r--r--common/cmd_bootm.c31
-rw-r--r--common/cmd_bootscan.c789
-rw-r--r--common/cmd_usb.c41
-rw-r--r--common/menu.c413
-rw-r--r--disk/part_efi.c2
-rw-r--r--doc/README.bootscan43
-rw-r--r--drivers/mmc/mmc.c3
-rw-r--r--drivers/net/netconsole.c15
-rw-r--r--drivers/usb/host/ehci-hcd.c12
-rw-r--r--fs/ext4/dev.c2
-rw-r--r--fs/ext4/ext4_common.c8
-rw-r--r--include/configs/smdk4412.h21
-rw-r--r--include/env_default.h3
-rw-r--r--include/part_efi.h129
-rw-r--r--include/vsprintf.h2
-rw-r--r--lib/vsprintf.c28
-rw-r--r--tools/Makefile6
-rw-r--r--tools/kwboot.c742
19 files changed, 2277 insertions, 15 deletions
diff --git a/common/Makefile b/common/Makefile
index 028323259f..d88262449c 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -81,6 +81,7 @@ COBJS-$(CONFIG_CMD_SOURCE) += cmd_source.o
COBJS-$(CONFIG_CMD_BDI) += cmd_bdinfo.o
COBJS-$(CONFIG_CMD_BEDBUG) += bedbug.o cmd_bedbug.o
COBJS-$(CONFIG_CMD_BMP) += cmd_bmp.o
+COBJS-$(CONFIG_CMD_BOOTSCAN) += cmd_bootscan.o
COBJS-$(CONFIG_CMD_BOOTLDR) += cmd_bootldr.o
COBJS-$(CONFIG_CMD_CACHE) += cmd_cache.o
COBJS-$(CONFIG_CMD_CONSOLE) += cmd_console.o
@@ -176,6 +177,7 @@ COBJS-$(CONFIG_CMD_KGDB) += kgdb.o kgdb_stubs.o
COBJS-$(CONFIG_KALLSYMS) += kallsyms.o
COBJS-$(CONFIG_LCD) += lcd.o
COBJS-$(CONFIG_LYNXKDI) += lynxkdi.o
+COBJS-$(CONFIG_MENU) += menu.o
COBJS-$(CONFIG_MODEM_SUPPORT) += modem.o
COBJS-$(CONFIG_UPDATE_TFTP) += update.o
COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o
diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c
index 314b0ae4e3..7411a30148 100644
--- a/common/cmd_bootm.c
+++ b/common/cmd_bootm.c
@@ -754,6 +754,10 @@ after_header_check:
return 1;
}
+ /* Netconsole causing freeze ups? - Suriyan */
+ eth_halt();
+ eth_unregister(eth_get_dev());
+
arch_preboot_os();
boot_fn(0, argc, argv, &images);
@@ -1146,6 +1150,30 @@ int do_iminfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
return rcode;
}
+struct zimage_header {
+ uint32_t code[9];
+ uint32_t zi_magic;
+ uint32_t zi_start;
+ uint32_t zi_end;
+};
+
+/* Return 1 if its a valid zImage */
+/* Also print stuff about this image */
+static int image_check_zImage(void * hdr)
+{
+ struct zimage_header *zi;
+
+ zi = (struct zimage_header *)map_sysmem(hdr, 0);
+ if (zi->zi_magic == LINUX_ZIMAGE_MAGIC) {
+ puts(" Legacy zImage found\n");
+ printf("Kernel image @ %#08lx [ %#08lx - %#08lx ]\n",
+ hdr, zi->zi_start, zi->zi_end);
+ return 1;
+ }
+ else
+ return 0;
+}
+
static int image_info (ulong addr)
{
void *hdr = (void *)addr;
@@ -1193,6 +1221,9 @@ static int image_info (ulong addr)
return 0;
#endif
default:
+ /* Lets check if its a zImage */
+ if (image_check_zImage(hdr))
+ return 0;
puts ("Unknown image format!\n");
break;
}
diff --git a/common/cmd_bootscan.c b/common/cmd_bootscan.c
new file mode 100644
index 0000000000..8a9ad7b75c
--- /dev/null
+++ b/common/cmd_bootscan.c
@@ -0,0 +1,789 @@
+/*
+ * Copyright (C) 2013 Suriyan Ramasami <suriyan.r@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+
+#if defined(CONFIG_MENU)
+/* Menu related code begins here */
+
+/* Added to use the various usb and extXfs interfaces */
+#include <usb.h>
+#include <ext4fs.h>
+#include <menu.h>
+
+#define BOOTSCAN_MAX_DEVICES 10
+#define BOOTSCAN_MAX_PARTITIONS 10
+#define BOOTSCAN_MAX_BOOTABLES 10
+
+#define BOOTSCAN_EXIT 1
+#define BOOTSCAN_SHOW 2
+
+#if 0
+#define BOOTSCAN_DFLT_BOOTARGS \
+ "setenv bootargs ${console} ubi.mtd=2,2048 " \
+ "root=ubi0:root rootfstype=ubifs debug"
+
+#define BOOTSCAN_DFLT_BOOTCMD \
+ "setenv bootcmd nand read.e 0x800000 0x100000 0x600000"
+#else
+#define BOOTSCAN_DFLT_BOOTARGS CONFIG_BOOTARGS
+#define BOOTSCAN_DFLT_BOOTCMD CONFIG_BOOTCOMMAND
+#endif
+
+#define BOOTSCAN_PROMPT_BOOTCMD \
+ "setenv bootcmd echo Dropping you to u-boot"
+
+#define BOOTSCAN_CHOSEN_BOOTARGS \
+ "setenv bootargs ${console} ${mem} rootwait " \
+ "root=${" BOOTSCAN_ROOT "} ${hdtv_type} ${hdtv_format}"
+
+#define BOOTSCAN_OPTIONS_HEADER \
+ "# Root Iface Dev Part FS FileName " \
+ "Label UUID\n" \
+ BOOTSCAN_OPTIONS_DIVIDER
+
+#define BOOTSCAN_OPTIONS_DIVIDER \
+ "--------------------------------------------------------------" \
+ "----------------"
+
+#define BOOTSCAN_DFLT_NOBOOTABLES \
+ "* Last boot options (None, and no bootables found!)"
+
+#define BOOTSCAN_BOOTARGS "bootscan_bootargs"
+#define UBOOT_BOOTARGS "bootargs"
+#define BOOTSCAN_BOOTCMD "bootscan_bootcmd"
+#define UBOOT_BOOTCMD "bootcmd"
+#define BOOTSCAN_CHOICE "bootscan_choice"
+#define BOOTSCAN_ROOT "bootscan_root"
+
+struct bootscan_bootables {
+ char interface[5];
+ int device;
+ int partition;
+ char label[32];
+ char uuid[37];
+ char bootimage[64];
+ char bootinitrd[64];
+ char bootscr[64];
+ char fstype; /* 'e' => extXfs, '0' => invalid */
+ char rootdev[16];
+};
+
+char *vars[] = { "initrd_high", "fdt_high", "hdtv_type",
+ "hdtv_format", "console", "mem", "" };
+char *var_defaults[] = { "0xffffffff", "0xffffffff", "hdmi",
+ "720p60hz", "tty1 ttySAC1,115200n8", "" };
+char var_values[sizeof(vars) / sizeof((vars)[0])][64];
+
+static void bootscan_menuprint(void *print_buffer)
+{
+ printf("%s\n", (char *)print_buffer);
+}
+
+/* Return non zero if we got some variables in the env.
+ * boot.scr has been loaded at address ptr. It is of length len.
+ * What we need to do is try to grab variables like
+ * a. initrd_high
+ * b. fdt_high
+ * c. hdtv_type
+ * d. hdtv_format
+ * e. mem
+ * f. console (multiple definitions)
+ * Once we have these variables we shall do a setenv of them
+ * Examples:
+ * a. setenv initrd_high initrd_high=<whatever>
+ * b. etc
+ * f. setenv console <console=xyz console=abc ...>
+ * Our bootargs shall be setenv bootargs ${console} ${initrd_high}
+ * ${fdt_high} ${mem} ${hdtv_type} ${hdtv_format} root...
+ */
+static int process_scr(void *ptr, int len) {
+ char *startptr = (char *) ptr;
+ char *endptr = &startptr[len];
+ char *sep = " \t=\n\"\r;%{}'$";
+ char *beg, *end;
+ int i;
+ int retval = 0;
+ char *s;
+ char cmd[128];
+
+ end = startptr;
+ while (end < endptr) {
+ beg = strsep(&end, sep);
+ // Now beg has beginning of a possible string. end points
+ // to end of that possible string
+ if (end == NULL) {
+ end = beg + 1;
+ continue;
+ }
+ if ((end - beg) < 3) {
+ // At least 3 characters long ?
+ continue;
+ }
+ // Got a potential word. Check it out
+ debug("Got word: %s\n", beg);
+ i = 0;
+ while (vars[i][0] != '\0') {
+ if (strcmp(vars[i], beg) == 0) {
+ // We got a word hit.
+ debug("Got a word hit: %s\n", beg);
+ // Now extract the following word
+ do {
+ beg = strsep(&end, sep);
+ if (end == NULL)
+ end = beg + 1;
+ } while (strlen(beg) == 0);
+ debug("Extracted word: %s\n", beg);
+ // cat it over in its entry if its not the same
+ if (strcmp(beg, vars[i])) {
+ if (strlen(var_values[i])) {
+ // multiple console lines
+ strcat(var_values[i], " ");
+ strcat(var_values[i], vars[i]);
+ strcat(var_values[i], "=");
+ }
+ strcat(var_values[i], beg);
+ }
+ }
+ i++;
+ }
+ }
+
+#ifdef DEBUG
+ // Lets print out what we got!
+ debug("-------------- Words extracted -----------------------\n");
+ i = 0;
+ while (vars[i][0] != '\0') {
+ debug("%s : %s\n", vars[i], var_values[i]);
+ i++;
+ }
+ debug("------------------------------------------------------\n");
+#endif
+
+ // Lets setenv the variables
+ i = 0;
+ while (vars[i][0] != '\0') {
+ if (var_values[i][0] == '\0') {
+ // We possibly dont set mem on our own
+ // if boot.scr does not refer to it.
+ if (strcmp(vars[i], "mem") == 0) {
+ i++;
+ continue;
+ }
+ beg = var_defaults[i];
+ }
+ else
+ beg = var_values[i];
+ // Generate the env string
+ sprintf(cmd, "%s=%s", vars[i], beg);
+ debug("Generated env string: %s\n", cmd);
+ s = getenv(vars[i]);
+ if ( (s == NULL) || strcmp(s, cmd)) {
+ setenv(vars[i], cmd);
+ retval = 1;
+ debug("setenv %s %s\n", vars[i], cmd);
+ }
+ else {
+ debug("Env same value: %s = %s\n", vars[i], s);
+ }
+ i++;
+ }
+
+ return retval;
+}
+
+/*
+ * We shall use bootscan_<> variables to capture the state of past menu
+ * choices.
+ * bootscan_bootargs corresponds to bootargs
+ * bootscan_bootcmd corresponds to bootcmd
+ * bootscan_choice corresponds to the last choice that was picked
+ * bootscan_choice will be NULL the first time and also
+ * if a choice was never made. In that case we should pick
+ * to boot from the 1st bootable option if present. Note that this
+ * condition is handled in bootscan_menu()
+*/
+static int evaluate_env()
+{
+ char *s;
+ int len;
+ char command[128];
+
+ debug("Entering %s\n", __func__);
+ run_command("run " BOOTSCAN_BOOTARGS, 0);
+ s = getenv(UBOOT_BOOTARGS);
+ printf(UBOOT_BOOTARGS " is %s\n", s);
+ run_command("run " BOOTSCAN_BOOTCMD, 0);
+ s = getenv(UBOOT_BOOTCMD);
+ printf(UBOOT_BOOTCMD " is %s\n", s);
+ if (run_command("run " UBOOT_BOOTCMD, 0) != 0) {
+ /* We failed to boot, present the menu */
+ return BOOTSCAN_SHOW;
+ }
+ if (strncmp(s, "echo", 4) == 0) {
+ /* User wants the u-boot prompt */
+ return BOOTSCAN_EXIT;
+ }
+ sprintf(command, "bootm %x", CONFIG_BOOTSCAN_SYS_LOAD_ADDR);
+
+ /*
+ * Check if we have to boot with the initrd. Easy check is to
+ * see if there is a ';' in UBOOT_BOOTCMD
+ */
+ if (strchr(s, ';')) {
+ len = strlen(command);
+ sprintf(&command[len], " %x", CONFIG_BOOTSCAN_INITRD_LOAD_ADDR);
+ }
+ debug("Issuing command: %s\n", command);
+ run_command(command, 0);
+
+ /* We are here, we failed to boot */
+ return BOOTSCAN_SHOW;
+}
+
+static int handle_choice(struct bootscan_bootables bootlist[], char *choice)
+{
+ char *s, *last_choice;
+ char command[128];
+ char load_command[16];
+ int index, call_saveenv;
+ int len;
+
+ debug("Entering %s\n", __func__);
+ call_saveenv = 0;
+ if (choice == NULL) {
+ /* Exit menu and let it do its auto boot */
+ debug("bootscan: %s choice is null.\n", __func__);
+ return BOOTSCAN_EXIT;
+ }
+ printf("\nYou chose: %s\n", choice);
+
+ last_choice = getenv(BOOTSCAN_CHOICE);
+ if (last_choice == NULL) {
+ /* User has not yet chosen before */
+ setenv(BOOTSCAN_BOOTARGS, BOOTSCAN_DFLT_BOOTARGS);
+ setenv(BOOTSCAN_BOOTCMD, BOOTSCAN_DFLT_BOOTCMD);
+ call_saveenv = 1;
+ debug("bootscan: %s last_choice is NULL\n", __func__);
+ }
+ if (choice[0] == '*') {
+ /* User wants same thing that was chosen the last time */
+ debug("bootscan: %s choice is *\n", __func__);
+ return BOOTSCAN_EXIT;
+ }
+ if (last_choice && strcmp(choice, last_choice) != 0) {
+ /* Save the choice chosen */
+ setenv(BOOTSCAN_CHOICE, choice);
+ call_saveenv = 1;
+ }
+ if (choice[0] == '+') {
+ /* User wants u-boot prompt */
+ debug("bootscan: %s choice is +\n", __func__);
+ s = getenv(BOOTSCAN_BOOTCMD);
+ if ( (s == NULL) ||
+ (strcmp(s, BOOTSCAN_PROMPT_BOOTCMD) != 0) ) {
+ setenv(BOOTSCAN_BOOTCMD, BOOTSCAN_PROMPT_BOOTCMD);
+ saveenv();
+ }
+ return BOOTSCAN_EXIT;
+ }
+
+ /* Steps to set the env variables to the chosen values */
+ index = simple_strtoul(choice, NULL, 10);
+
+ /* At least one of UUID or label will be valid */
+ /* Currently we go by device name, followed by LABEL and
+ * then by UUID. For LABEL and UUID we need initrd that
+ * ArchLinuxArm does not have.
+ */
+ if (bootlist[index].rootdev[0] != '\0')
+ sprintf(command, "/dev/%s", bootlist[index].rootdev);
+ else if (bootlist[index].label[0] != '\0')
+ sprintf(command, "%s", bootlist[index].label);
+ else
+ sprintf(command, "%s", bootlist[index].uuid);
+
+ s = getenv(BOOTSCAN_ROOT);
+ if ( (s == NULL) || (strcmp(s, command) != 0) ) {
+ setenv(BOOTSCAN_ROOT, command);
+ call_saveenv = 1;
+ }
+
+ s = getenv(BOOTSCAN_BOOTARGS);
+ if ( (s == NULL) || (strcmp(s, BOOTSCAN_CHOSEN_BOOTARGS) != 0) ) {
+ setenv(BOOTSCAN_BOOTARGS, BOOTSCAN_CHOSEN_BOOTARGS);
+ call_saveenv = 1;
+ }
+
+ switch (bootlist[index].fstype) {
+ case 'e':
+ strcpy(load_command, "ext4load");
+ break;
+ default:
+ return BOOTSCAN_EXIT;
+ }
+
+ /* Lets process the boot.scr file for gems */
+ len = 0;
+ if (bootlist[index].bootscr[0] != '\0') {
+ sprintf(command, "%s %s %d:%d %x %s",
+ load_command,
+ bootlist[index].interface,
+ bootlist[index].device,
+ bootlist[index].partition,
+ CONFIG_BOOTSCAN_SYS_LOAD_ADDR,
+ bootlist[index].bootscr);
+ debug("Issuing command: %s\n", command);
+ if (run_command(command, 0) != 0) {
+ /* Could not load boot.scr */
+ printf("Error reading %s ... ignoring\n",
+ bootlist[index].bootscr);
+ }
+ else {
+ s = getenv("filesize");
+ len = (int) simple_strtoul(s, NULL, 16);
+ debug("Read %d bytes from %s\n", len,
+ bootlist[index].bootscr);
+ }
+ }
+
+ // We call process_scr() even in the absence of a boot.scr file
+ // as it sets some default values.
+ call_saveenv += process_scr((void *) CONFIG_BOOTSCAN_SYS_LOAD_ADDR,
+ len);
+
+ /* Lets try to load and check the boot image */
+ sprintf(command, "%s %s %d:%d %x %s",
+ load_command,
+ bootlist[index].interface,
+ bootlist[index].device,
+ bootlist[index].partition,
+ CONFIG_BOOTSCAN_SYS_LOAD_ADDR,
+ bootlist[index].bootimage);
+ debug("Issuing command: %s\n", command);
+ if (run_command(command, 0) != 0) {
+ /* Could not load boot image */
+ printf("Selected image could not be loaded ...\n");
+ return BOOTSCAN_SHOW;
+ }
+ sprintf(command, "iminfo %x", CONFIG_BOOTSCAN_SYS_LOAD_ADDR);
+ debug("Issuing command: %s\n", command);
+ if (run_command(command, 0) != 0) {
+ /* The image is not a valid image */
+ printf("Selected image is not valid ...\n");
+ return BOOTSCAN_SHOW;
+ }
+
+ /* Lets try to load the initrd if any */
+ if (bootlist[index].bootinitrd[0] != '\0') {
+ sprintf(command, "%s %s %d:%d %x %s",
+ load_command,
+ bootlist[index].interface,
+ bootlist[index].device,
+ bootlist[index].partition,
+ CONFIG_BOOTSCAN_INITRD_LOAD_ADDR,
+ bootlist[index].bootinitrd);
+ debug("Issuing command: %s\n", command);
+ if (run_command(command, 0) != 0) {
+ /* Could not load initrd */
+ printf("Selected image could not be loaded ...\n");
+ return BOOTSCAN_SHOW;
+ }
+ sprintf(command, "iminfo %x", CONFIG_BOOTSCAN_INITRD_LOAD_ADDR);
+ debug("Issuing command: %s\n", command);
+ if (run_command(command, 0) != 0) {
+ /* The image is not a valid image */
+ printf("Selected image is not valid ...\n");
+ return BOOTSCAN_SHOW;
+ }
+ }
+
+ /* We generate these commands for unattended booting the
+ * next time the user does not choose an option
+ */
+ sprintf(command, "setenv bootcmd %s %s %d:%d %x %s",
+ load_command,
+ bootlist[index].interface,
+ bootlist[index].device,
+ bootlist[index].partition,
+ CONFIG_BOOTSCAN_SYS_LOAD_ADDR,
+ bootlist[index].bootimage);
+ if (bootlist[index].bootinitrd[0] != '\0') {
+ sprintf(command, "setenv bootcmd '%s %s %d:%d %x %s"
+ "; %s %s %d:%d %x %s'",
+ load_command,
+ bootlist[index].interface,
+ bootlist[index].device,
+ bootlist[index].partition,
+ CONFIG_BOOTSCAN_SYS_LOAD_ADDR,
+ bootlist[index].bootimage,
+ load_command,
+ bootlist[index].interface,
+ bootlist[index].device,
+ bootlist[index].partition,
+ CONFIG_BOOTSCAN_INITRD_LOAD_ADDR,
+ bootlist[index].bootinitrd);
+ }
+ debug(BOOTSCAN_BOOTCMD " is set as: %s\n", command);
+ s = getenv(BOOTSCAN_BOOTCMD);
+ if ( (s == NULL) || (strcmp(s, command) != 0) ) {
+ setenv(BOOTSCAN_BOOTCMD, command);
+ call_saveenv = 1;
+ }
+ if (call_saveenv)
+ saveenv();
+ return BOOTSCAN_EXIT;
+}
+
+static int bootscan_menu(struct bootscan_bootables bootlist[], int bootdelay)
+{
+ int index;
+ struct menu *m;
+ char menu_key[BOOTSCAN_MAX_BOOTABLES][5];
+ char menu_entry[BOOTSCAN_MAX_BOOTABLES][128];
+ char *menu_choice;
+ char *last_menu_choice;
+ char choice_menu_entry[64];
+ char choice_menu[3];
+ char bootimagedisp[64];
+
+ debug("Entering %s\n", __func__);
+ puts(BOOTSCAN_OPTIONS_DIVIDER "\n");
+ m = menu_create(BOOTSCAN_OPTIONS_HEADER, 60, 1, bootscan_menuprint,
+ NULL, NULL);
+ for (index = 0; index < BOOTSCAN_MAX_BOOTABLES; index++) {
+ if (bootlist[index].fstype == '0')
+ break;
+ snprintf(menu_key[index], sizeof(menu_key[index]), "%d", index);
+ /* We put a [I] next to boot image to indicate it has initrd */
+ if (bootlist[index].bootinitrd[0] == '\0')
+ strcpy(bootimagedisp, bootlist[index].bootimage);
+ else
+ sprintf(bootimagedisp, "%s[I]",
+ bootlist[index].bootimage);
+
+ snprintf(menu_entry[index], sizeof(menu_entry[index]),
+ "%d %-10s %-6s %d %d %c %-16s %-14s %8.8s-",
+ index,
+ bootlist[index].rootdev,
+ bootlist[index].interface,
+ bootlist[index].device,
+ bootlist[index].partition,
+ bootlist[index].fstype,
+ bootimagedisp,
+ bootlist[index].label,
+ bootlist[index].uuid);
+ if (menu_item_add(m, menu_key[index], menu_entry[index]) != 1) {
+ menu_destroy(m);
+ return BOOTSCAN_EXIT;
+ }
+ }
+
+ /* This is to just add a nice line at the end of the list */
+ if (menu_item_add(m, "-", BOOTSCAN_OPTIONS_DIVIDER) != 1) {
+ menu_destroy(m);
+ return BOOTSCAN_EXIT;
+ }
+
+ /* Prep for what should be the default menu choice */
+ /* If chosen before, choose the last boot options */
+ /* If nothing chosen yet, then choose the first bootable option */
+ /* If nothing chosen yet, and no first bootable option, then ? */
+ last_menu_choice = getenv(BOOTSCAN_CHOICE);
+ sprintf(choice_menu, "*");
+ if (last_menu_choice) {
+ sprintf(choice_menu_entry, "* Last boot options:\n(%s)",
+ last_menu_choice);
+ } else {
+ /* There was no last boot option */
+ /* If there is at least 1 boot entry, make that the default */
+ if (bootlist[0].fstype != '0') {
+ setenv(BOOTSCAN_CHOICE, menu_entry[0]);
+ sprintf(choice_menu_entry, menu_entry[0]);
+ } else {
+ /* This is answer to the ? asked above */
+ sprintf(choice_menu_entry, BOOTSCAN_DFLT_NOBOOTABLES);
+ }
+ }
+ if (menu_item_add(m, choice_menu, choice_menu_entry) != 1) {
+ menu_destroy(m);
+ return BOOTSCAN_EXIT;
+ }
+ /* Mark this as the default choice. */
+ menu_default_set(m, "*");
+ if (menu_item_add(m, "+", "+ UBoot prompt") != 1) {
+ menu_destroy(m);
+ return BOOTSCAN_EXIT;
+ }
+
+ menu_get_choice(m, (void **)&menu_choice);
+ return handle_choice(bootlist, menu_choice);
+}
+
+/*
+ * Here we assume that the /boot directory holds the linux image and
+ * if an initrd is being used, it exists in the same directory - /boot
+ * This implies that inited names are without path - just file name!
+ * Same goes for the boot.scr file.
+ */
+static void filesearch(struct bootscan_bootables bootlist[], int *bootindex)
+{
+ char *images[] = { "/boot/uImage", "/boot/zImage", "" };
+ char *initrds[] = { "uInitrd", "" };
+ char *scrs[] = { "boot.scr", "" };
+ char *s;
+
+ int findex, index;
+
+ debug("Entering %s\n", __func__);
+ findex = 0;
+ while (images[findex][0] != '\0') {
+ switch (bootlist[*bootindex].fstype) {
+ case 'e':
+ if (ext4fs_open(images[findex]) == -1) {
+ findex++;
+ continue;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* Got a hit, record it */
+ strcpy(bootlist[*bootindex].bootimage, images[findex]);
+
+ /* Lets check if there is an initrd in the same directory */
+ index = 0;
+ strcpy(bootlist[*bootindex].bootinitrd, images[findex]);
+ s = strrchr(bootlist[*bootindex].bootinitrd, '/');
+ while (initrds[index][0] != '\0') {
+ strcpy(s + 1, initrds[index]);
+ debug("initrd %s\n", bootlist[*bootindex].bootinitrd);
+ if (ext4fs_open(bootlist[*bootindex].bootinitrd)
+ == -1) {
+ index++;
+ continue;
+ }
+ break;
+ }
+ /* If we didnt get an initrd let it be reflected */
+ if (initrds[index][0] == '\0')
+ bootlist[*bootindex].bootinitrd[0] = '\0';
+
+ /* Lets check if there is a boot.scr in the same dir */
+ index = 0;
+ strcpy(bootlist[*bootindex].bootscr, images[findex]);
+ s = strrchr(bootlist[*bootindex].bootscr, '/');
+ while (scrs[index][0] != '\0') {
+ strcpy(s + 1, scrs[index]);
+ if (ext4fs_open(bootlist[*bootindex].bootscr)
+ == -1) {
+ index++;
+ continue;
+ }
+ debug("scr: %s\n", bootlist[*bootindex].bootscr);
+ break;
+ }
+ /* If we didnt get a boot.scr let it be reflected */
+ if (scrs[index][0] == '\0')
+ bootlist[*bootindex].bootscr[0] = '\0';
+
+ findex++;
+ (*bootindex)++;
+ if (*bootindex >= BOOTSCAN_MAX_BOOTABLES)
+ break;
+ /* Prep next bootlist structure */
+ memcpy(&bootlist[*bootindex], &bootlist[*bootindex - 1],
+ sizeof(struct bootscan_bootables));
+ }
+}
+
+static void populate_partitions(struct bootscan_bootables *bootlist,
+ block_dev_desc_t *dev_desc,
+ int *bootindex)
+{
+ int part;
+ disk_partition_t disk_part;
+
+ debug("Entering %s\n", __func__);
+ part = bootlist[*bootindex].partition;
+
+ /* Get the partition structure */
+ debug(" Calling get_partition_info()\n");
+ if (get_partition_info(dev_desc, part, &disk_part))
+ return;
+
+ /* Try to check if its extX */
+ debug(" Calling ext4fs_probe()\n");
+ if (ext4fs_probe(dev_desc, &disk_part) == 0) {
+ debug(" Back from ext4fs_probe()\n");
+ bootlist[*bootindex].fstype = 'e';
+ /* Update uuid and label from partition structure*/
+ strcpy(bootlist[*bootindex].label, (char *) disk_part.name);
+ strcpy(bootlist[*bootindex].uuid, disk_part.uuid);
+ debug("disk_part.name: %s disk_part.uuid: %s\n",
+ disk_part.name, disk_part.uuid);
+ filesearch(bootlist, bootindex);
+ ext4fs_close();
+ return;
+ }
+}
+
+static void populate_devices(struct bootscan_bootables bootlist[],
+ int *bootindex)
+{
+ block_dev_desc_t *dev_desc;
+ int device;
+ int part;
+
+ debug("Entering %s\n", __func__);
+ /* Populate bootlist from each device and the partitions within */
+ for (device = 0; device < BOOTSCAN_MAX_DEVICES; device++) {
+ dev_desc = get_dev(bootlist[*bootindex].interface, device);
+ if (dev_desc == NULL)
+ continue;
+ bootlist[*bootindex].device = device;
+ for (part = 0; part < BOOTSCAN_MAX_PARTITIONS; part++) {
+ bootlist[*bootindex].partition = part;
+ populate_partitions(bootlist, dev_desc, bootindex);
+ }
+ }
+}
+
+/* bootlist[] can hold a max of BOOTSCAN_MAX_BOOTABLES entries */
+static void populate_bootlist(struct bootscan_bootables bootlist[])
+{
+ /* Order is important - mimic how linux would number the devices */
+ char *interfaces[] = { "ide", "usb", "mmc", "" };
+ char mmcdrive, usbdrive;
+ int bootindex;
+ int i;
+
+ debug("Entering %s\n", __func__);
+ bootindex = 0;
+ i = 0;
+
+ /* Lets initialize the usb subsystem */
+ run_command("usb start", 0);
+
+#if defined(CONFIG_USB_KEYBOARD)
+# if defined(CONFIG_CONSOLE_MUX)
+ run_command("setenv stdin serial,usbkbd", 0);
+# else
+ run_command("setenv stdin usbkbd", 0);
+# endif
+#endif
+
+ /* This scans the partitions in the IDE storage */
+#if defined(CONFIG_CMD_IDE)
+ ide_init();
+#endif /* CONFIG_CMD_IDE */
+
+ /* Populate bootlist from each interface */
+ while ((interfaces[i][0] != '\0') &&
+ (bootindex < BOOTSCAN_MAX_BOOTABLES)) {
+ strcpy(bootlist[bootindex].interface, interfaces[i]);
+ populate_devices(bootlist, &bootindex);
+ i++;
+ }
+ if (bootindex < BOOTSCAN_MAX_BOOTABLES) {
+ /* End marker of list */
+ bootlist[bootindex].fstype = '0';
+ }
+
+ /* No boot list! */
+ if (bootlist[0].fstype == '0') return;
+
+ /*
+ * Lets set the drive letter and update rootdev
+ * Note that for ide and usb we use sdXY
+ * If we switch to mmc, then the device used is mmcblkXpY
+ */
+ usbdrive = 'a';
+ mmcdrive = '0';
+ /* Prep the first entry */
+ if (strcmp(bootlist[0].interface, "mmc") == 0) {
+ /* Its mmc, its mmcblk + dev + 'p' + part */
+ sprintf(bootlist[0].rootdev, "mmcblk%cp%d",
+ mmcdrive, bootlist[0].partition);
+ }
+ else {
+ sprintf(bootlist[0].rootdev, "sd%c%d", usbdrive,
+ bootlist[0].partition);
+ }
+
+ /* Lets get on with the rest of the entries */
+ for (i = 1; i < bootindex; i++) {
+ if (bootlist[i].fstype == '0')
+ break;
+ /* Lets update rootdev
+ * Logic is as follows -
+ * ide -> usb => usbdrive++
+ * ide -> ide and device # change => usbdrive++
+ * usb -> usb and device # change => usbdrive++
+ * mmc -> mmc and device # change => mmcdrive++
+ * XXX -> mmc => mmcdrive = '0'
+ */
+ if (strcmp(bootlist[i].interface, bootlist[i-1].interface)
+ == 0) {
+ /* Same interface => check for device # change */
+ if (bootlist[i].device != bootlist[i-1].device) {
+ usbdrive++;
+ mmcdrive++;
+ }
+ }
+ else {
+ /* The interface has changed */
+ if (strcmp(bootlist[i].interface, "mmc") == 0)
+ mmcdrive = '0';
+ else
+ usbdrive++;
+ }
+ if (strcmp(bootlist[i].interface, "mmc") == 0) {
+ /* Its mmc, its mmcblk + dev + 'p' + part */
+ sprintf(bootlist[i].rootdev, "mmcblk%cp%d",
+ mmcdrive, bootlist[i].partition);
+ }
+ else {
+ sprintf(bootlist[i].rootdev, "sd%c%d", usbdrive,
+ bootlist[i].partition);
+ }
+ }
+
+}
+
+int menu_show(int bootdelay)
+{
+ struct bootscan_bootables bootlist[BOOTSCAN_MAX_BOOTABLES];
+ int retval;
+
+ debug("Entering %s\n", __func__);
+ populate_bootlist(bootlist);
+ do {
+ retval = bootscan_menu(bootlist, bootdelay);
+ if (retval == BOOTSCAN_EXIT)
+ evaluate_env();
+ } while (retval == BOOTSCAN_SHOW);
+
+ return 0;
+}
+
+int do_bootscan(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
+{
+ debug("Entering %s\n", __func__);
+ menu_show(0);
+ return 0;
+}
+
+U_BOOT_CMD(
+ bootscan, 1, 1, do_bootscan,
+ "Scan media for boot image",
+ " - display an user selectable list of bootable options"
+);
+
+#endif /* CONFIG_MENU */
+
diff --git a/common/cmd_usb.c b/common/cmd_usb.c
index 2519497dad..9975afa43a 100644
--- a/common/cmd_usb.c
+++ b/common/cmd_usb.c
@@ -450,6 +450,17 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
if ((strncmp(argv[1], "reset", 5) == 0) ||
(strncmp(argv[1], "start", 5) == 0)) {
bootstage_mark_name(BOOTSTAGE_ID_USB_START, "usb_start");
+#if defined(CONFIG_USB_KEYBOARD) || defined (CONFIG_USB_HOST_ETHER)
+ iomux_doenv(stdin, "serial");
+ iomux_doenv(stdout, "serial");
+ iomux_doenv(stderr, "serial");
+# if defined(CONFIG_USB_HOST_ETHER)
+ usb_nc_deregister();
+# endif
+# if defined(CONFIG_USB_KEYBOARD)
+ usb_kbd_deregister();
+# endif
+#endif
usb_stop();
printf("(Re)start USB...\n");
if (usb_init() >= 0) {
@@ -460,25 +471,49 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
#ifdef CONFIG_USB_HOST_ETHER
/* try to recognize ethernet devices immediately */
usb_ether_curr_dev = usb_host_eth_scan(1);
+ if (usb_ether_curr_dev != -1) {
+ /* Lets enable nc again */
+ drv_nc_init();
+ }
#endif
#ifdef CONFIG_USB_KEYBOARD
- drv_usb_kbd_init();
+ puts("\tscanning usb for keyboard ... ");
+ if (drv_usb_kbd_init() == 1) {
+ /* Found a keyboard */
+ puts("1 Keyboard Device found\n");
+ }
#endif
}
return 0;
}
if (strncmp(argv[1], "stop", 4) == 0) {
-#ifdef CONFIG_USB_KEYBOARD
+#if defined(CONFIG_USB_KEYBOARD) || defined (CONFIG_USB_HOST_ETHER)
+ iomux_doenv(stdin, "serial");
+ iomux_doenv(stdout, "serial");
+ iomux_doenv(stderr, "serial");
if (argc == 2) {
+# if defined(CONFIG_USB_HOST_ETHER)
+ if (usb_nc_deregister() != 0) {
+ printf("USB not stopped: nc still"
+ " using USB\n");
+ return 1;
+ }
+# endif
+# if defined(CONFIG_USB_KEYBOARD)
if (usb_kbd_deregister() != 0) {
printf("USB not stopped: usbkbd still"
" using USB\n");
return 1;
}
+# endif
} else {
/* forced stop, switch console in to serial */
- console_assign(stdin, "serial");
+# if defined(CONFIG_USB_HOST_ETHER)
+ usb_nc_deregister();
+# endif
+# if defined(CONFIG_USB_KEYBOARD)
usb_kbd_deregister();
+# endif
}
#endif
printf("stopping USB..\n");
diff --git a/common/menu.c b/common/menu.c
new file mode 100644
index 0000000000..ba393adc32
--- /dev/null
+++ b/common/menu.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright 2010-2011 Calxeda, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <errno.h>
+#include <linux/list.h>
+
+#include "menu.h"
+
+/*
+ * Internally, each item in a menu is represented by a struct menu_item.
+ *
+ * These items will be alloc'd and initialized by menu_item_add and destroyed
+ * by menu_item_destroy, and the consumer of the interface never sees that
+ * this struct is used at all.
+ */
+struct menu_item {
+ char *key;
+ void *data;
+ struct list_head list;
+};
+
+/*
+ * The menu is composed of a list of items along with settings and callbacks
+ * provided by the user. An incomplete definition of this struct is available
+ * in menu.h, but the full definition is here to prevent consumers from
+ * relying on its contents.
+ */
+struct menu {
+ struct menu_item *default_item;
+ int timeout;
+ char *title;
+ int prompt;
+ void (*item_data_print)(void *);
+ char *(*item_choice)(void *);
+ void *item_choice_data;
+ struct list_head items;
+};
+
+/*
+ * An iterator function for menu items. callback will be called for each item
+ * in m, with m, a pointer to the item, and extra being passed to callback. If
+ * callback returns a value other than NULL, iteration stops and the value
+ * return by callback is returned from menu_items_iter. This allows it to be
+ * used for search type operations. It is also safe for callback to remove the
+ * item from the list of items.
+ */
+static inline void *menu_items_iter(struct menu *m,
+ void *(*callback)(struct menu *, struct menu_item *, void *),
+ void *extra)
+{
+ struct list_head *pos, *n;
+ struct menu_item *item;
+ void *ret;
+
+ list_for_each_safe(pos, n, &m->items) {
+ item = list_entry(pos, struct menu_item, list);
+
+ ret = callback(m, item, extra);
+
+ if (ret)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*
+ * Print a menu_item. If the consumer provided an item_data_print function
+ * when creating the menu, call it with a pointer to the item's private data.
+ * Otherwise, print the key of the item.
+ */
+static inline void *menu_item_print(struct menu *m,
+ struct menu_item *item,
+ void *extra)
+{
+ if (!m->item_data_print) {
+ puts(item->key);
+ putc('\n');
+ } else {
+ m->item_data_print(item->data);
+ }
+
+ return NULL;
+}
+
+/*
+ * Free the memory used by a menu item. This includes the memory used by its
+ * key.
+ */
+static inline void *menu_item_destroy(struct menu *m,
+ struct menu_item *item,
+ void *extra)
+{
+ if (item->key)
+ free(item->key);
+
+ free(item);
+
+ return NULL;
+}
+
+void __menu_display_statusline(struct menu *m)
+{
+ return;
+}
+void menu_display_statusline(struct menu *m)
+ __attribute__ ((weak, alias("__menu_display_statusline")));
+
+/*
+ * Display a menu so the user can make a choice of an item. First display its
+ * title, if any, and then each item in the menu.
+ */
+static inline void menu_display(struct menu *m)
+{
+ if (m->title) {
+ puts(m->title);
+ putc('\n');
+ }
+ menu_display_statusline(m);
+
+ menu_items_iter(m, menu_item_print, NULL);
+}
+
+/*
+ * Check if an item's key matches a provided string, pointed to by extra. If
+ * extra is NULL, an item with a NULL key will match. Otherwise, the item's
+ * key has to match according to strcmp.
+ *
+ * This is called via menu_items_iter, so it returns a pointer to the item if
+ * the key matches, and returns NULL otherwise.
+ */
+static inline void *menu_item_key_match(struct menu *m,
+ struct menu_item *item, void *extra)
+{
+ char *item_key = extra;
+
+ if (!item_key || !item->key) {
+ if (item_key == item->key)
+ return item;
+
+ return NULL;
+ }
+
+ if (strcmp(item->key, item_key) == 0)
+ return item;
+
+ return NULL;
+}
+
+/*
+ * Find the first item with a key matching item_key, if any exists.
+ */
+static inline struct menu_item *menu_item_by_key(struct menu *m,
+ char *item_key)
+{
+ return menu_items_iter(m, menu_item_key_match, item_key);
+}
+
+/*
+ * Set *choice to point to the default item's data, if any default item was
+ * set, and returns 1. If no default item was set, returns -ENOENT.
+ */
+int menu_default_choice(struct menu *m, void **choice)
+{
+ if (m->default_item) {
+ *choice = m->default_item->data;
+ return 1;
+ }
+
+ return -ENOENT;
+}
+
+/*
+ * Displays the menu and asks the user to choose an item. *choice will point
+ * to the private data of the item the user chooses. The user makes a choice
+ * by inputting a string matching the key of an item. Invalid choices will
+ * cause the user to be prompted again, repeatedly, until the user makes a
+ * valid choice. The user can exit the menu without making a choice via ^c.
+ *
+ * Returns 1 if the user made a choice, or -EINTR if they bail via ^c.
+ */
+static inline int menu_interactive_choice(struct menu *m, void **choice)
+{
+ char cbuf[CONFIG_SYS_CBSIZE];
+ struct menu_item *choice_item = NULL;
+ int readret;
+
+ while (!choice_item) {
+ cbuf[0] = '\0';
+
+ menu_display(m);
+
+ if (!m->item_choice) {
+ readret = readline_into_buffer("Enter choice: ", cbuf,
+ m->timeout / 10);
+
+ if (readret >= 0) {
+ choice_item = menu_item_by_key(m, cbuf);
+ if (!choice_item)
+ printf("%s not found\n", cbuf);
+ } else {
+ return menu_default_choice(m, choice);
+ }
+ } else {
+ char *key = m->item_choice(m->item_choice_data);
+
+ if (key)
+ choice_item = menu_item_by_key(m, key);
+ }
+
+ if (!choice_item)
+ m->timeout = 0;
+ }
+
+ *choice = choice_item->data;
+
+ return 1;
+}
+
+/*
+ * menu_default_set() - Sets the default choice for the menu. This is safe to
+ * call more than once on a menu.
+ *
+ * m - Points to a menu created by menu_create().
+ *
+ * item_key - Points to a string that, when compared using strcmp, matches the
+ * key for an existing item in the menu.
+ *
+ * Returns 1 if successful, -EINVAL if m is NULL, or -ENOENT if no item with a
+ * key matching item_key is found.
+ */
+int menu_default_set(struct menu *m, char *item_key)
+{
+ struct menu_item *item;
+
+ if (!m)
+ return -EINVAL;
+
+ item = menu_item_by_key(m, item_key);
+
+ if (!item)
+ return -ENOENT;
+
+ m->default_item = item;
+
+ return 1;
+}
+
+/*
+ * menu_get_choice() - Returns the user's selected menu entry, or the default
+ * if the menu is set to not prompt or the timeout expires. This is safe to
+ * call more than once.
+ *
+ * m - Points to a menu created by menu_create().
+ *
+ * choice - Points to a location that will store a pointer to the selected
+ * menu item. If no item is selected or there is an error, no value will be
+ * written at the location it points to.
+ *
+ * Returns 1 if successful, -EINVAL if m or choice is NULL, -ENOENT if no
+ * default has been set and the menu is set to not prompt or the timeout
+ * expires, or -EINTR if the user exits the menu via ^c.
+ */
+int menu_get_choice(struct menu *m, void **choice)
+{
+ if (!m || !choice)
+ return -EINVAL;
+
+ if (!m->prompt)
+ return menu_default_choice(m, choice);
+
+ return menu_interactive_choice(m, choice);
+}
+
+/*
+ * menu_item_add() - Adds or replaces a menu item. Note that this replaces the
+ * data of an item if it already exists, but doesn't change the order of the
+ * item.
+ *
+ * m - Points to a menu created by menu_create().
+ *
+ * item_key - Points to a string that will uniquely identify the item. The
+ * string will be copied to internal storage, and is safe to discard after
+ * passing to menu_item_add.
+ *
+ * item_data - An opaque pointer associated with an item. It is never
+ * dereferenced internally, but will be passed to the item_data_print, and
+ * will be returned from menu_get_choice if the menu item is selected.
+ *
+ * Returns 1 if successful, -EINVAL if m is NULL, or -ENOMEM if there is
+ * insufficient memory to add the menu item.
+ */
+int menu_item_add(struct menu *m, char *item_key, void *item_data)
+{
+ struct menu_item *item;
+
+ if (!m)
+ return -EINVAL;
+
+ item = menu_item_by_key(m, item_key);
+
+ if (item) {
+ item->data = item_data;
+ return 1;
+ }
+
+ item = malloc(sizeof *item);
+ if (!item)
+ return -ENOMEM;
+
+ item->key = strdup(item_key);
+
+ if (!item->key) {
+ free(item);
+ return -ENOMEM;
+ }
+
+ item->data = item_data;
+
+ list_add_tail(&item->list, &m->items);
+
+ return 1;
+}
+
+/*
+ * menu_create() - Creates a menu handle with default settings
+ *
+ * title - If not NULL, points to a string that will be displayed before the
+ * list of menu items. It will be copied to internal storage, and is safe to
+ * discard after passing to menu_create().
+ *
+ * timeout - A delay in seconds to wait for user input. If 0, timeout is
+ * disabled, and the default choice will be returned unless prompt is 1.
+ *
+ * prompt - If 0, don't ask for user input unless there is an interrupted
+ * timeout. If 1, the user will be prompted for input regardless of the value
+ * of timeout.
+ *
+ * item_data_print - If not NULL, will be called for each item when the menu
+ * is displayed, with the pointer to the item's data passed as the argument.
+ * If NULL, each item's key will be printed instead. Since an item's key is
+ * what must be entered to select an item, the item_data_print function should
+ * make it obvious what the key for each entry is.
+ *
+ * item_choice - If not NULL, will be called when asking the user to choose an
+ * item. Returns a key string corresponding to the choosen item or NULL if
+ * no item has been selected.
+ *
+ * item_choice_data - Will be passed as the argument to the item_choice function
+ *
+ * Returns a pointer to the menu if successful, or NULL if there is
+ * insufficient memory available to create the menu.
+ */
+struct menu *menu_create(char *title, int timeout, int prompt,
+ void (*item_data_print)(void *),
+ char *(*item_choice)(void *),
+ void *item_choice_data)
+{
+ struct menu *m;
+
+ m = malloc(sizeof *m);
+
+ if (!m)
+ return NULL;
+
+ m->default_item = NULL;
+ m->prompt = prompt;
+ m->timeout = timeout;
+ m->item_data_print = item_data_print;
+ m->item_choice = item_choice;
+ m->item_choice_data = item_choice_data;
+
+ if (title) {
+ m->title = strdup(title);
+ if (!m->title) {
+ free(m);
+ return NULL;
+ }
+ } else
+ m->title = NULL;
+
+
+ INIT_LIST_HEAD(&m->items);
+
+ return m;
+}
+
+/*
+ * menu_destroy() - frees the memory used by a menu and its items.
+ *
+ * m - Points to a menu created by menu_create().
+ *
+ * Returns 1 if successful, or -EINVAL if m is NULL.
+ */
+int menu_destroy(struct menu *m)
+{
+ if (!m)
+ return -EINVAL;
+
+ menu_items_iter(m, menu_item_destroy, NULL);
+
+ if (m->title)
+ free(m->title);
+
+ free(m);
+
+ return 1;
+}
diff --git a/disk/part_efi.c b/disk/part_efi.c
index 9c33ae7a31..2215786897 100644
--- a/disk/part_efi.c
+++ b/disk/part_efi.c
@@ -63,6 +63,7 @@ static char *print_efiname(gpt_entry *pte)
return name;
}
+#if 0
static void uuid_string(unsigned char *uuid, char *str)
{
static const u8 le[16] = {3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11,
@@ -82,6 +83,7 @@ static void uuid_string(unsigned char *uuid, char *str)
}
}
}
+#endif
static efi_guid_t system_guid = PARTITION_SYSTEM_GUID;
diff --git a/doc/README.bootscan b/doc/README.bootscan
new file mode 100644
index 0000000000..a526b02304
--- /dev/null
+++ b/doc/README.bootscan
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 2013 Suriyan Ramasami <suriyan.r@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+bootscan command
+
+The "bootscan" command uses U-Boot menu interfaces and provides
+a simple mechanism for presenting the user with a list of different
+boot items, which are gleaned by searching for files in various IDE/USB
+bootable media/partitions.
+
+The menu interface is used to enable the user to make a choice.
+selection. Navigation is achieved with the help of selecting the
+numbered menu items. The time to choose is defaulted to bootdelay
+seconds after which the default choice is activated. In the absence
+of a default choice, the first bootable option is attempted.
+
+bootscan remembers previous user choices by the environment variables
+which are prefixed bootscan_. The variables are bootscan_bootargs,
+bootscan_bootcmd, bootscan_root and bootscan_choice. Their usage is
+self explanatory. For example, bootscan_bootcmd is the bootcmd that
+was used during the last bootup.
+
+The menu items are further categorized by two characters:
+* -> Implies that is the default choice, if the user does not choose.
++ -> Allows the user to select the U-Boot prompt.
+
+Example Menu:
+
+
+To enable the "bootscan" command add following definitions to the
+board config file:
+
+ #define CONFIG_CMD_BOOTSCAN
+ #define CONFIG_MENU
+
+To run bootscan at startup, so that it presents a menu, add this
+additional definition:
+
+ #define CONFIG_MENU_SHOW
+
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index e1edb3c137..738d0fd074 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -92,7 +92,7 @@ struct mmc *find_mmc_device(int dev_num)
return m;
}
- printf("MMC Device %d not found\n", dev_num);
+ debug("MMC Device %d not found\n", dev_num);
return NULL;
}
@@ -1236,6 +1236,7 @@ if (strncmp(mmc->name, "S5P_MSHC", 8) == 0) {
mmc->block_dev.lun = 0;
mmc->block_dev.type = 0;
mmc->block_dev.blksz = mmc->read_bl_len;
+ mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz);
mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
sprintf(mmc->block_dev.vendor, "Man %06x Snr %08x", mmc->cid[0] >> 8,
(mmc->cid[2] << 8) | (mmc->cid[3] >> 24));
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index df8ab07b94..86a567cdaa 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -19,7 +19,7 @@ DECLARE_GLOBAL_DATA_PTR;
static char input_buffer[CONFIG_NETCONSOLE_BUFFER_SIZE];
static int input_size; /* char count in input buffer */
static int input_offset; /* offset to valid chars in input buffer */
-static int input_recursion;
+int input_recursion;
static int output_recursion;
static int net_timeout;
static uchar nc_ether[6]; /* server enet address */
@@ -184,7 +184,9 @@ static void nc_send_packet(const char *buf, int len)
return; /* inside net loop */
output_packet = buf;
output_packet_len = len;
+ input_recursion = 1;
NetLoop(NETCONS); /* wait for arp reply and send packet */
+ input_recursion = 0;
output_packet_len = 0;
return;
}
@@ -327,3 +329,14 @@ int drv_nc_init(void)
return (rc == 0) ? 1 : rc;
}
+
+/* Deregister nc - helpful when its thru USB */
+int usb_nc_deregister(void)
+{
+#ifdef CONFIG_SYS_STDIO_DEREGISTER
+ return stdio_deregister("nc");
+#else
+ return 1;
+#endif
+}
+
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 3ae04c0253..fbc7c2fa3f 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -36,6 +36,9 @@
#define CONFIG_USB_MAX_CONTROLLER_COUNT 1
#endif
+extern int input_recursion;
+#define USB_INTERACTIVE_RECV_TIMEOUT_MS 50
+
/*
* EHCI spec page 20 says that the HC may take up to 16 uFrames (= 4ms) to halt.
* Let's time out after 8 to have a little safety margin on top of that.
@@ -233,7 +236,7 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
int idx;
if (addr != ALIGN(addr, ARCH_DMA_MINALIGN))
- debug("EHCI-HCD: Misaligned buffer address (%p)\n", buf);
+ printf("EHCI-HCD: Misaligned buffer address (%p)\n", buf);
flush_dcache_range(addr, ALIGN(addr + sz, ARCH_DMA_MINALIGN));
@@ -541,7 +544,10 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
/* Wait for TDs to be processed. */
ts = get_timer(0);
vtd = &qtd[qtd_counter - 1];
- timeout = USB_TIMEOUT_MS(pipe);
+ if (input_recursion)
+ timeout = USB_INTERACTIVE_RECV_TIMEOUT_MS;
+ else
+ timeout = USB_TIMEOUT_MS(pipe);
do {
/* Invalidate dcache */
invalidate_dcache_range((uint32_t)&ctrl->qh_list,
@@ -571,7 +577,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
/* Check that the TD processing happened */
if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)
- printf("EHCI timed out on TD - token=%#x\n", token);
+ debug("EHCI timed out on TD - token=%#x\n", token);
/* Disable async schedule. */
cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
diff --git a/fs/ext4/dev.c b/fs/ext4/dev.c
index 787e04133a..d5ffe4a22e 100644
--- a/fs/ext4/dev.c
+++ b/fs/ext4/dev.c
@@ -32,7 +32,7 @@
lbaint_t part_offset;
static block_dev_desc_t *ext4fs_block_dev_desc;
-static disk_partition_t *part_info;
+disk_partition_t *part_info;
void ext4fs_set_blk_dev(block_dev_desc_t *rbdd, disk_partition_t *info)
{
diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c
index 352943ec51..b916f6bf77 100644
--- a/fs/ext4/ext4_common.c
+++ b/fs/ext4/ext4_common.c
@@ -29,6 +29,8 @@
#include <asm/byteorder.h>
#include "ext4_common.h"
+extern disk_partition_t *part_info;
+
struct ext2_data *ext4fs_root;
struct ext2fs_node *ext4fs_file;
uint32_t *ext4fs_indir1_block;
@@ -2206,6 +2208,12 @@ int ext4fs_mount(unsigned part_length)
if (__le16_to_cpu(data->sblock.magic) != EXT2_MAGIC)
goto fail;
+ /* Update the label and uuid in the part_info structure */
+ strcpy(part_info->name, data->sblock.volume_name);
+ debug("volume name: %s\n", part_info->name);
+ uuid_string(data->sblock.unique_id, part_info->uuid);
+ debug("UUID: %s\n", part_info->uuid);
+
if (__le32_to_cpu(data->sblock.revision_level == 0))
fs->inodesz = 128;
else
diff --git a/include/configs/smdk4412.h b/include/configs/smdk4412.h
index ecafdba4f6..4b3d6be45c 100644
--- a/include/configs/smdk4412.h
+++ b/include/configs/smdk4412.h
@@ -192,6 +192,7 @@
/* USB KEYBOARD */
#define CONFIG_USB_KEYBOARD
#define CONFIG_SYS_USB_EVENT_POLL
+#define CONFIG_SYS_STDIO_DEREGISTER
/* NETCONSOLE */
#define CONFIG_NETCONSOLE
@@ -234,6 +235,10 @@
#define CONFIG_CMD_FAT
#define CONFIG_CMD_FS_GENERIC
+#define CONFIG_CMD_BOOTSCAN
+#define CONFIG_MENU
+#define CONFIG_MENU_SHOW
+
#define CONFIG_SYS_NAND_QUIET_TEST
#define CONFIG_SYS_ONENAND_QUIET_TEST
@@ -255,11 +260,11 @@
#define CONFIG_BOOTP_HOSTNAME
#define CONFIG_BOOTP_BOOTPATH
-#define CONFIG_ETHADDR 00:40:5c:26:0a:5b
-#define CONFIG_NETMASK 255.255.255.0
-#define CONFIG_IPADDR 192.168.0.20
-#define CONFIG_SERVERIP 192.168.0.10
-#define CONFIG_GATEWAYIP 192.168.0.1
+#define CONFIG_USB_ETHADDR 00:10:75:2a:ae:e0
+#define CONFIG_NETMASK 255.255.255.0
+#define CONFIG_IPADDR 10.0.0.31
+#define CONFIG_SERVERIP 10.0.0.204
+#define CONFIG_GATEWAYIP 10.0.0.2
#define CONFIG_OF_LIBFDT 1
@@ -274,7 +279,7 @@
" if run loadbootscript_2; " \
" then run bootscript; " \
" else " \
- " run default_bootcmd; " \
+ " run default_bootcmd; " \
" fi ;" \
" fi ; "
@@ -386,6 +391,8 @@
#endif
#define CONFIG_SYS_LOAD_ADDR CONFIG_SYS_MAPPED_RAM_BASE + 0x3e00000
+#define CONFIG_BOOTSCAN_SYS_LOAD_ADDR (0x40008000)
+#define CONFIG_BOOTSCAN_INITRD_LOAD_ADDR (0x42000000)
#define CONFIG_PHY_UBOOT_BASE CONFIG_SYS_SDRAM_BASE + 0x3e00000
/*
@@ -477,6 +484,8 @@
#define CONFIG_ENV_SIZE 0x4000
#define CONFIG_DOS_PARTITION 1
+#define CONFIG_EFI_PARTITION
+#define CONFIG_PARTITION_UUIDS
//#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_LOAD_ADDR - 0x1000000)
#define CONFIG_SYS_INIT_SP_ADDR (0x43e00000 - 0x1000000)
diff --git a/include/env_default.h b/include/env_default.h
index b2d4e52e58..a81f66ceca 100644
--- a/include/env_default.h
+++ b/include/env_default.h
@@ -59,6 +59,9 @@ const unsigned char default_environment[] = {
#ifdef CONFIG_ETHADDR
"ethaddr=" __stringify(CONFIG_ETHADDR) "\0"
#endif
+#ifdef CONFIG_USB_ETHADDR
+ "usbethaddr=" __stringify(CONFIG_USB_ETHADDR) "\0"
+#endif
#ifdef CONFIG_ETH1ADDR
"eth1addr=" __stringify(CONFIG_ETH1ADDR) "\0"
#endif
diff --git a/include/part_efi.h b/include/part_efi.h
new file mode 100644
index 0000000000..d68ef3bb37
--- /dev/null
+++ b/include/part_efi.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008 RuggedCom, Inc.
+ * Richard Retanubun <RichardRetanubun@RuggedCom.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * See also linux/fs/partitions/efi.h
+ *
+ * EFI GUID Partition Table
+ * Per Intel EFI Specification v1.02
+ * http://developer.intel.com/technology/efi/efi.htm
+*/
+
+#include <linux/compiler.h>
+
+#ifndef _DISK_PART_EFI_H
+#define _DISK_PART_EFI_H
+
+#define MSDOS_MBR_SIGNATURE 0xAA55
+#define EFI_PMBR_OSTYPE_EFI 0xEF
+#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE
+
+#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL
+#define GPT_HEADER_REVISION_V1 0x00010000
+#define GPT_PRIMARY_PARTITION_TABLE_LBA 1ULL
+#define GPT_ENTRY_NAME "gpt"
+#define GPT_ENTRY_NUMBERS 128
+#define GPT_ENTRY_SIZE 128
+
+#define EFI_GUID(a,b,c,d0,d1,d2,d3,d4,d5,d6,d7) \
+ ((efi_guid_t) \
+ {{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
+ (b) & 0xff, ((b) >> 8) & 0xff, \
+ (c) & 0xff, ((c) >> 8) & 0xff, \
+ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }})
+
+#define PARTITION_SYSTEM_GUID \
+ EFI_GUID( 0xC12A7328, 0xF81F, 0x11d2, \
+ 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B)
+#define LEGACY_MBR_PARTITION_GUID \
+ EFI_GUID( 0x024DEE41, 0x33E7, 0x11d3, \
+ 0x9D, 0x69, 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F)
+#define PARTITION_MSFT_RESERVED_GUID \
+ EFI_GUID( 0xE3C9E316, 0x0B5C, 0x4DB8, \
+ 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE)
+#define PARTITION_BASIC_DATA_GUID \
+ EFI_GUID( 0xEBD0A0A2, 0xB9E5, 0x4433, \
+ 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7)
+#define PARTITION_LINUX_RAID_GUID \
+ EFI_GUID( 0xa19d880f, 0x05fc, 0x4d3b, \
+ 0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e)
+#define PARTITION_LINUX_SWAP_GUID \
+ EFI_GUID( 0x0657fd6d, 0xa4ab, 0x43c4, \
+ 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f)
+#define PARTITION_LINUX_LVM_GUID \
+ EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \
+ 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28)
+
+/* linux/include/efi.h */
+typedef u16 efi_char16_t;
+
+typedef struct {
+ u8 b[16];
+} efi_guid_t;
+
+/* based on linux/include/genhd.h */
+struct partition {
+ u8 boot_ind; /* 0x80 - active */
+ u8 head; /* starting head */
+ u8 sector; /* starting sector */
+ u8 cyl; /* starting cylinder */
+ u8 sys_ind; /* What partition type */
+ u8 end_head; /* end head */
+ u8 end_sector; /* end sector */
+ u8 end_cyl; /* end cylinder */
+ __le32 start_sect; /* starting sector counting from 0 */
+ __le32 nr_sects; /* nr of sectors in partition */
+} __packed;
+
+/* based on linux/fs/partitions/efi.h */
+typedef struct _gpt_header {
+ __le64 signature;
+ __le32 revision;
+ __le32 header_size;
+ __le32 header_crc32;
+ __le32 reserved1;
+ __le64 my_lba;
+ __le64 alternate_lba;
+ __le64 first_usable_lba;
+ __le64 last_usable_lba;
+ efi_guid_t disk_guid;
+ __le64 partition_entry_lba;
+ __le32 num_partition_entries;
+ __le32 sizeof_partition_entry;
+ __le32 partition_entry_array_crc32;
+} __packed gpt_header;
+
+typedef union _gpt_entry_attributes {
+ struct {
+ u64 required_to_function:1;
+ u64 no_block_io_protocol:1;
+ u64 legacy_bios_bootable:1;
+ u64 reserved:45;
+ u64 type_guid_specific:16;
+ } fields;
+ unsigned long long raw;
+} __packed gpt_entry_attributes;
+
+#define PARTNAME_SZ (72 / sizeof(efi_char16_t))
+typedef struct _gpt_entry {
+ efi_guid_t partition_type_guid;
+ efi_guid_t unique_partition_guid;
+ __le64 starting_lba;
+ __le64 ending_lba;
+ gpt_entry_attributes attributes;
+ efi_char16_t partition_name[PARTNAME_SZ];
+} __packed gpt_entry;
+
+typedef struct _legacy_mbr {
+ u8 boot_code[440];
+ __le32 unique_mbr_signature;
+ __le16 unknown;
+ struct partition partition_record[4];
+ __le16 signature;
+} __packed legacy_mbr;
+
+#endif /* _DISK_PART_EFI_H */
diff --git a/include/vsprintf.h b/include/vsprintf.h
index 2ac41d1923..c530a42df5 100644
--- a/include/vsprintf.h
+++ b/include/vsprintf.h
@@ -173,4 +173,6 @@ int vscnprintf(char *buf, size_t size, const char *fmt, va_list args);
*/
void print_grouped_ull(unsigned long long int_val, int digits);
+void uuid_string(unsigned char *uuid, char *str);
+
#endif
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 82e5c13653..e7d95f6703 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -886,3 +886,31 @@ void print_grouped_ull(unsigned long long int_val, int digits)
grab = 3;
}
}
+
+void uuid_string(unsigned char *uuid, char *str)
+{
+/* Don't know how the endianness should be taken care of */
+#if 0
+ static const u8 e[16] = {3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11,
+ 12, 13, 14, 15};
+#else
+ static const u8 e[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15};
+#endif
+
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ sprintf(str, "%02x", uuid[e[i]]);
+ str += 2;
+ switch (i) {
+ case 3:
+ case 5:
+ case 7:
+ case 9:
+ *str++ = '-';
+ break;
+ }
+ }
+}
+
diff --git a/tools/Makefile b/tools/Makefile
index 623f9086f3..67bb15b8e8 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -63,6 +63,7 @@ BIN_FILES-$(CONFIG_CMD_NET) += gen_eth_addr$(SFX)
BIN_FILES-$(CONFIG_CMD_LOADS) += img2srec$(SFX)
BIN_FILES-$(CONFIG_INCA_IP) += inca-swap-bytes$(SFX)
BIN_FILES-y += mkimage$(SFX)
+BIN_FILES-y += kwboot$(SFX)
BIN_FILES-$(CONFIG_NETCONSOLE) += ncb$(SFX)
BIN_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1$(SFX)
@@ -88,6 +89,7 @@ NOPED_OBJ_FILES-y += mkimage.o
OBJ_FILES-$(CONFIG_NETCONSOLE) += ncb.o
NOPED_OBJ_FILES-y += os_support.o
OBJ_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1.o
+NOPED_OBJ_FILES-y += kwboot.o
# Don't build by default
#ifeq ($(ARCH),ppc)
@@ -200,6 +202,10 @@ $(obj)ncb$(SFX): $(obj)ncb.o
$(obj)ubsha1$(SFX): $(obj)os_support.o $(obj)sha1.o $(obj)ubsha1.o
$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^
+$(obj)kwboot$(SFX): $(obj)kwboot.o
+ $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^
+ $(HOSTSTRIP) $@
+
# Some of the tool objects need to be accessed from outside the tools directory
$(obj)%.o: $(SRCTREE)/common/%.c
$(HOSTCC) -g $(HOSTCFLAGS_NOPED) -c -o $@ $<
diff --git a/tools/kwboot.c b/tools/kwboot.c
new file mode 100644
index 0000000000..e773f01df3
--- /dev/null
+++ b/tools/kwboot.c
@@ -0,0 +1,742 @@
+/*
+ * Boot a Marvell Kirkwood SoC, with Xmodem over UART0.
+ *
+ * (c) 2012 Daniel Stodden <daniel.stodden@gmail.com>
+ *
+ * References: marvell.com, "88F6180, 88F6190, 88F6192, and 88F6281
+ * Integrated Controller: Functional Specifications" December 2,
+ * 2008. Chapter 24.2 "BootROM Firmware".
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <libgen.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <termios.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include "kwbimage.h"
+
+#ifdef __GNUC__
+#define PACKED __attribute((packed))
+#else
+#define PACKED
+#endif
+
+/*
+ * Marvell BootROM UART Sensing
+ */
+
+static unsigned char kwboot_msg_boot[] = {
+ 0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
+};
+
+#define KWBOOT_MSG_REQ_DELAY 10 /* ms */
+#define KWBOOT_MSG_RSP_TIMEO 50 /* ms */
+
+/*
+ * Xmodem Transfers
+ */
+
+#define SOH 1 /* sender start of block header */
+#define EOT 4 /* sender end of block transfer */
+#define ACK 6 /* target block ack */
+#define NAK 21 /* target block negative ack */
+#define CAN 24 /* target/sender transfer cancellation */
+
+struct kwboot_block {
+ uint8_t soh;
+ uint8_t pnum;
+ uint8_t _pnum;
+ uint8_t data[128];
+ uint8_t csum;
+} PACKED;
+
+#define KWBOOT_BLK_RSP_TIMEO 1000 /* ms */
+
+static int kwboot_verbose;
+
+static void
+kwboot_printv(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (kwboot_verbose) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ fflush(stdout);
+ }
+}
+
+static void
+__spinner(void)
+{
+ const char seq[] = { '-', '\\', '|', '/' };
+ const int div = 8;
+ static int state, bs;
+
+ if (state % div == 0) {
+ fputc(bs, stdout);
+ fputc(seq[state / div % sizeof(seq)], stdout);
+ fflush(stdout);
+ }
+
+ bs = '\b';
+ state++;
+}
+
+static void
+kwboot_spinner(void)
+{
+ if (kwboot_verbose)
+ __spinner();
+}
+
+static void
+__progress(int pct, char c)
+{
+ const int width = 70;
+ static const char *nl = "";
+ static int pos;
+
+ if (pos % width == 0)
+ printf("%s%3d %% [", nl, pct);
+
+ fputc(c, stdout);
+
+ nl = "]\n";
+ pos++;
+
+ if (pct == 100) {
+ while (pos++ < width)
+ fputc(' ', stdout);
+ fputs(nl, stdout);
+ }
+
+ fflush(stdout);
+
+}
+
+static void
+kwboot_progress(int _pct, char c)
+{
+ static int pct;
+
+ if (_pct != -1)
+ pct = _pct;
+
+ if (kwboot_verbose)
+ __progress(pct, c);
+}
+
+static int
+kwboot_tty_recv(int fd, void *buf, size_t len, int timeo)
+{
+ int rc, nfds;
+ fd_set rfds;
+ struct timeval tv;
+ ssize_t n;
+
+ rc = -1;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ tv.tv_sec = 0;
+ tv.tv_usec = timeo * 1000;
+ if (tv.tv_usec > 1000000) {
+ tv.tv_sec += tv.tv_usec / 1000000;
+ tv.tv_usec %= 1000000;
+ }
+
+ do {
+ nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
+ if (nfds < 0)
+ goto out;
+ if (!nfds) {
+ errno = ETIMEDOUT;
+ goto out;
+ }
+
+ n = read(fd, buf, len);
+ if (n < 0)
+ goto out;
+
+ buf = (char *)buf + n;
+ len -= n;
+ } while (len > 0);
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static int
+kwboot_tty_send(int fd, const void *buf, size_t len)
+{
+ int rc;
+ ssize_t n;
+
+ rc = -1;
+
+ do {
+ n = write(fd, buf, len);
+ if (n < 0)
+ goto out;
+
+ buf = (char *)buf + n;
+ len -= n;
+ } while (len > 0);
+
+ rc = tcdrain(fd);
+out:
+ return rc;
+}
+
+static int
+kwboot_tty_send_char(int fd, unsigned char c)
+{
+ return kwboot_tty_send(fd, &c, 1);
+}
+
+static speed_t
+kwboot_tty_speed(int baudrate)
+{
+ switch (baudrate) {
+ case 115200:
+ return B115200;
+ case 57600:
+ return B57600;
+ case 38400:
+ return B38400;
+ case 19200:
+ return B19200;
+ case 9600:
+ return B9600;
+ }
+
+ return -1;
+}
+
+static int
+kwboot_open_tty(const char *path, speed_t speed)
+{
+ int rc, fd;
+ struct termios tio;
+
+ rc = -1;
+
+ fd = open(path, O_RDWR|O_NOCTTY|O_NDELAY);
+ if (fd < 0)
+ goto out;
+
+ memset(&tio, 0, sizeof(tio));
+
+ tio.c_iflag = 0;
+ tio.c_cflag = CREAD|CLOCAL|CS8;
+
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 10;
+
+ cfsetospeed(&tio, speed);
+ cfsetispeed(&tio, speed);
+
+ rc = tcsetattr(fd, TCSANOW, &tio);
+ if (rc)
+ goto out;
+
+ rc = fd;
+out:
+ if (rc < 0) {
+ if (fd >= 0)
+ close(fd);
+ }
+
+ return rc;
+}
+
+static int
+kwboot_bootmsg(int tty, void *msg)
+{
+ int rc;
+ char c;
+
+ kwboot_printv("Sending boot message. Please reboot the target...");
+
+ do {
+ rc = tcflush(tty, TCIOFLUSH);
+ if (rc)
+ break;
+
+ rc = kwboot_tty_send(tty, msg, 8);
+ if (rc) {
+ usleep(KWBOOT_MSG_REQ_DELAY * 1000);
+ continue;
+ }
+
+ rc = kwboot_tty_recv(tty, &c, 1, KWBOOT_MSG_RSP_TIMEO);
+
+ kwboot_spinner();
+
+ } while (rc || c != NAK);
+
+ kwboot_printv("\n");
+
+ return rc;
+}
+
+static int
+kwboot_xm_makeblock(struct kwboot_block *block, const void *data,
+ size_t size, int pnum)
+{
+ const size_t blksz = sizeof(block->data);
+ size_t n;
+ int i;
+
+ block->pnum = pnum;
+ block->_pnum = ~block->pnum;
+
+ n = size < blksz ? size : blksz;
+ memcpy(&block->data[0], data, n);
+ memset(&block->data[n], 0, blksz - n);
+
+ block->csum = 0;
+ for (i = 0; i < n; i++)
+ block->csum += block->data[i];
+
+ return n;
+}
+
+static int
+kwboot_xm_sendblock(int fd, struct kwboot_block *block)
+{
+ int rc, retries;
+ char c;
+
+ retries = 16;
+ do {
+ rc = kwboot_tty_send(fd, block, sizeof(*block));
+ if (rc)
+ break;
+
+ rc = kwboot_tty_recv(fd, &c, 1, KWBOOT_BLK_RSP_TIMEO);
+ if (rc)
+ break;
+
+ if (c != ACK)
+ kwboot_progress(-1, '+');
+
+ } while (c == NAK && retries-- > 0);
+
+ rc = -1;
+
+ switch (c) {
+ case ACK:
+ rc = 0;
+ break;
+ case NAK:
+ errno = EBADMSG;
+ break;
+ case CAN:
+ errno = ECANCELED;
+ break;
+ default:
+ errno = EPROTO;
+ break;
+ }
+
+ return rc;
+}
+
+static int
+kwboot_xmodem(int tty, const void *_data, size_t size)
+{
+ const uint8_t *data = _data;
+ int rc, pnum, N, err;
+
+ pnum = 1;
+ N = 0;
+
+ kwboot_printv("Sending boot image...\n");
+
+ do {
+ struct kwboot_block block;
+ int n;
+
+ n = kwboot_xm_makeblock(&block,
+ data + N, size - N,
+ pnum++);
+ if (n < 0)
+ goto can;
+
+ if (!n)
+ break;
+
+ rc = kwboot_xm_sendblock(tty, &block);
+ if (rc)
+ goto out;
+
+ N += n;
+ kwboot_progress(N * 100 / size, '.');
+ } while (1);
+
+ rc = kwboot_tty_send_char(tty, EOT);
+
+out:
+ return rc;
+
+can:
+ err = errno;
+ kwboot_tty_send_char(tty, CAN);
+ errno = err;
+ goto out;
+}
+
+static int
+kwboot_term_pipe(int in, int out, char *quit, int *s)
+{
+ ssize_t nin, nout;
+ char _buf[128], *buf = _buf;
+
+ nin = read(in, buf, sizeof(buf));
+ if (nin < 0)
+ return -1;
+
+ if (quit) {
+ int i;
+
+ for (i = 0; i < nin; i++) {
+ if (*buf == quit[*s]) {
+ (*s)++;
+ if (!quit[*s])
+ return 0;
+ buf++;
+ nin--;
+ } else
+ while (*s > 0) {
+ nout = write(out, quit, *s);
+ if (nout <= 0)
+ return -1;
+ (*s) -= nout;
+ }
+ }
+ }
+
+ while (nin > 0) {
+ nout = write(out, buf, nin);
+ if (nout <= 0)
+ return -1;
+ nin -= nout;
+ }
+
+ return 0;
+}
+
+static int
+kwboot_terminal(int tty)
+{
+ int rc, in, s;
+ char *quit = "\34c";
+ struct termios otio, tio;
+
+ rc = -1;
+
+ in = STDIN_FILENO;
+ if (isatty(in)) {
+ rc = tcgetattr(in, &otio);
+ if (!rc) {
+ tio = otio;
+ cfmakeraw(&tio);
+ rc = tcsetattr(in, TCSANOW, &tio);
+ }
+ if (rc) {
+ perror("tcsetattr");
+ goto out;
+ }
+
+ kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
+ quit[0]|0100, quit[1]);
+ } else
+ in = -1;
+
+ rc = 0;
+ s = 0;
+
+ do {
+ fd_set rfds;
+ int nfds = 0;
+
+ FD_SET(tty, &rfds);
+ nfds = nfds < tty ? tty : nfds;
+
+ if (in >= 0) {
+ FD_SET(in, &rfds);
+ nfds = nfds < in ? in : nfds;
+ }
+
+ nfds = select(nfds + 1, &rfds, NULL, NULL, NULL);
+ if (nfds < 0)
+ break;
+
+ if (FD_ISSET(tty, &rfds)) {
+ rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL);
+ if (rc)
+ break;
+ }
+
+ if (FD_ISSET(in, &rfds)) {
+ rc = kwboot_term_pipe(in, tty, quit, &s);
+ if (rc)
+ break;
+ }
+ } while (quit[s] != 0);
+
+ tcsetattr(in, TCSANOW, &otio);
+out:
+ return rc;
+}
+
+static void *
+kwboot_mmap_image(const char *path, size_t *size, int prot)
+{
+ int rc, fd, flags;
+ struct stat st;
+ void *img;
+
+ rc = -1;
+ fd = -1;
+ img = NULL;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ rc = fstat(fd, &st);
+ if (rc)
+ goto out;
+
+ flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED;
+
+ img = mmap(NULL, st.st_size, prot, flags, fd, 0);
+ if (img == MAP_FAILED) {
+ img = NULL;
+ goto out;
+ }
+
+ rc = 0;
+ *size = st.st_size;
+out:
+ if (rc && img) {
+ munmap(img, st.st_size);
+ img = NULL;
+ }
+ if (fd >= 0)
+ close(fd);
+
+ return img;
+}
+
+static uint8_t
+kwboot_img_csum8(void *_data, size_t size)
+{
+ uint8_t *data = _data, csum;
+
+ for (csum = 0; size-- > 0; data++)
+ csum += *data;
+
+ return csum;
+}
+
+static int
+kwboot_img_patch_hdr(void *img, size_t size)
+{
+ int rc;
+ bhr_t *hdr;
+ uint8_t csum;
+ const size_t hdrsz = sizeof(*hdr);
+
+ rc = -1;
+ hdr = img;
+
+ if (size < hdrsz) {
+ errno = EINVAL;
+ goto out;
+ }
+
+ csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checkSum;
+ if (csum != hdr->checkSum) {
+ errno = EINVAL;
+ goto out;
+ }
+
+ if (hdr->blockid == IBR_HDR_UART_ID) {
+ rc = 0;
+ goto out;
+ }
+
+ hdr->blockid = IBR_HDR_UART_ID;
+
+ hdr->nandeccmode = IBR_HDR_ECC_DISABLED;
+ hdr->nandpagesize = 0;
+
+ hdr->srcaddr = hdr->ext
+ ? sizeof(struct kwb_header)
+ : sizeof(*hdr);
+
+ hdr->checkSum = kwboot_img_csum8(hdr, hdrsz) - csum;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static void
+kwboot_usage(FILE *stream, char *progname)
+{
+ fprintf(stream,
+ "Usage: %s -b <image> [ -p ] [ -t ] "
+ "[-B <baud> ] <TTY>\n", progname);
+ fprintf(stream, "\n");
+ fprintf(stream, " -b <image>: boot <image>\n");
+ fprintf(stream, " -p: patch <image> to type 0x69 (uart boot)\n");
+ fprintf(stream, "\n");
+ fprintf(stream, " -t: mini terminal\n");
+ fprintf(stream, "\n");
+ fprintf(stream, " -B <baud>: set baud rate\n");
+ fprintf(stream, "\n");
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *ttypath, *imgpath;
+ int rv, rc, tty, term, prot, patch;
+ void *bootmsg;
+ void *img;
+ size_t size;
+ speed_t speed;
+
+ rv = 1;
+ tty = -1;
+ bootmsg = NULL;
+ imgpath = NULL;
+ img = NULL;
+ term = 0;
+ patch = 0;
+ size = 0;
+ speed = B115200;
+
+ kwboot_verbose = isatty(STDOUT_FILENO);
+
+ do {
+ int c = getopt(argc, argv, "hb:ptB:");
+ if (c < 0)
+ break;
+
+ switch (c) {
+ case 'b':
+ bootmsg = kwboot_msg_boot;
+ imgpath = optarg;
+ break;
+
+ case 'p':
+ patch = 1;
+ break;
+
+ case 't':
+ term = 1;
+ break;
+
+ case 'B':
+ speed = kwboot_tty_speed(atoi(optarg));
+ if (speed == -1)
+ goto usage;
+ break;
+
+ case 'h':
+ rv = 0;
+ default:
+ goto usage;
+ }
+ } while (1);
+
+ if (!bootmsg && !term)
+ goto usage;
+
+ if (patch && !imgpath)
+ goto usage;
+
+ if (argc - optind < 1)
+ goto usage;
+
+ ttypath = argv[optind++];
+
+ tty = kwboot_open_tty(ttypath, speed);
+ if (tty < 0) {
+ perror(ttypath);
+ goto out;
+ }
+
+ if (imgpath) {
+ prot = PROT_READ | (patch ? PROT_WRITE : 0);
+
+ img = kwboot_mmap_image(imgpath, &size, prot);
+ if (!img) {
+ perror(imgpath);
+ goto out;
+ }
+ }
+
+ if (patch) {
+ rc = kwboot_img_patch_hdr(img, size);
+ if (rc) {
+ fprintf(stderr, "%s: Invalid image.\n", imgpath);
+ goto out;
+ }
+ }
+
+ if (bootmsg) {
+ rc = kwboot_bootmsg(tty, bootmsg);
+ if (rc) {
+ perror("bootmsg");
+ goto out;
+ }
+ }
+
+ if (img) {
+ rc = kwboot_xmodem(tty, img, size);
+ if (rc) {
+ perror("xmodem");
+ goto out;
+ }
+ }
+
+ if (term) {
+ rc = kwboot_terminal(tty);
+ if (rc && !(errno == EINTR)) {
+ perror("terminal");
+ goto out;
+ }
+ }
+
+ rv = 0;
+out:
+ if (tty >= 0)
+ close(tty);
+
+ if (img)
+ munmap(img, size);
+
+ return rv;
+
+usage:
+ kwboot_usage(rv ? stderr : stdout, basename(argv[0]));
+ goto out;
+}