[PATCH v5 10/17] bootmenu: add distro boot entry
Masahisa Kojima
masahisa.kojima at linaro.org
Thu Apr 28 10:09:43 CEST 2022
This commit adds the distro_boot entries into the bootmenu.
The bootmenu read the "boot_targets" U-Boot environment variable
and enumerate it.
User can select the distro boot entry, then bootmenu executes
"run bootcmd_xxx" command.
The bootmenu also checks the existing block devices and network
option("dhcp" and "pxe") availability, then filter out
the "boot_targets" appeared in bootmenu.
The bootmenu example is as follows, distro boot entry has the
"distro_boot" prefix.
*** U-Boot Boot Menu ***
distro_boot : usb0
distro_boot : scsi0
distro_boot : virtio0
distro_boot : dhcp
Signed-off-by: Masahisa Kojima <masahisa.kojima at linaro.org>
---
Changes in v5:
- split into the separate patch
- add function description comment
- handle the case boot_targets variable is empty
- filter out the non-exist device entry
cmd/bootmenu.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 177 insertions(+)
diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index da688e6213..afe42b8041 100644
--- a/cmd/bootmenu.c
+++ b/cmd/bootmenu.c
@@ -7,6 +7,7 @@
#include <common.h>
#include <command.h>
#include <ansi.h>
+#include <dm.h>
#include <efi_loader.h>
#include <efi_variable.h>
#include <env.h>
@@ -14,6 +15,7 @@
#include <menu.h>
#include <watchdog.h>
#include <malloc.h>
+#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/string.h>
@@ -31,6 +33,7 @@ enum boot_type {
BOOTMENU_TYPE_NONE = 0,
BOOTMENU_TYPE_BOOTMENU,
BOOTMENU_TYPE_UEFI_BOOT_OPTION,
+ BOOTMENU_TYPE_DISTRO_BOOT,
};
struct bootmenu_entry {
@@ -90,6 +93,8 @@ static void bootmenu_print_entry(void *data)
printf("bootmenu_%02d : %ls", entry->bootorder, entry->title);
else if (entry->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION)
printf("UEFI BOOT%04X : %ls", entry->bootorder, entry->title);
+ else if (entry->type == BOOTMENU_TYPE_DISTRO_BOOT)
+ printf("distro_boot : %ls", entry->title);
else
printf("%ls", entry->title);
@@ -465,6 +470,172 @@ static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
return 1;
}
+static int is_blk_device_available(char *token)
+{
+ struct driver *d = ll_entry_start(struct driver, driver);
+ const int n_ents = ll_entry_count(struct driver, driver);
+ struct driver *entry;
+ struct udevice *udev;
+ struct uclass *uc;
+ struct blk_desc *desc;
+ int ret, i;
+ const char *if_type_name;
+
+ ret = uclass_get(UCLASS_BLK, &uc);
+ if (ret)
+ return -ENODEV;
+
+ for (entry = d; entry < d + n_ents; entry++) {
+ if (entry->id != UCLASS_BLK)
+ continue;
+ i = 0;
+ uclass_foreach_dev(udev, uc) {
+ if (udev->driver != entry)
+ continue;
+ desc = dev_get_uclass_plat(udev);
+ if_type_name = blk_get_if_type_name(desc->if_type);
+ if (strncmp(token, if_type_name, strlen(if_type_name)) == 0) {
+ char *p;
+ int j, len;
+ int devnum = 0;
+
+ p = token + strlen(if_type_name);
+ len = strlen(p);
+ if (!len)
+ continue; /* no device number */
+
+ for (j = 0; j < len; j++) {
+ if (!isdigit(*p)) {
+ /* invalid device number */
+ devnum = INT_MAX;
+ break;
+ }
+ devnum = (devnum * 10) + (*p++ - '0');
+ }
+
+ if (devnum == INT_MAX)
+ continue;
+
+ if (devnum == desc->devnum)
+ return 1;
+ }
+ }
+ }
+
+ if (strncmp(token, "dhcp", strlen("dhcp")) == 0) {
+ if (IS_ENABLED(CONFIG_CMD_DHCP))
+ return 1;
+ }
+
+ if (strncmp(token, "pxe", strlen("pxe")) == 0) {
+ if (IS_ENABLED(CONFIG_CMD_PXE))
+ return 1;
+ }
+
+ return -ENODEV;
+}
+
+/**
+ * prepare_distro_boot_entry() - generate the distro boot entries
+ *
+ * This function read the "boot_targets" U-Boot environment variable
+ * and generate the bootmenu entries.
+ *
+ * @menu: pointer to the bootmenu structure
+ * @current: pointer to the last bootmenu entry list
+ * @index: pointer to the index of the last bootmenu entry,
+ * the number of uefi entry is added by this function
+ * Return: 1 on success, negative value on error
+ */
+static int prepare_distro_boot_entry(struct bootmenu_data *menu,
+ struct bootmenu_entry **current,
+ unsigned short int *index)
+{
+ char *p;
+ int len;
+ char *token;
+ char *boot_targets;
+ unsigned short int i = *index;
+ struct bootmenu_entry *entry = NULL;
+ struct bootmenu_entry *iter = *current;
+
+ /* list the distro boot "boot_targets" */
+ boot_targets = env_get("boot_targets");
+ if (!boot_targets)
+ return -ENOENT;
+
+ len = strlen(boot_targets);
+ if (!len)
+ return -ENOENT;
+
+ p = calloc(1, len + 1);
+ strlcpy(p, boot_targets, len);
+
+ token = strtok(p, " ");
+
+ do {
+ u16 *buf;
+ char *command;
+ int command_size;
+
+ if (is_blk_device_available(token) != 1) {
+ token = strtok(NULL, " ");
+ continue;
+ }
+
+ entry = malloc(sizeof(struct bootmenu_entry));
+ if (!entry) {
+ free(p);
+ return -ENOMEM;
+ }
+
+ len = strlen(token);
+ buf = calloc(1, (len + 1) * sizeof(u16));
+ entry->title = buf;
+ if (!entry->title) {
+ free(entry);
+ free(p);
+ return -ENOMEM;
+ }
+ utf8_utf16_strncpy(&buf, token, len);
+ sprintf(entry->key, "%d", i);
+ entry->num = i;
+ entry->menu = menu;
+
+ command_size = sizeof("run bootcmd_") + len;
+ command = calloc(1, command_size);
+ if (!command) {
+ free(entry->title);
+ free(entry);
+ free(p);
+ return -ENOMEM;
+ }
+ snprintf(command, command_size, "run bootcmd_%s", token);
+ entry->command = command;
+ entry->type = BOOTMENU_TYPE_DISTRO_BOOT;
+ entry->next = NULL;
+
+ if (!iter)
+ menu->first = entry;
+ else
+ iter->next = entry;
+
+ iter = entry;
+ i++;
+
+ if (i == MAX_COUNT - 1)
+ break;
+
+ token = strtok(NULL, " ");
+ } while (token);
+
+ free(p);
+ *index = i;
+ *current = iter;
+
+ return 1;
+}
+
static struct bootmenu_data *bootmenu_create(int delay)
{
int ret;
@@ -498,6 +669,12 @@ static struct bootmenu_data *bootmenu_create(int delay)
}
}
+ if (i < MAX_COUNT - 1) {
+ ret = prepare_distro_boot_entry(menu, &iter, &i);
+ if (ret < 0 && ret != -ENOENT)
+ goto cleanup;
+ }
+
/* Add U-Boot console entry at the end */
if (i <= MAX_COUNT - 1) {
entry = malloc(sizeof(struct bootmenu_entry));
--
2.17.1
More information about the U-Boot
mailing list