u-boot/boot/vbe_abrec.c
Simon Glass f1eb367d76 vbe: Add an implementation of VBE-ABrec
So far only VBE-simple is implemented in U-Boot. This supports a single
image which can be updated in situ.

It is often necessary to support two images (A and B) so that the board
is not bricked if the update is interrupted or is bad.

In some cases, a non-updatable recovery image is desirable, so that the
board can be returned to a known-good state in the event of a serious
failure.

Introduce ABrec which provides these features. It supports three
independent images and the logic to select the desired one on boot.

While we are here, fix a debug message to indicate the function it
called. Provide a maintainers entry for VBE.

Note that fwupdated only supports VBE-simple so far, but supports for
ABrec will appear in time.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-02-03 16:01:36 -06:00

83 lines
2.3 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Verified Boot for Embedded (VBE) 'simple' method
*
* Copyright 2024 Google LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#define LOG_CATEGORY LOGC_BOOT
#include <dm.h>
#include <memalign.h>
#include <mmc.h>
#include <dm/ofnode.h>
#include "vbe_abrec.h"
int abrec_read_priv(ofnode node, struct abrec_priv *priv)
{
memset(priv, '\0', sizeof(*priv));
if (ofnode_read_u32(node, "area-start", &priv->area_start) ||
ofnode_read_u32(node, "area-size", &priv->area_size) ||
ofnode_read_u32(node, "version-offset", &priv->version_offset) ||
ofnode_read_u32(node, "version-size", &priv->version_size) ||
ofnode_read_u32(node, "state-offset", &priv->state_offset) ||
ofnode_read_u32(node, "state-size", &priv->state_size))
return log_msg_ret("read", -EINVAL);
ofnode_read_u32(node, "skip-offset", &priv->skip_offset);
priv->storage = strdup(ofnode_read_string(node, "storage"));
if (!priv->storage)
return log_msg_ret("str", -EINVAL);
return 0;
}
int abrec_read_nvdata(struct abrec_priv *priv, struct udevice *blk,
struct abrec_state *state)
{
ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN);
const struct vbe_nvdata *nvd = (struct vbe_nvdata *)buf;
uint flags;
int ret;
ret = vbe_read_nvdata(blk, priv->area_start + priv->state_offset,
priv->state_size, buf);
if (ret == -EPERM) {
memset(buf, '\0', MMC_MAX_BLOCK_LEN);
log_warning("Starting with empty state\n");
} else if (ret) {
return log_msg_ret("nv", ret);
}
state->fw_vernum = nvd->fw_vernum;
flags = nvd->flags;
state->try_count = flags & VBEF_TRY_COUNT_MASK;
state->try_b = flags & VBEF_TRY_B;
state->recovery = flags & VBEF_RECOVERY;
state->pick = (flags & VBEF_PICK_MASK) >> VBEF_PICK_SHIFT;
return 0;
}
int abrec_read_state(struct udevice *dev, struct abrec_state *state)
{
struct abrec_priv *priv = dev_get_priv(dev);
struct udevice *blk;
int ret;
ret = vbe_get_blk(priv->storage, &blk);
if (ret)
return log_msg_ret("blk", ret);
ret = vbe_read_version(blk, priv->area_start + priv->version_offset,
state->fw_version, MAX_VERSION_LEN);
if (ret)
return log_msg_ret("ver", ret);
log_debug("version=%s\n", state->fw_version);
ret = abrec_read_nvdata(priv, blk, state);
if (ret)
return log_msg_ret("nvd", ret);
return 0;
}