[PATCH 00/11] boot: Allow lmb-allocation of compression buffer with booti

From: Simon Glass <sjg@chromium.org> ARM 'booti' images (as created by Linux 'make Image') typically need to be compressed. At present the decompression buffer is described by a pair of environment variables: kernel_comp_addr_r and kernel_comp_size This is quite inflexible, since the buffer size and location must be pre-set in the environment, before the OS size is known. For the EFI app, it is quite difficult to work with a fixed buffer. It is not in control of the memory map so it may be that the requested buffer address is not available. This series updates the boot implementation to allow the variables to be omitted, with lmb allocations done in this case. This resolves the problem in the EFI app, with kernels which come from extlinux.conf When the EFI app loads another app, a similar problem is present, but with the EFI bootmeth, also resolved in this series. Simon Glass (11): efi: Allocate the kernel buffer when running in the app boot: Add more debugging to bootm code boot: Add more debugging to image-baord and image-fdt boot: Read environment variables at start boot: Move compression-buffer handling to its own function boot: Add an OS-size field in bootm_info boot: Record the kernel size in pxe_utils boot: Check OS size when decompressing with booti boot: Support lmb reservation in resolve_os_comp_buf() boot: Correct a comment in image_setup_libfdt() boot: Emit an event just before starting the OS boot/bootm.c | 174 +++++++++++++++++++++++++++++++++++--------- boot/bootm_final.c | 10 +++ boot/bootmeth_efi.c | 25 ++++++- boot/image-board.c | 16 +++- boot/image-fdt.c | 5 +- boot/pxe_utils.c | 23 ++++-- cmd/booti.c | 1 + cmd/bootm.c | 1 + common/event.c | 1 + include/bootm.h | 18 +++++ include/event.h | 8 ++ include/pxe_utils.h | 2 + 12 files changed, 238 insertions(+), 46 deletions(-) -- 2.43.0 base-commit: c9dbf55855b3b0ca143e71e9204b74fc88117808 branch: loadm

