All of lore.kernel.org
 help / color / mirror / Atom feed
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

  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: link
Be 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.