diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/Kconfig | 9 | ||||
-rw-r--r-- | common/Makefile | 1 | ||||
-rw-r--r-- | common/bootm.c | 488 | ||||
-rw-r--r-- | common/date.c | 18 | ||||
-rw-r--r-- | common/hush.c | 40 | ||||
-rw-r--r-- | common/image-fit.c | 584 | ||||
-rw-r--r-- | common/partitions.c | 20 | ||||
-rw-r--r-- | common/partitions/Kconfig | 1 | ||||
-rw-r--r-- | common/partitions/efi.c | 1 | ||||
-rw-r--r-- | common/poller.c | 26 | ||||
-rw-r--r-- | common/startup.c | 5 | ||||
-rw-r--r-- | common/state.c | 33 | ||||
-rw-r--r-- | common/uimage.c | 7 |
13 files changed, 991 insertions, 242 deletions
diff --git a/common/Kconfig b/common/Kconfig index 2b5943be8b..fc3ed8e57d 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -66,6 +66,15 @@ config UIMAGE select CRC32 bool +config FITIMAGE + bool + select OFTREE + select DIGEST + +config FITIMAGE_SIGNATURE + select CRYPTO_RSA + bool + config LOGBUF bool diff --git a/common/Makefile b/common/Makefile index 5eb3c96f45..d99ca7b7ac 100644 --- a/common/Makefile +++ b/common/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_SHELL_SIMPLE) += parser.o obj-$(CONFIG_STATE) += state.o obj-$(CONFIG_RATP) += ratp.o obj-$(CONFIG_UIMAGE) += image.o uimage.o +obj-$(CONFIG_FITIMAGE) += image-fit.o obj-$(CONFIG_MENUTREE) += menutree.o obj-$(CONFIG_EFI_GUID) += efi-guid.o obj-$(CONFIG_EFI_DEVICEPATH) += efi-devicepath.o diff --git a/common/bootm.c b/common/bootm.c index 08125e7bb0..79833e045d 100644 --- a/common/bootm.c +++ b/common/bootm.c @@ -17,8 +17,10 @@ #include <malloc.h> #include <memory.h> #include <libfile.h> +#include <image-fit.h> #include <globalvar.h> #include <init.h> +#include <linux/stat.h> static LIST_HEAD(handler_list); @@ -46,6 +48,33 @@ static struct image_handler *bootm_find_handler(enum filetype filetype, return NULL; } +void bootm_data_init_defaults(struct bootm_data *data) +{ + data->initrd_address = UIMAGE_INVALID_ADDRESS; + data->os_address = UIMAGE_SOME_ADDRESS; + data->oftree_file = getenv_nonempty("global.bootm.oftree"); + data->os_file = getenv_nonempty("global.bootm.image"); + getenv_ul("global.bootm.image.loadaddr", &data->os_address); + getenv_ul("global.bootm.initrd.loadaddr", &data->initrd_address); + data->initrd_file = getenv_nonempty("global.bootm.initrd"); + data->verify = bootm_get_verify_mode(); +} + +static enum bootm_verify bootm_verify_mode = BOOTM_VERIFY_HASH; + +enum bootm_verify bootm_get_verify_mode(void) +{ + return bootm_verify_mode; +} + +static const char * const bootm_verify_names[] = { +#ifndef CONFIG_BOOTM_FORCE_SIGNED_IMAGES + [BOOTM_VERIFY_NONE] = "none", + [BOOTM_VERIFY_HASH] = "hash", +#endif + [BOOTM_VERIFY_SIGNATURE] = "signature", +}; + /* * bootm_load_os() - load OS to RAM * @@ -66,9 +95,24 @@ int bootm_load_os(struct image_data *data, unsigned long load_address) if (load_address == UIMAGE_INVALID_ADDRESS) return -EINVAL; + if (data->os_fit) { + data->os_res = request_sdram_region("kernel", + load_address, + data->os_fit->kernel_size); + if (!data->os_res) + return -ENOMEM; + memcpy((void *)load_address, data->os_fit->kernel, + data->os_fit->kernel_size); + return 0; + } + if (data->os) { + int num; + + num = simple_strtoul(data->os_part, NULL, 0); + data->os_res = uimage_load_to_sdram(data->os, - data->os_num, load_address); + num, load_address); if (!data->os_res) return -ENOMEM; @@ -86,6 +130,44 @@ int bootm_load_os(struct image_data *data, unsigned long load_address) return -EINVAL; } +bool bootm_has_initrd(struct image_data *data) +{ + if (!IS_ENABLED(CONFIG_CMD_BOOTM_INITRD)) + return false; + + if (data->os_fit && data->os_fit->initrd) + return true; + + if (data->initrd_file) + return true; + + return false; +} + +static int bootm_open_initrd_uimage(struct image_data *data) +{ + int ret; + + if (strcmp(data->os_file, data->initrd_file)) { + data->initrd = uimage_open(data->initrd_file); + if (!data->initrd) + return -EINVAL; + + if (bootm_get_verify_mode() > BOOTM_VERIFY_NONE) { + ret = uimage_verify(data->initrd); + if (ret) { + printf("Checking data crc failed with %s\n", + strerror(-ret)); + } + } + uimage_print_contents(data->initrd); + } else { + data->initrd = data->os; + } + + return 0; +} + /* * bootm_load_initrd() - load initrd to RAM * @@ -101,24 +183,112 @@ int bootm_load_os(struct image_data *data, unsigned long load_address) */ int bootm_load_initrd(struct image_data *data, unsigned long load_address) { + enum filetype type; + int ret; + + if (!IS_ENABLED(CONFIG_CMD_BOOTM_INITRD)) + return -ENOSYS; + + if (!bootm_has_initrd(data)) + return -EINVAL; + if (data->initrd_res) return 0; - if (data->initrd) { - data->initrd_res = uimage_load_to_sdram(data->initrd, - data->initrd_num, load_address); + if (data->os_fit && data->os_fit->initrd) { + data->initrd_res = request_sdram_region("initrd", + load_address, + data->os_fit->initrd_size); if (!data->initrd_res) return -ENOMEM; + memcpy((void *)load_address, data->os_fit->initrd, + data->os_fit->initrd_size); + printf("Loaded initrd from FIT image\n"); + goto done1; + } - return 0; + type = file_name_detect_type(data->initrd_file); + + if ((int)type < 0) { + printf("could not open %s: %s\n", data->initrd_file, + strerror(-type)); + return (int)type; } - if (data->initrd_file) { - data->initrd_res = file_to_sdram(data->initrd_file, load_address); + if (type == filetype_uimage) { + int num; + ret = bootm_open_initrd_uimage(data); + if (ret) { + printf("loading initrd failed with %s\n", + strerror(-ret)); + return ret; + } + + num = simple_strtoul(data->initrd_part, NULL, 0); + + data->initrd_res = uimage_load_to_sdram(data->initrd, + num, load_address); if (!data->initrd_res) return -ENOMEM; - return 0; + goto done; + } + + data->initrd_res = file_to_sdram(data->initrd_file, load_address); + if (!data->initrd_res) + return -ENOMEM; + +done: + + printf("Loaded initrd %s '%s'", file_type_to_string(type), + data->initrd_file); + if (type == filetype_uimage && data->initrd->header.ih_type == IH_TYPE_MULTI) + printf(", multifile image %s", data->initrd_part); + printf("\n"); +done1: + printf("initrd is at %pa-%pa\n", + data->initrd_res->start, + data->initrd_res->end); + + return 0; +} + +static int bootm_open_oftree_uimage(struct image_data *data, size_t *size, + struct fdt_header **fdt) +{ + enum filetype ft; + const char *oftree = data->oftree_file; + int num = simple_strtoul(data->oftree_part, NULL, 0); + struct uimage_handle *of_handle; + int release = 0; + + printf("Loading devicetree from '%s'@%d\n", oftree, num); + + if (!IS_ENABLED(CONFIG_CMD_BOOTM_OFTREE_UIMAGE)) + return -EINVAL; + + if (!strcmp(data->os_file, oftree)) { + of_handle = data->os; + } else if (!strcmp(data->initrd_file, oftree)) { + of_handle = data->initrd; + } else { + of_handle = uimage_open(oftree); + if (!of_handle) + return -ENODEV; + uimage_print_contents(of_handle); + release = 1; + } + + *fdt = uimage_load_to_buf(of_handle, num, size); + + if (release) + uimage_close(of_handle); + + ft = file_detect_type(*fdt, *size); + if (ft != filetype_oftree) { + printf("%s is not an oftree but %s\n", + data->oftree_file, file_type_to_string(ft)); + return -EINVAL; } return 0; @@ -137,8 +307,10 @@ int bootm_load_initrd(struct image_data *data, unsigned long load_address) */ int bootm_load_devicetree(struct image_data *data, unsigned long load_address) { + enum filetype type; int fdt_size; struct fdt_header *oftree; + int ret; if (data->oftree) return 0; @@ -146,8 +318,51 @@ int bootm_load_devicetree(struct image_data *data, unsigned long load_address) if (!IS_ENABLED(CONFIG_OFTREE)) return 0; - if (!data->of_root_node) - return 0; + if (data->os_fit && data->os_fit->oftree) { + data->of_root_node = of_unflatten_dtb(data->os_fit->oftree); + } else if (data->oftree_file) { + size_t size; + + type = file_name_detect_type(data->oftree_file); + + if ((int)type < 0) { + printf("could not open %s: %s\n", data->oftree_file, + strerror(-type)); + return (int)type; + } + + switch (type) { + case filetype_uimage: + ret = bootm_open_oftree_uimage(data, &size, &oftree); + break; + case filetype_oftree: + ret = read_file_2(data->oftree_file, &size, (void *)&oftree, + FILESIZE_MAX); + break; + default: + return -EINVAL; + } + + if (ret) + return ret; + + data->of_root_node = of_unflatten_dtb(oftree); + + free(oftree); + + if (!data->of_root_node) { + pr_err("unable to unflatten devicetree\n"); + return -EINVAL; + } + + } else { + data->of_root_node = of_get_root_node(); + if (!data->of_root_node) + return 0; + + if (bootm_verbose(data) > 1 && data->of_root_node) + printf("using internal devicetree\n"); + } if (data->initrd_res) { of_add_initrd(data->of_root_node, data->initrd_res->start, @@ -185,6 +400,27 @@ int bootm_load_devicetree(struct image_data *data, unsigned long load_address) return 0; } +int bootm_get_os_size(struct image_data *data) +{ + int ret; + + if (data->os) + return uimage_get_size(data->os, + simple_strtoul(data->os_part, NULL, 0)); + if (data->os_fit) + return data->os_fit->kernel_size; + + if (data->os_file) { + struct stat s; + ret = stat(data->os_file, &s); + if (ret) + return ret; + return s.st_size; + } + + return -EINVAL; +} + static int bootm_open_os_uimage(struct image_data *data) { int ret; @@ -193,7 +429,7 @@ static int bootm_open_os_uimage(struct image_data *data) if (!data->os) return -EINVAL; - if (data->verify) { + if (bootm_get_verify_mode() > BOOTM_VERIFY_NONE) { ret = uimage_verify(data->os); if (ret) { printf("Checking data crc failed with %s\n", @@ -208,7 +444,6 @@ static int bootm_open_os_uimage(struct image_data *data) if (data->os->header.ih_arch != IH_ARCH) { printf("Unsupported Architecture 0x%x\n", data->os->header.ih_arch); - uimage_close(data->os); return -EINVAL; } @@ -218,158 +453,37 @@ static int bootm_open_os_uimage(struct image_data *data) return 0; } -static int bootm_open_initrd_uimage(struct image_data *data) -{ - int ret; - - if (!data->initrd_file) - return 0; - - if (strcmp(data->os_file, data->initrd_file)) { - data->initrd = uimage_open(data->initrd_file); - if (!data->initrd) - return -EINVAL; - - if (data->verify) { - ret = uimage_verify(data->initrd); - if (ret) { - printf("Checking data crc failed with %s\n", - strerror(-ret)); - } - } - uimage_print_contents(data->initrd); - } else { - data->initrd = data->os; - } - - return 0; -} - -static int bootm_open_oftree_uimage(struct image_data *data) -{ - struct fdt_header *fdt; - enum filetype ft; - const char *oftree = data->oftree_file; - int num = data->oftree_num; - struct uimage_handle *of_handle; - int release = 0; - size_t size; - - printf("Loading devicetree from '%s'@%d\n", oftree, num); - - if (IS_ENABLED(CONFIG_CMD_BOOTM_OFTREE_UIMAGE)) { - if (!strcmp(data->os_file, oftree)) { - of_handle = data->os; - } else if (!strcmp(data->initrd_file, oftree)) { - of_handle = data->initrd; - } else { - of_handle = uimage_open(oftree); - if (!of_handle) - return -ENODEV; - uimage_print_contents(of_handle); - release = 1; - } - - fdt = uimage_load_to_buf(of_handle, num, &size); - - if (release) - uimage_close(of_handle); - - ft = file_detect_type(fdt, size); - if (ft != filetype_oftree) { - printf("%s is not an oftree but %s\n", - data->oftree_file, file_type_to_string(ft)); - return -EINVAL; - } - - data->of_root_node = of_unflatten_dtb(fdt); - if (!data->of_root_node) { - pr_err("unable to unflatten devicetree\n"); - free(fdt); - return -EINVAL; - } - - free(fdt); - - return 0; - } else { - return -EINVAL; - } -} - -static int bootm_open_oftree(struct image_data *data) -{ - struct fdt_header *fdt; - const char *oftree = data->oftree_file; - size_t size; - - printf("Loading devicetree from '%s'\n", oftree); - - fdt = read_file(oftree, &size); - if (!fdt) { - perror("open"); - return -ENODEV; - } - - data->of_root_node = of_unflatten_dtb(fdt); - if (!data->of_root_node) { - pr_err("unable to unflatten devicetree\n"); - free(fdt); - return -EINVAL; - } - - free(fdt); - - return 0; -} - static void bootm_print_info(struct image_data *data) { if (data->os_res) - printf("OS image is at " PRINTF_CONVERSION_RESOURCE "-" PRINTF_CONVERSION_RESOURCE "\n", - data->os_res->start, - data->os_res->end); + printf("OS image is at %pa-%pa\n", + &data->os_res->start, + &data->os_res->end); else printf("OS image not yet relocated\n"); - - if (data->initrd_file) { - enum filetype initrd_type = file_name_detect_type(data->initrd_file); - - printf("Loading initrd %s '%s'", - file_type_to_string(initrd_type), - data->initrd_file); - if (initrd_type == filetype_uimage && - data->initrd->header.ih_type == IH_TYPE_MULTI) - printf(", multifile image %d", data->initrd_num); - printf("\n"); - if (data->initrd_res) - printf("initrd is at " PRINTF_CONVERSION_RESOURCE "-" PRINTF_CONVERSION_RESOURCE "\n", - data->initrd_res->start, - data->initrd_res->end); - else - printf("initrd image not yet relocated\n"); - } } -static char *bootm_image_name_and_no(const char *name, int *no) +static int bootm_image_name_and_part(const char *name, char **filename, char **part) { char *at, *ret; if (!name || !*name) - return NULL; - - *no = 0; + return -EINVAL; ret = xstrdup(name); + + *filename = ret; + *part = NULL; + at = strchr(ret, '@'); if (!at) - return ret; + return 0; *at++ = 0; - *no = simple_strtoul(at, NULL, 10); + *part = at; - return ret; + return 0; } /* @@ -380,8 +494,7 @@ int bootm_boot(struct bootm_data *bootm_data) struct image_data *data; struct image_handler *handler; int ret; - enum filetype os_type, initrd_type = filetype_unknown; - enum filetype oftree_type = filetype_unknown; + enum filetype os_type; if (!bootm_data->os_file) { printf("no image given\n"); @@ -390,9 +503,9 @@ int bootm_boot(struct bootm_data *bootm_data) data = xzalloc(sizeof(*data)); - data->os_file = bootm_image_name_and_no(bootm_data->os_file, &data->os_num); - data->oftree_file = bootm_image_name_and_no(bootm_data->oftree_file, &data->oftree_num); - data->initrd_file = bootm_image_name_and_no(bootm_data->initrd_file, &data->initrd_num); + bootm_image_name_and_part(bootm_data->os_file, &data->os_file, &data->os_part); + bootm_image_name_and_part(bootm_data->oftree_file, &data->oftree_part, &data->os_part); + bootm_image_name_and_part(bootm_data->initrd_file, &data->initrd_part, &data->os_part); data->verbose = bootm_data->verbose; data->verify = bootm_data->verify; data->force = bootm_data->force; @@ -415,70 +528,53 @@ int bootm_boot(struct bootm_data *bootm_data) goto err_out; } - if (IS_ENABLED(CONFIG_CMD_BOOTM_INITRD) && data->initrd_file) { - initrd_type = file_name_detect_type(data->initrd_file); - - if ((int)initrd_type < 0) { - printf("could not open %s: %s\n", data->initrd_file, - strerror(-initrd_type)); - ret = (int)initrd_type; + if (IS_ENABLED(CONFIG_BOOTM_FORCE_SIGNED_IMAGES)) { + data->verify = BOOTM_VERIFY_SIGNATURE; + + /* + * When we only allow booting signed images make sure everything + * we boot is in the OS image and not given separately. + */ + data->oftree = NULL; + data->oftree_file = NULL; + data->initrd_file = NULL; + if (os_type != filetype_oftree) { + printf("Signed boot and image is no FIT image, aborting\n"); + ret = -EINVAL; goto err_out; } } - if (IS_ENABLED(CONFIG_OFTREE) && data->oftree_file) { - oftree_type = file_name_detect_type(data->oftree_file); + if (IS_ENABLED(CONFIG_FITIMAGE) && os_type == filetype_oftree) { + struct fit_handle *fit; - if ((int)oftree_type < 0) { - printf("could not open %s: %s\n", data->oftree_file, - strerror(-oftree_type)); - ret = (int) oftree_type; + fit = fit_open(data->os_file, data->os_part, data->verbose, data->verify); + if (IS_ERR(fit)) { + printf("Loading FIT image %s failed with: %s\n", data->os_file, + strerrorp(fit)); + ret = PTR_ERR(fit); goto err_out; } + + data->os_fit = fit; } if (os_type == filetype_uimage) { ret = bootm_open_os_uimage(data); if (ret) { - printf("Loading OS image failed with %s\n", + printf("Loading OS image failed with: %s\n", strerror(-ret)); goto err_out; } } - if (IS_ENABLED(CONFIG_CMD_BOOTM_INITRD) && data->initrd_file) { - if (initrd_type == filetype_uimage) { - ret = bootm_open_initrd_uimage(data); - if (ret) { - printf("loading initrd failed with %s\n", - strerror(-ret)); - goto err_out; - } - } - } - printf("\nLoading %s '%s'", file_type_to_string(os_type), data->os_file); if (os_type == filetype_uimage && data->os->header.ih_type == IH_TYPE_MULTI) - printf(", multifile image %d", data->os_num); + printf(", multifile image %s", data->os_part); printf("\n"); - if (IS_ENABLED(CONFIG_OFTREE)) { - if (data->oftree_file) { - if (oftree_type == filetype_uimage) - ret = bootm_open_oftree_uimage(data); - if (oftree_type == filetype_oftree) - ret = bootm_open_oftree(data); - if (ret) - goto err_out; - } else { - data->of_root_node = of_get_root_node(); - if (bootm_verbose(data) > 1 && data->of_root_node) - printf("using internal devicetree\n"); - } - } - if (data->os_address == UIMAGE_SOME_ADDRESS) data->os_address = UIMAGE_INVALID_ADDRESS; @@ -497,10 +593,10 @@ int bootm_boot(struct bootm_data *bootm_data) printf("Passing control to %s handler\n", handler->name); } + ret = handler->bootm(data); if (data->dryrun) - ret = 0; - else - ret = handler->bootm(data); + printf("Dryrun. Aborted\n"); + err_out: if (data->os_res) release_sdram_region(data->os_res); @@ -512,6 +608,8 @@ err_out: uimage_close(data->initrd); if (data->os) uimage_close(data->os); + if (IS_ENABLED(CONFIG_FITIMAGE) && data->os_fit) + fit_close(data->os_fit); if (data->of_root_node && data->of_root_node != of_get_root_node()) of_delete_node(data->of_root_node); @@ -532,6 +630,8 @@ static int bootm_init(void) globalvar_add_simple("bootm.initrd", NULL); globalvar_add_simple("bootm.initrd.loadaddr", NULL); } + globalvar_add_simple_enum("bootm.verify", (unsigned int *)&bootm_verify_mode, + bootm_verify_names, ARRAY_SIZE(bootm_verify_names)); return 0; } diff --git a/common/date.c b/common/date.c index 6b6b7ab495..129192e232 100644 --- a/common/date.c +++ b/common/date.c @@ -148,3 +148,21 @@ mktime (unsigned int year, unsigned int mon, )*60 + min /* now have minutes */ )*60 + sec; /* finally seconds */ } + +const char *time_str(struct rtc_time *tm) +{ + const char *weekdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" }; + static char buf[128]; + + sprintf(buf, "%s %02d %s %4d %02d:%02d:%02d", + weekdays[tm->tm_wday], + tm->tm_mday, + months[tm->tm_mon], + tm->tm_year + 1900, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); + return buf; +} diff --git a/common/hush.c b/common/hush.c index 084dd858dc..d3f7bf330d 100644 --- a/common/hush.c +++ b/common/hush.c @@ -863,29 +863,29 @@ static int run_list_real(struct p_context *ctx, struct pipe *pi) if ((rpipe->r_mode == RES_IN || rpipe->r_mode == RES_FOR) && (rpipe->next == NULL)) { - syntax(); - return 1; + syntax(); + return 1; } if ((rpipe->r_mode == RES_IN && - (rpipe->next->r_mode == RES_IN && - rpipe->next->progs->argv != NULL))|| - (rpipe->r_mode == RES_FOR && - rpipe->next->r_mode != RES_IN)) { - syntax(); - return 1; + (rpipe->next->r_mode == RES_IN && + rpipe->next->progs->argv != NULL))|| + (rpipe->r_mode == RES_FOR && + rpipe->next->r_mode != RES_IN)) { + syntax(); + return 1; } } for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) { if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL || - pi->r_mode == RES_FOR) { - /* check Ctrl-C */ - if (ctrlc()) - return 1; - flag_restore = 0; - if (!rpipe) { - flag_rep = 0; - rpipe = pi; - } + pi->r_mode == RES_FOR) { + /* check Ctrl-C */ + if (ctrlc()) + return 1; + flag_restore = 0; + if (!rpipe) { + flag_rep = 0; + rpipe = pi; + } } rmode = pi->r_mode; debug("rmode=%d if_code=%d next_if_code=%d skip_more=%d\n", @@ -1865,7 +1865,7 @@ static char * make_string(char ** inp) int run_command(const char *cmd) { - struct p_context ctx; + struct p_context ctx = {}; int ret; initialize_context(&ctx); @@ -1889,7 +1889,7 @@ static int execute_script(const char *path, int argc, char *argv[]) static int source_script(const char *path, int argc, char *argv[]) { - struct p_context ctx; + struct p_context ctx = {}; char *script; int ret; @@ -1918,7 +1918,7 @@ int run_shell(void) { int rcode; struct in_str input; - struct p_context ctx; + struct p_context ctx = {}; int exit = 0; login(); diff --git a/common/image-fit.c b/common/image-fit.c new file mode 100644 index 0000000000..4f5c1f16c8 --- /dev/null +++ b/common/image-fit.c @@ -0,0 +1,584 @@ +/* + * Copyright (C) Jan Lübbe, 2014 + * + * This code is inspired by the U-Boot FIT image code. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#define pr_fmt(fmt) "FIT: " fmt +#include <common.h> +#include <init.h> +#include <boot.h> +#include <libfile.h> +#include <fdt.h> +#include <digest.h> +#include <of.h> +#include <fs.h> +#include <malloc.h> +#include <linux/ctype.h> +#include <asm/byteorder.h> +#include <errno.h> +#include <linux/err.h> +#include <stringlist.h> +#include <rsa.h> +#include <image-fit.h> + +#define FDT_MAX_DEPTH 32 +#define FDT_MAX_PATH_LEN 200 + +#define CHECK_LEVEL_NONE 0 +#define CHECK_LEVEL_HASH 1 +#define CHECK_LEVEL_SIG 2 +#define CHECK_LEVEL_MAX 3 + +static uint32_t dt_struct_advance(struct fdt_header *f, uint32_t dt, int size) +{ + dt += size; + dt = ALIGN(dt, 4); + + if (dt > f->off_dt_struct + f->size_dt_struct) + return 0; + + return dt; +} + +static char *dt_string(struct fdt_header *f, char *strstart, uint32_t ofs) +{ + if (ofs > f->size_dt_strings) + return NULL; + else + return strstart + ofs; +} + +static int of_read_string_list(struct device_node *np, const char *name, struct string_list *sl) +{ + struct property *prop; + const char *s; + + of_property_for_each_string(np, name, prop, s) { + string_list_add(sl, s); + } + + return prop ? 0 : -EINVAL; +} + +static int fit_digest(void *fit, struct digest *digest, + struct string_list *inc_nodes, struct string_list *exc_props, + uint32_t hashed_strings_start, uint32_t hashed_strings_size) +{ + struct fdt_header *fdt = fit; + uint32_t dt_struct; + void *dt_strings; + struct fdt_header f = {}; + int stack[FDT_MAX_DEPTH]; + char path[FDT_MAX_PATH_LEN]; + char *end; + uint32_t tag; + int start = -1; + int depth = -1; + int want = 0; + + f.totalsize = fdt32_to_cpu(fdt->totalsize); + f.off_dt_struct = fdt32_to_cpu(fdt->off_dt_struct); + f.size_dt_struct = fdt32_to_cpu(fdt->size_dt_struct); + f.off_dt_strings = fdt32_to_cpu(fdt->off_dt_strings); + f.size_dt_strings = fdt32_to_cpu(fdt->size_dt_strings); + + if (hashed_strings_start > f.size_dt_strings || + hashed_strings_size > f.size_dt_strings || + hashed_strings_start + hashed_strings_size > f.size_dt_strings) { + pr_err("%s: hashed-strings too large\n", __func__); + return -EINVAL; + } + + dt_struct = f.off_dt_struct; + dt_strings = (void *)fdt + f.off_dt_strings; + + end = path; + *end = '\0'; + + do { + const struct fdt_property *fdt_prop; + const struct fdt_node_header *fnh; + const char *name; + int include = 0; + int stop_at = 0; + int offset = dt_struct; + int maxlen, len; + + tag = be32_to_cpu(*(uint32_t *)(fit + dt_struct)); + + switch (tag) { + case FDT_BEGIN_NODE: + fnh = fit + dt_struct; + name = fnh->name; + maxlen = (unsigned long)fdt + f.off_dt_struct + + f.size_dt_struct - (unsigned long)name; + + len = strnlen(name, maxlen + 1); + if (len > maxlen) + return -ESPIPE; + + dt_struct = dt_struct_advance(&f, dt_struct, + sizeof(struct fdt_node_header) + len + 1); + + depth++; + if (depth == FDT_MAX_DEPTH) + return -ESPIPE; + if (end - path + 2 + len >= FDT_MAX_PATH_LEN) + return -ESPIPE; + if (end != path + 1) + *end++ = '/'; + strcpy(end, name); + end += len; + stack[depth] = want; + if (want == 1) + stop_at = offset; + if (string_list_contains(inc_nodes, path)) + want = 2; + else if (want) + want--; + else + stop_at = offset; + include = want; + + break; + + case FDT_END_NODE: + dt_struct = dt_struct_advance(&f, dt_struct, FDT_TAGSIZE); + + include = want; + want = stack[depth--]; + while (end > path && *--end != '/') + ; + *end = '\0'; + + break; + + case FDT_PROP: + fdt_prop = fit + dt_struct; + len = fdt32_to_cpu(fdt_prop->len); + + name = dt_string(&f, dt_strings, fdt32_to_cpu(fdt_prop->nameoff)); + if (!name) + return -ESPIPE; + + dt_struct = dt_struct_advance(&f, dt_struct, + sizeof(struct fdt_property) + len); + + include = want >= 2; + stop_at = offset; + if (string_list_contains(exc_props, name)) + include = 0; + + break; + + case FDT_NOP: + dt_struct = dt_struct_advance(&f, dt_struct, FDT_TAGSIZE); + + include = want >= 2; + stop_at = offset; + + break; + + case FDT_END: + dt_struct = dt_struct_advance(&f, dt_struct, FDT_TAGSIZE); + + include = 1; + + break; + + default: + pr_err("%s: Unknown tag 0x%08X\n", __func__, tag); + return -EINVAL; + } + + if (!dt_struct) + return -ESPIPE; + + pr_debug("%s: include %d, want %d, offset 0x%x, len 0x%x\n", + path, include, want, offset, dt_struct-offset); + + if (include && start == -1) + start = offset; + + if (!include && start != -1) { + pr_debug("region: 0x%p+0x%x\n", fit + start, offset - start); + digest_update(digest, fit + start, offset - start); + start = -1; + } + } while (tag != FDT_END); + + pr_debug("region: 0x%p+0x%x\n", fit + start, dt_struct - start); + digest_update(digest, fit + start, dt_struct - start); + + pr_debug("strings: 0x%p+0x%x\n", dt_strings+hashed_strings_start, hashed_strings_size); + digest_update(digest, dt_strings + hashed_strings_start, hashed_strings_size); + + return 0; +} + +/* + * The consistency of the FTD structure was already checked by of_unflatten_dtb() + */ +static int fit_verify_signature(struct device_node *sig_node, void *fit) +{ + uint32_t hashed_strings_start, hashed_strings_size; + struct string_list inc_nodes, exc_props; + struct rsa_public_key key = {}; + struct digest *digest; + int sig_len; + const char *algo_name, *key_name, *sig_value; + char *key_path; + struct device_node *key_node; + enum hash_algo algo; + void *hash; + int ret; + + if (of_property_read_string(sig_node, "algo", &algo_name)) { + pr_err("algo not found\n"); + ret = -EINVAL; + goto out; + } + if (strcmp(algo_name, "sha1,rsa2048") == 0) { + algo = HASH_ALGO_SHA1; + } else if (strcmp(algo_name, "sha256,rsa4096") == 0) { + algo = HASH_ALGO_SHA256; + } else { + pr_err("unknown algo %s\n", algo_name); + ret = -EINVAL; + goto out; + } + digest = digest_alloc_by_algo(algo); + if (!digest) { + pr_err("unsupported algo %s\n", algo_name); + ret = -EINVAL; + goto out; + } + + sig_value = of_get_property(sig_node, "value", &sig_len); + if (!sig_value) { + pr_err("signature value not found in %s\n", sig_node->full_name); + ret = -EINVAL; + goto out_free_digest; + } + + if (of_property_read_string(sig_node, "key-name-hint", &key_name)) { + pr_err("key name not found in %s\n", sig_node->full_name); + ret = -EINVAL; + goto out_free_digest; + } + key_path = xasprintf("/signature/key-%s", key_name); + key_node = of_find_node_by_path(key_path); + free(key_path); + if (!key_node) { + pr_info("failed to find key node\n"); + ret = -ENOENT; + goto out_free_digest; + } + + ret = rsa_of_read_key(key_node, &key); + if (ret) { + pr_info("failed to read key in %s\n", key_node->full_name); + ret = -ENOENT; + goto out_free_digest; + } + + if (of_property_read_u32_index(sig_node, "hashed-strings", 0, &hashed_strings_start)) { + pr_err("hashed-strings start not found in %s\n", sig_node->full_name); + ret = -EINVAL; + goto out_free_digest; + } + if (of_property_read_u32_index(sig_node, "hashed-strings", 1, &hashed_strings_size)) { + pr_err("hashed-strings size not found in %s\n", sig_node->full_name); + ret = -EINVAL; + goto out_free_digest; + } + + string_list_init(&inc_nodes); + string_list_init(&exc_props); + + if (of_read_string_list(sig_node, "hashed-nodes", &inc_nodes)) { + pr_err("hashed-nodes property not found in %s\n", sig_node->full_name); + ret = -EINVAL; + goto out_sl; + } + + string_list_add(&exc_props, "data"); + + digest_init(digest); + ret = fit_digest(fit, digest, &inc_nodes, &exc_props, hashed_strings_start, hashed_strings_size); + hash = xzalloc(digest_length(digest)); + digest_final(digest, hash); + + ret = rsa_verify(&key, sig_value, sig_len, hash, algo); + if (ret) { + pr_info("image signature BAD\n"); + ret = -EBADMSG; + } else { + pr_info("image signature OK\n"); + ret = 0; + } + + free(hash); + out_sl: + string_list_free(&inc_nodes); + string_list_free(&exc_props); + out_free_digest: + digest_free(digest); + out: + return ret; +} + +static int fit_verify_hash(struct device_node *hash, const void *data, int data_len) +{ + struct digest *d; + const char *algo; + const char *value_read; + char *value_calc; + int hash_len, ret; + + value_read = of_get_property(hash, "value", &hash_len); + if (!value_read) { + pr_err("%s: \"value\" property not found\n", hash->full_name); + return -EINVAL; + } + + if (of_property_read_string(hash, "algo", &algo)) { + pr_err("%s: \"algo\" property not found\n", hash->full_name); + return -EINVAL; + } + + d = digest_alloc(algo); + if (!d) { + pr_err("%s: unsupported algo %s\n", hash->full_name, algo); + return -EINVAL; + } + + if (hash_len != digest_length(d)) { + pr_err("%s: invalid hash length %d\n", hash->full_name, hash_len); + ret = -EINVAL; + goto err_digest_free; + } + + value_calc = xmalloc(hash_len); + + digest_init(d); + digest_update(d, data, data_len); + digest_final(d, value_calc); + + if (memcmp(value_read, value_calc, hash_len)) { + pr_info("%s: hash BAD\n", hash->full_name); + ret = -EBADMSG; + } else { + pr_info("%s: hash OK\n", hash->full_name); + ret = 0; + } + + free(value_calc); + +err_digest_free: + digest_free(d); + + return ret; +} + +static int fit_open_image(struct fit_handle *handle, const char *unit, const void **outdata, + unsigned long *outsize) +{ + struct device_node *image = NULL, *hash; + const char *type = NULL, *desc= "(no description)"; + const void *data; + int data_len; + int ret = 0; + + image = of_get_child_by_name(handle->root, "images"); + if (!image) + return -ENOENT; + + image = of_get_child_by_name(image, unit); + if (!image) + return -ENOENT; + + of_property_read_string(image, "description", &desc); + pr_info("image '%s': '%s'\n", unit, desc); + + of_property_read_string(image, "type", &type); + if (!type) { + pr_err("No \"type\" property found in %s\n", image->full_name); + return -EINVAL; + } + + data = of_get_property(image, "data", &data_len); + if (!data) { + pr_err("data not found\n"); + return -EINVAL; + } + + if (handle->verify > BOOTM_VERIFY_NONE) { + ret = -EINVAL; + for_each_child_of_node(image, hash) { + if (handle->verbose) + of_print_nodes(hash, 0); + ret = fit_verify_hash(hash, data, data_len); + if (ret < 0) + return ret; + } + } + if (ret < 0) + return ret; + + *outdata = data; + *outsize = data_len; + + return 0; +} + +static int fit_open_configuration(struct fit_handle *handle, const char *name) +{ + struct device_node *conf_node = NULL, *sig_node; + const char *unit, *desc = "(no description)"; + int ret; + + conf_node = of_get_child_by_name(handle->root, "configurations"); + if (!conf_node) + return -ENOENT; + + if (name) { + unit = name; + } else if (of_property_read_string(conf_node, "default", &unit)) { + unit = "conf@1"; + } + + conf_node = of_get_child_by_name(conf_node, unit); + if (!conf_node) { + pr_err("configuration '%s' not found\n", unit); + return -ENOENT; + } + + of_property_read_string(conf_node, "description", &desc); + pr_info("configuration '%s': %s\n", unit, desc); + + if (IS_ENABLED(CONFIG_FITIMAGE_SIGNATURE) && + handle->verify == BOOTM_VERIFY_SIGNATURE) { + ret = -EINVAL; + for_each_child_of_node(conf_node, sig_node) { + if (handle->verbose) + of_print_nodes(sig_node, 0); + ret = fit_verify_signature(sig_node, handle->fit); + if (ret < 0) + return ret; + } + + if (ret < 0) + return ret; + } + + if (of_property_read_string(conf_node, "kernel", &unit) == 0) { + ret = fit_open_image(handle, unit, &handle->kernel, &handle->kernel_size); + if (ret) + return ret; + } else { + return -ENOENT; + } + + if (of_property_read_string(conf_node, "fdt", &unit) == 0) { + ret = fit_open_image(handle, unit, &handle->oftree, &handle->oftree_size); + if (ret) + return ret; + } + + if (of_property_read_string(conf_node, "ramdisk", &unit) == 0) { + ret = fit_open_image(handle, unit, &handle->initrd, &handle->initrd_size); + if (ret) + return ret; + } + + return 0; +} + +struct fit_handle *fit_open(const char *filename, const char *config, bool verbose, + enum bootm_verify verify) +{ + struct fit_handle *handle = NULL; + const char *desc = "(no description)"; + int ret; + + handle = xzalloc(sizeof(struct fit_handle)); + + handle->verbose = verbose; + + ret = read_file_2(filename, &handle->size, &handle->fit, FILESIZE_MAX); + if (ret) { + pr_err("unable to read %s: %s\n", filename, strerror(-ret)); + goto err; + } + + handle->root = of_unflatten_dtb(handle->fit); + if (IS_ERR(handle->root)) { + ret = PTR_ERR(handle->root); + goto err; + } + + handle->verify = verify; + + of_property_read_string(handle->root, "description", &desc); + pr_info("'%s': %s\n", filename, desc); + + ret = fit_open_configuration(handle, config); + if (ret) + goto err; + + return handle; + err: + if (handle->root) + of_delete_node(handle->root); + free(handle->fit); + free(handle); + + return ERR_PTR(ret); +} + +void fit_close(struct fit_handle *handle) +{ + if (handle->root) + of_delete_node(handle->root); + if (handle->fit) + free(handle->fit); + free(handle); +} + +#ifdef CONFIG_SANDBOX +static int do_bootm_sandbox_fit(struct image_data *data) +{ + struct fit_handle *handle; + handle = fit_open(data->os_file, data->os_part, data->verbose); + if (handle) + fit_close(handle); + return 0; +} + +static struct image_handler sandbox_fit_handler = { + .name = "FIT image", + .bootm = do_bootm_sandbox_fit, + .filetype = filetype_oftree, +}; + +static int sandbox_fit_register(void) +{ + return register_image_handler(&sandbox_fit_handler); +} +late_initcall(sandbox_fit_register); +#endif diff --git a/common/partitions.c b/common/partitions.c index 4f50bfeff1..82ec508e64 100644 --- a/common/partitions.c +++ b/common/partitions.c @@ -71,20 +71,14 @@ static int register_one_partition(struct block_device *blk, if (!part->name[0]) return 0; - partition_name = asprintf("%s.%s", blk->cdev.name, part->name); - if (!partition_name) - return -ENOMEM; - - dev_dbg(blk->dev, "Registering partition %s on drive %s\n", - partition_name, blk->cdev.name); - cdev = devfs_add_partition(blk->cdev.name, - start, size, 0, partition_name); - - if (IS_ERR(cdev)) - dev_warn(blk->dev, "Registering partition %s on drive %s failed\n", - partition_name, blk->cdev.name); + partition_name = xasprintf("%s.%s", blk->cdev.name, part->name); + ret = devfs_create_link(cdev, partition_name); + if (ret) + dev_warn(blk->dev, "Failed to create link from %s to %s\n", + partition_name, blk->cdev.name); + free(partition_name); - ret = 0; + return 0; out: free(partition_name); return ret; diff --git a/common/partitions/Kconfig b/common/partitions/Kconfig index 90238ad382..be9405a649 100644 --- a/common/partitions/Kconfig +++ b/common/partitions/Kconfig @@ -16,6 +16,7 @@ config PARTITION_DISK_DOS config PARTITION_DISK_EFI depends on PARTITION_DISK select CRC32 + select PRINTF_UUID bool "EFI: GPT partition support" help Add support to handle partitions in GUID Partition Table style. diff --git a/common/partitions/efi.c b/common/partitions/efi.c index 61abf00050..a9945dd9eb 100644 --- a/common/partitions/efi.c +++ b/common/partitions/efi.c @@ -457,6 +457,7 @@ static void efi_partition(void *buf, struct block_device *blk, pentry->size = le64_to_cpu(ptes[i].ending_lba) - pentry->first_sec; pentry->size++; part_set_efi_name(&ptes[i], pentry->name); + snprintf(pentry->partuuid, sizeof(pentry->partuuid), "%pUl", &ptes[i].unique_partition_guid); pd->used_entries++; } diff --git a/common/poller.c b/common/poller.c index 496636d1d7..32795b641f 100644 --- a/common/poller.c +++ b/common/poller.c @@ -32,6 +32,7 @@ int poller_unregister(struct poller_struct *poller) if (!poller->registered) return -ENODEV; + list_del(&poller->list); poller->registered = 0; @@ -42,12 +43,14 @@ static void poller_async_callback(struct poller_struct *poller) { struct poller_async *pa = container_of(poller, struct poller_async, poller); + if (!pa->active) + return; + if (get_time_ns() < pa->end) return; + pa->active = 0; pa->fn(pa->ctx); - pa->fn = NULL; - poller_unregister(&pa->poller); } /* @@ -62,7 +65,9 @@ static void poller_async_callback(struct poller_struct *poller) */ int poller_async_cancel(struct poller_async *pa) { - return poller_unregister(&pa->poller); + pa->active = 0; + + return 0; } /* @@ -80,13 +85,26 @@ int poller_call_async(struct poller_async *pa, uint64_t delay_ns, void (*fn)(void *), void *ctx) { pa->ctx = ctx; - pa->poller.func = poller_async_callback; pa->end = get_time_ns() + delay_ns; pa->fn = fn; + pa->active = 1; + + return 0; +} + +int poller_async_register(struct poller_async *pa) +{ + pa->poller.func = poller_async_callback; + pa->active = 0; return poller_register(&pa->poller); } +int poller_async_unregister(struct poller_async *pa) +{ + return poller_unregister(&pa->poller); +} + void poller_call(void) { struct poller_struct *poller, *tmp; diff --git a/common/startup.c b/common/startup.c index 4a303b297a..093a23ba08 100644 --- a/common/startup.c +++ b/common/startup.c @@ -60,6 +60,11 @@ static int mount_root(void) mount("none", "efivarfs", "/efivars", NULL); } + if (IS_ENABLED(CONFIG_FS_PSTORE)) { + mkdir("/pstore", 0); + mount("none", "pstore", "/pstore", NULL); + } + return 0; } fs_initcall(mount_root); diff --git a/common/state.c b/common/state.c index ec72dbd9a0..3e95efd911 100644 --- a/common/state.c +++ b/common/state.c @@ -66,6 +66,7 @@ enum state_variable_type { STATE_TYPE_ENUM, STATE_TYPE_U8, STATE_TYPE_U32, + STATE_TYPE_S32, STATE_TYPE_MAC, STATE_TYPE_STRING, }; @@ -209,8 +210,8 @@ static struct state_variable *state_uint8_create(struct state *state, return &su32->var; } -static struct state_variable *state_uint32_create(struct state *state, - const char *name, struct device_node *node) +static struct state_variable *state_int32_create(struct state *state, + const char *name, struct device_node *node, const char *format) { struct state_uint32 *su32; struct param_d *param; @@ -218,7 +219,7 @@ static struct state_variable *state_uint32_create(struct state *state, su32 = xzalloc(sizeof(*su32)); param = dev_add_param_int(&state->dev, name, state_set_dirty, - NULL, &su32->value, "%u", state); + NULL, &su32->value, format, state); if (IS_ERR(param)) { free(su32); return ERR_CAST(param); @@ -231,6 +232,18 @@ static struct state_variable *state_uint32_create(struct state *state, return &su32->var; } +static struct state_variable *state_uint32_create(struct state *state, + const char *name, struct device_node *node) +{ + return state_int32_create(state, name, node, "%u"); +} + +static struct state_variable *state_sint32_create(struct state *state, + const char *name, struct device_node *node) +{ + return state_int32_create(state, name, node, "%d"); +} + /* * enum32 */ @@ -605,6 +618,12 @@ static struct variable_type types[] = { .export = state_string_export, .import = state_string_import, .create = state_string_create, + }, { + .type = STATE_TYPE_S32, + .type_name = "int32", + .export = state_uint32_export, + .import = state_uint32_import, + .create = state_sint32_create, }, }; @@ -1564,6 +1583,10 @@ static int state_backend_raw_file_init_digest(struct state *state, struct state_ if (!p) /* does not exist */ return 0; + ret = of_property_read_string(state->root, "algo", &algo); + if (ret) + return ret; + if (!IS_ENABLED(CONFIG_STATE_CRYPTO)) { dev_err(&state->dev, "algo %s specified, but crypto support for state framework (CONFIG_STATE_CRYPTO) not enabled.\n", @@ -1571,10 +1594,6 @@ static int state_backend_raw_file_init_digest(struct state *state, struct state_ return -EINVAL; } - ret = of_property_read_string(state->root, "algo", &algo); - if (ret) - return ret; - ret = keystore_get_secret(state->name, &key, &key_len); if (ret == -ENOENT) /* -ENOENT == does not exist */ return -EPROBE_DEFER; diff --git a/common/uimage.c b/common/uimage.c index a7011a7623..59d7b65c90 100644 --- a/common/uimage.c +++ b/common/uimage.c @@ -354,10 +354,9 @@ static int uimage_sdram_flush(void *buf, unsigned int len) uimage_resource = request_sdram_region("uimage", start, size); if (!uimage_resource) { - printf("unable to request SDRAM " - PRINTF_CONVERSION_RESOURCE "-" - PRINTF_CONVERSION_RESOURCE "\n", - start, start + size - 1); + resource_size_t prsize = start + size - 1; + printf("unable to request SDRAM %pa - %pa\n", + &start, &prsize); return -ENOMEM; } } |