From: Russell King - ARM Linux admin <linux@armlinux.org.uk> To: "Łukasz Stelmach" <l.stelmach@samsung.com> Cc: Masahiro Yamada <masahiroy@kernel.org>, Nick Desaulniers <ndesaulniers@google.com>, Thomas Gleixner <tglx@linutronix.de>, Enrico Weigelt <info@metux.net>, Kees Cook <keescook@chromium.org>, Ingo Molnar <mingo@kernel.org>, Ben Dooks <ben-linux@fluff.org>, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, AKASHI Takahiro <takahiro.akashi@linaro.org>, Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>, Marek Szyprowski <m.szyprowski@samsung.com> Subject: Re: [PATCH 5/5] arm: kexec_file: load zImage or uImage, initrd and dtb Date: Mon, 1 Jun 2020 16:07:45 +0100 [thread overview] Message-ID: <20200601150745.GL1551@shell.armlinux.org.uk> (raw) In-Reply-To: <20200601142754.26139-6-l.stelmach@samsung.com> On Mon, Jun 01, 2020 at 04:27:54PM +0200, Łukasz Stelmach wrote: > This is kexec_file_load implementation for ARM. It loads zImage and > initrd from file descripters and resuses DTB. > > Most code is derived from arm64 kexec_file_load implementation > and from kexec-tools. Please make the uImage loader able to be configured out of the kernel; it's a legacy image format now, and some of us just don't use it anymore. That way, those who wish to have it can, and those who wish not to can have a smaller kernel. > Cc: AKASHI Takahiro <takahiro.akashi@linaro.org> > Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com> > --- > arch/arm/Kconfig | 15 ++ > arch/arm/include/asm/image.h | 26 ++++ > arch/arm/include/asm/kexec.h | 14 ++ > arch/arm/kernel/Makefile | 5 +- > arch/arm/kernel/kexec_uimage.c | 80 ++++++++++ > arch/arm/kernel/kexec_zimage.c | 199 +++++++++++++++++++++++++ > arch/arm/kernel/machine_kexec.c | 11 +- > arch/arm/kernel/machine_kexec_file.c | 209 +++++++++++++++++++++++++++ > 8 files changed, 554 insertions(+), 5 deletions(-) > create mode 100644 arch/arm/kernel/kexec_uimage.c > create mode 100644 arch/arm/kernel/kexec_zimage.c > create mode 100644 arch/arm/kernel/machine_kexec_file.c > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > index c77c93c485a0..6adb849cb304 100644 > --- a/arch/arm/Kconfig > +++ b/arch/arm/Kconfig > @@ -1917,6 +1917,21 @@ config KEXEC > is properly shutdown, so do not be surprised if this code does not > initially work for you. > > +config KEXEC_FILE > + bool "Kexec file based system call (EXPERIMENTAL)" > + depends on (!SMP || PM_SLEEP_SMP) > + depends on USE_OF > + select KEXEC_CORE > + select CRC32 > + help > + This is new version of kexec system call. This system call is > + file based and takes file descriptors as system call argument > + for kernel and initramfs as opposed to list of segments as > + accepted by previous system call. > + > + The kernel to be loaded MUST support Flattened Device Tree > + (selected with CONFIG_USE_OF). > + > config ATAGS_PROC > bool "Export atags in procfs" > depends on ATAGS && KEXEC > diff --git a/arch/arm/include/asm/image.h b/arch/arm/include/asm/image.h > index 624438740f23..95f23837b04f 100644 > --- a/arch/arm/include/asm/image.h > +++ b/arch/arm/include/asm/image.h > @@ -8,8 +8,13 @@ > (((x) >> 8) & 0x0000ff00) | \ > (((x) << 8) & 0x00ff0000) | \ > (((x) << 24) & 0xff000000)) > +#define UIMAGE_MAGIC(x) (x) > #else > #define ZIMAGE_MAGIC(x) (x) > +#define UIMAGE_MAGIC(x) ((((x) >> 24) & 0x000000ff) | \ > + (((x) >> 8) & 0x0000ff00) | \ > + (((x) << 8) & 0x00ff0000) | \ > + (((x) << 24) & 0xff000000)) > #endif > > #define ARM_ZIMAGE_MAGIC1 ZIMAGE_MAGIC(0x016f2818) > @@ -17,6 +22,12 @@ > #define ARM_ZIMAGE_MAGIC3 ZIMAGE_MAGIC(0x5a534c4b) > #define ARM_ZIMAGE_MAGIC4 ZIMAGE_MAGIC(0x5a534344) > > +#define ARM_UIMAGE_MAGIC UIMAGE_MAGIC(0x27051956) > +#define ARM_UIMAGE_NAME_LEN 32 > +#define ARM_UIMAGE_TYPE_KERNEL 2 > +#define ARM_UIMAGE_TYPE_KERNEL_NOLOAD 14 > +#define ARM_UIMAGE_ARCH_ARM 2 > + > #ifndef __ASSEMBLY__ > > #include <linux/types.h> > @@ -33,6 +44,21 @@ struct arm_zimage_header { > __le32 extension_tag_offset; > }; > > +struct arm_uimage_header { > + __be32 magic; > + __be32 hdr_crc; > + __be32 time; > + __be32 size; > + __be32 load; > + __be32 entry; > + __be32 crc; > + __u8 os; > + __u8 arch; > + __u8 type; > + __u8 comp; > + __u8 name[ARM_UIMAGE_NAME_LEN]; > +}; > + > struct arm_zimage_tag { > struct tag_header hdr; > union { > diff --git a/arch/arm/include/asm/kexec.h b/arch/arm/include/asm/kexec.h > index 22751b5b5735..fda35afa7195 100644 > --- a/arch/arm/include/asm/kexec.h > +++ b/arch/arm/include/asm/kexec.h > @@ -83,6 +83,20 @@ static inline struct page *boot_pfn_to_page(unsigned long boot_pfn) > } > #define boot_pfn_to_page boot_pfn_to_page > > +#ifdef CONFIG_KEXEC_FILE > + > +extern const struct kexec_file_ops kexec_zimage_ops; > +extern const struct kexec_file_ops kexec_uimage_ops; > + > +struct kimage; > + > +extern int load_other_segments(struct kimage *image, > + unsigned long kernel_load_addr, unsigned long kernel_size, > + char *initrd, unsigned long initrd_len, > + unsigned long initrd_offset, char *cmdline); > + > +#endif /* CONFIG_KEXEC_FILE */ > + > #endif /* __ASSEMBLY__ */ > > #endif /* CONFIG_KEXEC */ > diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile > index 89e5d864e923..466c683bb551 100644 > --- a/arch/arm/kernel/Makefile > +++ b/arch/arm/kernel/Makefile > @@ -3,6 +3,7 @@ > # Makefile for the linux kernel. > # > > +CFLAGS_kexec_zimage.o := -DTEXT_OFFSET=$(TEXT_OFFSET) > CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) > AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) > > @@ -56,7 +57,9 @@ obj-$(CONFIG_FUNCTION_TRACER) += entry-ftrace.o > obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o patch.o > obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o patch.o > obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o > -obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o > +obj-$(CONFIG_KEXEC_CORE) += machine_kexec.o relocate_kernel.o > +obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_zimage.o \ > + kexec_uimage.o > # Main staffs in KPROBES are in arch/arm/probes/ . > obj-$(CONFIG_KPROBES) += patch.o insn.o > obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o > diff --git a/arch/arm/kernel/kexec_uimage.c b/arch/arm/kernel/kexec_uimage.c > new file mode 100644 > index 000000000000..47033574e24e > --- /dev/null > +++ b/arch/arm/kernel/kexec_uimage.c > @@ -0,0 +1,80 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Kexec uImage loader > + * > + * Copyright (C) 2020 Samsung Electronics > + * Author: Łukasz Stelmach <l.stelmach@samsung.com> > + */ > + > +#define pr_fmt(fmt) "kexec_file(uImage): " fmt > + > +#include <asm/image.h> > +#include <linux/crc32.h> > +#include <linux/err.h> > +#include <linux/kexec.h> > + > +#define crc32_ones(crc, buf, len) \ > + (crc32(crc ^ 0xffffffff, buf, len) ^ 0xffffffff) > + > +static int uimage_probe(const char *uimage_buf, unsigned long uimage_len) > +{ > + const struct arm_uimage_header *h = > + (struct arm_uimage_header *) uimage_buf; > + struct arm_uimage_header uhdr; > + unsigned long zoff = sizeof(struct arm_uimage_header); > + uint32_t crc; > + > + if (h->magic != ARM_UIMAGE_MAGIC) > + return -EINVAL; > + > + if (h->type != ARM_UIMAGE_TYPE_KERNEL && > + h->type != ARM_UIMAGE_TYPE_KERNEL_NOLOAD){ > + pr_debug("Invalid image type: %d\n", h->type); > + return -EINVAL; > + } > + > + if (h->arch != ARM_UIMAGE_ARCH_ARM) { > + pr_debug("Invalidy image arch: %d\n", h->arch); > + return -EINVAL; > + } > + > + memcpy((char *)&uhdr, h, sizeof(uhdr)); > + crc = be32_to_cpu(uhdr.hdr_crc); > + uhdr.hdr_crc = 0; > + > + if (crc32_ones(0, (char *)&uhdr, sizeof(uhdr)) != crc) { > + pr_debug("Corrupt header, CRC do not match\n"); > + return -EINVAL; > + } > + > + crc = be32_to_cpu(uhdr.crc); > + if (crc32_ones(0, uimage_buf + zoff, uimage_len - zoff) != crc) { > + pr_debug("Corrupt zImage, CRC do not match\n"); > + return -EINVAL; > + } > + > + return kexec_zimage_ops.probe(uimage_buf + zoff, > + uimage_len - zoff); > +} > + > +static void *uimage_load(struct kimage *image, > + char *uimage, unsigned long uimage_len, > + char *initrd, unsigned long initrd_len, > + char *cmdline, unsigned long cmdline_len) > +{ > + const struct arm_uimage_header *h = > + (struct arm_uimage_header *) uimage; > + unsigned long zimage_offset = sizeof(struct arm_uimage_header); > + > + pr_debug("Loading uImage"); > + return kexec_zimage_ops.load(image, > + uimage + zimage_offset, > + uimage_len - zimage_offset, > + initrd, initrd_len, > + cmdline, cmdline_len); > +} > + > +const struct kexec_file_ops kexec_uimage_ops = { > + .probe = uimage_probe, > + .load = uimage_load, > +}; > diff --git a/arch/arm/kernel/kexec_zimage.c b/arch/arm/kernel/kexec_zimage.c > new file mode 100644 > index 000000000000..d09795fc9072 > --- /dev/null > +++ b/arch/arm/kernel/kexec_zimage.c > @@ -0,0 +1,199 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Kexec zImage loader > + * > + * Copyright (C) 2020 Samsung Electronics > + * Author: Łukasz Stelmach <l.stelmach@samsung.com> Please credit me as part author of this - you have taken some of my code from the userspace kexec tool (such as the contents of find_extension_tag()) and copied it in here, so this is not all your own work. > + */ > + > +#define pr_fmt(fmt) "kexec_file(zImage): " fmt > + > +#include <asm/image.h> > +#include <asm/unaligned.h> > +#include <linux/err.h> > +#include <linux/kexec.h> > +#include <linux/memblock.h> > + > +#define byte_size(t) ((t)->hdr.size << 2) > + > +static const void *find_extension_tag(const char *buf, > + unsigned long len, > + uint32_t tag_id) > +{ > + const struct arm_zimage_header *h = (const struct arm_zimage_header *)buf; > + const struct arm_zimage_tag *tag; > + uint32_t offset, size; > + uint32_t max = len - sizeof(struct tag_header); > + > + if (len < sizeof(*h) || > + h->magic != ARM_ZIMAGE_MAGIC1 || > + h->magic2 != ARM_ZIMAGE_MAGIC2) > + return NULL; > + > + for (offset = h->extension_tag_offset; > + (tag = (void *)(buf + offset)) != NULL && > + offset < max && > + (size = le32_to_cpu(byte_size(tag))) != 0 && > + offset + size < len; > + offset += size) { > + pr_debug(" offset 0x%08x tag 0x%08x size %u\n", > + offset, le32_to_cpu(tag->hdr.tag), size); > + if (tag->hdr.tag == tag_id) > + return tag; > + } > + > + return NULL; > +} > + > +static int zimage_probe(const char *kernel_buf, unsigned long kernel_len) > +{ > + const struct arm_zimage_header *h = > + (struct arm_zimage_header *)(kernel_buf); > + > + if (!h || (kernel_len < sizeof(*h))) > + return -EINVAL; > + > + if ((h->magic != ARM_ZIMAGE_MAGIC1) || > + (h->magic2 != ARM_ZIMAGE_MAGIC2)) > + return -EINVAL; > + > + return 0; > +} > + > + > +#if defined(DEBUG) > +#define debug_offsets() ({ \ > + pr_debug("Image offsets:\n"); \ > + pr_debug(" kernel 0x%08lx 0x%08lx\n", kernel_offset, kernel_len); \ > + pr_debug(" zimage 0x%08lx 0x%08lx\n", zimage_offset, zimage_len); \ > + pr_debug(" initrd 0x%08lx 0x%08lx\n", initrd_offset, initrd_len); \ > +}) > +#else > +#define debug_offsets() > +#endif > + > +static void *zimage_load(struct kimage *image, > + char *zimage, unsigned long zimage_len, > + char *initrd, unsigned long initrd_len, > + char *cmdline, unsigned long cmdline_len) > +{ > + struct arm_zimage_header *h; > + struct kexec_buf kbuf; > + struct kexec_segment *zimage_segment; > + const struct arm_zimage_tag *klsz_tag; > + const struct arm_zimage_tag_dc *dcsz_tag; > + int ret = -EINVAL; > + > + unsigned long zimage_mem = 0x20000; /* malloc 64kB + stack 4 kB + some bss */ > + unsigned long kernel_len = zimage_len * 4; /* 4:1 compression */ This has been proven wrong. > + unsigned long kernel_offset = memblock_start_of_DRAM() + > + ALIGN(TEXT_OFFSET, PAGE_SIZE); TEXT_OFFSET is actually a property of the loaded kernel, not of the currently running kernel. I have a patch to add that into the zImage tag. > + unsigned long zimage_offset = kernel_offset + > + ALIGN(kernel_len, PAGE_SIZE); > + unsigned long initrd_offset = zimage_offset + > + ALIGN(zimage_len + zimage_mem, PAGE_SIZE); Since kernel_len is wrong, these will be wrong... please only fall back to this if you don't find the extension tag - in other words, declare the variables here, but don't initialise them, set them lower down in the file if we fail to find the extension tag. > + > + if (image->type == KEXEC_TYPE_CRASH) { > + kernel_offset += crashk_res.start; > + zimage_offset += crashk_res.start; > + initrd_offset += crashk_res.start; > + } > + debug_offsets(); > + > + h = (struct arm_zimage_header *)zimage; > + > + klsz_tag = find_extension_tag(zimage, zimage_len, ZIMAGE_TAG_KRNL_SIZE); > + if (klsz_tag) { > + uint32_t *p = (void *)zimage + > + le32_to_cpu(klsz_tag->u.krnl_size.size_ptr); > + uint32_t edata_size = le32_to_cpu(get_unaligned(p)); > + uint32_t bss_size = le32_to_cpu(klsz_tag->u.krnl_size.bss_size); > + > + kernel_len = edata_size + bss_size; > + > + pr_debug("Decompressed kernel sizes:\n"); > + pr_debug(" text+data 0x%08lx bss 0x%08lx total 0x%08lx\n", > + (unsigned long)edata_size, > + (unsigned long)bss_size, > + (unsigned long)kernel_len); > + > + zimage_offset = kernel_offset + ALIGN(edata_size, PAGE_SIZE); > + initrd_offset = zimage_offset + > + max(ALIGN(zimage_len + 0x20000, PAGE_SIZE), > + ALIGN((unsigned long)bss_size, PAGE_SIZE)); > + debug_offsets(); This looks incorrect to me. Please see the kexec tool - what its doing in its corresponding section that you copied some of this code from is carefully thought out and can't be simplified. Ergo, I think this is wrong. > + } > + > + dcsz_tag = find_extension_tag(zimage, zimage_len, > + ZIMAGE_TAG_DECOMP_SIZE); > + if (dcsz_tag) { > + uint32_t bss_size = le32_to_cpu(dcsz_tag->u.decomp_size.bss_size); > + uint32_t stack_size = le32_to_cpu(dcsz_tag->u.decomp_size.stack_size); > + uint32_t malloc_size = le32_to_cpu(dcsz_tag->u.decomp_size.malloc_size); > + > + zimage_mem = bss_size + stack_size + malloc_size; > + > + pr_debug("Decompressor memory requirements:\n"); > + pr_debug(" bss 0x%08lx stack 0x%08lx malloc 0x%08lx total 0x%08lx\n", > + (unsigned long)bss_size, > + (unsigned long)stack_size, > + (unsigned long)malloc_size, > + zimage_mem); > + > + initrd_offset = max(ALIGN(zimage_offset + zimage_len + > + bss_size + stack_size + > + malloc_size, PAGE_SIZE), > + ALIGN(kernel_offset + kernel_len, PAGE_SIZE)); > + debug_offsets(); > + } > + > + /* > + * zImage MUST be loaded into the first 128 MiB of physical > + * memory for proper memory detection. Should the uncompressed > + * kernel be larger than 128 MiB, zImage relocation becomes > + * unavoidable and it is best to rely on the relocation code. > + */ > + if (((zimage_offset - kernel_offset) + PAGE_SIZE + 0x8000) >= SZ_128M) { > + pr_debug("The kernel is too big (%ld MiB) to avoid " > + "zImage relocation. Loading zimage at 0x%08lx\n", > + ((zimage_offset - kernel_offset) >> 20), > + kernel_offset); > + zimage_offset = kernel_offset; > + } > + > + kbuf.image = image; > + kbuf.top_down = false; > + > + kbuf.buf_min = zimage_offset; > + kbuf.buf_max = ULONG_MAX; > + kbuf.buffer = zimage; > + kbuf.bufsz = zimage_len; > + kbuf.buf_align = PAGE_SIZE; > + > + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; > + kbuf.memsz = zimage_len; > + > + ret = kexec_add_buffer(&kbuf); > + if (ret) > + return ERR_PTR(ret); > + > + pr_debug("Loaded zImage at 0x%lx bufsz=0x%lx memsz=0x%lx\n", > + kbuf.mem, kbuf.bufsz, kbuf.memsz); > + > + initrd_offset += kbuf.mem - zimage_offset; > + debug_offsets(); > + > + zimage_segment = &image->segment[image->nr_segments - 1]; > + image->start = zimage_segment->mem; > + > + ret = load_other_segments(image, > + zimage_segment->mem, zimage_segment->memsz, > + initrd, initrd_len, initrd_offset, > + cmdline); > + return ERR_PTR(ret); > +} > + > +const struct kexec_file_ops kexec_zimage_ops = { > + .probe = zimage_probe, > + .load = zimage_load, > +}; > diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c > index c10a2dfd53d1..2e4780efabb4 100644 > --- a/arch/arm/kernel/machine_kexec.c > +++ b/arch/arm/kernel/machine_kexec.c > @@ -93,10 +93,13 @@ int machine_kexec_prepare(struct kimage *image) > current_segment->memsz)) > return -EINVAL; > > - err = get_user(header, (__be32*)current_segment->buf); > - if (err) > - return err; > - > + if (image->file_mode) { > + header = *(__be32 *)current_segment->buf; > + } else { > + err = get_user(header, (__be32 *)current_segment->buf); > + if (err) > + return err; > + } > if (header == cpu_to_be32(OF_DT_HEADER)) > image->arch.kernel_r2 = current_segment->mem; > } > diff --git a/arch/arm/kernel/machine_kexec_file.c b/arch/arm/kernel/machine_kexec_file.c > new file mode 100644 > index 000000000000..ead680f1e795 > --- /dev/null > +++ b/arch/arm/kernel/machine_kexec_file.c > @@ -0,0 +1,209 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * kexec_file for arm > + * > + * Copyright (C) 2018 Linaro Limited > + * Copyright (C) 2020 Samsung Electronics > + * Authors: > + * AKASHI Takahiro <takahiro.akashi@linaro.org> > + * Łukasz Stelmach <l.stelmach@samsung.com> > + * > + */ > + > +#define pr_fmt(fmt) "kexec_file: " fmt > + > +#include <linux/kexec.h> > +#include <linux/libfdt.h> > +#include <linux/of_fdt.h> > +#include <linux/random.h> > + > +/* relevant device tree properties */ > +#define FDT_PROP_INITRD_START "linux,initrd-start" > +#define FDT_PROP_INITRD_END "linux,initrd-end" > +#define FDT_PROP_BOOTARGS "bootargs" > +#define FDT_PROP_RNG_SEED "rng-seed" > + > +static int setup_dtb(struct kimage *image, > + unsigned long initrd_load_addr, unsigned long initrd_len, > + char *cmdline, void *dtb) > +{ > + int off, ret; > + > + ret = fdt_path_offset(dtb, "/chosen"); > + if (ret < 0) > + goto out; > + > + off = ret; > + > + /* add bootargs */ > + if (cmdline) { > + ret = fdt_setprop_string(dtb, off, FDT_PROP_BOOTARGS, cmdline); > + if (ret) > + goto out; > + } else { > + ret = fdt_delprop(dtb, off, FDT_PROP_BOOTARGS); > + if (ret && (ret != -FDT_ERR_NOTFOUND)) > + goto out; > + } > + > + /* add initrd-* */ > + if (initrd_load_addr) { > + ret = fdt_setprop_u64(dtb, off, FDT_PROP_INITRD_START, > + initrd_load_addr); > + if (ret) > + goto out; > + > + ret = fdt_setprop_u64(dtb, off, FDT_PROP_INITRD_END, > + initrd_load_addr + initrd_len); > + if (ret) > + goto out; > + } else { > + ret = fdt_delprop(dtb, off, FDT_PROP_INITRD_START); > + if (ret && (ret != -FDT_ERR_NOTFOUND)) > + goto out; > + > + ret = fdt_delprop(dtb, off, FDT_PROP_INITRD_END); > + if (ret && (ret != -FDT_ERR_NOTFOUND)) > + goto out; > + } > + > + /* add rng-seed */ > + if (rng_is_initialized()) { > + char seed[128]; > + get_random_bytes(seed, sizeof(seed)); > + > + ret = fdt_setprop(dtb, off, FDT_PROP_RNG_SEED, > + seed, sizeof(seed)); > + if (ret) > + goto out; > + } else { > + pr_notice("RNG is not initialised: omitting \"%s\" property\n", > + FDT_PROP_RNG_SEED); > + ret = 0; > + } > + > +out: > + if (ret) > + return (ret == -FDT_ERR_NOSPACE) ? -ENOMEM : -EINVAL; > + > + return 0; > +} > +/* > + * More space needed so that we can add initrd, bootargs and kaslr-seed. > + */ > +#define DTB_EXTRA_SPACE 0x1000 > + > +static int create_dtb(struct kimage *image, > + unsigned long initrd_load_addr, unsigned long initrd_len, > + char *cmdline, void **dtb) > +{ > + void *buf; > + size_t buf_size; > + size_t cmdline_len; > + int ret; > + > + cmdline_len = cmdline ? strlen(cmdline) : 0; > + buf_size = fdt_totalsize(initial_boot_params) > + + cmdline_len + DTB_EXTRA_SPACE; > + > + for (;;) { > + buf = vmalloc(buf_size); > + if (!buf) > + return -ENOMEM; > + > + /* duplicate a device tree blob */ > + ret = fdt_open_into(initial_boot_params, buf, buf_size); > + if (ret) > + return -EINVAL; > + > + ret = setup_dtb(image, initrd_load_addr, initrd_len, > + cmdline, buf); > + if (ret) { > + vfree(buf); > + if (ret == -ENOMEM) { > + /* unlikely, but just in case */ > + buf_size += DTB_EXTRA_SPACE; > + continue; > + } else { > + return ret; > + } > + } > + > + /* trim it */ > + fdt_pack(buf); > + *dtb = buf; > + > + return 0; > + } > +} > + > +int load_other_segments(struct kimage *image, > + unsigned long zimage_load_addr, > + unsigned long zimage_len, > + char *initrd, > + unsigned long initrd_len, > + unsigned long initrd_offset, > + char *cmdline) > +{ > + struct kexec_buf kbuf; > + void *dtb = NULL; > + unsigned long initrd_load_addr = 0; > + unsigned long dtb_len; > + int ret = 0; > + > + kbuf.image = image; > + /* not allocate anything below the kernel */ > + kbuf.buf_min = initrd_offset; > + if (initrd) { > + kbuf.buffer = initrd; > + kbuf.bufsz = initrd_len; > + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; > + kbuf.memsz = initrd_len; > + kbuf.buf_align = PAGE_SIZE; > + kbuf.buf_max = ULONG_MAX; > + kbuf.top_down = false; > + > + ret = kexec_add_buffer(&kbuf); > + if (ret) > + goto out_err; > + > + pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n", > + kbuf.mem, kbuf.bufsz, kbuf.memsz); > + > + initrd_load_addr = kbuf.mem; > + kbuf.buf_min = initrd_load_addr + kbuf.memsz; > + } > + > + /* load dtb */ > + ret = create_dtb(image, initrd_load_addr, initrd_len, cmdline, &dtb); > + if (ret) { > + pr_err("Preparing for new dtb failed\n"); > + goto out_err; > + } > + > + dtb_len = fdt_totalsize(dtb); > + kbuf.buffer = dtb; > + kbuf.bufsz = dtb_len; > + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; > + kbuf.memsz = dtb_len; > + kbuf.buf_align = PAGE_SIZE; > + kbuf.buf_max = ULONG_MAX; > + kbuf.top_down = false; > + > + ret = kexec_add_buffer(&kbuf); > + if (ret) > + goto out_err; > + > + pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n", > + kbuf.mem, kbuf.bufsz, kbuf.memsz); > + return 0; > +out_err: > + vfree(dtb); > + return ret; > +} > + > +const struct kexec_file_ops * const kexec_file_loaders[] = { > + &kexec_uimage_ops, > + &kexec_zimage_ops, > + NULL > +}; > -- > 2.26.2 > > -- RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ FTTC for 0.8m (est. 1762m) line in suburbia: sync at 13.1Mbps down 424kbps up
WARNING: multiple messages have this Message-ID (diff)
From: Russell King - ARM Linux admin <linux@armlinux.org.uk> To: "Łukasz Stelmach" <l.stelmach@samsung.com> Cc: Kees Cook <keescook@chromium.org>, Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>, Masahiro Yamada <masahiroy@kernel.org>, Nick Desaulniers <ndesaulniers@google.com>, linux-kernel@vger.kernel.org, AKASHI Takahiro <takahiro.akashi@linaro.org>, Ben Dooks <ben-linux@fluff.org>, Thomas Gleixner <tglx@linutronix.de>, Enrico Weigelt <info@metux.net>, Ingo Molnar <mingo@kernel.org>, linux-arm-kernel@lists.infradead.org, Marek Szyprowski <m.szyprowski@samsung.com> Subject: Re: [PATCH 5/5] arm: kexec_file: load zImage or uImage, initrd and dtb Date: Mon, 1 Jun 2020 16:07:45 +0100 [thread overview] Message-ID: <20200601150745.GL1551@shell.armlinux.org.uk> (raw) In-Reply-To: <20200601142754.26139-6-l.stelmach@samsung.com> On Mon, Jun 01, 2020 at 04:27:54PM +0200, Łukasz Stelmach wrote: > This is kexec_file_load implementation for ARM. It loads zImage and > initrd from file descripters and resuses DTB. > > Most code is derived from arm64 kexec_file_load implementation > and from kexec-tools. Please make the uImage loader able to be configured out of the kernel; it's a legacy image format now, and some of us just don't use it anymore. That way, those who wish to have it can, and those who wish not to can have a smaller kernel. > Cc: AKASHI Takahiro <takahiro.akashi@linaro.org> > Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com> > --- > arch/arm/Kconfig | 15 ++ > arch/arm/include/asm/image.h | 26 ++++ > arch/arm/include/asm/kexec.h | 14 ++ > arch/arm/kernel/Makefile | 5 +- > arch/arm/kernel/kexec_uimage.c | 80 ++++++++++ > arch/arm/kernel/kexec_zimage.c | 199 +++++++++++++++++++++++++ > arch/arm/kernel/machine_kexec.c | 11 +- > arch/arm/kernel/machine_kexec_file.c | 209 +++++++++++++++++++++++++++ > 8 files changed, 554 insertions(+), 5 deletions(-) > create mode 100644 arch/arm/kernel/kexec_uimage.c > create mode 100644 arch/arm/kernel/kexec_zimage.c > create mode 100644 arch/arm/kernel/machine_kexec_file.c > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > index c77c93c485a0..6adb849cb304 100644 > --- a/arch/arm/Kconfig > +++ b/arch/arm/Kconfig > @@ -1917,6 +1917,21 @@ config KEXEC > is properly shutdown, so do not be surprised if this code does not > initially work for you. > > +config KEXEC_FILE > + bool "Kexec file based system call (EXPERIMENTAL)" > + depends on (!SMP || PM_SLEEP_SMP) > + depends on USE_OF > + select KEXEC_CORE > + select CRC32 > + help > + This is new version of kexec system call. This system call is > + file based and takes file descriptors as system call argument > + for kernel and initramfs as opposed to list of segments as > + accepted by previous system call. > + > + The kernel to be loaded MUST support Flattened Device Tree > + (selected with CONFIG_USE_OF). > + > config ATAGS_PROC > bool "Export atags in procfs" > depends on ATAGS && KEXEC > diff --git a/arch/arm/include/asm/image.h b/arch/arm/include/asm/image.h > index 624438740f23..95f23837b04f 100644 > --- a/arch/arm/include/asm/image.h > +++ b/arch/arm/include/asm/image.h > @@ -8,8 +8,13 @@ > (((x) >> 8) & 0x0000ff00) | \ > (((x) << 8) & 0x00ff0000) | \ > (((x) << 24) & 0xff000000)) > +#define UIMAGE_MAGIC(x) (x) > #else > #define ZIMAGE_MAGIC(x) (x) > +#define UIMAGE_MAGIC(x) ((((x) >> 24) & 0x000000ff) | \ > + (((x) >> 8) & 0x0000ff00) | \ > + (((x) << 8) & 0x00ff0000) | \ > + (((x) << 24) & 0xff000000)) > #endif > > #define ARM_ZIMAGE_MAGIC1 ZIMAGE_MAGIC(0x016f2818) > @@ -17,6 +22,12 @@ > #define ARM_ZIMAGE_MAGIC3 ZIMAGE_MAGIC(0x5a534c4b) > #define ARM_ZIMAGE_MAGIC4 ZIMAGE_MAGIC(0x5a534344) > > +#define ARM_UIMAGE_MAGIC UIMAGE_MAGIC(0x27051956) > +#define ARM_UIMAGE_NAME_LEN 32 > +#define ARM_UIMAGE_TYPE_KERNEL 2 > +#define ARM_UIMAGE_TYPE_KERNEL_NOLOAD 14 > +#define ARM_UIMAGE_ARCH_ARM 2 > + > #ifndef __ASSEMBLY__ > > #include <linux/types.h> > @@ -33,6 +44,21 @@ struct arm_zimage_header { > __le32 extension_tag_offset; > }; > > +struct arm_uimage_header { > + __be32 magic; > + __be32 hdr_crc; > + __be32 time; > + __be32 size; > + __be32 load; > + __be32 entry; > + __be32 crc; > + __u8 os; > + __u8 arch; > + __u8 type; > + __u8 comp; > + __u8 name[ARM_UIMAGE_NAME_LEN]; > +}; > + > struct arm_zimage_tag { > struct tag_header hdr; > union { > diff --git a/arch/arm/include/asm/kexec.h b/arch/arm/include/asm/kexec.h > index 22751b5b5735..fda35afa7195 100644 > --- a/arch/arm/include/asm/kexec.h > +++ b/arch/arm/include/asm/kexec.h > @@ -83,6 +83,20 @@ static inline struct page *boot_pfn_to_page(unsigned long boot_pfn) > } > #define boot_pfn_to_page boot_pfn_to_page > > +#ifdef CONFIG_KEXEC_FILE > + > +extern const struct kexec_file_ops kexec_zimage_ops; > +extern const struct kexec_file_ops kexec_uimage_ops; > + > +struct kimage; > + > +extern int load_other_segments(struct kimage *image, > + unsigned long kernel_load_addr, unsigned long kernel_size, > + char *initrd, unsigned long initrd_len, > + unsigned long initrd_offset, char *cmdline); > + > +#endif /* CONFIG_KEXEC_FILE */ > + > #endif /* __ASSEMBLY__ */ > > #endif /* CONFIG_KEXEC */ > diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile > index 89e5d864e923..466c683bb551 100644 > --- a/arch/arm/kernel/Makefile > +++ b/arch/arm/kernel/Makefile > @@ -3,6 +3,7 @@ > # Makefile for the linux kernel. > # > > +CFLAGS_kexec_zimage.o := -DTEXT_OFFSET=$(TEXT_OFFSET) > CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) > AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) > > @@ -56,7 +57,9 @@ obj-$(CONFIG_FUNCTION_TRACER) += entry-ftrace.o > obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o patch.o > obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o patch.o > obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o > -obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o > +obj-$(CONFIG_KEXEC_CORE) += machine_kexec.o relocate_kernel.o > +obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_zimage.o \ > + kexec_uimage.o > # Main staffs in KPROBES are in arch/arm/probes/ . > obj-$(CONFIG_KPROBES) += patch.o insn.o > obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o > diff --git a/arch/arm/kernel/kexec_uimage.c b/arch/arm/kernel/kexec_uimage.c > new file mode 100644 > index 000000000000..47033574e24e > --- /dev/null > +++ b/arch/arm/kernel/kexec_uimage.c > @@ -0,0 +1,80 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Kexec uImage loader > + * > + * Copyright (C) 2020 Samsung Electronics > + * Author: Łukasz Stelmach <l.stelmach@samsung.com> > + */ > + > +#define pr_fmt(fmt) "kexec_file(uImage): " fmt > + > +#include <asm/image.h> > +#include <linux/crc32.h> > +#include <linux/err.h> > +#include <linux/kexec.h> > + > +#define crc32_ones(crc, buf, len) \ > + (crc32(crc ^ 0xffffffff, buf, len) ^ 0xffffffff) > + > +static int uimage_probe(const char *uimage_buf, unsigned long uimage_len) > +{ > + const struct arm_uimage_header *h = > + (struct arm_uimage_header *) uimage_buf; > + struct arm_uimage_header uhdr; > + unsigned long zoff = sizeof(struct arm_uimage_header); > + uint32_t crc; > + > + if (h->magic != ARM_UIMAGE_MAGIC) > + return -EINVAL; > + > + if (h->type != ARM_UIMAGE_TYPE_KERNEL && > + h->type != ARM_UIMAGE_TYPE_KERNEL_NOLOAD){ > + pr_debug("Invalid image type: %d\n", h->type); > + return -EINVAL; > + } > + > + if (h->arch != ARM_UIMAGE_ARCH_ARM) { > + pr_debug("Invalidy image arch: %d\n", h->arch); > + return -EINVAL; > + } > + > + memcpy((char *)&uhdr, h, sizeof(uhdr)); > + crc = be32_to_cpu(uhdr.hdr_crc); > + uhdr.hdr_crc = 0; > + > + if (crc32_ones(0, (char *)&uhdr, sizeof(uhdr)) != crc) { > + pr_debug("Corrupt header, CRC do not match\n"); > + return -EINVAL; > + } > + > + crc = be32_to_cpu(uhdr.crc); > + if (crc32_ones(0, uimage_buf + zoff, uimage_len - zoff) != crc) { > + pr_debug("Corrupt zImage, CRC do not match\n"); > + return -EINVAL; > + } > + > + return kexec_zimage_ops.probe(uimage_buf + zoff, > + uimage_len - zoff); > +} > + > +static void *uimage_load(struct kimage *image, > + char *uimage, unsigned long uimage_len, > + char *initrd, unsigned long initrd_len, > + char *cmdline, unsigned long cmdline_len) > +{ > + const struct arm_uimage_header *h = > + (struct arm_uimage_header *) uimage; > + unsigned long zimage_offset = sizeof(struct arm_uimage_header); > + > + pr_debug("Loading uImage"); > + return kexec_zimage_ops.load(image, > + uimage + zimage_offset, > + uimage_len - zimage_offset, > + initrd, initrd_len, > + cmdline, cmdline_len); > +} > + > +const struct kexec_file_ops kexec_uimage_ops = { > + .probe = uimage_probe, > + .load = uimage_load, > +}; > diff --git a/arch/arm/kernel/kexec_zimage.c b/arch/arm/kernel/kexec_zimage.c > new file mode 100644 > index 000000000000..d09795fc9072 > --- /dev/null > +++ b/arch/arm/kernel/kexec_zimage.c > @@ -0,0 +1,199 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Kexec zImage loader > + * > + * Copyright (C) 2020 Samsung Electronics > + * Author: Łukasz Stelmach <l.stelmach@samsung.com> Please credit me as part author of this - you have taken some of my code from the userspace kexec tool (such as the contents of find_extension_tag()) and copied it in here, so this is not all your own work. > + */ > + > +#define pr_fmt(fmt) "kexec_file(zImage): " fmt > + > +#include <asm/image.h> > +#include <asm/unaligned.h> > +#include <linux/err.h> > +#include <linux/kexec.h> > +#include <linux/memblock.h> > + > +#define byte_size(t) ((t)->hdr.size << 2) > + > +static const void *find_extension_tag(const char *buf, > + unsigned long len, > + uint32_t tag_id) > +{ > + const struct arm_zimage_header *h = (const struct arm_zimage_header *)buf; > + const struct arm_zimage_tag *tag; > + uint32_t offset, size; > + uint32_t max = len - sizeof(struct tag_header); > + > + if (len < sizeof(*h) || > + h->magic != ARM_ZIMAGE_MAGIC1 || > + h->magic2 != ARM_ZIMAGE_MAGIC2) > + return NULL; > + > + for (offset = h->extension_tag_offset; > + (tag = (void *)(buf + offset)) != NULL && > + offset < max && > + (size = le32_to_cpu(byte_size(tag))) != 0 && > + offset + size < len; > + offset += size) { > + pr_debug(" offset 0x%08x tag 0x%08x size %u\n", > + offset, le32_to_cpu(tag->hdr.tag), size); > + if (tag->hdr.tag == tag_id) > + return tag; > + } > + > + return NULL; > +} > + > +static int zimage_probe(const char *kernel_buf, unsigned long kernel_len) > +{ > + const struct arm_zimage_header *h = > + (struct arm_zimage_header *)(kernel_buf); > + > + if (!h || (kernel_len < sizeof(*h))) > + return -EINVAL; > + > + if ((h->magic != ARM_ZIMAGE_MAGIC1) || > + (h->magic2 != ARM_ZIMAGE_MAGIC2)) > + return -EINVAL; > + > + return 0; > +} > + > + > +#if defined(DEBUG) > +#define debug_offsets() ({ \ > + pr_debug("Image offsets:\n"); \ > + pr_debug(" kernel 0x%08lx 0x%08lx\n", kernel_offset, kernel_len); \ > + pr_debug(" zimage 0x%08lx 0x%08lx\n", zimage_offset, zimage_len); \ > + pr_debug(" initrd 0x%08lx 0x%08lx\n", initrd_offset, initrd_len); \ > +}) > +#else > +#define debug_offsets() > +#endif > + > +static void *zimage_load(struct kimage *image, > + char *zimage, unsigned long zimage_len, > + char *initrd, unsigned long initrd_len, > + char *cmdline, unsigned long cmdline_len) > +{ > + struct arm_zimage_header *h; > + struct kexec_buf kbuf; > + struct kexec_segment *zimage_segment; > + const struct arm_zimage_tag *klsz_tag; > + const struct arm_zimage_tag_dc *dcsz_tag; > + int ret = -EINVAL; > + > + unsigned long zimage_mem = 0x20000; /* malloc 64kB + stack 4 kB + some bss */ > + unsigned long kernel_len = zimage_len * 4; /* 4:1 compression */ This has been proven wrong. > + unsigned long kernel_offset = memblock_start_of_DRAM() + > + ALIGN(TEXT_OFFSET, PAGE_SIZE); TEXT_OFFSET is actually a property of the loaded kernel, not of the currently running kernel. I have a patch to add that into the zImage tag. > + unsigned long zimage_offset = kernel_offset + > + ALIGN(kernel_len, PAGE_SIZE); > + unsigned long initrd_offset = zimage_offset + > + ALIGN(zimage_len + zimage_mem, PAGE_SIZE); Since kernel_len is wrong, these will be wrong... please only fall back to this if you don't find the extension tag - in other words, declare the variables here, but don't initialise them, set them lower down in the file if we fail to find the extension tag. > + > + if (image->type == KEXEC_TYPE_CRASH) { > + kernel_offset += crashk_res.start; > + zimage_offset += crashk_res.start; > + initrd_offset += crashk_res.start; > + } > + debug_offsets(); > + > + h = (struct arm_zimage_header *)zimage; > + > + klsz_tag = find_extension_tag(zimage, zimage_len, ZIMAGE_TAG_KRNL_SIZE); > + if (klsz_tag) { > + uint32_t *p = (void *)zimage + > + le32_to_cpu(klsz_tag->u.krnl_size.size_ptr); > + uint32_t edata_size = le32_to_cpu(get_unaligned(p)); > + uint32_t bss_size = le32_to_cpu(klsz_tag->u.krnl_size.bss_size); > + > + kernel_len = edata_size + bss_size; > + > + pr_debug("Decompressed kernel sizes:\n"); > + pr_debug(" text+data 0x%08lx bss 0x%08lx total 0x%08lx\n", > + (unsigned long)edata_size, > + (unsigned long)bss_size, > + (unsigned long)kernel_len); > + > + zimage_offset = kernel_offset + ALIGN(edata_size, PAGE_SIZE); > + initrd_offset = zimage_offset + > + max(ALIGN(zimage_len + 0x20000, PAGE_SIZE), > + ALIGN((unsigned long)bss_size, PAGE_SIZE)); > + debug_offsets(); This looks incorrect to me. Please see the kexec tool - what its doing in its corresponding section that you copied some of this code from is carefully thought out and can't be simplified. Ergo, I think this is wrong. > + } > + > + dcsz_tag = find_extension_tag(zimage, zimage_len, > + ZIMAGE_TAG_DECOMP_SIZE); > + if (dcsz_tag) { > + uint32_t bss_size = le32_to_cpu(dcsz_tag->u.decomp_size.bss_size); > + uint32_t stack_size = le32_to_cpu(dcsz_tag->u.decomp_size.stack_size); > + uint32_t malloc_size = le32_to_cpu(dcsz_tag->u.decomp_size.malloc_size); > + > + zimage_mem = bss_size + stack_size + malloc_size; > + > + pr_debug("Decompressor memory requirements:\n"); > + pr_debug(" bss 0x%08lx stack 0x%08lx malloc 0x%08lx total 0x%08lx\n", > + (unsigned long)bss_size, > + (unsigned long)stack_size, > + (unsigned long)malloc_size, > + zimage_mem); > + > + initrd_offset = max(ALIGN(zimage_offset + zimage_len + > + bss_size + stack_size + > + malloc_size, PAGE_SIZE), > + ALIGN(kernel_offset + kernel_len, PAGE_SIZE)); > + debug_offsets(); > + } > + > + /* > + * zImage MUST be loaded into the first 128 MiB of physical > + * memory for proper memory detection. Should the uncompressed > + * kernel be larger than 128 MiB, zImage relocation becomes > + * unavoidable and it is best to rely on the relocation code. > + */ > + if (((zimage_offset - kernel_offset) + PAGE_SIZE + 0x8000) >= SZ_128M) { > + pr_debug("The kernel is too big (%ld MiB) to avoid " > + "zImage relocation. Loading zimage at 0x%08lx\n", > + ((zimage_offset - kernel_offset) >> 20), > + kernel_offset); > + zimage_offset = kernel_offset; > + } > + > + kbuf.image = image; > + kbuf.top_down = false; > + > + kbuf.buf_min = zimage_offset; > + kbuf.buf_max = ULONG_MAX; > + kbuf.buffer = zimage; > + kbuf.bufsz = zimage_len; > + kbuf.buf_align = PAGE_SIZE; > + > + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; > + kbuf.memsz = zimage_len; > + > + ret = kexec_add_buffer(&kbuf); > + if (ret) > + return ERR_PTR(ret); > + > + pr_debug("Loaded zImage at 0x%lx bufsz=0x%lx memsz=0x%lx\n", > + kbuf.mem, kbuf.bufsz, kbuf.memsz); > + > + initrd_offset += kbuf.mem - zimage_offset; > + debug_offsets(); > + > + zimage_segment = &image->segment[image->nr_segments - 1]; > + image->start = zimage_segment->mem; > + > + ret = load_other_segments(image, > + zimage_segment->mem, zimage_segment->memsz, > + initrd, initrd_len, initrd_offset, > + cmdline); > + return ERR_PTR(ret); > +} > + > +const struct kexec_file_ops kexec_zimage_ops = { > + .probe = zimage_probe, > + .load = zimage_load, > +}; > diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c > index c10a2dfd53d1..2e4780efabb4 100644 > --- a/arch/arm/kernel/machine_kexec.c > +++ b/arch/arm/kernel/machine_kexec.c > @@ -93,10 +93,13 @@ int machine_kexec_prepare(struct kimage *image) > current_segment->memsz)) > return -EINVAL; > > - err = get_user(header, (__be32*)current_segment->buf); > - if (err) > - return err; > - > + if (image->file_mode) { > + header = *(__be32 *)current_segment->buf; > + } else { > + err = get_user(header, (__be32 *)current_segment->buf); > + if (err) > + return err; > + } > if (header == cpu_to_be32(OF_DT_HEADER)) > image->arch.kernel_r2 = current_segment->mem; > } > diff --git a/arch/arm/kernel/machine_kexec_file.c b/arch/arm/kernel/machine_kexec_file.c > new file mode 100644 > index 000000000000..ead680f1e795 > --- /dev/null > +++ b/arch/arm/kernel/machine_kexec_file.c > @@ -0,0 +1,209 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * kexec_file for arm > + * > + * Copyright (C) 2018 Linaro Limited > + * Copyright (C) 2020 Samsung Electronics > + * Authors: > + * AKASHI Takahiro <takahiro.akashi@linaro.org> > + * Łukasz Stelmach <l.stelmach@samsung.com> > + * > + */ > + > +#define pr_fmt(fmt) "kexec_file: " fmt > + > +#include <linux/kexec.h> > +#include <linux/libfdt.h> > +#include <linux/of_fdt.h> > +#include <linux/random.h> > + > +/* relevant device tree properties */ > +#define FDT_PROP_INITRD_START "linux,initrd-start" > +#define FDT_PROP_INITRD_END "linux,initrd-end" > +#define FDT_PROP_BOOTARGS "bootargs" > +#define FDT_PROP_RNG_SEED "rng-seed" > + > +static int setup_dtb(struct kimage *image, > + unsigned long initrd_load_addr, unsigned long initrd_len, > + char *cmdline, void *dtb) > +{ > + int off, ret; > + > + ret = fdt_path_offset(dtb, "/chosen"); > + if (ret < 0) > + goto out; > + > + off = ret; > + > + /* add bootargs */ > + if (cmdline) { > + ret = fdt_setprop_string(dtb, off, FDT_PROP_BOOTARGS, cmdline); > + if (ret) > + goto out; > + } else { > + ret = fdt_delprop(dtb, off, FDT_PROP_BOOTARGS); > + if (ret && (ret != -FDT_ERR_NOTFOUND)) > + goto out; > + } > + > + /* add initrd-* */ > + if (initrd_load_addr) { > + ret = fdt_setprop_u64(dtb, off, FDT_PROP_INITRD_START, > + initrd_load_addr); > + if (ret) > + goto out; > + > + ret = fdt_setprop_u64(dtb, off, FDT_PROP_INITRD_END, > + initrd_load_addr + initrd_len); > + if (ret) > + goto out; > + } else { > + ret = fdt_delprop(dtb, off, FDT_PROP_INITRD_START); > + if (ret && (ret != -FDT_ERR_NOTFOUND)) > + goto out; > + > + ret = fdt_delprop(dtb, off, FDT_PROP_INITRD_END); > + if (ret && (ret != -FDT_ERR_NOTFOUND)) > + goto out; > + } > + > + /* add rng-seed */ > + if (rng_is_initialized()) { > + char seed[128]; > + get_random_bytes(seed, sizeof(seed)); > + > + ret = fdt_setprop(dtb, off, FDT_PROP_RNG_SEED, > + seed, sizeof(seed)); > + if (ret) > + goto out; > + } else { > + pr_notice("RNG is not initialised: omitting \"%s\" property\n", > + FDT_PROP_RNG_SEED); > + ret = 0; > + } > + > +out: > + if (ret) > + return (ret == -FDT_ERR_NOSPACE) ? -ENOMEM : -EINVAL; > + > + return 0; > +} > +/* > + * More space needed so that we can add initrd, bootargs and kaslr-seed. > + */ > +#define DTB_EXTRA_SPACE 0x1000 > + > +static int create_dtb(struct kimage *image, > + unsigned long initrd_load_addr, unsigned long initrd_len, > + char *cmdline, void **dtb) > +{ > + void *buf; > + size_t buf_size; > + size_t cmdline_len; > + int ret; > + > + cmdline_len = cmdline ? strlen(cmdline) : 0; > + buf_size = fdt_totalsize(initial_boot_params) > + + cmdline_len + DTB_EXTRA_SPACE; > + > + for (;;) { > + buf = vmalloc(buf_size); > + if (!buf) > + return -ENOMEM; > + > + /* duplicate a device tree blob */ > + ret = fdt_open_into(initial_boot_params, buf, buf_size); > + if (ret) > + return -EINVAL; > + > + ret = setup_dtb(image, initrd_load_addr, initrd_len, > + cmdline, buf); > + if (ret) { > + vfree(buf); > + if (ret == -ENOMEM) { > + /* unlikely, but just in case */ > + buf_size += DTB_EXTRA_SPACE; > + continue; > + } else { > + return ret; > + } > + } > + > + /* trim it */ > + fdt_pack(buf); > + *dtb = buf; > + > + return 0; > + } > +} > + > +int load_other_segments(struct kimage *image, > + unsigned long zimage_load_addr, > + unsigned long zimage_len, > + char *initrd, > + unsigned long initrd_len, > + unsigned long initrd_offset, > + char *cmdline) > +{ > + struct kexec_buf kbuf; > + void *dtb = NULL; > + unsigned long initrd_load_addr = 0; > + unsigned long dtb_len; > + int ret = 0; > + > + kbuf.image = image; > + /* not allocate anything below the kernel */ > + kbuf.buf_min = initrd_offset; > + if (initrd) { > + kbuf.buffer = initrd; > + kbuf.bufsz = initrd_len; > + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; > + kbuf.memsz = initrd_len; > + kbuf.buf_align = PAGE_SIZE; > + kbuf.buf_max = ULONG_MAX; > + kbuf.top_down = false; > + > + ret = kexec_add_buffer(&kbuf); > + if (ret) > + goto out_err; > + > + pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n", > + kbuf.mem, kbuf.bufsz, kbuf.memsz); > + > + initrd_load_addr = kbuf.mem; > + kbuf.buf_min = initrd_load_addr + kbuf.memsz; > + } > + > + /* load dtb */ > + ret = create_dtb(image, initrd_load_addr, initrd_len, cmdline, &dtb); > + if (ret) { > + pr_err("Preparing for new dtb failed\n"); > + goto out_err; > + } > + > + dtb_len = fdt_totalsize(dtb); > + kbuf.buffer = dtb; > + kbuf.bufsz = dtb_len; > + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; > + kbuf.memsz = dtb_len; > + kbuf.buf_align = PAGE_SIZE; > + kbuf.buf_max = ULONG_MAX; > + kbuf.top_down = false; > + > + ret = kexec_add_buffer(&kbuf); > + if (ret) > + goto out_err; > + > + pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n", > + kbuf.mem, kbuf.bufsz, kbuf.memsz); > + return 0; > +out_err: > + vfree(dtb); > + return ret; > +} > + > +const struct kexec_file_ops * const kexec_file_loaders[] = { > + &kexec_uimage_ops, > + &kexec_zimage_ops, > + NULL > +}; > -- > 2.26.2 > > -- RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ FTTC for 0.8m (est. 1762m) line in suburbia: sync at 13.1Mbps down 424kbps up _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
next prev parent reply other threads:[~2020-06-01 15:08 UTC|newest] Thread overview: 76+ messages / expand[flat|nested] mbox.gz Atom feed top [not found] <CGME20200601142806eucas1p2680c5625411e7a695d8469760a926520@eucas1p2.samsung.com> 2020-06-01 14:27 ` [PATCH 0/5] kexec_file_load() for arm Łukasz Stelmach 2020-06-01 14:27 ` Łukasz Stelmach [not found] ` <CGME20200601142810eucas1p1767585cf172d26aedb551d7453aa7402@eucas1p1.samsung.com> 2020-06-01 14:27 ` [PATCH 1/5] arm: decompressor: set malloc pool size for the decompressor Łukasz Stelmach 2020-06-01 14:27 ` Łukasz Stelmach 2020-06-01 14:46 ` Russell King - ARM Linux admin 2020-06-01 14:46 ` Russell King - ARM Linux admin [not found] ` <CGME20200601145652eucas1p11dcea1cea21824d0a6bfe6ab38c1cab7@eucas1p1.samsung.com> 2020-06-01 14:56 ` Lukasz Stelmach 2020-06-01 14:56 ` Lukasz Stelmach 2020-06-01 15:10 ` Russell King - ARM Linux admin 2020-06-01 15:10 ` Russell King - ARM Linux admin [not found] ` <CGME20200601165420eucas1p273e0cdb143312b9a621e2444c5daae9b@eucas1p2.samsung.com> 2020-06-01 16:54 ` Lukasz Stelmach 2020-06-01 16:54 ` Lukasz Stelmach [not found] ` <CGME20200601142810eucas1p23056f7997a880ff7d676c64703f87115@eucas1p2.samsung.com> 2020-06-01 14:27 ` [PATCH 2/5] arm: add image header definitions Łukasz Stelmach 2020-06-01 14:27 ` Łukasz Stelmach [not found] ` <CGME20200601142810eucas1p1c42ff7c9b417f04bc506261726f08b4f@eucas1p1.samsung.com> 2020-06-01 14:27 ` [PATCH 3/5] arm: decompressor: define a new zImage tag Łukasz Stelmach 2020-06-01 14:27 ` Łukasz Stelmach 2020-06-01 14:55 ` Russell King - ARM Linux admin 2020-06-01 14:55 ` Russell King - ARM Linux admin [not found] ` <CGME20200601162002eucas1p28eb08a42de6f313458e9391bd5976e90@eucas1p2.samsung.com> 2020-06-01 16:19 ` Lukasz Stelmach 2020-06-01 16:19 ` Lukasz Stelmach 2020-06-01 18:25 ` Russell King - ARM Linux admin 2020-06-01 18:25 ` Russell King - ARM Linux admin [not found] ` <CGME20200601202757eucas1p11d380be9e0b2fe912a358d21e2d8dc2a@eucas1p1.samsung.com> 2020-06-01 20:27 ` Lukasz Stelmach 2020-06-01 20:27 ` Lukasz Stelmach 2020-06-01 20:41 ` Russell King - ARM Linux admin 2020-06-01 20:41 ` Russell King - ARM Linux admin [not found] ` <CGME20200602161720eucas1p257e9e892ed4679ed1d168db34d089a82@eucas1p2.samsung.com> 2020-06-02 16:17 ` Lukasz Stelmach 2020-06-02 16:17 ` Lukasz Stelmach [not found] ` <CGME20200601142811eucas1p260e5a434ea7743eecdb37c4d975c5f05@eucas1p2.samsung.com> 2020-06-01 14:27 ` [PATCH 4/5] arm: Add kexec_image_info Łukasz Stelmach 2020-06-01 14:27 ` Łukasz Stelmach 2020-06-01 14:56 ` Russell King - ARM Linux admin 2020-06-01 14:56 ` Russell King - ARM Linux admin [not found] ` <CGME20200601163034eucas1p1f9c726b605c18bf3944254cd83dd67b3@eucas1p1.samsung.com> 2020-06-01 16:30 ` Lukasz Stelmach 2020-06-01 16:30 ` Lukasz Stelmach [not found] ` <CGME20200601142811eucas1p1604c8e6ca06c09f1ec821ea5e1918c53@eucas1p1.samsung.com> 2020-06-01 14:27 ` [PATCH 5/5] arm: kexec_file: load zImage or uImage, initrd and dtb Łukasz Stelmach 2020-06-01 14:27 ` Łukasz Stelmach 2020-06-01 15:07 ` Russell King - ARM Linux admin [this message] 2020-06-01 15:07 ` Russell King - ARM Linux admin 2020-06-01 15:14 ` Russell King - ARM Linux admin 2020-06-01 15:14 ` Russell King - ARM Linux admin [not found] ` <CGME20200601164700eucas1p2e30af458bae7e820ca55f7936ac3579a@eucas1p2.samsung.com> 2020-06-01 16:46 ` Lukasz Stelmach 2020-06-01 16:46 ` Lukasz Stelmach [not found] ` <CGME20200601184829eucas1p1b06bfc130083f6248d624febed1de9fc@eucas1p1.samsung.com> 2020-06-01 18:48 ` Lukasz Stelmach 2020-06-01 18:48 ` Lukasz Stelmach [not found] ` <CGME20200602161737eucas1p241dd0e0a9b5eea7c5d5774c46b3c570b@eucas1p2.samsung.com> 2020-06-02 16:17 ` [PATCH v2 0/5] kexec_file_load() for arm Łukasz Stelmach 2020-06-02 16:17 ` Łukasz Stelmach [not found] ` <CGME20200602161737eucas1p2c83700f7c17296c4367ee3fda1c6e783@eucas1p2.samsung.com> 2020-06-02 16:17 ` [PATCH v2 1/5] arm: decompressor: set malloc pool size for the decompressor Łukasz Stelmach 2020-06-02 16:17 ` Łukasz Stelmach [not found] ` <CGME20200602161738eucas1p27dfbe386bd76555598d5574faf4fdad3@eucas1p2.samsung.com> 2020-06-02 16:17 ` [PATCH v2 2/5] arm: add image header definitions Łukasz Stelmach 2020-06-02 16:17 ` Łukasz Stelmach [not found] ` <CGME20200602161738eucas1p2151f88b526bb009c27820a4f290a961e@eucas1p2.samsung.com> 2020-06-02 16:17 ` [PATCH v2 3/5] arm: decompressor: define a new zImage tag Łukasz Stelmach 2020-06-02 16:17 ` Łukasz Stelmach [not found] ` <CGME20200602161738eucas1p2ccfaa7610dc6f76e209ba96d6278259e@eucas1p2.samsung.com> 2020-06-02 16:17 ` [PATCH v2 4/5] arm: Add kexec_image_info Łukasz Stelmach 2020-06-02 16:17 ` Łukasz Stelmach [not found] ` <CGME20200602161739eucas1p16a56ff590bf44e747d5ad6e178d57067@eucas1p1.samsung.com> 2020-06-02 16:17 ` [PATCH v2 5/5] arm: kexec_file: load zImage or uImage, initrd and dtb Łukasz Stelmach 2020-06-02 16:17 ` Łukasz Stelmach 2020-06-11 10:37 ` [PATCH 0/5] kexec_file_load() for arm Dave Young 2020-06-11 10:37 ` Dave Young 2020-06-11 10:37 ` Dave Young [not found] ` <CGME20200930183921eucas1p11a56f805421a614be67f869f5ed18b9b@eucas1p1.samsung.com> 2020-09-30 18:34 ` [PATCH v3 0/4] " Łukasz Stelmach 2020-09-30 18:34 ` Łukasz Stelmach 2020-09-30 18:34 ` Łukasz Stelmach [not found] ` <CGME20200930183923eucas1p2241d3e1b8d4d05a2228448ff8fc4bb74@eucas1p2.samsung.com> 2020-09-30 18:34 ` [PATCH v3 1/4] arm: add image header definitions Łukasz Stelmach 2020-09-30 18:34 ` Łukasz Stelmach 2020-09-30 18:34 ` Łukasz Stelmach [not found] ` <CGME20200930183924eucas1p2869520cc28e705073cb08c37c1e2fc6d@eucas1p2.samsung.com> 2020-09-30 18:34 ` [PATCH v3 2/4] arm: decompressor: define a new zImage tag Łukasz Stelmach 2020-09-30 18:34 ` Łukasz Stelmach 2020-09-30 18:34 ` Łukasz Stelmach [not found] ` <CGME20200930183924eucas1p1eba72052e1723ce75e00cbadbe03b6fa@eucas1p1.samsung.com> 2020-09-30 18:34 ` [PATCH v3 3/4] arm: Add kexec_image_info Łukasz Stelmach 2020-09-30 18:34 ` Łukasz Stelmach 2020-09-30 18:34 ` Łukasz Stelmach [not found] ` <CGME20200930183924eucas1p281730f3d651fc2c78d6a95e47a2c5220@eucas1p2.samsung.com> 2020-09-30 18:34 ` [PATCH v3 4/4] arm: kexec_file: load zImage or uImage, initrd and dtb Łukasz Stelmach 2020-09-30 18:34 ` Łukasz Stelmach 2020-09-30 18:34 ` Łukasz Stelmach 2020-10-01 10:09 ` kernel test robot 2020-10-01 10:09 ` kernel test robot
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20200601150745.GL1551@shell.armlinux.org.uk \ --to=linux@armlinux.org.uk \ --cc=b.zolnierkie@samsung.com \ --cc=ben-linux@fluff.org \ --cc=info@metux.net \ --cc=keescook@chromium.org \ --cc=l.stelmach@samsung.com \ --cc=linux-arm-kernel@lists.infradead.org \ --cc=linux-kernel@vger.kernel.org \ --cc=m.szyprowski@samsung.com \ --cc=masahiroy@kernel.org \ --cc=mingo@kernel.org \ --cc=ndesaulniers@google.com \ --cc=takahiro.akashi@linaro.org \ --cc=tglx@linutronix.de \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.