All of lore.kernel.org
 help / color / mirror / Atom feed
From: Simon Glass <sjg@chromium.org>
To: U-Boot Mailing List <u-boot@lists.denx.de>
Cc: Tom Rini <trini@konsulko.com>, Simon Glass <sjg@chromium.org>,
	Devarsh Thakkar <devarsht@ti.com>,
	Gary Bisson <bisson.gary@gmail.com>,
	Greg Malysa <greg.malysa@timesys.com>,
	Jerome Forissier <jerome.forissier@linaro.org>,
	Lukas Funke <lukas.funke@weidmueller.com>,
	Marek Vasut <marex@denx.de>, Michal Simek <michal.simek@amd.com>,
	Nathan Barrett-Morrison <nathan.morrison@timesys.com>,
	Oliver Gaskell <Oliver.Gaskell@analog.com>,
	Paul Kocialkowski <contact@paulk.fr>, Peng Fan <peng.fan@nxp.com>,
	Samuel Holland <samuel@sholland.org>,
	Sean Anderson <seanga2@gmail.com>,
	Trevor Woerner <twoerner@gmail.com>
Subject: [PATCH 11/15] spl: Add support for a relocating jump to the next phase
Date: Thu,  9 Jan 2025 05:30:06 -0700	[thread overview]
Message-ID: <20250109123010.4005298-12-sjg@chromium.org> (raw)
In-Reply-To: <20250109123010.4005298-1-sjg@chromium.org>

When one xPL phase wants to jump to the next, the next phase must be
loaded into its required address. This means that the TEXT_BASE for the
two phases must be different and there cannot be any memory overlap
between the code used by the two phases. It also can mean that phases
need to be moved around to accommodate any size growth.

Having two xPL phases in SRAM at the same time can be tricky if SRAM
is limited, which it often is. It would be better if the second phase
could be loaded somewhere else, then decompressed into place over the
top of the first phase.

Introduce a relocating jump for xPL to support this. This selects a
suitable place to load the (typically compressed) next phase, copies
some decompression code out of the first phase, then jumps to this code
to decompress and start the next phase.

This feature makes it much easier to support Verified Boot for Embedded
(VBE) on RK3399 boards, which have 192KB of SRAM.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 common/spl/Kconfig     |   8 ++
 common/spl/Kconfig.tpl |   8 ++
 common/spl/Kconfig.vpl |   8 ++
 common/spl/Makefile    |   1 +
 common/spl/spl_reloc.c | 183 +++++++++++++++++++++++++++++++++++++++++
 include/spl.h          |  45 ++++++++++
 6 files changed, 253 insertions(+)
 create mode 100644 common/spl/spl_reloc.c

diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index 045fcac10a5..a250bde779f 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -981,6 +981,14 @@ config SPL_NAND_IDENT
 	help
 	  SPL uses the chip ID list to identify the NAND flash.
 
+config SPL_RELOC_LOADER
+	bool "Allow relocating the next phase"
+	help
+	  In some cases multiple U-Boot phases need to run in SRAM, typically
+	  at the same address. Enable this to support loading the next phase
+	  to temporary memory, then copying it into place afterwards, then
+	  jumping to it.
+
 config SPL_UBI
 	bool "Support UBI"
 	help
diff --git a/common/spl/Kconfig.tpl b/common/spl/Kconfig.tpl
index 92d4d43ec87..22ca7016453 100644
--- a/common/spl/Kconfig.tpl
+++ b/common/spl/Kconfig.tpl
@@ -268,6 +268,14 @@ config TPL_RAM_DEVICE
 	  be already in memory when TPL takes over, e.g. loaded by the boot
 	  ROM.
 
+config TPL_RELOC_LOADER
+	bool "Allow relocating the next phase"
+	help
+	  In some cases multiple U-Boot phases need to run in SRAM, typically
+	  at the same address. Enable this to support loading the next phase
+	  to temporary memory, then copying it into place afterwards, then
+	  jumping to it.
+
 config TPL_RTC
 	bool "Support RTC drivers"
 	help
diff --git a/common/spl/Kconfig.vpl b/common/spl/Kconfig.vpl
index eb57dfabea5..97dfc630152 100644
--- a/common/spl/Kconfig.vpl
+++ b/common/spl/Kconfig.vpl
@@ -181,6 +181,14 @@ config VPL_PCI
 	  necessary driver support. This enables the drivers in drivers/pci
 	  as part of a VPL build.
 
+config VPL_RELOC_LOADER
+	bool "Allow relocating the next phase"
+	help
+	  In some cases multiple U-Boot phases need to run in SRAM, typically
+	  at the same address. Enable this to support loading the next phase
+	  to temporary memory, then copying it into place afterwards, then
+	  jumping to it.
+
 config VPL_RTC
 	bool "Support RTC drivers"
 	help
