[U-Boot] [PATCH v2 8/8] ARM: Present a menu of bootable options on boot

Suriyan Ramasami suriyan.r at gmail.com
Wed Apr 10 15:12:17 CEST 2013


Initialize usb and ide.
Scan through the usb for storage and boot capable partitions.
Scan through the ide interface for boot capable partitions.
Present such bootable options to the user to choose to boot from
If the user does not choose any choose the default option
the default option is the option chosen by the user the last time
If no such default option exists, boot from the first possible
bootable option.

Signed-off-by: Suriyan Ramasami <suriyan.r at gmail.com>
---
Changes in v2:
	- Coding style changes

 board/Seagate/goflexhome/goflexhomemenu.c |  415 +++++++++++++++++++++++++++++
 1 files changed, 415 insertions(+), 0 deletions(-)
 create mode 100644 board/Seagate/goflexhome/goflexhomemenu.c

diff --git a/board/Seagate/goflexhome/goflexhomemenu.c b/board/Seagate/goflexhome/goflexhomemenu.c
new file mode 100644
index 0000000..6169cf8
--- /dev/null
+++ b/board/Seagate/goflexhome/goflexhomemenu.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2013 Suriyan Ramasami <suriyan.r at gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <common.h>
+
+#if defined(CONFIG_MENU)
+/* Menu related code begins here */
+
+/* Added to use the various usb/fat/ext4fs interfaces */
+#include <usb.h>
+#include <ext4fs.h>
+#include <menu.h>
+
+#define MENU_MAX_DEVICES        10
+#define MENU_MAX_PARTITIONS     10
+#define MENU_MAX_BOOTABLES      10
+
+#define MENU_EXIT 1
+#define MENU_SHOW 2
+
+#define MENU_DEFAULT_BOOTARGS \
+	"setenv bootargs ${console} ubi.mtd=2,2048 " \
+	"root=ubi0:root rootfstype=ubifs debug"
+
+#define MENU_DEFAULT_BOOTCMD \
+	"setenv bootcmd nand read.e 0x800000 0x100000 0x600000"
+
+#define MENU_PROMPT_BOOTCMD \
+	"setenv bootcmd echo Dropping you to u-boot"
+
+#define MENU_CHOSEN_BOOTARGS \
+	"setenv bootargs $console rootdelay=10 root=${menu_root} debug"
+
+#define MENU_OPTIONS_HEADER \
+	"Bootables:\nChoice\tIntface\tDrive\tDevice\tPart\tFS\tFileName\n" \
+	"---------------------------------------------------------------"
+
+#define MENU_DEFAULT_NOBOOTABLES \
+	"* Last boot options (None, and no bootables found!"
+
+struct menu_bootables {
+	char	interface[5];
+	char	drive;
+	int	device;
+	int	partition;
+	char	filename[64];
+	char	fstype;		/* f => fat, e => ext2/4 0 => invalid */
+};
+
+static void goflexhome_menuprint(void *print_buffer)
+{
+	printf("%s\n", (char *)print_buffer);
+}
+
+/*
+ * We shall use menu_<> variables to capture the state of past menu
+ * choices.
+ * menu_bootargs corresponds to bootargs
+ * menu_bootcmd corresponds to bootcmd
+ * menu_choice corresponds to the last choice that was picked
+ * menu_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.
+*/
+static int goflexhome_evaluate_env(void)
+{
+char *s;
+
+	run_command("run menu_bootargs", 0);
+	s = getenv("bootargs");
+	printf("bootargs is %s\n", s);
+	run_command("run menu_bootcmd", 0);
+	s = getenv("bootcmd");
+	printf("bootcmd is %s\n", s);
+	if (run_command("run bootcmd", 0) != 0) {
+		/* We failed to boot, present the menu */
+		return MENU_SHOW;
+	}
+	if (strncmp(s, "echo", 4) == 0) {
+		/* User wants the u-boot prmpt */
+		return MENU_EXIT;
+	}
+	run_command("bootm", 0);
+
+	/* We are here, we failed to boot */
+	return MENU_SHOW;
+}
+
+static int goflexhome_handle_choice(struct menu_bootables menu_bootlist[],
+				    char *choice)
+{
+char *s, *last_menu_choice;
+char menu_command[128];
+char load_command[16];
+int index;
+int call_saveenv;
+
+	call_saveenv = 0;
+	if (choice == NULL) {
+		/* Exit menu and let it do its auto boot */
+		return MENU_EXIT;
+	}
+	printf("\nYou chose: %s\n", choice);
+
+	last_menu_choice = getenv("menu_choice");
+	if (last_menu_choice == NULL) {
+		/* User has not yet chosen before */
+		/* Lets default to boot from nand */
+		setenv("menu_bootargs", MENU_DEFAULT_BOOTARGS);
+		setenv("menu_bootcmd", MENU_DEFAULT_BOOTCMD);
+		call_saveenv = 1;
+	}
+	if (choice[0] == '*') {
+		/* User wants same thing that was chosen the last time */
+		return MENU_EXIT;
+	}
+	if (last_menu_choice && strcmp(choice, last_menu_choice) != 0) {
+		/* Save the choice chosen */
+		setenv("menu_choice", choice);
+		call_saveenv = 1;
+	}
+	if (choice[0] == '+') {
+		/* User wants u-boot prompt */
+		s = getenv("menu_bootcmd");
+		if (strcmp(s, MENU_PROMPT_BOOTCMD) !=  0) {
+			setenv("menu_bootcmd", MENU_PROMPT_BOOTCMD);
+			saveenv();
+		}
+		return MENU_EXIT;
+	}
+
+	/* Steps to set the env variables to the chosen values */
+	index = simple_strtoul(choice, NULL, 10);
+	sprintf(menu_command, "/dev/sd%c%d", menu_bootlist[index].drive,
+		menu_bootlist[index].partition);
+	s = getenv("menu_root");
+	if (strcmp(s, menu_command) != 0) {
+		setenv("menu_root", menu_command);
+		call_saveenv = 1;
+	}
+	s = getenv("menu_bootargs");
+	if (strcmp(s, MENU_CHOSEN_BOOTARGS) != 0) {
+		setenv("menu_bootargs", MENU_CHOSEN_BOOTARGS);
+		call_saveenv = 1;
+	}
+	switch (menu_bootlist[index].fstype) {
+	case 'e':
+		strcpy(load_command, "ext4load");
+		break;
+	default:
+		return MENU_EXIT;
+	}
+
+	/* Lets try to load and check the image */
+	sprintf(menu_command, "%s %s %d:%d %x %s",
+		load_command,
+		menu_bootlist[index].interface,
+		menu_bootlist[index].device,
+		menu_bootlist[index].partition,
+		CONFIG_SYS_LOAD_ADDR,
+		menu_bootlist[index].filename);
+	if (run_command(menu_command, 0) != 0) {
+		/* Could not load image */
+		printf("Selected image could not be loaded ...\n");
+		return MENU_SHOW;
+	}
+	sprintf(menu_command, "iminfo %x", CONFIG_SYS_LOAD_ADDR);
+	if (run_command(menu_command, 0) != 0) {
+		/* The image is not a valid image */
+		printf("Selected image is not valid ...\n");
+		return MENU_SHOW;
+	}
+
+	sprintf(menu_command, "setenv bootcmd %s %s %d:%d %x %s",
+		load_command,
+			menu_bootlist[index].interface,
+			menu_bootlist[index].device,
+			menu_bootlist[index].partition,
+			CONFIG_SYS_LOAD_ADDR,
+			menu_bootlist[index].filename);
+	s = getenv("menu_bootcmd");
+	if (strcmp(s, menu_command) != 0) {
+		setenv("menu_bootcmd", menu_command);
+		call_saveenv = 1;
+	}
+	if (call_saveenv)
+		saveenv();
+	return MENU_EXIT;
+}
+
+static int goflexhome_menu(struct menu_bootables menu_bootlist[], int bootdelay)
+{
+int index;
+struct menu *m;
+char menu_key[MENU_MAX_BOOTABLES][5];
+char menu_entry[MENU_MAX_BOOTABLES][64];
+char *menu_choice;
+char *last_menu_choice;
+char choice_menu_entry[64];
+char choice_menu[3];
+
+	m = menu_create(MENU_OPTIONS_HEADER, 60, 1, goflexhome_menuprint,
+			NULL, NULL);
+	for (index = 0; index < MENU_MAX_BOOTABLES; index++) {
+		if (menu_bootlist[index].fstype == '0')
+			break;
+		snprintf(menu_key[index], sizeof(menu_key[index]), "%d", index);
+		snprintf(menu_entry[index], sizeof(menu_entry[index]),
+			 "%d\t%s\t%c\t%d\t%d\t%c\t%s", index,
+			 menu_bootlist[index].interface,
+			 menu_bootlist[index].drive,
+			 menu_bootlist[index].device,
+			 menu_bootlist[index].partition,
+			 menu_bootlist[index].fstype,
+			 menu_bootlist[index].filename);
+		if (menu_item_add(m, menu_key[index], menu_entry[index]) != 1) {
+			menu_destroy(m);
+			return MENU_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 boot */
+	/* from nand */
+	last_menu_choice = getenv("menu_choice");
+	sprintf(choice_menu, "*");
+	if (last_menu_choice) {
+		sprintf(choice_menu_entry, "* Last boot options (%s)",
+			last_menu_choice);
+	} else {
+		/* There was no last boot option */
+		/* If there is at least 1 boot entry, make that the default */
+		if (menu_bootlist[0].fstype != '0') {
+			setenv("menu_choice", menu_entry[0]);
+			sprintf(choice_menu_entry, menu_entry[0]);
+		} else {
+			sprintf(choice_menu_entry, MENU_DEFAULT_NOBOOTABLES);
+		}
+	}
+	if (menu_item_add(m, choice_menu, choice_menu_entry) != 1) {
+		menu_destroy(m);
+		return MENU_EXIT;
+	}
+	/* Mark this as the default choice. */
+	menu_default_set(m, "*");
+	if (menu_item_add(m, "+", "+ UBoot prompt") != 1) {
+		menu_destroy(m);
+		return MENU_EXIT;
+	}
+
+	menu_get_choice(m, (void **)&menu_choice);
+	return goflexhome_handle_choice(menu_bootlist, menu_choice);
+}
+
+static void goflexhome_filesearch(struct menu_bootables menu_bootlist[],
+				  int *bootindex) {
+char *filenames[] = { "/uImage", "/boot/uImage", "" };
+int index;
+
+	index = 0;
+	while (filenames[index][0] != '\0') {
+		switch (menu_bootlist[*bootindex].fstype) {
+		case 'e':
+			if (ext4fs_open(filenames[index]) == -1) {
+				index++;
+				continue;
+			}
+			break;
+
+		default:
+			break;
+		}
+
+		/* Got a hit, record it */
+		strcpy(menu_bootlist[*bootindex].filename, filenames[index]);
+		index++;
+		(*bootindex)++;
+		if (*bootindex >= MENU_MAX_BOOTABLES)
+			break;
+		/* Prep next bootlist structure */
+		memcpy(&menu_bootlist[*bootindex],
+		       &menu_bootlist[*bootindex - 1],
+		       sizeof(struct menu_bootables));
+	}
+}
+
+static void goflexhome_populate_partitions(struct menu_bootables *menu_bootlist,
+					   block_dev_desc_t *dev_desc,
+					   int *bootindex)
+{
+int part;
+disk_partition_t disk_part;
+
+	part = menu_bootlist[*bootindex].partition;
+
+	/* Get the partition structure */
+	if (get_partition_info(dev_desc, part, &disk_part))
+		return;
+
+	/* Try to check if its extX */
+	if (ext4fs_probe(dev_desc, &disk_part) == 0) {
+		menu_bootlist[*bootindex].fstype = 'e';
+		goflexhome_filesearch(menu_bootlist, bootindex);
+		ext4fs_close();
+		return;
+	}
+}
+
+static void goflexhome_populate_devices(struct menu_bootables menu_bootlist[],
+					int *bootindex)
+{
+block_dev_desc_t *dev_desc;
+int device;
+int part;
+
+	/* Populate bootlist from each device and the partitions within */
+	for (device = 0; device < MENU_MAX_DEVICES; device++) {
+		dev_desc = get_dev(menu_bootlist[*bootindex].interface, device);
+		if (dev_desc == NULL)
+			continue;
+		menu_bootlist[*bootindex].device = device;
+		for (part = 0; part < MENU_MAX_PARTITIONS; part++) {
+			menu_bootlist[*bootindex].partition = part;
+			goflexhome_populate_partitions(menu_bootlist, dev_desc,
+						       bootindex);
+		}
+	}
+}
+
+/* menu_bootlist[] can hold a max of MENU_MAX_BOOTABLES entries */
+static void goflexhome_populate_bootlist(struct menu_bootables menu_bootlist[])
+{
+/* ide is always first */
+char *interfaces[] = { "ide", "usb", "" };
+int bootindex;
+int i;
+
+	bootindex = 0;
+	i = 0;
+	/* Lets initialize the usb sub system */
+	usb_init();
+	usb_stor_scan(0);
+
+	/* This scans the partitions in the IDE storage */
+	ide_init();
+
+	/* Populate bootlist from each interface */
+	while ((interfaces[i][0] != '\0') &&
+	       (bootindex < MENU_MAX_BOOTABLES)) {
+		strcpy(menu_bootlist[bootindex].interface, interfaces[i]);
+		goflexhome_populate_devices(menu_bootlist, &bootindex);
+		i++;
+	}
+	if (bootindex < MENU_MAX_BOOTABLES) {
+		/* End marker of list */
+		menu_bootlist[bootindex].fstype = '0';
+	}
+
+	/* Lets set the drive letter */
+	menu_bootlist[0].drive = 'a';
+	for (i = 1; i < bootindex; i++) {
+		if (menu_bootlist[i].fstype == '0')
+			break;
+		/* Increase drive letter when interface changes */
+		/* Or when device numbers change for same interface */
+		menu_bootlist[i].drive = menu_bootlist[i - 1].drive;
+		if (strcmp(menu_bootlist[i].interface,
+			   menu_bootlist[i - 1].interface) != 0) {
+			menu_bootlist[i].drive++;
+		} else {
+			if (menu_bootlist[i].device >
+			    menu_bootlist[i - 1].device) {
+				menu_bootlist[i].drive++;
+			}
+		}
+	}
+}
+
+int menu_show(int bootdelay)
+{
+struct menu_bootables menu_bootlist[MENU_MAX_BOOTABLES];
+int retval;
+
+	goflexhome_populate_bootlist(menu_bootlist);
+	do {
+		retval = goflexhome_menu(menu_bootlist, bootdelay);
+		if (retval == MENU_EXIT)
+			retval = goflexhome_evaluate_env();
+	} while (retval == MENU_SHOW);
+
+	return 0;
+}
+
+#endif /* CONFIG_MENU */
-- 
1.7.1



More information about the U-Boot mailing list