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

Suriyan Ramasami suriyan.r at gmail.com
Thu Apr 11 06:27:29 CEST 2013


I shall probably move it to a generic location with a more generic name.
Any reocmmendations?

- Suriyan


On Wed, Apr 10, 2013 at 6:43 PM, Rob Herring <robherring2 at gmail.com> wrote:

> On Wed, Apr 10, 2013 at 8:12 AM, Suriyan Ramasami <suriyan.r at gmail.com>
> wrote:
> > 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.
>
> This all sounds very generic but...
>
> >
> > Signed-off-by: Suriyan Ramasami <suriyan.r at gmail.com>
> > ---
> > Changes in v2:
> >         - Coding style changes
> >
> >  board/Seagate/goflexhome/goflexhomemenu.c |  415
> +++++++++++++++++++++++++++++
>
> but this is not a generic location. This feature would interest me and
> probably Stephen as well.
>
> Rob
>
> >  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
> >
> > _______________________________________________
> > U-Boot mailing list
> > U-Boot at lists.denx.de
> > http://lists.denx.de/mailman/listinfo/u-boot
>


More information about the U-Boot mailing list