From 9f407d4ef092c2ce7ab0f4f366aee252611dab3c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 15 Nov 2018 18:43:50 -0700 Subject: Add core support for a bloblist to convey data from SPL At present there is no standard way in U-Boot to pass information from SPL to U-Boot proper. But sometimes SPL wants to convey information to U-Boot that U-Boot cannot easily figure out. For example, if SPL sets up SDRAM then it might want to pass the size of SDRAM, or the location of each bank, to U-Boot proper. Add a new 'bloblist' feature which provides this. A bloblist is set up in the first phase of U-Boot that runs (i.e. TPL or SPL). The location of this info may be in SRAM or CAR (x86 cache-as-RAM) or somewhere else. Information placed in this region is preserved (with a checksum) through TPL and SPL and ends up in U-Boot. At this point it is copied into SDRAM so it can be used after relocation. Reviewed-by: Tom Rini Signed-off-by: Simon Glass Acked-by: Andreas Dannenberg --- common/Kconfig | 48 +++++++++++ common/Makefile | 1 + common/bloblist.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 common/bloblist.c (limited to 'common') diff --git a/common/Kconfig b/common/Kconfig index 68e4ddf..57bd16d 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -750,4 +750,52 @@ config UPDATE_TFTP_MSEC_MAX endmenu +menu "Blob list" + +config BLOBLIST + bool "Support for a bloblist" + help + This enables support for a bloblist in U-Boot, which can be passed + from TPL to SPL to U-Boot proper (and potentially to Linux). The + blob list supports multiple binary blobs of data, each with a tag, + so that different U-Boot components can store data which can survive + through to the next stage of the boot. + +config SPL_BLOBLIST + bool "Support for a bloblist in SPL" + depends on BLOBLIST + default y if SPL + help + This enables a bloblist in SPL. If this is the first part of U-Boot + to run, then the bloblist is set up in SPL and passed to U-Boot + proper. If TPL also has a bloblist, then SPL uses the one from there. + +config TPL_BLOBLIST + bool "Support for a bloblist in TPL" + depends on BLOBLIST + default y if TPL + help + This enables a bloblist in TPL. The bloblist is set up in TPL and + passed to SPL and U-Boot proper. + +config BLOBLIST_SIZE + hex "Size of bloblist" + depends on BLOBLIST + default 0x400 + help + Sets the size of the bloblist in bytes. This must include all + overhead (alignment, bloblist header, record header). The bloblist + is set up in the first part of U-Boot to run (TPL, SPL or U-Boot + proper), and this sane bloblist is used for subsequent stages. + +config BLOBLIST_ADDR + hex "Address of bloblist" + depends on BLOBLIST + default 0xe000 if SANDBOX + help + Sets the address of the bloblist, set up by the first part of U-Boot + which runs. Subsequent U-Boot stages typically use the same address. + +endmenu + source "common/spl/Kconfig" diff --git a/common/Makefile b/common/Makefile index a1edb7c..88079d1 100644 --- a/common/Makefile +++ b/common/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_CMDLINE) += cli_readline.o cli_simple.o endif # !CONFIG_SPL_BUILD obj-$(CONFIG_$(SPL_TPL_)BOOTSTAGE) += bootstage.o +obj-$(CONFIG_$(SPL_TPL_)BLOBLIST) += bloblist.o ifdef CONFIG_SPL_BUILD ifdef CONFIG_SPL_DFU_SUPPORT diff --git a/common/bloblist.c b/common/bloblist.c new file mode 100644 index 0000000..b4cf169 --- /dev/null +++ b/common/bloblist.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google, Inc + * Written by Simon Glass + */ + +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct bloblist_rec *bloblist_first_blob(struct bloblist_hdr *hdr) +{ + if (hdr->alloced <= hdr->hdr_size) + return NULL; + return (struct bloblist_rec *)((void *)hdr + hdr->hdr_size); +} + +struct bloblist_rec *bloblist_next_blob(struct bloblist_hdr *hdr, + struct bloblist_rec *rec) +{ + ulong offset; + + offset = (void *)rec - (void *)hdr; + offset += rec->hdr_size + ALIGN(rec->size, BLOBLIST_ALIGN); + if (offset >= hdr->alloced) + return NULL; + return (struct bloblist_rec *)((void *)hdr + offset); +} + +#define foreach_rec(_rec, _hdr) \ + for (_rec = bloblist_first_blob(_hdr); \ + _rec; \ + _rec = bloblist_next_blob(_hdr, _rec)) + +static struct bloblist_rec *bloblist_findrec(uint tag) +{ + struct bloblist_hdr *hdr = gd->bloblist; + struct bloblist_rec *rec; + + if (!hdr) + return NULL; + + foreach_rec(rec, hdr) { + if (rec->tag == tag) + return rec; + } + + return NULL; +} + +static int bloblist_addrec(uint tag, int size, struct bloblist_rec **recp) +{ + struct bloblist_hdr *hdr = gd->bloblist; + struct bloblist_rec *rec; + int new_alloced; + + new_alloced = hdr->alloced + sizeof(*rec) + + ALIGN(size, BLOBLIST_ALIGN); + if (new_alloced >= hdr->size) { + log(LOGC_BLOBLIST, LOGL_ERR, + "Failed to allocate %x bytes size=%x, need size>=%x\n", + size, hdr->size, new_alloced); + return log_msg_ret("bloblist add", -ENOSPC); + } + rec = (void *)hdr + hdr->alloced; + hdr->alloced = new_alloced; + + rec->tag = tag; + rec->hdr_size = sizeof(*rec); + rec->size = size; + rec->spare = 0; + *recp = rec; + + return 0; +} + +static int bloblist_ensurerec(uint tag, struct bloblist_rec **recp, int size) +{ + struct bloblist_rec *rec; + + rec = bloblist_findrec(tag); + if (rec) { + if (size && size != rec->size) + return -ESPIPE; + } else { + int ret; + + ret = bloblist_addrec(tag, size, &rec); + if (ret) + return ret; + } + *recp = rec; + + return 0; +} + +void *bloblist_find(uint tag, int size) +{ + struct bloblist_rec *rec; + + rec = bloblist_findrec(tag); + if (!rec) + return NULL; + if (size && size != rec->size) + return NULL; + + return (void *)rec + rec->hdr_size; +} + +void *bloblist_add(uint tag, int size) +{ + struct bloblist_rec *rec; + + if (bloblist_addrec(tag, size, &rec)) + return NULL; + + return rec + 1; +} + +int bloblist_ensure_size(uint tag, int size, void **blobp) +{ + struct bloblist_rec *rec; + int ret; + + ret = bloblist_ensurerec(tag, &rec, size); + if (ret) + return ret; + *blobp = (void *)rec + rec->hdr_size; + + return 0; +} + +void *bloblist_ensure(uint tag, int size) +{ + struct bloblist_rec *rec; + + if (bloblist_ensurerec(tag, &rec, size)) + return NULL; + + return (void *)rec + rec->hdr_size; +} + +static u32 bloblist_calc_chksum(struct bloblist_hdr *hdr) +{ + struct bloblist_rec *rec; + u32 chksum; + + chksum = crc32(0, (unsigned char *)hdr, + offsetof(struct bloblist_hdr, chksum)); + foreach_rec(rec, hdr) { + chksum = crc32(chksum, (void *)rec, rec->hdr_size); + chksum = crc32(chksum, (void *)rec + rec->hdr_size, rec->size); + } + + return chksum; +} + +int bloblist_new(ulong addr, uint size, uint flags) +{ + struct bloblist_hdr *hdr; + + if (size < sizeof(*hdr)) + return log_ret(-ENOSPC); + if (addr & (BLOBLIST_ALIGN - 1)) + return log_ret(-EFAULT); + hdr = map_sysmem(addr, size); + memset(hdr, '\0', sizeof(*hdr)); + hdr->version = BLOBLIST_VERSION; + hdr->hdr_size = sizeof(*hdr); + hdr->flags = flags; + hdr->magic = BLOBLIST_MAGIC; + hdr->size = size; + hdr->alloced = hdr->hdr_size; + hdr->chksum = 0; + gd->bloblist = hdr; + + return 0; +} + +int bloblist_check(ulong addr, uint size) +{ + struct bloblist_hdr *hdr; + u32 chksum; + + hdr = map_sysmem(addr, sizeof(*hdr)); + if (hdr->magic != BLOBLIST_MAGIC) + return log_msg_ret("Bad magic", -ENOENT); + if (hdr->version != BLOBLIST_VERSION) + return log_msg_ret("Bad version", -EPROTONOSUPPORT); + if (size && hdr->size != size) + return log_msg_ret("Bad size", -EFBIG); + chksum = bloblist_calc_chksum(hdr); + if (hdr->chksum != chksum) { + log(LOGC_BLOBLIST, LOGL_ERR, "Checksum %x != %x\n", hdr->chksum, + chksum); + return log_msg_ret("Bad checksum", -EIO); + } + gd->bloblist = hdr; + + return 0; +} + +int bloblist_finish(void) +{ + struct bloblist_hdr *hdr = gd->bloblist; + + hdr->chksum = bloblist_calc_chksum(hdr); + + return 0; +} + +int bloblist_init(void) +{ + bool expected; + int ret = -ENOENT; + + /** + * Wed expect to find an existing bloblist in the first phase of U-Boot + * that runs + */ + expected = !u_boot_first_phase(); + if (expected) + ret = bloblist_check(CONFIG_BLOBLIST_ADDR, + CONFIG_BLOBLIST_SIZE); + if (ret) { + log(LOGC_BLOBLIST, expected ? LOGL_WARNING : LOGL_DEBUG, + "Existing bloblist not found: creating new bloblist\n"); + ret = bloblist_new(CONFIG_BLOBLIST_ADDR, CONFIG_BLOBLIST_SIZE, + 0); + } else { + log(LOGC_BLOBLIST, LOGL_DEBUG, "Found existing bloblist\n"); + } + + return ret; +} -- cgit v1.1