diff --git a/common/spl/Makefile b/common/spl/Makefile
index 75123eb666b..4c9482bd309 100644
--- a/common/spl/Makefile
+++ b/common/spl/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_$(PHASE_)BOOTROM_SUPPORT) += spl_bootrom.o
 obj-$(CONFIG_$(PHASE_)LOAD_FIT) += spl_fit.o
 obj-$(CONFIG_$(PHASE_)BLK_FS) += spl_blk_fs.o
 obj-$(CONFIG_$(PHASE_)LEGACY_IMAGE_FORMAT) += spl_legacy.o
+obj-$(CONFIG_$(PHASE_)RELOC_LOADER) += spl_reloc.o
 obj-$(CONFIG_$(PHASE_)NOR_SUPPORT) += spl_nor.o
 obj-$(CONFIG_$(PHASE_)XIP_SUPPORT) += spl_xip.o
 obj-$(CONFIG_$(PHASE_)YMODEM_SUPPORT) += spl_ymodem.o
diff --git a/common/spl/spl_reloc.c b/common/spl/spl_reloc.c
new file mode 100644
index 00000000000..be8349b535b
--- /dev/null
+++ b/common/spl/spl_reloc.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2024 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <gzip.h>
+#include <image.h>
+#include <log.h>
+#include <mapmem.h>
+#include <spl.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+#include <asm/unaligned.h>
+#include <linux/types.h>
+#include <lzma/LzmaTypes.h>
+#include <lzma/LzmaDec.h>
+#include <lzma/LzmaTools.h>
+#include <u-boot/crc.h>
+#include <u-boot/lz4.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* provide a way to jump straight into the relocation code, for debugging */
+#define DEBUG_JUMP	0
+
+enum {
+	/* margin to allow for stack growth */
+	RELOC_STACK_MARGIN	= 0x800,
+
+	/* align base address for DMA controllers which require it */
+	BASE_ALIGN		= 0x200,
+
+	STACK_PROT_VALUE	= 0x51ce4697,
+};
+
+typedef int (*rcode_func)(struct spl_image_info *image);
+
+static int setup_layout(struct spl_image_info *image, ulong *addrp)
+{
+	ulong base, fdt_size;
+	ulong limit, rcode_base;
+	uint rcode_size;
+	int buf_size, margin;
+	char *rcode_buf;
+
+	limit = ALIGN(map_to_sysmem(&limit) - RELOC_STACK_MARGIN, 8);
+	image->stack_prot = map_sysmem(limit, sizeof(uint));
+	*image->stack_prot = STACK_PROT_VALUE;
+
+	fdt_size = fdt_totalsize(gd->fdt_blob);
+	base = ALIGN(map_to_sysmem(gd->fdt_blob) + fdt_size + BASE_ALIGN - 1,
+		     BASE_ALIGN);
+
+	rcode_size = _rcode_end - _rcode_start;
+	rcode_base = limit - rcode_size;
+	buf_size = rcode_base - base;
+	uint need_size = image->size + image->fdt_size;
+	margin = buf_size - need_size;
+	log_debug("spl_reloc %s->%s: margin%s%lx limit %lx fdt_size %lx base %lx avail %x image %x fdt %lx need %x\n",
+		  spl_phase_name(spl_phase()), spl_phase_name(spl_phase() + 1),
+		  margin >= 0 ? " " : " -", abs(margin), limit, fdt_size, base,
+		  buf_size, image->size, image->fdt_size, need_size);
+	if (margin < 0) {
+		log_err("Image size %x but buffer is only %x\n", need_size,
+			buf_size);
+		return -ENOSPC;
+	}
+
+	rcode_buf = map_sysmem(rcode_base, rcode_size);
+	log_debug("_rcode_start %p: %x -- func %p %x\n", _rcode_start,
+		  *(uint *)_rcode_start, setup_layout, *(uint *)setup_layout);
+
+	image->reloc_offset = rcode_buf - _rcode_start;
+	log_debug("_rcode start %lx base %lx size %x offset %lx\n",
+		  (ulong)map_to_sysmem(_rcode_start), rcode_base, rcode_size,
+		  image->reloc_offset);
+
+	memcpy(rcode_buf, _rcode_start, rcode_size);
+
+	image->buf = map_sysmem(base, need_size);
+	image->fdt_buf = image->buf + image->size;
+	image->rcode_buf = rcode_buf;
+	*addrp = base;
+
+	return 0;
+}
+
+int spl_reloc_prepare(struct spl_image_info *image, ulong *addrp)
+{
+	int ret;
+
+	ret = setup_layout(image, addrp);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+typedef void __noreturn (*image_entry_noargs_t)(uint crc, uint unc_len);
+
+/* this is the relocation + jump code that is copied to the top of memory */
+__rcode int rcode_reloc_and_jump(struct spl_image_info *image)
+{
+	image_entry_noargs_t entry = (image_entry_noargs_t)image->entry_point;
+	u32 *dst;
+	ulong image_len;
+	size_t unc_len;
+	int ret, crc;
+	uint magic;
+
+	dst = map_sysmem(image->load_addr, image->size);
+	unc_len = (void *)image->rcode_buf - (void *)dst;
+	image_len = image->size;
+	if (*image->stack_prot != STACK_PROT_VALUE)
+		return -EFAULT;
+	magic = get_unaligned_le32(image->buf);
+	if (CONFIG_IS_ENABLED(LZMA)) {
+		SizeT lzma_len = unc_len;
+
+		ret = lzmaBuffToBuffDecompress((u8 *)dst, &lzma_len,
+					       image->buf, image_len);
+		unc_len = lzma_len;
+	} else if (CONFIG_IS_ENABLED(GZIP)) {
+		ret = gunzip(dst, unc_len, image->buf, &image_len);
+	} else if (CONFIG_IS_ENABLED(LZ4) && magic == LZ4F_MAGIC) {
+		ret = ulz4fn(image->buf, image_len, dst, &unc_len);
+		if (ret)
+			return ret;
+	} else {
+		u32 *src, *end, *ptr;
+
+		unc_len = image->size;
+		for (src = image->buf, end = (void *)src + image->size,
+		     ptr = dst; src < end;)
+			*ptr++ = *src++;
+	}
+	if (*image->stack_prot != STACK_PROT_VALUE)
+		return -EFAULT;
+
+	/* copy in the FDT if needed */
+	if (image->fdt_size)
+		memcpy(image->fdt_start, image->fdt_buf, image->fdt_size);
+
+	crc = crc8(0, (u8 *)dst, unc_len);
+
+	/* jump to the entry point */
+	entry(crc, unc_len);
+}
+
+int spl_reloc_jump(struct spl_image_info *image, spl_jump_to_image_t jump)
+{
+	rcode_func loader;
+	int ret;
+
+	log_debug("malloc usage %lx bytes (%ld KB of %d KB)\n", gd->malloc_ptr,
+		  gd->malloc_ptr / 1024, CONFIG_VAL(SYS_MALLOC_F_LEN) / 1024);
+
+	if (*image->stack_prot != STACK_PROT_VALUE) {
+		log_err("stack busted, cannot continue\n");
+		return -EFAULT;
+	}
+	loader = (rcode_func)(void *)rcode_reloc_and_jump + image->reloc_offset;
+	log_debug("Jumping via %p to %lx - image %p size %x load %lx\n", loader,
+		  image->entry_point, image, image->size, image->load_addr);
+
+	log_debug("unc_len %lx\n",
+		  image->rcode_buf - map_sysmem(image->load_addr, image->size));
+	if (DEBUG_JUMP) {
+		rcode_reloc_and_jump(image);
+	} else {
+		/*
+		 * Must disable LOG_DEBUG since the decompressor cannot call
+		 * log functions, printf(), etc.
+		 */
+		_Static_assert(DEBUG_JUMP || !_DEBUG,
+			       "Cannot have debug output from decompressor");
+		ret = loader(image);
+	}
+
+	return -EFAULT;
+}
diff --git a/include/spl.h b/include/spl.h
index 488adbeb1bc..9cfba98db55 100644
--- a/include/spl.h
+++ b/include/spl.h
@@ -270,6 +270,16 @@ enum spl_sandbox_flags {
  * struct spl_image_info - Information about the SPL image being loaded
  *
  * @fdt_size: Size of the FDT for the image (0 if none)
+ * @buf: Buffer where the image should be loaded
+ * @fdt_buf: Buffer where the FDT will be copied by spl_reloc_jump(), only used
+ *	if @fdt_size is non-zero
+ * @fdt_start: Pointer to the FDT to be copied (must be set up before calling
+ *	spl_reloc_jump()
+ * @rcode_buf: Buffer to hold the relocating-jump code
+ * @stack_prot: Pointer to the stack-protection value, used to ensure the stack
+ *	does not overflow
+ * @reloc_offset: offset between the relocating-jump code and its place in the
+ *	currently running image
  */
 struct spl_image_info {
 	const char *name;
@@ -290,6 +300,14 @@ struct spl_image_info {
 	ulong dcrc_length;
 	ulong dcrc;
 #endif
+#if CONFIG_IS_ENABLED(RELOC_LOADER)
+	void *buf;
+	void *fdt_buf;
+	void *fdt_start;
+	void *rcode_buf;
+	uint *stack_prot;
+	ulong reloc_offset;
+#endif
 };
 
 /* function to jump to an image from SPL */
@@ -1155,4 +1173,31 @@ int spl_write_upl_handoff(struct spl_image_info *spl_image);
  */
 void spl_upl_init(void);
 
+/**
+ * spl_reloc_prepare() - Prepare the relocating loader ready for use
+ *
+ * Sets up the relocating loader ready for use. This must be called before
+ * spl_reloc_jump() can be used.
+ *
+ * The memory layout is figured out, making use of the space between the top of
+ * the current image and the top of memory.
+ *
+ * Once this is done, the relocating-jump code is copied into place at
+ * image->rcode_buf
+ *
+ * @image: SPL image containing information. This is updated with various
+ * necessary values. On entry, the size and fdt_size fields must be valid
+ * @addrp: Returns the address to which the image should be loaded into memory
+ * Return 0 if OK, -ENOSPC if there is not enough memory available
+ */
+int spl_reloc_prepare(struct spl_image_info *image, ulong *addrp);
+
+/**
+ * spl_reloc_jump() - Jump to an image, via a 'relocating-jump' region
+ *
+ * @image: SPL image to jump to
+ * @func: Function to call in the final image
+ */
+int spl_reloc_jump(struct spl_image_info *image, spl_jump_to_image_t func);
+
 #endif
-- 
2.34.1


  parent reply	other threads:[~2025-01-09 12:32 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-01-09 12:29 [PATCH 00/15] vbe: Series part F Simon Glass
2025-01-09 12:29 ` [PATCH 01/15] vbe: Split out some VBE code into a common file Simon Glass
2025-01-14  1:22   ` Tom Rini
2025-01-14 12:58     ` Simon Glass
2025-01-14 13:26       ` Simon Glass
2025-01-14 16:58       ` Tom Rini
2025-01-14 17:33         ` Tom Rini
2025-01-15  1:18           ` Simon Glass
2025-01-15  1:41             ` Tom Rini
2025-01-15  2:48               ` Simon Glass
2025-01-15 14:10                 ` Simon Glass
2025-01-09 12:29 ` [PATCH 02/15] vbe: Split out reading a FIT " Simon Glass
2025-01-11 22:54   ` Tom Rini
2025-01-13 20:03     ` Simon Glass
2025-01-13 20:44       ` Tom Rini
2025-01-14  0:13         ` Simon Glass
2025-01-14  1:22           ` Tom Rini
2025-01-15 14:43             ` Tom Rini
2025-01-15 23:21               ` Simon Glass
2025-01-09 12:29 ` [PATCH 03/15] vbe: Allocate space for the FIT header Simon Glass
2025-01-09 12:29 ` [PATCH 04/15] vbe: Allow VBE to load FITs on any architecture Simon Glass
2025-01-09 12:30 ` [PATCH 05/15] vbe: Tidy up error checking with blk_read() Simon Glass
2025-01-09 12:30 ` [PATCH 06/15] vbe: Handle loading from an unaligned offset Simon Glass
2025-01-09 12:30 ` [PATCH 07/15] vbe: Allow loading loadables if there is no firmware Simon Glass
2025-01-09 12:30 ` [PATCH 08/15] vbe: Support loading an FDT from the FIT Simon Glass
2025-01-09 12:30 ` [PATCH 09/15] spl: Add fields for VBE Simon Glass
2025-01-09 12:30 ` [PATCH 10/15] spl: Add a type for the jumper function Simon Glass
2025-01-09 12:30 ` Simon Glass [this message]
2025-01-09 12:30 ` [PATCH 12/15] spl: Plumb in the relocating loader Simon Glass
2025-01-09 12:30 ` [PATCH 13/15] vbe: Support loading an FDT with " Simon Glass
2025-01-09 12:30 ` [PATCH 14/15] vbe: Support loading SPL images Simon Glass
2025-01-09 12:30 ` [PATCH 15/15] vbe: Update simple-fw to support using the SPL loader Simon Glass

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=20250109123010.4005298-12-sjg@chromium.org \
    --to=sjg@chromium.org \
    --cc=Oliver.Gaskell@analog.com \
    --cc=bisson.gary@gmail.com \
    --cc=contact@paulk.fr \
    --cc=devarsht@ti.com \
    --cc=greg.malysa@timesys.com \
    --cc=jerome.forissier@linaro.org \
    --cc=lukas.funke@weidmueller.com \
    --cc=marex@denx.de \
    --cc=michal.simek@amd.com \
    --cc=nathan.morrison@timesys.com \
    --cc=peng.fan@nxp.com \
    --cc=samuel@sholland.org \
    --cc=seanga2@gmail.com \
    --cc=trini@konsulko.com \
    --cc=twoerner@gmail.com \
    --cc=u-boot@lists.denx.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.