summaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
Diffstat (limited to 'boot')
-rw-r--r--boot/Kconfig146
-rw-r--r--boot/Makefile4
-rw-r--r--boot/bootdev-uclass.c5
-rw-r--r--boot/bootflow.c9
-rw-r--r--boot/bootmeth-uclass.c5
-rw-r--r--boot/image-fit.c126
-rw-r--r--boot/image.c18
-rw-r--r--boot/vbe_request.c (renamed from boot/vbe_fixup.c)8
-rw-r--r--boot/vbe_simple.c142
-rw-r--r--boot/vbe_simple.h71
-rw-r--r--boot/vbe_simple_fw.c206
-rw-r--r--boot/vbe_simple_os.c104
12 files changed, 659 insertions, 185 deletions
diff --git a/boot/Kconfig b/boot/Kconfig
index 45f86e946cd..d5c582ebe8c 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -292,6 +292,56 @@ config SPL_FIT_GENERATOR
endif # SPL
+if VPL
+
+config VPL_FIT
+ bool "Support Flattened Image Tree within VPL"
+ depends on VPL
+ default y
+ select VPL_HASH
+ select VPL_OF_LIBFDT
+
+config VPL_FIT_PRINT
+ bool "Support FIT printing within VPL"
+ depends on VPL_FIT
+ default y
+ help
+ Support printing the content of the fitImage in a verbose manner in VPL.
+
+config VPL_FIT_FULL_CHECK
+ bool "Do a full check of the FIT before using it"
+ default y
+ help
+ Enable this do a full check of the FIT to make sure it is valid. This
+ helps to protect against carefully crafted FITs which take advantage
+ of bugs or omissions in the code. This includes a bad structure,
+ multiple root nodes and the like.
+
+config VPL_FIT_SIGNATURE
+ bool "Enable signature verification of FIT firmware within VPL"
+ depends on VPL_DM
+ default y
+ select FIT_SIGNATURE
+ select VPL_FIT
+ select VPL_CRYPTO
+ select VPL_HASH
+ imply VPL_RSA
+ imply VPL_RSA_VERIFY
+ select VPL_IMAGE_SIGN_INFO
+ select VPL_FIT_FULL_CHECK
+
+config VPL_FIT_SIGNATURE_MAX_SIZE
+ hex "Max size of signed FIT structures in VPL"
+ depends on VPL_FIT_SIGNATURE
+ default 0x10000000
+ help
+ This option sets a max size in bytes for verified FIT uImages.
+ A sane value of 256MB protects corrupted DTB structures from overlapping
+ device memory. Assure this size does not extend past expected storage
+ space.
+
+endif # VPL
+
endif # FIT
config PXE_UTILS
@@ -334,6 +384,26 @@ config BOOTSTD_FULL
- support for selecting the ordering of bootdevs using the devicetree
as well as the "boot_targets" environment variable
+config SPL_BOOTSTD
+ bool "Standard boot support in VPL"
+ depends on SPL && SPL_DM && SPL_OF_CONTROL && SPL_BLK
+ default y if VPL
+ help
+ This enables standard boot in SPL. This is neeeded so that VBE
+ (Verified Boot for Embedded) can be used, since it depends on standard
+ boot. It is enabled by default since the main purpose of VPL is to
+ handle the firmware part of VBE.
+
+config VPL_BOOTSTD
+ bool "Standard boot support in VPL"
+ depends on VPL && VPL_DM && VPL_OF_CONTROL && VPL_BLK
+ default y
+ help
+ This enables standard boot in SPL. This is neeeded so that VBE
+ (Verified Boot for Embedded) can be used, since it depends on standard
+ boot. It is enabled by default since the main purpose of VPL is to
+ handle the firmware part of VBE.
+
if BOOTSTD
config BOOTSTD_BOOTCOMMAND
@@ -408,6 +478,24 @@ config BOOTMETH_VBE
supports selection of various firmware components, seleciton of an OS to
boot as well as updating these using fwupd.
+config SPL_BOOTMETH_VBE
+ bool "Bootdev support for Verified Boot for Embedded (SPL)"
+ depends on SPL && FIT
+ default y if VPL
+ help
+ Enables support for VBE boot. This is a standard boot method which
+ supports selection of various firmware components, seleciton of an OS to
+ boot as well as updating these using fwupd.
+
+config VPL_BOOTMETH_VBE
+ bool "Bootdev support for Verified Boot for Embedded (VPL)"
+ depends on VPL && FIT
+ default y
+ help
+ Enables support for VBE boot. This is a standard boot method which
+ supports selection of various firmware components, seleciton of an OS to
+ boot as well as updating these using fwupd.
+
if BOOTMETH_VBE
config BOOTMETH_VBE_SIMPLE
@@ -418,6 +506,54 @@ config BOOTMETH_VBE_SIMPLE
firmware image in boot media such as MMC. It does not support any sort
of rollback, recovery or A/B boot.
+config BOOTMETH_VBE_SIMPLE_OS
+ bool "Bootdev support for VBE 'simple' method OS phase"
+ default y
+ help
+ Enables support for the OS parts of VBE 'simple' boot. This includes
+ fixing up the device tree with the required VBE information, ready
+ for booting into the OS. This option is only enabled for U-Boot
+ proper, since it is the phase where device tree fixups happen.
+
+config SPL_BOOTMETH_VBE_SIMPLE
+ bool "Bootdev support for VBE 'simple' method (SPL)"
+ depends on SPL
+ default y if VPL
+ help
+ Enables support for VBE 'simple' boot. This allows updating a single
+ firmware image in boot media such as MMC. It does not support any sort
+ of rollback, recovery or A/B boot.
+
+config VPL_BOOTMETH_VBE_SIMPLE
+ bool "Bootdev support for VBE 'simple' method (VPL)"
+ depends on VPL
+ default y
+ help
+ Enables support for VBE 'simple' boot. This allows updating a single
+ firmware image in boot media such as MMC. It does not support any sort
+ of rollback, recovery or A/B boot.
+
+config SPL_BOOTMETH_VBE_SIMPLE_FW
+ bool "Bootdev support for VBE 'simple' method firmware phase (SPL)"
+ depends on VPL
+ default y
+ help
+ Enables support for the firmware parts of VBE 'simple' boot. This
+ includes an SPL loader which locates the correct U-Boot to boot into.
+ This option should really only be enabled for VPL, since it is the
+ phase where the SPL + U-Boot decision should be made. But for now,
+ SPL does its own FIT-configuration selection.
+
+config VPL_BOOTMETH_VBE_SIMPLE_FW
+ bool "Bootdev support for VBE 'simple' method firmware phase (VPL)"
+ depends on VPL
+ default y
+ help
+ Enables support for the firmware parts of VBE 'simple' boot. This
+ includes an SPL loader which locates the correct SPL to boot into.
+ This option enabled for VPL, since it is the phase where the SPL
+ decision is made.
+
endif # BOOTMETH_VBE
config BOOTMETH_SANDBOX
@@ -487,14 +623,14 @@ config OF_STDOUT_VIA_ALIAS
incorrect when used with device tree as this option does not
exist / should not be used.
-config HAVE_SYS_TEXT_BASE
+config HAVE_TEXT_BASE
bool
depends on !NIOS2 && !XTENSA
depends on !EFI_APP
default y
-config SYS_TEXT_BASE
- depends on HAVE_SYS_TEXT_BASE
+config TEXT_BASE
+ depends on HAVE_TEXT_BASE
default 0x0 if POSITION_INDEPENDENT
default 0x80800000 if ARCH_OMAP2PLUS || ARCH_K3
default 0x81700000 if MACH_SUNIV
@@ -515,10 +651,10 @@ config HAVE_SYS_MONITOR_BASE
config SYS_MONITOR_BASE
depends on HAVE_SYS_MONITOR_BASE
hex "Physical start address of boot monitor code"
- default SYS_TEXT_BASE
+ default TEXT_BASE
help
The physical start address of boot monitor code (which is the same as
- CONFIG_SYS_TEXT_BASE when linking) and the same as CONFIG_SYS_FLASH_BASE
+ CONFIG_TEXT_BASE when linking) and the same as CONFIG_SYS_FLASH_BASE
when booting from flash.
config SPL_SYS_MONITOR_BASE
diff --git a/boot/Makefile b/boot/Makefile
index dd45d786f8c..f0c31549213 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -47,5 +47,7 @@ ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_LOAD_FIT) += common_fit.o
endif
-obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE) += vbe.o vbe_fixup.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE) += vbe.o vbe_request.o
obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE) += vbe_simple.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
diff --git a/boot/bootdev-uclass.c b/boot/bootdev-uclass.c
index 9d98bee4549..affe0d3e04e 100644
--- a/boot/bootdev-uclass.c
+++ b/boot/bootdev-uclass.c
@@ -440,10 +440,7 @@ int bootdev_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
if (!ops->get_bootflow)
return -ENOSYS;
- memset(bflow, '\0', sizeof(*bflow));
- bflow->dev = dev;
- bflow->method = iter->method;
- bflow->state = BOOTFLOWST_BASE;
+ bootflow_init(bflow, dev, iter->method);
return ops->get_bootflow(dev, iter, bflow);
}
diff --git a/boot/bootflow.c b/boot/bootflow.c
index 5d94a27ff84..f9ad4099244 100644
--- a/boot/bootflow.c
+++ b/boot/bootflow.c
@@ -339,6 +339,15 @@ int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow)
} while (1);
}
+void bootflow_init(struct bootflow *bflow, struct udevice *bootdev,
+ struct udevice *meth)
+{
+ memset(bflow, '\0', sizeof(*bflow));
+ bflow->dev = bootdev;
+ bflow->method = meth;
+ bflow->state = BOOTFLOWST_BASE;
+}
+
void bootflow_free(struct bootflow *bflow)
{
free(bflow->name);
diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c
index 2d7652edeab..25552dd96f6 100644
--- a/boot/bootmeth-uclass.c
+++ b/boot/bootmeth-uclass.c
@@ -77,10 +77,7 @@ int bootmeth_get_bootflow(struct udevice *dev, struct bootflow *bflow)
if (!ops->read_bootflow)
return -ENOSYS;
- memset(bflow, '\0', sizeof(*bflow));
- bflow->dev = NULL;
- bflow->method = dev;
- bflow->state = BOOTFLOWST_BASE;
+ bootflow_init(bflow, NULL, dev);
return ops->read_bootflow(dev, bflow);
}
diff --git a/boot/image-fit.c b/boot/image-fit.c
index 6e503f827dd..3cc556b727f 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -802,6 +802,40 @@ int fit_image_get_comp(const void *fit, int noffset, uint8_t *comp)
return 0;
}
+/**
+ * fit_image_get_phase() - get the phase for a configuration node
+ * @fit: pointer to the FIT format image header
+ * @offset: configuration-node offset
+ * @phasep: returns the phase
+ *
+ * Finds the phase property in a given configuration node. If the property is
+ * found, its (string) value is translated to the numeric id which is returned
+ * to the caller.
+ *
+ * Returns: 0 on success, -ENOENT if missing, -EINVAL for invalid value
+ */
+int fit_image_get_phase(const void *fit, int offset, enum image_phase_t *phasep)
+{
+ const void *data;
+ int len, ret;
+
+ /* Get phase name from property data */
+ data = fdt_getprop(fit, offset, FIT_PHASE_PROP, &len);
+ if (!data) {
+ fit_get_debug(fit, offset, FIT_PHASE_PROP, len);
+ *phasep = 0;
+ return -ENOENT;
+ }
+
+ /* Translate phase name to id */
+ ret = genimg_get_phase_id(data);
+ if (ret < 0)
+ return ret;
+ *phasep = ret;
+
+ return 0;
+}
+
static int fit_image_get_address(const void *fit, int noffset, char *name,
ulong *load)
{
@@ -1686,49 +1720,6 @@ int fit_check_format(const void *fit, ulong size)
return 0;
}
-/**
- * fit_conf_find_compat
- * @fit: pointer to the FIT format image header
- * @fdt: pointer to the device tree to compare against
- *
- * fit_conf_find_compat() attempts to find the configuration whose fdt is the
- * most compatible with the passed in device tree.
- *
- * Example:
- *
- * / o image-tree
- * |-o images
- * | |-o fdt-1
- * | |-o fdt-2
- * |
- * |-o configurations
- * |-o config-1
- * | |-fdt = fdt-1
- * |
- * |-o config-2
- * |-fdt = fdt-2
- *
- * / o U-Boot fdt
- * |-compatible = "foo,bar", "bim,bam"
- *
- * / o kernel fdt1
- * |-compatible = "foo,bar",
- *
- * / o kernel fdt2
- * |-compatible = "bim,bam", "baz,biz"
- *
- * Configuration 1 would be picked because the first string in U-Boot's
- * compatible list, "foo,bar", matches a compatible string in the root of fdt1.
- * "bim,bam" in fdt2 matches the second string which isn't as good as fdt1.
- *
- * As an optimization, the compatible property from the FDT's root node can be
- * copied into the configuration node in the FIT image. This is required to
- * match configurations with compressed FDTs.
- *
- * returns:
- * offset to the configuration to use if one was found
- * -1 otherwise
- */
int fit_conf_find_compat(const void *fit, const void *fdt)
{
int ndepth = 0;
@@ -1910,10 +1901,37 @@ int fit_conf_get_prop_node_index(const void *fit, int noffset,
return fit_image_get_node(fit, uname);
}
-int fit_conf_get_prop_node(const void *fit, int noffset,
- const char *prop_name)
+int fit_conf_get_prop_node(const void *fit, int noffset, const char *prop_name,
+ enum image_phase_t sel_phase)
{
- return fit_conf_get_prop_node_index(fit, noffset, prop_name, 0);
+ int i, count;
+
+ if (sel_phase == IH_PHASE_NONE)
+ return fit_conf_get_prop_node_index(fit, noffset, prop_name, 0);
+
+ count = fit_conf_get_prop_node_count(fit, noffset, prop_name);
+ if (count < 0)
+ return count;
+
+ /* check each image in the list */
+ for (i = 0; i < count; i++) {
+ enum image_phase_t phase;
+ int ret, node;
+
+ node = fit_conf_get_prop_node_index(fit, noffset, prop_name, i);
+ ret = fit_image_get_phase(fit, node, &phase);
+
+ /* if the image is for any phase, let's use it */
+ if (ret == -ENOENT)
+ return node;
+ else if (ret < 0)
+ return ret;
+
+ if (phase == sel_phase)
+ return node;
+ }
+
+ return -ENOENT;
}
static int fit_get_data_tail(const void *fit, int noffset,
@@ -1949,7 +1967,8 @@ int fit_get_data_conf_prop(const void *fit, const char *prop_name,
{
int noffset = fit_conf_get_node(fit, NULL);
- noffset = fit_conf_get_prop_node(fit, noffset, prop_name);
+ noffset = fit_conf_get_prop_node(fit, noffset, prop_name,
+ IH_PHASE_NONE);
return fit_get_data_tail(fit, noffset, data, size);
}
@@ -1987,7 +2006,8 @@ int fit_get_node_from_config(struct bootm_headers *images,
return -EINVAL;
}
- noffset = fit_conf_get_prop_node(fit_hdr, cfg_noffset, prop_name);
+ noffset = fit_conf_get_prop_node(fit_hdr, cfg_noffset, prop_name,
+ IH_PHASE_NONE);
if (noffset < 0) {
debug("* %s: no '%s' in config\n", prop_name, prop_name);
return -ENOENT;
@@ -2033,9 +2053,10 @@ static const char *fit_get_image_type_property(int type)
int fit_image_load(struct bootm_headers *images, ulong addr,
const char **fit_unamep, const char **fit_uname_configp,
- int arch, int image_type, int bootstage_id,
+ int arch, int ph_type, int bootstage_id,
enum fit_load_op load_op, ulong *datap, ulong *lenp)
{
+ int image_type = image_ph_type(ph_type);
int cfg_noffset, noffset;
const char *fit_uname;
const char *fit_uname_config;
@@ -2081,8 +2102,7 @@ int fit_image_load(struct bootm_headers *images, ulong addr,
if (IS_ENABLED(CONFIG_FIT_BEST_MATCH) && !fit_uname_config) {
cfg_noffset = fit_conf_find_compat(fit, gd_fdt_blob());
} else {
- cfg_noffset = fit_conf_get_node(fit,
- fit_uname_config);
+ cfg_noffset = fit_conf_get_node(fit, fit_uname_config);
}
if (cfg_noffset < 0) {
puts("Could not find configuration node\n");
@@ -2110,8 +2130,8 @@ int fit_image_load(struct bootm_headers *images, ulong addr,
bootstage_mark(BOOTSTAGE_ID_FIT_CONFIG);
- noffset = fit_conf_get_prop_node(fit, cfg_noffset,
- prop_name);
+ noffset = fit_conf_get_prop_node(fit, cfg_noffset, prop_name,
+ image_ph_phase(ph_type));
fit_uname = fit_get_name(fit, noffset, NULL);
}
if (noffset < 0) {
diff --git a/boot/image.c b/boot/image.c
index 9f95b3260a8..b33d1dfc6b3 100644
--- a/boot/image.c
+++ b/boot/image.c
@@ -194,6 +194,13 @@ static const table_entry_t uimage_comp[] = {
{ -1, "", "", },
};
+static const table_entry_t uimage_phase[] = {
+ { IH_PHASE_NONE, "none", "any", },
+ { IH_PHASE_U_BOOT, "u-boot", "U-Boot phase", },
+ { IH_PHASE_SPL, "spl", "SPL Phase", },
+ { -1, "", "", },
+};
+
struct table_info {
const char *desc;
int count;
@@ -215,6 +222,7 @@ static const struct table_info table_info[IH_COUNT] = {
{ "compression", IH_COMP_COUNT, uimage_comp },
{ "operating system", IH_OS_COUNT, uimage_os },
{ "image type", IH_TYPE_COUNT, uimage_type },
+ { "phase", IH_PHASE_COUNT, uimage_phase },
};
/*****************************************************************************/
@@ -656,6 +664,11 @@ const char *genimg_get_comp_name(uint8_t comp)
comp));
}
+const char *genimg_get_phase_name(enum image_phase_t phase)
+{
+ return get_table_entry_name(uimage_phase, "Unknown Phase", phase);
+}
+
static const char *genimg_get_short_name(const table_entry_t *table, int val)
{
table = get_table_entry(table, val);
@@ -731,3 +744,8 @@ int genimg_get_comp_id(const char *name)
{
return (get_table_entry_id(uimage_comp, "Compression", name));
}
+
+int genimg_get_phase_id(const char *name)
+{
+ return get_table_entry_id(uimage_phase, "Phase", name);
+}
diff --git a/boot/vbe_fixup.c b/boot/vbe_request.c
index 53d88678c92..45f1d2b7e17 100644
--- a/boot/vbe_fixup.c
+++ b/boot/vbe_request.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Verified Boot for Embedded (VBE) device tree fixup functions
+ * Verified Boot for Embedded (VBE) OS request (device tree fixup) functions
*
* Copyright 2022 Google LLC
* Written by Simon Glass <sjg@chromium.org>
@@ -36,7 +36,7 @@ static int handle_random_req(ofnode node, int default_size,
u32 size;
int ret;
- if (!IS_ENABLED(CONFIG_DM_RNG))
+ if (!CONFIG_IS_ENABLED(DM_RNG))
return -ENOTSUPP;
if (ofnode_read_u32(node, "vbe,size", &size)) {
@@ -195,8 +195,8 @@ static int bootmeth_vbe_ft_fixup(void *ctx, struct event *event)
ret = vbe_process_request(dest, &result);
if (ret) {
result.errnum = ret;
- log_err("Failed to process VBE request %s (err=%d)\n",
- ofnode_get_name(dest), ret);
+ log_warning("Failed to process VBE request %s (err=%d)\n",
+ ofnode_get_name(dest), ret);
if (*result.err_str) {
char *msg = strdup(result.err_str);
diff --git a/boot/vbe_simple.c b/boot/vbe_simple.c
index 076b650c25a..59676d8613f 100644
--- a/boot/vbe_simple.c
+++ b/boot/vbe_simple.c
@@ -9,55 +9,18 @@
#define LOG_CATEGORY LOGC_BOOT
#include <common.h>
-#include <log.h>
-#include <memalign.h>
-#include <part.h>
+#include <bootdev.h>
#include <bootflow.h>
#include <bootmeth.h>
#include <dm.h>
+#include <log.h>
+#include <memalign.h>
#include <mmc.h>
#include <vbe.h>
-#include <version_string.h>
#include <dm/device-internal.h>
#include <dm/ofnode.h>
#include <u-boot/crc.h>
-
-enum {
- MAX_VERSION_LEN = 256,
-
- NVD_HDR_VER_SHIFT = 0,
- NVD_HDR_VER_MASK = 0xf,
- NVD_HDR_SIZE_SHIFT = 4,
- NVD_HDR_SIZE_MASK = 0xf << NVD_HDR_SIZE_SHIFT,
-
- /* Firmware key-version is in the top 16 bits of fw_ver */
- FWVER_KEY_SHIFT = 16,
- FWVER_FW_MASK = 0xffff,
-
- NVD_HDR_VER_CUR = 1, /* current version */
-};
-
-/** struct simple_priv - information read from the device tree */
-struct simple_priv {
- u32 area_start;
- u32 area_size;
- u32 skip_offset;
- u32 state_offset;
- u32 state_size;
- u32 version_offset;
- u32 version_size;
- const char *storage;
-};
-
-/** struct simple_state - state information read from media
- *
- * @fw_version: Firmware version string
- * @fw_vernum: Firmware version number
- */
-struct simple_state {
- char fw_version[MAX_VERSION_LEN];
- u32 fw_vernum;
-};
+#include "vbe_simple.h"
/** struct simple_nvdata - storage format for non-volatile data */
struct simple_nvdata {
@@ -127,7 +90,7 @@ static int simple_read_nvdata(struct udevice *dev, struct blk_desc *desc,
return 0;
}
-static int simple_read_state(struct udevice *dev, struct simple_state *state)
+int vbe_simple_read_state(struct udevice *dev, struct simple_state *state)
{
ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN);
struct simple_priv *priv = dev_get_priv(dev);
@@ -168,7 +131,7 @@ static int vbe_simple_get_state_desc(struct udevice *dev, char *buf,
struct simple_state state;
int ret;
- ret = simple_read_state(dev, &state);
+ ret = vbe_simple_read_state(dev, &state);
if (ret)
return log_msg_ret("read", ret);
@@ -183,88 +146,39 @@ static int vbe_simple_get_state_desc(struct udevice *dev, char *buf,
static int vbe_simple_read_bootflow(struct udevice *dev, struct bootflow *bflow)
{
- /* To be implemented */
-
- return -EINVAL;
-}
-
-static struct bootmeth_ops bootmeth_vbe_simple_ops = {
- .get_state_desc = vbe_simple_get_state_desc,
- .read_bootflow = vbe_simple_read_bootflow,
- .read_file = bootmeth_common_read_file,
-};
-
-int vbe_simple_fixup_node(ofnode node, struct simple_state *state)
-{
- char *version;
int ret;
- version = strdup(state->fw_version);
- if (!version)
- return log_msg_ret("dup", -ENOMEM);
-
- ret = ofnode_write_string(node, "cur-version", version);
- if (ret)
- return log_msg_ret("ver", ret);
- ret = ofnode_write_u32(node, "cur-vernum", state->fw_vernum);
- if (ret)
- return log_msg_ret("num", ret);
- ret = ofnode_write_string(node, "bootloader-version", version_string);
- if (ret)
- return log_msg_ret("bl", ret);
+ if (vbe_phase() == VBE_PHASE_FIRMWARE) {
+ ret = vbe_simple_read_bootflow_fw(dev, bflow);
+ if (ret)
+ return log_msg_ret("fw", ret);
+ return 0;
+ }
- return 0;
+ return -EINVAL;
}
-/**
- * bootmeth_vbe_simple_ft_fixup() - Write out all VBE simple data to the DT
- *
- * @ctx: Context for event
- * @event: Event to process
- * @return 0 if OK, -ve on error
- */
-static int bootmeth_vbe_simple_ft_fixup(void *ctx, struct event *event)
+static int vbe_simple_read_file(struct udevice *dev, struct bootflow *bflow,
+ const char *file_path, ulong addr, ulong *sizep)
{
- oftree tree = event->data.ft_fixup.tree;
- struct udevice *dev;
-
- /*
- * Ideally we would have driver model support for fixups, but that does
- * not exist yet. It is a step too far to try to do this before VBE is
- * in place.
- */
- for (vbe_find_first_device(&dev); dev; vbe_find_next_device(&dev)) {
- struct simple_state state;
- ofnode node, subnode;
- int ret;
-
- if (strcmp("vbe_simple", dev->driver->name))
- continue;
-
- /* Check if there is a node to fix up */
- node = oftree_path(tree, "/chosen/fwupd");
- if (!ofnode_valid(node))
- continue;
- subnode = ofnode_find_subnode(node, dev->name);
- if (!ofnode_valid(subnode))
- continue;
-
- log_debug("Fixing up: %s\n", dev->name);
- ret = device_probe(dev);
- if (ret)
- return log_msg_ret("probe", ret);
- ret = simple_read_state(dev, &state);
- if (ret)
- return log_msg_ret("read", ret);
+ int ret;
- ret = vbe_simple_fixup_node(subnode, &state);
+ if (vbe_phase() == VBE_PHASE_OS) {
+ ret = bootmeth_common_read_file(dev, bflow, file_path, addr,
+ sizep);
if (ret)
- return log_msg_ret("fix", ret);
+ return log_msg_ret("os", ret);
}
- return 0;
+ /* To be implemented */
+ return -EINVAL;
}
-EVENT_SPY(EVT_FT_FIXUP, bootmeth_vbe_simple_ft_fixup);
+
+static struct bootmeth_ops bootmeth_vbe_simple_ops = {
+ .get_state_desc = vbe_simple_get_state_desc,
+ .read_bootflow = vbe_simple_read_bootflow,
+ .read_file = vbe_simple_read_file,
+};
static int bootmeth_vbe_simple_probe(struct udevice *dev)
{
diff --git a/boot/vbe_simple.h b/boot/vbe_simple.h
new file mode 100644
index 00000000000..56d319206f2
--- /dev/null
+++ b/boot/vbe_simple.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Verified Boot for Embedded (VBE) vbe-simple common file
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef __VBE_SIMPLE_H
+#define __VBE_SIMPLE_H
+
+enum {
+ MAX_VERSION_LEN = 256,
+
+ NVD_HDR_VER_SHIFT = 0,
+ NVD_HDR_VER_MASK = 0xf,
+ NVD_HDR_SIZE_SHIFT = 4,
+ NVD_HDR_SIZE_MASK = 0xf << NVD_HDR_SIZE_SHIFT,
+
+ /* Firmware key-version is in the top 16 bits of fw_ver */
+ FWVER_KEY_SHIFT = 16,
+ FWVER_FW_MASK = 0xffff,
+
+ NVD_HDR_VER_CUR = 1, /* current version */
+};
+
+/** struct simple_priv - information read from the device tree */
+struct simple_priv {
+ u32 area_start;
+ u32 area_size;
+ u32 skip_offset;
+ u32 state_offset;
+ u32 state_size;
+ u32 version_offset;
+ u32 version_size;
+ const char *storage;
+};
+
+/** struct simple_state - state information read from media
+ *
+ * @fw_version: Firmware version string
+ * @fw_vernum: Firmware version number
+ */
+struct simple_state {
+ char fw_version[MAX_VERSION_LEN];
+ u32 fw_vernum;
+};
+
+/**
+ * vbe_simple_read_fw_bootflow() - Read a bootflow for firmware
+ *
+ * Locates and loads the firmware image (FIT) needed for the next phase. The FIT
+ * should ideally use external data, to reduce the amount of it that needs to be
+ * read.
+ *
+ * @bdev: bootdev device containing the firmwre
+ * @blow: Place to put the created bootflow, on success
+ * @return 0 if OK, -ve on error
+ */
+int vbe_simple_read_bootflow_fw(struct udevice *dev, struct bootflow *bflow);
+
+/**
+ * vbe_simple_read_state() - Read the VBE simple state information
+ *
+ * @dev: VBE bootmeth
+ * @state: Place to put the state
+ * @return 0 if OK, -ve on error
+ */
+int vbe_simple_read_state(struct udevice *dev, struct simple_state *state);
+
+#endif /* __VBE_SIMPLE_H */
diff --git a/boot/vbe_simple_fw.c b/boot/vbe_simple_fw.c
new file mode 100644
index 00000000000..0a49d286703
--- /dev/null
+++ b/boot/vbe_simple_fw.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Verified Boot for Embedded (VBE) loading firmware phases
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY LOGC_BOOT
+
+#include <common.h>
+#include <bloblist.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <bootstage.h>
+#include <dm.h>
+#include <image.h>
+#include <log.h>
+#include <mapmem.h>
+#include <memalign.h>
+#include <mmc.h>
+#include <spl.h>
+#include <vbe.h>
+#include <dm/device-internal.h>
+#include "vbe_simple.h"
+
+/**
+ * vbe_simple_read_bootflow_fw() - Create a bootflow for firmware
+ *
+ * Locates and loads the firmware image (FIT) needed for the next phase. The FIT
+ * should ideally use external data, to reduce the amount of it that needs to be
+ * read.
+ *
+ * @bdev: bootdev device containing the firmwre
+ * @meth: VBE simple bootmeth
+ * @blow: Place to put the created bootflow, on success
+ * @return 0 if OK, -ve on error
+ */
+int vbe_simple_read_bootflow_fw(struct udevice *dev, struct bootflow *bflow)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(u8, sbuf, MMC_MAX_BLOCK_LEN);
+ struct udevice *media = dev_get_parent(bflow->dev);
+ struct udevice *meth = bflow->method;
+ struct simple_priv *priv = dev_get_priv(meth);
+ const char *fit_uname, *fit_uname_config;
+ struct bootm_headers images = {};
+ ulong offset, size, blknum, addr, len, load_addr, num_blks;
+ enum image_phase_t phase;
+ struct blk_desc *desc;
+ struct udevice *blk;
+ int node, ret;
+ void *buf;
+
+ log_debug("media=%s\n", media->name);
+ ret = blk_get_from_parent(media, &blk);
+ if (ret)
+ return log_msg_ret("med", ret);
+ log_debug("blk=%s\n", blk->name);
+ desc = dev_get_uclass_plat(blk);
+
+ offset = priv->area_start + priv->skip_offset;
+
+ /* read in one block to find the FIT size */
+ blknum = offset / desc->blksz;
+ log_debug("read at %lx, blknum %lx\n", offset, blknum);
+ ret = blk_read(blk, blknum, 1, sbuf);
+ if (ret < 0)
+ return log_msg_ret("rd", ret);
+
+ ret = fdt_check_header(sbuf);
+ if (ret < 0)
+ return log_msg_ret("fdt", -EINVAL);
+ size = fdt_totalsize(sbuf);
+ if (size > priv->area_size)
+ return log_msg_ret("fdt", -E2BIG);
+ log_debug("FIT size %lx\n", size);
+
+ /*
+ * Load the FIT into the SPL memory. This is typically a FIT with
+ * external data, so this is quite small, perhaps a few KB.
+ */
+ addr = CONFIG_VAL(TEXT_BASE);
+ buf = map_sysmem(addr, size);
+ num_blks = DIV_ROUND_UP(size, desc->blksz);
+ log_debug("read %lx, %lx blocks to %lx / %p\n", size, num_blks, addr,
+ buf);
+ ret = blk_read(blk, blknum, num_blks, buf);
+ if (ret < 0)
+ return log_msg_ret("rd", ret);
+
+ /* figure out the phase to load */
+ phase = IS_ENABLED(CONFIG_VPL_BUILD) ? IH_PHASE_SPL : IH_PHASE_U_BOOT;
+
+ /*
+ * Load the image from the FIT. We ignore any load-address information
+ * so in practice this simply locates the image in the external-data
+ * region and returns its address and size. Since we only loaded the FIT
+ * itself, only a part of the image will be present, at best.
+ */
+ fit_uname = NULL;
+ fit_uname_config = NULL;
+ log_debug("loading FIT\n");
+ ret = fit_image_load(&images, addr, &fit_uname, &fit_uname_config,
+ IH_ARCH_SANDBOX, image_ph(phase, IH_TYPE_FIRMWARE),
+ BOOTSTAGE_ID_FIT_SPL_START, FIT_LOAD_IGNORED,
+ &load_addr, &len);
+ if (ret < 0)
+ return log_msg_ret("ld", ret);
+ node = ret;
+ log_debug("loaded to %lx\n", load_addr);
+
+ /* For FIT external data, read in the external data */
+ if (load_addr + len > addr + size) {
+ ulong base, full_size;
+ void *base_buf;
+
+ /* Find the start address to load from */
+ base = ALIGN_DOWN(load_addr, desc->blksz);
+
+ /*
+ * Get the total number of bytes to load, taking care of
+ * block alignment
+ */
+ full_size = load_addr + len - base;
+
+ /*
+ * Get the start block number, number of blocks and the address
+ * to load to, then load the blocks
+ */
+ blknum = (offset + base - addr) / desc->blksz;
+ num_blks = DIV_ROUND_UP(full_size, desc->blksz);
+ base_buf = map_sysmem(base, full_size);
+ ret = blk_read(blk, blknum, num_blks, base_buf);
+ log_debug("read %lx %lx, %lx blocks to %lx / %p: ret=%d\n",
+ blknum, full_size, num_blks, base, base_buf, ret);
+ if (ret < 0)
+ return log_msg_ret("rd", ret);
+ }
+
+ /* set up the bootflow with the info we obtained */
+ bflow->name = strdup(fdt_get_name(buf, node, NULL));
+ if (!bflow->name)
+ return log_msg_ret("name", -ENOMEM);
+ bflow->blk = blk;
+ bflow->buf = map_sysmem(load_addr, len);
+ bflow->size = len;
+
+ return 0;
+}
+
+static int simple_load_from_image(struct spl_image_info *spl_image,
+ struct spl_boot_device *bootdev)
+{
+ struct udevice *meth, *bdev;
+ struct simple_priv *priv;
+ struct bootflow bflow;
+ struct vbe_handoff *handoff;
+ int ret;
+
+ if (spl_phase() != PHASE_VPL && spl_phase() != PHASE_SPL)
+ return -ENOENT;
+
+ ret = bloblist_ensure_size(BLOBLISTT_VBE, sizeof(struct vbe_handoff),
+ 0, (void **)&handoff);
+ if (ret)
+ return log_msg_ret("ro", ret);
+
+ vbe_find_first_device(&meth);
+ if (!meth)
+ return log_msg_ret("vd", -ENODEV);
+ log_debug("vbe dev %s\n", meth->name);
+ ret = device_probe(meth);
+ if (ret)
+ return log_msg_ret("probe", ret);
+
+ priv = dev_get_priv(meth);
+ log_debug("simple %s\n", priv->storage);
+ ret = bootdev_find_by_label(priv->storage, &bdev);
+ if (ret)
+ return log_msg_ret("bd", ret);
+ log_debug("bootdev %s\n", bdev->name);
+
+ bootflow_init(&bflow, bdev, meth);
+ ret = bootmeth_read_bootflow(meth, &bflow);
+ log_debug("\nfw ret=%d\n", ret);
+ if (ret)
+ return log_msg_ret("rd", ret);
+
+ /* jump to the image */
+ spl_image->flags = SPL_SANDBOXF_ARG_IS_BUF;
+ spl_image->arg = bflow.buf;
+ spl_image->size = bflow.size;
+ log_debug("Image: %s at %p size %x\n", bflow.name, bflow.buf,
+ bflow.size);
+
+ /* this is not used from now on, so free it */
+ bootflow_free(&bflow);
+
+ /* Record that VBE was used in this phase */
+ handoff->phases |= 1 << spl_phase();
+
+ return 0;
+}
+SPL_LOAD_IMAGE_METHOD("vbe_simple", 5, BOOT_DEVICE_VBE,
+ simple_load_from_image);
diff --git a/boot/vbe_simple_os.c b/boot/vbe_simple_os.c
new file mode 100644
index 00000000000..b2041a95a30
--- /dev/null
+++ b/boot/vbe_simple_os.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Verified Boot for Embedded (VBE) loading firmware phases
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY LOGC_BOOT
+
+#include <common.h>
+#include <dm.h>
+#include <bootflow.h>
+#include <vbe.h>
+#include <version_string.h>
+#include <dm/device-internal.h>
+#include "vbe_simple.h"
+
+int vbe_simple_fixup_node(ofnode node, struct simple_state *state)
+{
+ const char *version, *str;
+ int ret;
+
+ version = strdup(state->fw_version);
+ if (!version)
+ return log_msg_ret("dup", -ENOMEM);
+
+ ret = ofnode_write_string(node, "cur-version", version);
+ if (ret)
+ return log_msg_ret("ver", ret);
+ ret = ofnode_write_u32(node, "cur-vernum", state->fw_vernum);
+ if (ret)
+ return log_msg_ret("num", ret);
+
+ /* Drop the 'U-Boot ' at the start */
+ str = version_string;
+ if (!strncmp("U-Boot ", str, 7))
+ str += 7;
+ ret = ofnode_write_string(node, "bootloader-version", str);
+ if (ret)
+ return log_msg_ret("bl", ret);
+
+ return 0;
+}
+
+/**
+ * bootmeth_vbe_simple_ft_fixup() - Write out all VBE simple data to the DT
+ *
+ * @ctx: Context for event
+ * @event: Event to process
+ * @return 0 if OK, -ve on error
+ */
+static int bootmeth_vbe_simple_ft_fixup(void *ctx, struct event *event)
+{
+ oftree tree = event->data.ft_fixup.tree;
+ struct udevice *dev;
+
+ /*
+ * Ideally we would have driver model support for fixups, but that does
+ * not exist yet. It is a step too far to try to do this before VBE is
+ * in place.
+ */
+ for (vbe_find_first_device(&dev); dev; vbe_find_next_device(&dev)) {
+ struct simple_state state;
+ ofnode node, subnode, chosen;
+ int ret;
+
+ if (strcmp("vbe_simple", dev->driver->name))
+ continue;
+
+ /* Check if there is a node to fix up, adding if not */
+ chosen = oftree_path(tree, "/chosen");
+ if (!ofnode_valid(chosen))
+ continue;
+ ret = ofnode_add_subnode(chosen, "fwupd", &node);
+ if (ret && ret != -EEXIST)
+ return log_msg_ret("fwu", ret);
+
+ ret = ofnode_add_subnode(node, dev->name, &subnode);
+ if (ret && ret != -EEXIST)
+ return log_msg_ret("dev", ret);
+
+ ret = device_probe(dev);
+ if (ret)
+ return log_msg_ret("probe", ret);
+
+ /* Copy over the vbe properties for fwupd */
+ log_debug("Fixing up: %s\n", dev->name);
+ ret = ofnode_copy_props(dev_ofnode(dev), subnode);
+ if (ret)
+ return log_msg_ret("cp", ret);
+
+ ret = vbe_simple_read_state(dev, &state);
+ if (ret)
+ return log_msg_ret("read", ret);
+
+ ret = vbe_simple_fixup_node(subnode, &state);
+ if (ret)
+ return log_msg_ret("fix", ret);
+ }
+
+ return 0;
+}
+EVENT_SPY(EVT_FT_FIXUP, bootmeth_vbe_simple_ft_fixup);