From: Simon Glass <sjg@chromium.org> The app cannot know the address to use in advance, so does not have a value for kernel_addr_r in its environment, if it even has an environment. Allocate memory for the kernel instead. Limit it to 64MB which seems like plenty for the distros out there. It is enough to load a full kernel if needed. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootmeth_efi.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c index 78fb84334e5..6903734c546 100644 --- a/boot/bootmeth_efi.c +++ b/boot/bootmeth_efi.c @@ -281,13 +281,34 @@ static int distro_efi_read_bootflow(struct udevice *dev, struct bootflow *bflow) static int distro_efi_boot(struct udevice *dev, struct bootflow *bflow) { + int size = SZ_1G; ulong kernel, fdt; int ret; log_debug("distro EFI boot\n"); - kernel = env_get_hex("kernel_addr_r", 0); + if (IS_ENABLED(CONFIG_EFI_APP)) { + struct efi_priv *priv = efi_get_priv(); + efi_status_t eret; + void *ptr; + + /* + * we don't expect the app to be larger than 64M and in fact, + * for Linux, it is typically only a few MB + */ + size = SZ_64M; + ptr = efi_malloc(priv, size, &eret); + if (!ptr) { + log_err("Out of memory for image (%x bytes): err=%lx\n", + size, eret); + return -ENOMEM; + } + kernel = map_to_sysmem(ptr); + } else { + kernel = env_get_hex("kernel_addr_r", 0); + } + if (!bootmeth_uses_network(bflow)) { - ret = efiload_read_file(bflow, kernel, SZ_1G); + ret = efiload_read_file(bflow, kernel, size); if (ret) return log_msg_ret("read", ret); -- 2.43.0

From: Simon Glass <sjg@chromium.org> The bootm code handles a large number of variables. It is helpful to be able to see which code paths are taken, when something doesn't work as expected. Add some debugging which can be enabled if needed. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootm.c | 60 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/boot/bootm.c b/boot/bootm.c index b7c7bf11f0a..9626fff13b0 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -4,6 +4,8 @@ * Wolfgang Denk, DENX Software Engineering, wd@denx.de. */ +#define LOG_CATEGORY LOGC_BOOT + #ifndef USE_HOSTCC #include <bootm.h> #include <bootstage.h> @@ -132,6 +134,8 @@ static int boot_get_kernel(const char *addr_fit, struct bootm_headers *images, const void *boot_img; const void *vendor_boot_img; #endif + + log_debug("addr_fit '%s'\n", addr_fit); img_addr = genimg_get_kernel_addr_fit(addr_fit, &fit_uname_config, &fit_uname_kernel); @@ -188,6 +192,7 @@ static int boot_get_kernel(const char *addr_fit, struct bootm_headers *images, #endif #if CONFIG_IS_ENABLED(FIT) case IMAGE_FORMAT_FIT: + log_debug("fit: fit_image_load()\n"); os_noffset = fit_image_load(images, img_addr, &fit_uname_kernel, &fit_uname_config, IH_ARCH_DEFAULT, IH_TYPE_KERNEL, @@ -213,6 +218,7 @@ static int boot_get_kernel(const char *addr_fit, struct bootm_headers *images, case IMAGE_FORMAT_ANDROID: { int ret; + log_debug("android: get_kernel()\n"); boot_img = buf; vendor_boot_img = NULL; if (IS_ENABLED(CONFIG_CMD_ABOOTIMG)) { @@ -232,6 +238,7 @@ static int boot_get_kernel(const char *addr_fit, struct bootm_headers *images, } #endif case IMAGE_FORMAT_BOOTI: + log_debug("booti\n"); *os_data = img_addr; break; default: @@ -368,6 +375,7 @@ static int bootm_find_os(struct bootm_info *bmi) switch (genimg_get_format(os_hdr)) { #if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) case IMAGE_FORMAT_LEGACY: + log_debug("legacy"); images.os.type = image_get_type(os_hdr); images.os.comp = image_get_comp(os_hdr); images.os.os = image_get_os(os_hdr); @@ -379,6 +387,7 @@ static int bootm_find_os(struct bootm_info *bmi) #endif #if CONFIG_IS_ENABLED(FIT) case IMAGE_FORMAT_FIT: + log_debug("fit"); if (fit_image_get_type(images.fit_hdr_os, images.fit_noffset_os, &images.os.type)) { @@ -421,6 +430,7 @@ static int bootm_find_os(struct bootm_info *bmi) #endif #ifdef CONFIG_ANDROID_BOOT_IMAGE case IMAGE_FORMAT_ANDROID: + log_debug("android"); boot_img = os_hdr; vendor_boot_img = NULL; if (IS_ENABLED(CONFIG_CMD_ABOOTIMG)) { @@ -441,6 +451,7 @@ static int bootm_find_os(struct bootm_info *bmi) break; #endif case IMAGE_FORMAT_BOOTI: + log_debug("booti"); if (IS_ENABLED(CONFIG_CMD_BOOTI)) { if (found_booti_os(IH_COMP_NONE)) return 1; @@ -454,6 +465,8 @@ static int bootm_find_os(struct bootm_info *bmi) int comp; comp = image_decomp_type(os_hdr, 2); + log_debug("booti decomp: %s\n", + genimg_get_comp_name(comp)); if (comp != IH_COMP_NONE) { if (found_booti_os(comp)) return 1; @@ -554,9 +567,11 @@ int bootm_find_images(ulong img_addr, const char *conf_ramdisk, } /* find ramdisk */ + log_debug("ramdisk\n"); ret = boot_get_ramdisk(select, &images, IH_INITRD_ARCH, &images.rd_start, &images.rd_end); if (ret && ret != -ENOPKG) { + log_debug("ramdisk err %d\n", ret); puts("Ramdisk image is corrupt or invalid\n"); return 1; } @@ -566,6 +581,7 @@ int bootm_find_images(ulong img_addr, const char *conf_ramdisk, return 1; if (CONFIG_IS_ENABLED(OF_LIBFDT)) { + log_debug("fdt\n"); buf = map_sysmem(img_addr, 0); /* find flattened device tree */ @@ -587,6 +603,7 @@ int bootm_find_images(ulong img_addr, const char *conf_ramdisk, #if CONFIG_IS_ENABLED(FIT) if (IS_ENABLED(CONFIG_FPGA)) { + log_debug("fpga"); /* find bitstreams */ ret = boot_get_fpga(&images); if (ret) { @@ -702,6 +719,9 @@ static int bootm_load_os(struct bootm_info *bmi, int boot_progress) ulong decomp_len; int err; + log_debug("load_os type '%s' comp '%s'\n", + genimg_get_os_short_name(os.type), + genimg_get_comp_short_name(os.comp)); /* * For a "noload" compressed kernel we need to allocate a buffer large * enough to decompress in to and use that as the load address now. @@ -1066,37 +1086,50 @@ int bootm_run_states(struct bootm_info *bmi, int states) * Work through the states and see how far we get. We stop on * any error. */ - if (states & BOOTM_STATE_START) + if (states & BOOTM_STATE_START) { + log_debug("start\n"); ret = bootm_start(); + } - if (states & BOOTM_STATE_RESTART) + if (states & BOOTM_STATE_RESTART) { + log_debug("restart\n"); ret = bootm_restart(); + } - if (!ret && (states & BOOTM_STATE_PRE_LOAD)) + if (!ret && (states & BOOTM_STATE_PRE_LOAD)) { + log_debug("pre_load\n"); ret = bootm_pre_load(bmi->addr_img); + } - if (!ret && (states & BOOTM_STATE_FINDOS)) + if (!ret && (states & BOOTM_STATE_FINDOS)) { + log_debug("findos\n"); ret = bootm_find_os(bmi); + } if (!ret && (states & BOOTM_STATE_FINDOTHER)) { ulong img_addr; img_addr = bmi->addr_img ? hextoul(bmi->addr_img, NULL) : image_load_addr; + log_debug("findother\n"); ret = bootm_find_other(img_addr, bmi->conf_ramdisk, bmi->conf_fdt); } if (IS_ENABLED(CONFIG_MEASURED_BOOT) && !ret && - (states & BOOTM_STATE_MEASURE)) + (states & BOOTM_STATE_MEASURE)) { + log_debug("measure\n"); bootm_measure(images); + } /* Load the OS */ if (!ret && (states & BOOTM_STATE_LOADOS) && !images->no_os) { + log_debug("loados\n"); if (IS_ENABLED(CONFIG_EVENT)) { struct event_os_load data; data.addr = images->os.load; + log_debug("notify EVT_BOOT_OS_ADDR\n"); ret = event_notify(EVT_BOOT_OS_ADDR, &data, sizeof(data)); if (ret) @@ -1114,6 +1147,7 @@ int bootm_run_states(struct bootm_info *bmi, int states) if (!ret && (states & BOOTM_STATE_RAMDISK)) { ulong rd_len = images->rd_end - images->rd_start; + log_debug("ramdisk\n"); ret = boot_ramdisk_high(images->rd_start, rd_len, &images->initrd_start, &images->initrd_end); @@ -1125,6 +1159,7 @@ int bootm_run_states(struct bootm_info *bmi, int states) #endif #if CONFIG_IS_ENABLED(OF_LIBFDT) && CONFIG_IS_ENABLED(LMB) if (!ret && (states & BOOTM_STATE_FDT)) { + log_debug("fdt\n"); boot_fdt_add_mem_rsv_regions(images->ft_addr); ret = boot_relocate_fdt(&images->ft_addr, &images->ft_len); } @@ -1148,12 +1183,18 @@ int bootm_run_states(struct bootm_info *bmi, int states) } /* Call various other states that are not generally used */ - if (!ret && (states & BOOTM_STATE_OS_CMDLINE)) + if (!ret && (states & BOOTM_STATE_OS_CMDLINE)) { + log_debug("cmdline\n"); ret = boot_fn(BOOTM_STATE_OS_CMDLINE, bmi); - if (!ret && (states & BOOTM_STATE_OS_BD_T)) + } + if (!ret && (states & BOOTM_STATE_OS_BD_T)) { + log_debug("bd_t\n"); ret = boot_fn(BOOTM_STATE_OS_BD_T, bmi); + } if (!ret && (states & BOOTM_STATE_OS_PREP)) { int flags = 0; + + log_debug("prep\n"); /* For Linux OS do all substitutions at console processing */ if (images->os.os == IH_OS_LINUX) flags = BOOTM_CL_ALL; @@ -1171,6 +1212,7 @@ int bootm_run_states(struct bootm_info *bmi, int states) if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) { char *cmd_list = env_get("fakegocmd"); + log_debug("fake_go\n"); ret = boot_selected_os(BOOTM_STATE_OS_FAKE_GO, bmi, boot_fn); if (!ret && cmd_list) ret = run_command_list(cmd_list, -1, 0); @@ -1184,8 +1226,10 @@ int bootm_run_states(struct bootm_info *bmi, int states) } /* Now run the OS! We hope this doesn't return */ - if (!ret && (states & BOOTM_STATE_OS_GO)) + if (!ret && (states & BOOTM_STATE_OS_GO)) { + log_debug("go\n"); ret = boot_selected_os(BOOTM_STATE_OS_GO, bmi, boot_fn); + } /* Deal with any fallout */ err: -- 2.43.0

From: Simon Glass <sjg@chromium.org> Add some debugging to these files, which can be enabled if needed. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/image-board.c | 16 +++++++++++++++- boot/image-fdt.c | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/boot/image-board.c b/boot/image-board.c index 4b285587cd9..9d75def96e2 100644 --- a/boot/image-board.c +++ b/boot/image-board.c @@ -8,6 +8,8 @@ * Wolfgang Denk, DENX Software Engineering, wd@denx.de. */ +#define LOG_CATEGORY LOGC_BOOT + #include <config.h> #include <bootstage.h> #include <cpu_func.h> @@ -309,6 +311,8 @@ static int select_ramdisk(struct bootm_headers *images, const char *select, u8 a ulong rd_addr = 0; char *buf; + log_debug("select '%s' arch %s\n", select, + genimg_get_arch_short_name(arch)); if (CONFIG_IS_ENABLED(FIT)) { fit_uname_config = images->fit_uname_cfg; fit_uname_ramdisk = NULL; @@ -338,6 +342,8 @@ static int select_ramdisk(struct bootm_headers *images, const char *select, u8 a fit_uname_ramdisk, rd_addr); done_select = true; } + + log_debug("FIT select done %d\n", done_select); } } if (!done_select) { @@ -386,6 +392,7 @@ static int select_ramdisk(struct bootm_headers *images, const char *select, u8 a break; case IMAGE_FORMAT_FIT: if (CONFIG_IS_ENABLED(FIT)) { + log_debug("ramdisk fit load"); rd_noffset = fit_image_load(images, rd_addr, &fit_uname_ramdisk, &fit_uname_config, @@ -411,6 +418,7 @@ static int select_ramdisk(struct bootm_headers *images, const char *select, u8 a void *vendor_boot_img = map_sysmem(get_avendor_bootimg_addr(), 0); void *ramdisk_img; + log_debug("android abootimg"); if (init_boot_img == -1) ramdisk_img = map_sysmem(boot_img, 0); else @@ -423,6 +431,7 @@ static int select_ramdisk(struct bootm_headers *images, const char *select, u8 a } else { void *ptr = map_sysmem(images->os.start, 0); + log_debug("android other"); ret = android_image_get_ramdisk(ptr, NULL, rd_datap, rd_lenp); unmap_sysmem(ptr); } @@ -451,6 +460,7 @@ static int select_ramdisk(struct bootm_headers *images, const char *select, u8 a *rd_datap = rd_addr; done = true; } + log_debug("ramdisk raw done %d\n", done); } if (!done) { @@ -468,14 +478,17 @@ int boot_get_ramdisk(char const *select, struct bootm_headers *images, ulong rd_data, rd_len; int ret; + log_debug("ramdisk select '%s' arch %s\n", select, + genimg_get_arch_short_name(arch)); /* * Look for a '-' which indicates to ignore the * ramdisk argument */ if (select && strcmp(select, "-") == 0) { - debug("## Skipping init Ramdisk\n"); + log_debug("## Skipping init Ramdisk\n"); return -ENOPKG; } else if (select || genimg_has_config(images)) { + log_debug("select_ramdisk\n"); ret = select_ramdisk(images, select, arch, &rd_data, &rd_len); if (ret) goto err; @@ -497,6 +510,7 @@ int boot_get_ramdisk(char const *select, struct bootm_headers *images, } } else { /* no initrd image */ + log_debug("no ramdisk\n"); ret = -ENOPKG; goto err; } diff --git a/boot/image-fdt.c b/boot/image-fdt.c index 6859a7c848c..3f779ea4dc7 100644 --- a/boot/image-fdt.c +++ b/boot/image-fdt.c @@ -8,6 +8,8 @@ * Wolfgang Denk, DENX Software Engineering, wd@denx.de. */ +#define LOG_CATEGORY LOGC_BOOT + #include <command.h> #include <fdt_support.h> #include <fdtdec.h> @@ -567,6 +569,7 @@ int image_setup_libfdt(struct bootm_headers *images, void *blob, bool lmb) ulong *initrd_end = &images->initrd_end; int ret, fdt_ret, of_size; + log_debug("fixup fdt at %p lmb %d\n", blob, lmb); if (IS_ENABLED(CONFIG_OF_ENV_SETUP)) { const char *fdt_fixup; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Some environment variables affect the operation of bootm. Reading these variables is buried within the boot code at present. Ideally this information should be in struct bootm_info so that it can be provided directly, without needing the environment variables. Also it should support allocation if the variables are not provided. As a first step towards this, add an explicit read of the variables, storing the values. For now this only covers kernel_comp_addr_r and kernel_comp_size Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootm.c | 17 ++++++++++++----- cmd/booti.c | 1 + cmd/bootm.c | 1 + include/bootm.h | 16 ++++++++++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/boot/bootm.c b/boot/bootm.c index 9626fff13b0..8c51f71ed76 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -310,7 +310,7 @@ static int bootm_pre_load(const char *addr_str) return ret; } -static int found_booti_os(enum image_comp_t comp) +static int found_booti_os(struct bootm_info *bmi, enum image_comp_t comp) { images.os.load = images.os.image_start; images.os.type = IH_TYPE_KERNEL; @@ -325,8 +325,8 @@ static int found_booti_os(enum image_comp_t comp) images.os.load, images.os.image_start, images.os.image_len, images.ep, images.os.os, images.os.comp); if (comp != IH_COMP_NONE) { - images.os.load = env_get_hex("kernel_comp_addr_r", 0); - images.os.image_len = env_get_ulong("kernel_comp_size", 16, 0); + images.os.load = bmi->kern_comp_addr; + images.os.image_len = bmi->kern_comp_size; if (!images.os.load || !images.os.image_len) { puts("kernel_comp_addr_r or kernel_comp_size is not provided!\n"); return -ENOTSUPP; @@ -453,7 +453,7 @@ static int bootm_find_os(struct bootm_info *bmi) case IMAGE_FORMAT_BOOTI: log_debug("booti"); if (IS_ENABLED(CONFIG_CMD_BOOTI)) { - if (found_booti_os(IH_COMP_NONE)) + if (found_booti_os(bmi, IH_COMP_NONE)) return 1; ep_found = true; break; @@ -468,7 +468,7 @@ static int bootm_find_os(struct bootm_info *bmi) log_debug("booti decomp: %s\n", genimg_get_comp_name(comp)); if (comp != IH_COMP_NONE) { - if (found_booti_os(comp)) + if (found_booti_os(bmi, comp)) return 1; ep_found = true; } @@ -1315,6 +1315,12 @@ int booti_run(struct bootm_info *bmi) BOOTM_STATE_LOADOS); } +void bootm_read_env(struct bootm_info *bmi) +{ + bmi->kern_comp_addr = env_get_hex("kernel_comp_addr_r", 0); + bmi->kern_comp_size = env_get_ulong("kernel_comp_size", 16, 0); +} + int bootm_boot_start(ulong addr, const char *cmdline) { char addr_str[BOOTM_STRLEN]; @@ -1340,6 +1346,7 @@ int bootm_boot_start(ulong addr, const char *cmdline) return ret; } bootm_init(&bmi); + bootm_read_env(&bmi); bootm_set_addr_img(&bmi, addr, addr_str); bmi.cmd_name = "bootm"; ret = bootm_run_states(&bmi, states); diff --git a/cmd/booti.c b/cmd/booti.c index c1a61e4752d..bfbe530cb15 100644 --- a/cmd/booti.c +++ b/cmd/booti.c @@ -110,6 +110,7 @@ int do_booti(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) argc--; argv++; bootm_init(&bmi); + bootm_read_env(&bmi); if (argc) bmi.addr_img = argv[0]; if (argc > 1) diff --git a/cmd/bootm.c b/cmd/bootm.c index 23b3d56c101..cb86f576a25 100644 --- a/cmd/bootm.c +++ b/cmd/bootm.c @@ -159,6 +159,7 @@ int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) } bootm_init(&bmi); + bootm_read_env(&bmi); if (argc) bmi.addr_img = argv[0]; if (argc > 1) diff --git a/include/bootm.h b/include/bootm.h index 7420c657293..b24148c394d 100644 --- a/include/bootm.h +++ b/include/bootm.h @@ -58,6 +58,10 @@ enum bootm_final_t { * @argv: NULL-terminated list of arguments, or NULL if there are no arguments * @ignore_bootm_len: Ignore the value CONFIG_SYS_BOOTM_LEN and use 10x the * compressed length as the maximum uncompressed size + * @kern_comp_addr: Address to decompress the kernel to, if needed. If 0, space + * is reserved using lmb and this value is updated + * @kern_comp_size: Maximum size of the decompressed kernel. If 0, the size is + * calculated based on 4x the size of the kernel, up to a limit of 1G * * For zboot: * @bzimage_addr: Address of the bzImage to boot, or 0 if the image has already @@ -84,6 +88,8 @@ struct bootm_info { int argc; char *const *argv; bool ignore_bootm_len; + ulong kern_comp_addr; + ulong kern_comp_size; /* zboot items */ #ifdef CONFIG_X86 @@ -174,6 +180,16 @@ static inline ulong bootm_len(void) return 0; } +/** + * bootm_read_env() - Read environment variables used during the boot + * + * If bootm needs to decompress a kernel, it can use the 'kernel_comp_addr_r' + * and 'kernel_comp_size' variables to specify a region to decompress into. + * Call this function after bootm_init() to set up these variables. Otherwise, + * space will be reserved using lmb + */ +void bootm_read_env(struct bootm_info *bmi); + /** * bootm_init() - Set up a bootm_info struct with useful defaults * -- 2.43.0

From: Simon Glass <sjg@chromium.org> Before expanding this code, create a new function to hold it. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootm.c | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/boot/bootm.c b/boot/bootm.c index 8c51f71ed76..dac251cfb29 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -310,8 +310,31 @@ static int bootm_pre_load(const char *addr_str) return ret; } +/** + * resolve_os_comp_buf() - Figure out where to decompress OS to + * + * @bmi: Bootm info + * Return: 0 if OK, -ENOTSUPP if the required env variables are not set, -EXDEV + * if the memory region is already reserved + */ +static int resolve_os_comp_buf(struct bootm_info *bmi) +{ + images.os.load = bmi->kern_comp_addr; + images.os.image_len = bmi->kern_comp_size; + if (!images.os.load || !images.os.image_len) { + puts("kernel_comp_addr_r or kernel_comp_size is not provided!\n"); + return -ENOTSUPP; + } + if (lmb_reserve(images.os.load, images.os.image_len) < 0) + return -EXDEV; + + return 0; +} + static int found_booti_os(struct bootm_info *bmi, enum image_comp_t comp) { + int ret; + images.os.load = images.os.image_start; images.os.type = IH_TYPE_KERNEL; images.os.os = IH_OS_LINUX; @@ -324,16 +347,15 @@ static int found_booti_os(struct bootm_info *bmi, enum image_comp_t comp) log_debug("load %lx start %lx len %lx ep %lx os %x comp %x\n", images.os.load, images.os.image_start, images.os.image_len, images.ep, images.os.os, images.os.comp); - if (comp != IH_COMP_NONE) { - images.os.load = bmi->kern_comp_addr; - images.os.image_len = bmi->kern_comp_size; - if (!images.os.load || !images.os.image_len) { - puts("kernel_comp_addr_r or kernel_comp_size is not provided!\n"); - return -ENOTSUPP; - } - if (lmb_reserve(images.os.load, images.os.image_len) < 0) - return -EXDEV; - } + if (!comp) + return 0; + + ret = resolve_os_comp_buf(bmi); + if (ret) + return log_msg_ret("fbo", ret); + + images.os.load = bmi->kern_comp_addr; + images.os.image_len = bmi->kern_comp_size; return 0; } -- 2.43.0

From: Simon Glass <sjg@chromium.org> The size of the loaded OS file can be useful when decompressing, or to check that the. FIT image matches its size. Add an os_size field for this in struct bootm_info so it can be recorded. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootm.c | 5 +++-- include/bootm.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/boot/bootm.c b/boot/bootm.c index dac251cfb29..109bcb75142 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -1276,9 +1276,10 @@ int boot_run(struct bootm_info *bmi, const char *cmd, int extra_states) states |= BOOTM_STATE_RAMDISK; states |= extra_states; - log_debug("cmd '%s' states %x addr_img '%s' conf_ramdisk '%s' conf_fdt '%s' images %p\n", + log_debug("cmd '%s' states %x addr_img '%s' conf_ramdisk '%s' " + "conf_fdt '%s' os_size %lx images %p\n", cmd, states, bmi->addr_img, bmi->conf_ramdisk, bmi->conf_fdt, - bmi->images); + bmi->os_size, bmi->images); return bootm_run_states(bmi, states); } diff --git a/include/bootm.h b/include/bootm.h index b24148c394d..b026e1dd80d 100644 --- a/include/bootm.h +++ b/include/bootm.h @@ -62,6 +62,7 @@ enum bootm_final_t { * is reserved using lmb and this value is updated * @kern_comp_size: Maximum size of the decompressed kernel. If 0, the size is * calculated based on 4x the size of the kernel, up to a limit of 1G + * @os_size: Size of the loaded OS image in bytes, 0 if not loaded/not known * * For zboot: * @bzimage_addr: Address of the bzImage to boot, or 0 if the image has already @@ -88,6 +89,7 @@ struct bootm_info { int argc; char *const *argv; bool ignore_bootm_len; + ulong os_size; ulong kern_comp_addr; ulong kern_comp_size; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Write the kernel size into the bootm_info so it can be used as needed. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/pxe_utils.c | 23 +++++++++++++++++------ include/pxe_utils.h | 2 ++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/boot/pxe_utils.c b/boot/pxe_utils.c index 2970d1fa2ed..13fd815451d 100644 --- a/boot/pxe_utils.c +++ b/boot/pxe_utils.c @@ -543,6 +543,7 @@ static int label_process_fdt(struct pxe_context *ctx, struct pxe_label *label, * @kern_addr_str: String containing kernel address and possible FIT * configuration (cannot be NULL) * @kern_addr: Kernel address (cannot be 0) + * @kern_size: Kernel size in bytes * @initrd_addr: String containing initrd address (0 if none) * @initrd_size: initrd size (only used if @initrd_addr) * @initrd_str: initrd string to process (only used if @initrd_addr) @@ -552,7 +553,7 @@ static int label_process_fdt(struct pxe_context *ctx, struct pxe_label *label, * returned, or -ve error value on error */ static int label_run_boot(struct pxe_context *ctx, struct pxe_label *label, - char *kern_addr_str, ulong kern_addr, + char *kern_addr_str, ulong kern_addr, ulong kern_size, ulong initrd_addr, ulong initrd_size, char *initrd_str, const char *conf_fdt_str, ulong conf_fdt) @@ -562,11 +563,17 @@ static int label_run_boot(struct pxe_context *ctx, struct pxe_label *label, void *buf; enum image_fmt_t fmt; + log_debug("label '%s' kern_addr_str '%s' kern_addr %lx initrd_addr %lx " + "initrd_size %lx initrd_str '%s' conf_fdt_str '%s' " + "conf_fdt %lx\n", label->name, kern_addr_str, kern_addr, + initrd_addr, initrd_size, initrd_str, conf_fdt_str, conf_fdt); + bootm_init(&bmi); bmi.addr_img = kern_addr_str; bmi.conf_fdt = conf_fdt_str; bootm_x86_set(&bmi, bzimage_addr, hextoul(kern_addr_str, NULL)); + bmi.os_size = kern_size; if (initrd_addr) { bmi.conf_ramdisk = initrd_str; @@ -663,6 +670,7 @@ static int label_boot(struct pxe_context *ctx, struct pxe_label *label) char fit_addr[200]; const char *conf_fdt_str; ulong conf_fdt = 0; + ulong kern_size; int ret; label_print(label); @@ -691,7 +699,7 @@ static int label_boot(struct pxe_context *ctx, struct pxe_label *label) if (get_relfile_envaddr(ctx, label->kernel, "kernel_addr_r", SZ_2M, (enum bootflow_img_t)IH_TYPE_KERNEL, - &kern_addr, NULL) < 0) { + &kern_addr, &kern_size) < 0) { printf("Skipping %s for failure retrieving kernel\n", label->name); return 1; @@ -799,6 +807,7 @@ static int label_boot(struct pxe_context *ctx, struct pxe_label *label) ctx->label = label; ctx->kern_addr_str = strdup(kern_addr_str); ctx->kern_addr = kern_addr; + ctx->kern_size = kern_size; if (initrd_addr) { ctx->initrd_addr = initrd_addr; ctx->initrd_size = initrd_size; @@ -822,8 +831,9 @@ static int label_boot(struct pxe_context *ctx, struct pxe_label *label) return 0; } - label_run_boot(ctx, label, kern_addr_str, kern_addr, initrd_addr, - initrd_size, initrd_str, conf_fdt_str, conf_fdt); + label_run_boot(ctx, label, kern_addr_str, kern_addr, kern_size, + initrd_addr, initrd_size, initrd_str, conf_fdt_str, + conf_fdt); /* ignore the error value since we are going to fail anyway */ return 1; /* returning is always failure */ @@ -1118,8 +1128,9 @@ int pxe_do_boot(struct pxe_context *ctx) return log_msg_ret("pxb", -ENOENT); ret = label_run_boot(ctx, ctx->label, ctx->kern_addr_str, - ctx->kern_addr, ctx->initrd_addr, ctx->initrd_size, - ctx->initrd_str, ctx->conf_fdt_str, ctx->conf_fdt); + ctx->kern_addr, ctx->kern_size, ctx->initrd_addr, + ctx->initrd_size, ctx->initrd_str, + ctx->conf_fdt_str, ctx->conf_fdt); if (ret) return log_msg_ret("lrb", ret); diff --git a/include/pxe_utils.h b/include/pxe_utils.h index 1eb0230445b..9100a861ba1 100644 --- a/include/pxe_utils.h +++ b/include/pxe_utils.h @@ -127,6 +127,7 @@ typedef int (*pxe_getfile_func)(struct pxe_context *ctx, const char *file_path, * @label: Label to process * @kern_addr_str: String containing kernel address (cannot be NULL) * @kern_addr: Kernel address (cannot be 0) + * @kern_size: Kernel size in bytes * @initrd_addr: initaddr address (0 if none) * @initrd_size: initrd size (only used if @initrd_addr) * @initrd_str: initrd string to process (only used if @initrd_addr) @@ -162,6 +163,7 @@ struct pxe_context { struct pxe_label *label; char *kern_addr_str; ulong kern_addr; + ulong kern_size; ulong initrd_addr; ulong initrd_size; char *initrd_str; -- 2.43.0

From: Simon Glass <sjg@chromium.org> If the OS size is not known, or zero, refuse to decompress it. Note that the 'booti' command does not have this information, but in that case, kernel loading is handled by its own function called booti_start() Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootm.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/boot/bootm.c b/boot/bootm.c index 109bcb75142..37ddebd66c3 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -105,6 +105,7 @@ static struct legacy_img_hdr *image_get_kernel(ulong img_addr, int verify) * boot_get_kernel() - find kernel image * * @addr_fit: first argument to bootm: address, fit configuration, etc. + * @os_size_in: size of the entire OS file (for FIT it is the size of the FIT) * @os_data: pointer to a ulong variable, will hold os data start address * @os_len: pointer to a ulong variable, will hold os data length * address and length, otherwise NULL @@ -117,7 +118,8 @@ static struct legacy_img_hdr *image_get_kernel(ulong img_addr, int verify) * Return: 0 on success, -ve on error. -EPROTOTYPE means that the image is in * a wrong or unsupported format */ -static int boot_get_kernel(const char *addr_fit, struct bootm_headers *images, +static int boot_get_kernel(const char *addr_fit, ulong os_size_in, + struct bootm_headers *images, ulong *os_data, ulong *os_len, const void **kernp) { #if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) @@ -240,6 +242,7 @@ static int boot_get_kernel(const char *addr_fit, struct bootm_headers *images, case IMAGE_FORMAT_BOOTI: log_debug("booti\n"); *os_data = img_addr; + *os_len = os_size_in; break; default: bootstage_error(BOOTSTAGE_ID_CHECK_IMAGETYPE); @@ -315,10 +318,15 @@ static int bootm_pre_load(const char *addr_str) * * @bmi: Bootm info * Return: 0 if OK, -ENOTSUPP if the required env variables are not set, -EXDEV - * if the memory region is already reserved + * if the memory region is already reserved, -EINVAL if OS size is zero + * (bmi->os_size) */ static int resolve_os_comp_buf(struct bootm_info *bmi) { + if (!bmi->os_size) { + printf("Kernel size is zero\n"); + return -EINVAL; + } images.os.load = bmi->kern_comp_addr; images.os.image_len = bmi->kern_comp_size; if (!images.os.load || !images.os.image_len) { @@ -377,8 +385,9 @@ static int bootm_find_os(struct bootm_info *bmi) int ret; /* get kernel image header, start address and length */ - ret = boot_get_kernel(bmi->addr_img, &images, &images.os.image_start, - &images.os.image_len, &os_hdr); + ret = boot_get_kernel(bmi->addr_img, bmi->os_size, &images, + &images.os.image_start, &images.os.image_len, + &os_hdr); if (ret) { /* no OS present, but that is OK */ if (ret == -ENOPKG) { -- 2.43.0

From: Simon Glass <sjg@chromium.org> This function is very simple at present, only supporting a fixed buffer of a fixed size. For cases where the address is not provided, we may still want to set a limit on the size. Support lmb reservation of an existing buffer as using lmb to reserved a new buffer. Use the provided size information to select a suitable size for the decompression buffer. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootm.c | 63 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/boot/bootm.c b/boot/bootm.c index 37ddebd66c3..715b78da336 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -316,25 +316,48 @@ static int bootm_pre_load(const char *addr_str) /** * resolve_os_comp_buf() - Figure out where to decompress OS to * + * Assume that the kernel compression is at most a factor of 4 since + * zstd almost achieves that. + * Use an alignment of 2MB since this might help arm64 + * * @bmi: Bootm info * Return: 0 if OK, -ENOTSUPP if the required env variables are not set, -EXDEV * if the memory region is already reserved, -EINVAL if OS size is zero - * (bmi->os_size) + * (bmi->os_size), -E2BIG if OS might decompress to >1G, -ENOSPC if lmb + * allocation failed */ static int resolve_os_comp_buf(struct bootm_info *bmi) { - if (!bmi->os_size) { - printf("Kernel size is zero\n"); - return -EINVAL; - } - images.os.load = bmi->kern_comp_addr; - images.os.image_len = bmi->kern_comp_size; - if (!images.os.load || !images.os.image_len) { - puts("kernel_comp_addr_r or kernel_comp_size is not provided!\n"); - return -ENOTSUPP; + if (bmi->kern_comp_addr) { + if (lmb_reserve(bmi->kern_comp_addr, bmi->kern_comp_size) < 0) + return -EXDEV; + } else { + phys_addr_t addr; + + if (!bmi->kern_comp_size) { + if (!bmi->os_size) { + printf("Kernel size is zero\n"); + return -EINVAL; + } + + if (bmi->os_size > SZ_1G / 4) { + printf("Kernel size might exceed 1G limit\n"); + return -E2BIG; + } + + bmi->kern_comp_size = bmi->os_size * 4; + } + + addr = lmb_alloc(bmi->kern_comp_size, SZ_2M); + if (!addr) { + printf("Cannot reserve space for kernel decompression (size %lx)\n", + bmi->kern_comp_size); + printf("- provide kernel_comp_addr_r and kernel_comp_size?\n"); + return -ENOSPC; + } + + bmi->kern_comp_addr = addr; } - if (lmb_reserve(images.os.load, images.os.image_len) < 0) - return -EXDEV; return 0; } @@ -756,20 +779,16 @@ static int bootm_load_os(struct bootm_info *bmi, int boot_progress) /* * For a "noload" compressed kernel we need to allocate a buffer large * enough to decompress in to and use that as the load address now. - * Assume that the kernel compression is at most a factor of 4 since - * zstd almost achieves that. - * Use an alignment of 2MB since this might help arm64 */ - if (os.type == IH_TYPE_KERNEL_NOLOAD && os.comp != IH_COMP_NONE) { - ulong req_size = ALIGN(image_len * 4, SZ_1M); + if (os.type == IH_TYPE_KERNEL_NOLOAD && os.comp) { + int ret; + + ret = resolve_os_comp_buf(bmi); + if (ret) + return log_msg_ret("fbo", ret); - load = lmb_alloc(req_size, SZ_2M); - if (!load) - return 1; os.load = load; images->ep = load; - debug("Allocated %lx bytes at %lx for kernel (size %lx) decompression\n", - req_size, load, image_len); } log_debug("load_os load %lx image_start %lx image_len %lx\n", load, image_start, image_len); -- 2.43.0

From: Simon Glass <sjg@chromium.org> Unfortunately no one else has taken up this migration. For now, reword a a comment to more correctly describe what the event uses. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/image-fdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boot/image-fdt.c b/boot/image-fdt.c index 3f779ea4dc7..87c1fef2193 100644 --- a/boot/image-fdt.c +++ b/boot/image-fdt.c @@ -647,7 +647,7 @@ int image_setup_libfdt(struct bootm_headers *images, void *blob, bool lmb) if (!ft_verify_fdt(blob)) goto err; - /* after here we are using a livetree */ + /* after here we are using the ofnode interface */ if (!of_live_active() && CONFIG_IS_ENABLED(EVENT)) { struct event_ft_fixup fixup; -- 2.43.0

From: Simon Glass <sjg@chromium.org> In some cases a board may wish to do some final processing before starting the OS. An example is the EFI app, which may wish to call exit-boot-services. Add an event to permit this. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootm_final.c | 10 ++++++++++ common/event.c | 1 + include/event.h | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/boot/bootm_final.c b/boot/bootm_final.c index 79ec35e23b0..7594880399e 100644 --- a/boot/bootm_final.c +++ b/boot/bootm_final.c @@ -7,6 +7,7 @@ #include <bootm.h> #include <bootstage.h> +#include <event.h> #include <usb.h> #include <dm/root.h> @@ -16,6 +17,8 @@ __weak void board_quiesce_devices(void) void bootm_final(enum bootm_final_t flags) { + int ret; + printf("\nStarting kernel ...%s\n\n", flags & BOOTM_FINAL_FAKE ? "(fake run for tracing)" : ""); @@ -40,6 +43,13 @@ void bootm_final(enum bootm_final_t flags) */ dm_remove_devices_active(); + ret = event_notify_null(EVT_BOOTM_FINAL); + if (ret) { + printf("Event handler failed to finalise (err %dE\n", + ret); + return; + } + bootm_disable_interrupts(); if (!(flags & BOOTM_FINAL_NO_CLEANUP)) diff --git a/common/event.c b/common/event.c index 26c96210c50..a48ca6c549d 100644 --- a/common/event.c +++ b/common/event.c @@ -52,6 +52,7 @@ const char *const type_name[] = { /* booting */ "boot_os_addr", + "bootm_final", }; _Static_assert(ARRAY_SIZE(type_name) == EVT_COUNT, "event type_name size"); diff --git a/include/event.h b/include/event.h index e12a140152a..67b5bdd8a8b 100644 --- a/include/event.h +++ b/include/event.h @@ -179,6 +179,14 @@ enum event_t { */ EVT_BOOT_OS_ADDR, + /** + * @EVT_BOOTM_FINAL: + * Triggered after any required device-removals are complete, bootstage + * report is shown, etc. and before any machine-specific poking, such as + * disabling interrupts, changing exception level + */ + EVT_BOOTM_FINAL, + /** * @EVT_COUNT: * This constants holds the maximum event number + 1 and is used when -- 2.43.0
participants (1)
-
Simon Glass