diff --git a/commands/Kconfig b/commands/Kconfig index 3a0977b..0685664 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -294,6 +294,7 @@ config CMD_BOOT tristate depends on BOOTM + select BOOT prompt "boot" help Select this for booting based on scripts. Unlike the bootm command which diff --git a/commands/boot.c b/commands/boot.c index 58968a2..e757011 100644 --- a/commands/boot.c +++ b/commands/boot.c @@ -11,339 +11,21 @@ * */ -#include #include -#include -#include #include -#include #include #include -#include -#include #include -#include -#include -#include -#include -#include -#include +#include #include #include -static int verbose; -static int dryrun; -static int timeout; - -int bootentries_add_entry(struct bootentries *entries, struct bootentry *entry) -{ - list_add_tail(&entry->list, &entries->entries); - - return 0; -} - -static struct bootentries *bootentries_alloc(void) -{ - struct bootentries *bootentries; - - bootentries = xzalloc(sizeof(*bootentries)); - INIT_LIST_HEAD(&bootentries->entries); - - if (IS_ENABLED(CONFIG_MENU)) { - bootentries->menu = menu_alloc(); - bootentries->menu->display = basprintf("boot"); - } - - return bootentries; -} - -static void bootentries_free(struct bootentries *bootentries) -{ - struct bootentry *be, *tmp; - - list_for_each_entry_safe(be, tmp, &bootentries->entries, list) { - list_del(&be->list); - free(be->title); - free(be->description); - free(be->me.display); - be->release(be); - } - - if (bootentries->menu) - free(bootentries->menu->display); - free(bootentries->menu); - free(bootentries); -} - -struct bootentry_script { - struct bootentry entry; - char *scriptpath; -}; - -/* - * Start a single boot script. 'path' is a full path to a boot script. - */ -static int bootscript_boot(struct bootentry *entry, int verbose, int dryrun) -{ - struct bootentry_script *bs = container_of(entry, struct bootentry_script, entry); - int ret; - - struct bootm_data data = {}; - - if (dryrun) { - printf("Would run %s\n", bs->scriptpath); - return 0; - } - - globalvar_set_match("linux.bootargs.dyn.", ""); - - ret = run_command(bs->scriptpath); - if (ret) { - printf("Running %s failed\n", bs->scriptpath); - goto out; - } - - bootm_data_init_defaults(&data); - - if (verbose) - data.verbose = verbose; - if (dryrun) - data.dryrun = dryrun; - - ret = bootm_boot(&data); - if (ret) - pr_err("Booting %s failed: %s\n", basename(bs->scriptpath), strerror(-ret)); -out: - return ret; -} - -static unsigned int boot_watchdog_timeout; - -static int init_boot_watchdog_timeout(void) -{ - return globalvar_add_simple_int("boot.watchdog_timeout", - &boot_watchdog_timeout, "%u"); -} -late_initcall(init_boot_watchdog_timeout); - -BAREBOX_MAGICVAR_NAMED(global_watchdog_timeout, global.boot.watchdog_timeout, - "Watchdog enable timeout in seconds before booting"); - -static int boot_entry(struct bootentry *be) -{ - int ret; - - printf("booting %s\n", be->title); - - if (IS_ENABLED(CONFIG_WATCHDOG) && boot_watchdog_timeout) { - ret = watchdog_set_timeout(boot_watchdog_timeout); - if (ret) - pr_warn("Failed to enable watchdog: %s\n", strerror(-ret)); - } - - ret = be->boot(be, verbose, dryrun); - - if (ret) - printf("booting %s failed: %s\n", be->title, strerror(-ret)); - - return ret; -} - -static void bootsource_action(struct menu *m, struct menu_entry *me) -{ - struct bootentry *be = container_of(me, struct bootentry, me); - int ret; - - ret = boot_entry(be); - if (ret) - printf("Booting failed with: %s\n", strerror(-ret)); - - printf("Press any key to continue\n"); - - read_key(); -} - -static void bootscript_entry_release(struct bootentry *entry) -{ - struct bootentry_script *bs = container_of(entry, struct bootentry_script, entry); - - free(bs->scriptpath); - free(bs->entry.me.display); - free(bs); -} - -/* - * bootscript_create_entry - create a boot entry from a script name - */ -static int bootscript_create_entry(struct bootentries *bootentries, const char *name) -{ - struct bootentry_script *bs; - enum filetype type; - - type = file_name_detect_type(name); - if (type != filetype_sh) - return -EINVAL; - - bs = xzalloc(sizeof(*bs)); - bs->entry.me.type = MENU_ENTRY_NORMAL; - bs->entry.release = bootscript_entry_release; - bs->entry.boot = bootscript_boot; - bs->scriptpath = xstrdup(name); - bs->entry.title = xstrdup(basename(bs->scriptpath)); - bs->entry.description = basprintf("script: %s", name); - bootentries_add_entry(bootentries, &bs->entry); - - return 0; -} - -/* - * bootscript_scan_path - create boot entries from a path - * - * path can either be a full path to a bootscript or a full path to a diretory - * containing bootscripts. - */ -static int bootscript_scan_path(struct bootentries *bootentries, const char *path) -{ - struct stat s; - char *files; - int ret, i; - int found = 0; - glob_t globb; - - ret = stat(path, &s); - if (ret) - return ret; - - if (!S_ISDIR(s.st_mode)) { - ret = bootscript_create_entry(bootentries, path); - if (ret) - return ret; - return 1; - } - - files = basprintf("%s/*", path); - - glob(files, 0, NULL, &globb); - - for (i = 0; i < globb.gl_pathc; i++) { - char *bootscript_path = globb.gl_pathv[i]; - - if (*basename(bootscript_path) == '.') - continue; - - bootscript_create_entry(bootentries, bootscript_path); - found++; - } - - globfree(&globb); - free(files); - - ret = found; - - return ret; -} - -/* - * bootentry_parse_one - create boot entries from a name - * - * name can be: - * - a name of a boot script under /env/boot - * - a full path of a boot script - * - a device name - * - a cdev name - * - a full path of a directory containing bootloader spec entries - * - a full path of a directory containing bootscripts - * - * Returns the number of entries found or a negative error code. - */ -static int bootentry_parse_one(struct bootentries *bootentries, const char *name) -{ - int found = 0, ret; - - if (IS_ENABLED(CONFIG_BLSPEC)) { - ret = blspec_scan_devicename(bootentries, name); - if (ret > 0) - found += ret; - - if (*name == '/') { - ret = blspec_scan_directory(bootentries, name); - if (ret > 0) - found += ret; - } - } - - if (!found) { - char *path; - - if (*name != '/') - path = basprintf("/env/boot/%s", name); - else - path = xstrdup(name); - - ret = bootscript_scan_path(bootentries, path); - if (ret > 0) - found += ret; - - free(path); - } - - return found; -} - -/* - * bootsources_menu - show a menu from an array of names - */ -static void bootsources_menu(struct bootentries *bootentries) -{ - struct bootentry *entry; - struct menu_entry *back_entry; - - if (!IS_ENABLED(CONFIG_MENU)) { - printf("no menu support available\n"); - return; - } - - bootentries_for_each_entry(bootentries, entry) { - if (!entry->me.display) - entry->me.display = xstrdup(entry->title); - entry->me.action = bootsource_action; - menu_add_entry(bootentries->menu, &entry->me); - } - - back_entry = xzalloc(sizeof(*back_entry)); - back_entry->display = "back"; - back_entry->type = MENU_ENTRY_NORMAL; - back_entry->non_re_ent = 1; - menu_add_entry(bootentries->menu, back_entry); - - if (timeout >= 0) - bootentries->menu->auto_select = timeout; - - menu_show(bootentries->menu); - - free(back_entry); -} - -/* - * bootsources_list - list boot entries from an array of names - */ -static void bootsources_list(struct bootentries *bootentries) -{ - struct bootentry *entry; - - printf("%-20s\n", "title"); - printf("%-20s\n", "------"); - - bootentries_for_each_entry(bootentries, entry) - printf("%-20s %s\n", entry->title, entry->description); -} - static int do_boot(int argc, char *argv[]) { char *freep = NULL; int opt, ret = 0, do_list = 0, do_menu = 0; - int i; + int i, dryrun = 0, verbose = 0, timeout = -1; struct bootentries *entries; struct bootentry *entry; @@ -369,7 +51,7 @@ timeout = simple_strtoul(optarg, NULL, 0); break; case 'w': - boot_watchdog_timeout = simple_strtoul(optarg, NULL, 0); + boot_set_watchdog_timeout(simple_strtoul(optarg, NULL, 0)); break; } } @@ -378,7 +60,7 @@ if (optind < argc) { for (i = optind; i < argc; i++) - bootentry_parse_one(entries, argv[i]); + bootentry_create_from_name(entries, argv[i]); } else { const char *def; char *sep, *name; @@ -390,7 +72,7 @@ sep = freep = xstrdup(def); while ((name = strsep(&sep, " ")) != NULL) - bootentry_parse_one(entries, name); + bootentry_create_from_name(entries, name); free(freep); } @@ -401,12 +83,12 @@ } if (do_menu) { - bootsources_menu(entries); + bootsources_menu(entries, timeout); goto out; } bootentries_for_each_entry(entries, entry) { - ret = boot_entry(entry); + ret = boot_entry(entry, verbose, dryrun); if (!ret) break; } @@ -450,5 +132,3 @@ BAREBOX_CMD_GROUP(CMD_GRP_BOOT) BAREBOX_CMD_HELP(cmd_boot_help) BAREBOX_CMD_END - -BAREBOX_MAGICVAR_NAMED(global_boot_default, global.boot.default, "default boot order"); diff --git a/common/Kconfig b/common/Kconfig index 6ce2a76..8326340 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -110,6 +110,9 @@ depends on MTD_UBI default y +config BOOT + bool + menu "General Settings" config LOCALVERSION diff --git a/common/Makefile b/common/Makefile index 17fcb5f..00bc0e8 100644 --- a/common/Makefile +++ b/common/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_UBIFORMAT) += ubiformat.o obj-$(CONFIG_BAREBOX_UPDATE_IMX_NAND_FCB) += imx-bbu-nand-fcb.o obj-$(CONFIG_CONSOLE_RATP) += ratp.o +obj-$(CONFIG_BOOT) += boot.o quiet_cmd_pwd_h = PWDH $@ ifdef CONFIG_PASSWORD diff --git a/common/boot.c b/common/boot.c new file mode 100644 index 0000000..4d29349 --- /dev/null +++ b/common/boot.c @@ -0,0 +1,339 @@ +/* + * 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; version 2. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int bootentries_add_entry(struct bootentries *entries, struct bootentry *entry) +{ + list_add_tail(&entry->list, &entries->entries); + + return 0; +} + +struct bootentries *bootentries_alloc(void) +{ + struct bootentries *bootentries; + + bootentries = xzalloc(sizeof(*bootentries)); + INIT_LIST_HEAD(&bootentries->entries); + + if (IS_ENABLED(CONFIG_MENU)) { + bootentries->menu = menu_alloc(); + bootentries->menu->display = basprintf("boot"); + } + + return bootentries; +} + +void bootentries_free(struct bootentries *bootentries) +{ + struct bootentry *be, *tmp; + + list_for_each_entry_safe(be, tmp, &bootentries->entries, list) { + list_del(&be->list); + free(be->title); + free(be->description); + free(be->me.display); + be->release(be); + } + + if (bootentries->menu) + free(bootentries->menu->display); + free(bootentries->menu); + free(bootentries); +} + +struct bootentry_script { + struct bootentry entry; + char *scriptpath; +}; + +/* + * Start a single boot script. 'path' is a full path to a boot script. + */ +static int bootscript_boot(struct bootentry *entry, int verbose, int dryrun) +{ + struct bootentry_script *bs = container_of(entry, struct bootentry_script, entry); + int ret; + + struct bootm_data data = {}; + + if (dryrun) { + printf("Would run %s\n", bs->scriptpath); + return 0; + } + + globalvar_set_match("linux.bootargs.dyn.", ""); + + ret = run_command(bs->scriptpath); + if (ret) { + printf("Running %s failed\n", bs->scriptpath); + goto out; + } + + bootm_data_init_defaults(&data); + + if (verbose) + data.verbose = verbose; + if (dryrun) + data.dryrun = dryrun; + + ret = bootm_boot(&data); + if (ret) + pr_err("Booting %s failed: %s\n", basename(bs->scriptpath), strerror(-ret)); +out: + return ret; +} + +static unsigned int boot_watchdog_timeout; + +void boot_set_watchdog_timeout(unsigned int timeout) +{ + boot_watchdog_timeout = timeout; +} + +static int init_boot_watchdog_timeout(void) +{ + return globalvar_add_simple_int("boot.watchdog_timeout", + &boot_watchdog_timeout, "%u"); +} +late_initcall(init_boot_watchdog_timeout); + +BAREBOX_MAGICVAR_NAMED(global_watchdog_timeout, global.boot.watchdog_timeout, + "Watchdog enable timeout in seconds before booting"); + +int boot_entry(struct bootentry *be, int verbose, int dryrun) +{ + int ret; + + printf("booting %s\n", be->title); + + if (IS_ENABLED(CONFIG_WATCHDOG) && boot_watchdog_timeout) { + ret = watchdog_set_timeout(boot_watchdog_timeout); + if (ret) + pr_warn("Failed to enable watchdog: %s\n", strerror(-ret)); + } + + ret = be->boot(be, verbose, dryrun); + + if (ret) + printf("booting %s failed: %s\n", be->title, strerror(-ret)); + + return ret; +} + +static void bootsource_action(struct menu *m, struct menu_entry *me) +{ + struct bootentry *be = container_of(me, struct bootentry, me); + int ret; + + ret = boot_entry(be, 0, 0); + if (ret) + printf("Booting failed with: %s\n", strerror(-ret)); + + printf("Press any key to continue\n"); + + read_key(); +} + +static void bootscript_entry_release(struct bootentry *entry) +{ + struct bootentry_script *bs = container_of(entry, struct bootentry_script, entry); + + free(bs->scriptpath); + free(bs->entry.me.display); + free(bs); +} + +/* + * bootscript_create_entry - create a boot entry from a script name + */ +static int bootscript_create_entry(struct bootentries *bootentries, const char *name) +{ + struct bootentry_script *bs; + enum filetype type; + + type = file_name_detect_type(name); + if (type != filetype_sh) + return -EINVAL; + + bs = xzalloc(sizeof(*bs)); + bs->entry.me.type = MENU_ENTRY_NORMAL; + bs->entry.release = bootscript_entry_release; + bs->entry.boot = bootscript_boot; + bs->scriptpath = xstrdup(name); + bs->entry.title = xstrdup(basename(bs->scriptpath)); + bs->entry.description = basprintf("script: %s", name); + bootentries_add_entry(bootentries, &bs->entry); + + return 0; +} + +/* + * bootscript_scan_path - create boot entries from a path + * + * path can either be a full path to a bootscript or a full path to a diretory + * containing bootscripts. + */ +static int bootscript_scan_path(struct bootentries *bootentries, const char *path) +{ + struct stat s; + char *files; + int ret, i; + int found = 0; + glob_t globb; + + ret = stat(path, &s); + if (ret) + return ret; + + if (!S_ISDIR(s.st_mode)) { + ret = bootscript_create_entry(bootentries, path); + if (ret) + return ret; + return 1; + } + + files = basprintf("%s/*", path); + + glob(files, 0, NULL, &globb); + + for (i = 0; i < globb.gl_pathc; i++) { + char *bootscript_path = globb.gl_pathv[i]; + + if (*basename(bootscript_path) == '.') + continue; + + bootscript_create_entry(bootentries, bootscript_path); + found++; + } + + globfree(&globb); + free(files); + + ret = found; + + return ret; +} + +/* + * bootentry_create_from_name - create boot entries from a name + * + * name can be: + * - a name of a boot script under /env/boot + * - a full path of a boot script + * - a device name + * - a cdev name + * - a full path of a directory containing bootloader spec entries + * - a full path of a directory containing bootscripts + * + * Returns the number of entries found or a negative error code. + */ +int bootentry_create_from_name(struct bootentries *bootentries, + const char *name) +{ + int found = 0, ret; + + if (IS_ENABLED(CONFIG_BLSPEC)) { + ret = blspec_scan_devicename(bootentries, name); + if (ret > 0) + found += ret; + + if (*name == '/') { + ret = blspec_scan_directory(bootentries, name); + if (ret > 0) + found += ret; + } + } + + if (!found) { + char *path; + + if (*name != '/') + path = basprintf("/env/boot/%s", name); + else + path = xstrdup(name); + + ret = bootscript_scan_path(bootentries, path); + if (ret > 0) + found += ret; + + free(path); + } + + return found; +} + +/* + * bootsources_menu - show a menu from an array of names + */ +void bootsources_menu(struct bootentries *bootentries, int timeout) +{ + struct bootentry *entry; + struct menu_entry *back_entry; + + if (!IS_ENABLED(CONFIG_MENU)) { + printf("no menu support available\n"); + return; + } + + bootentries_for_each_entry(bootentries, entry) { + if (!entry->me.display) + entry->me.display = xstrdup(entry->title); + entry->me.action = bootsource_action; + menu_add_entry(bootentries->menu, &entry->me); + } + + back_entry = xzalloc(sizeof(*back_entry)); + back_entry->display = "back"; + back_entry->type = MENU_ENTRY_NORMAL; + back_entry->non_re_ent = 1; + menu_add_entry(bootentries->menu, back_entry); + + if (timeout >= 0) + bootentries->menu->auto_select = timeout; + + menu_show(bootentries->menu); + + free(back_entry); +} + +/* + * bootsources_list - list boot entries from an array of names + */ +void bootsources_list(struct bootentries *bootentries) +{ + struct bootentry *entry; + + printf("%-20s\n", "title"); + printf("%-20s\n", "------"); + + bootentries_for_each_entry(bootentries, entry) + printf("%-20s %s\n", entry->title, entry->description); +} + +BAREBOX_MAGICVAR_NAMED(global_boot_default, global.boot.default, "default boot order"); diff --git a/include/boot.h b/include/boot.h index e0a61c5..a855cbe 100644 --- a/include/boot.h +++ b/include/boot.h @@ -39,4 +39,13 @@ #define bootentries_for_each_entry(bootentries, entry) \ list_for_each_entry(entry, &bootentries->entries, list) +void boot_set_watchdog_timeout(unsigned int timeout); +struct bootentries *bootentries_alloc(void); +void bootentries_free(struct bootentries *bootentries); +int bootentry_create_from_name(struct bootentries *bootentries, + const char *name); +void bootsources_menu(struct bootentries *bootentries, int timeout); +void bootsources_list(struct bootentries *bootentries); +int boot_entry(struct bootentry *be, int verbose, int dryrun); + #endif /* __BOOT_H */