Merge branch '2023-06-09-fwu-updates' into next

Two sets of FWU updates from Jassi Brar.  First:
The patchset reduces ~400 lines of code, while keeping the functionality same and making
meta-data operations much faster (by using cached structures).

Issue:
meta-data copies (primary and secondary) are being handled by the
backend/storage layer instead of the common core in fwu.c (as also noted by
Ilias)  that is, gpt_blk.c manages meta-data and similarly raw_mtd.c will have
to do the same when it arrives. The code could by make smaller, cleaner and
optimised.

Basic idea:
Introduce  .read_mdata() and .write_mdata() in fwu_mdata_ops  that simply
read/write meta-data copy. The core code takes care of integrity and redundancy
of the meta-data, as a result we can get rid of every other callback
.get_mdata() .update_mdata() .get_mdata_part_num()  .read_mdata_partition()
.write_mdata_partition() and the corresponding wrapper functions thereby making
the code 100s of LOC smaller.

Get rid of fwu_check_mdata_validity() and fwu_mdata_check() which expected
underlying layer to manage and verify mdata copies.
Implement  fwu_get_verified_mdata(struct fwu_mdata *mdata) public function that
reads, verifies and, if needed, fixes the meta-data copies.

Verified copy of meta-data is now cached as 'g_mdata' in fwu.c, which avoids
multiple low-level expensive read and parse calls.
gpt meta-data partition numbers are now cached in gpt_blk.c, so that we don't
have to do expensive part_get_info() and uid ops.

And second:
Introduce support for mtd backed storage for FWU feature and enable it on
Synquacer platform based DeveloperBox.
This commit is contained in:
Tom Rini 2023-06-09 20:35:02 -04:00
commit 5f41ef792c
23 changed files with 1515 additions and 675 deletions

View file

@ -21,7 +21,7 @@
#size-cells = <0>;
status = "okay";
flash@0 {
flash0: flash@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "jedec,spi-nor";
@ -74,8 +74,24 @@
};
partition@500000 {
label = "Ex-OPTEE";
reg = <0x500000 0x200000>;
label = "MDATA-Pri";
reg = <0x500000 0x1000>;
};
partition@530000 {
label = "MDATA-Sec";
reg = <0x530000 0x1000>;
};
/* FWU Multi bank update partitions */
partition@600000 {
label = "FIP-Bank0";
reg = <0x600000 0x400000>;
};
partition@a00000 {
label = "FIP-Bank1";
reg = <0xa00000 0x400000>;
};
};
};
@ -102,6 +118,33 @@
optee {
status = "okay";
};
fwu-mdata {
compatible = "u-boot,fwu-mdata-mtd";
fwu-mdata-store = <&flash0>;
mdata-parts = "MDATA-Pri", "MDATA-Sec";
fwu-bank0 {
id = <0>;
label = "FIP-Bank0";
fwu-image0 {
id = <0>;
offset = <0x0>;
size = <0x400000>;
uuid = "5a66a702-99fd-4fef-a392-c26e261a2828";
};
};
fwu-bank1 {
id = <1>;
label = "FIP-Bank1";
fwu-image0 {
id = <0>;
offset = <0x0>;
size = <0x400000>;
uuid = "a8f868a1-6e5c-4757-878d-ce63375ef2c0";
};
};
};
};
};

View file

@ -7,3 +7,4 @@
#
obj-y := developerbox.o
obj-$(CONFIG_FWU_MDATA_MTD) += fwu_plat.o

View file

@ -20,6 +20,13 @@
#if IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT)
struct efi_fw_image fw_images[] = {
#if CONFIG_IS_ENABLED(FWU_MULTI_BANK_UPDATE)
{
.image_type_id = DEVELOPERBOX_FIP_IMAGE_GUID,
.fw_name = u"DEVELOPERBOX-FIP",
.image_index = 1,
},
#else
{
.image_type_id = DEVELOPERBOX_UBOOT_IMAGE_GUID,
.fw_name = u"DEVELOPERBOX-UBOOT",
@ -35,6 +42,7 @@ struct efi_fw_image fw_images[] = {
.fw_name = u"DEVELOPERBOX-OPTEE",
.image_index = 3,
},
#endif
};
struct efi_capsule_update_info update_info = {

View file

@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2023, Linaro Limited
*/
#include <efi_loader.h>
#include <fwu.h>
#include <fwu_mdata.h>
#include <memalign.h>
#include <mtd.h>
#define DFU_ALT_BUF_LEN 256
/* Generate dfu_alt_info from partitions */
void set_dfu_alt_info(char *interface, char *devstr)
{
ALLOC_CACHE_ALIGN_BUFFER(char, buf, DFU_ALT_BUF_LEN);
struct mtd_info *mtd;
int ret;
memset(buf, 0, sizeof(buf));
mtd_probe_devices();
mtd = get_mtd_device_nm("nor1");
if (IS_ERR_OR_NULL(mtd))
return;
ret = fwu_gen_alt_info_from_mtd(buf, DFU_ALT_BUF_LEN, mtd);
if (ret < 0) {
log_err("Error: Failed to generate dfu_alt_info. (%d)\n", ret);
return;
}
log_debug("Make dfu_alt_info: '%s'\n", buf);
env_set("dfu_alt_info", buf);
}

View file

@ -43,23 +43,10 @@ static void print_mdata(struct fwu_mdata *mdata)
int do_fwu_mdata_read(struct cmd_tbl *cmdtp, int flag,
int argc, char * const argv[])
{
struct udevice *dev;
int ret = CMD_RET_SUCCESS, res;
struct fwu_mdata mdata = { 0 };
struct fwu_mdata mdata;
if (uclass_get_device(UCLASS_FWU_MDATA, 0, &dev) || !dev) {
log_err("Unable to get FWU metadata device\n");
return CMD_RET_FAILURE;
}
res = fwu_check_mdata_validity();
if (res < 0) {
log_err("FWU Metadata check failed\n");
ret = CMD_RET_FAILURE;
goto out;
}
res = fwu_get_mdata(dev, &mdata);
res = fwu_get_mdata(&mdata);
if (res < 0) {
log_err("Unable to get valid FWU metadata\n");
ret = CMD_RET_FAILURE;

View file

@ -1,13 +1,13 @@
CONFIG_ARM=y
CONFIG_ARCH_SYNQUACER=y
CONFIG_TEXT_BASE=0x08200000
CONFIG_POSITION_INDEPENDENT=y
CONFIG_SYS_MALLOC_LEN=0x1000000
CONFIG_SYS_MALLOC_F_LEN=0x400
CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0xe0000000
CONFIG_SF_DEFAULT_SPEED=31250000
CONFIG_ENV_SIZE=0x30000
CONFIG_ENV_OFFSET=0x300000
CONFIG_ENV_OFFSET=0x580000
CONFIG_ENV_SECT_SIZE=0x10000
CONFIG_DM_GPIO=y
CONFIG_DEFAULT_DEVICE_TREE="synquacer-sc2a11-developerbox"
@ -97,3 +97,11 @@ CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y
CONFIG_EFI_CAPSULE_ON_DISK=y
CONFIG_EFI_IGNORE_OSINDICATIONS=y
CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
CONFIG_EFI_SECURE_BOOT=y
CONFIG_FWU_MULTI_BANK_UPDATE=y
CONFIG_FWU_MDATA=y
CONFIG_FWU_MDATA_MTD=y
CONFIG_FWU_NUM_BANKS=2
CONFIG_FWU_NUM_IMAGES_PER_BANK=1
CONFIG_CMD_FWU_METADATA=y
CONFIG_TOOLS_MKFWUMDATA=y

View file

@ -57,14 +57,20 @@ Installation
You can install the SNI_NOR_UBOOT.fd via NOR flash writer.
Flashing the U-Boot image on DeveloperBox requires a 96boards UART mezzanine or other mezzanine which can connect to LS-UART0 port.
Connect USB cable from host to the LS-UART0 and set DSW2-7 to ON, and turn the board on again. The flash writer program will be started automatically; dont forget to turn the DSW2-7 off again after flashing.
Flashing the U-Boot image on DeveloperBox requires a 96boards UART mezzanine
or other mezzanine which can connect to the LS-UART0 port.
Connect USB cable from host to the LS-UART0 and set DSW2-7 to ON, and turn the
board on again. The flash writer program will be started automatically;
don't forget to turn the DSW2-7 off again after flashing.
*!!CAUTION!! If you failed to write the U-Boot image on wrong address, the board can be bricked. See below page if you need to recover the bricked board. See the following page for more detail*
*!!CAUTION!! If you write the U-Boot image on wrong address, the board can
be bricked. See below page if you need to recover the bricked board. See
the following page for more details*
https://www.96boards.org/documentation/enterprise/developerbox/installation/board-recovery.md.html
When the serial flasher is running correctly is will show the following boot messages shown via LS-UART0::
When the serial flasher is running correctly it will show the following boot
messages printed to the LS-UART0 console::
/*------------------------------------------*/
@ -81,7 +87,143 @@ Once the flasher tool is running we are ready flash the UEFI image::
flash rawwrite 200000 100000
>> Send SPI_NOR_UBOOT.fd via XMODEM (Control-A S in minicom) <<
*!!NOTE!! The flasher command parameter is different from the command for board recovery. U-Boot uses the offset 200000 (2-five-0, 2M in hex) and the size 100000 (1-five-0, 1M in hex).*
*!!NOTE!! The flasher command parameter is different from the command for
board recovery. U-Boot uses the offset 200000 (2-five-0, 2M in hex) and the
size 100000 (1-five-0, 1M in hex).*
After transferring the SPI_NOR_UBOOT.fd, turn off the DSW2-7 and reset the board.
After transferring the SPI_NOR_UBOOT.fd, turn off the DSW2-7 and
reset the board.
Enable FWU Multi Bank Update
============================
DeveloperBox supports the FWU Multi Bank Update. You *MUST* update both
*SCP firmware* and *TF-A* for this feature. This will change the layout and
the boot process but you can switch back to the normal one by changing
the DSW 1-4 off.
Configure U-Boot
----------------
To enable the FWU Multi Bank Update on the DeveloperBox board the
configs/synquacer_developerbox_defconfig enables default FWU configuration ::
CONFIG_FWU_MULTI_BANK_UPDATE=y
CONFIG_FWU_MDATA=y
CONFIG_FWU_MDATA_MTD=y
CONFIG_FWU_NUM_BANKS=2
CONFIG_FWU_NUM_IMAGES_PER_BANK=1
CONFIG_CMD_FWU_METADATA=y
And build it::
cd u-boot/
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make synquacer_developerbox_defconfig
make -j `noproc`
cd ../
By default, the CONFIG_FWU_NUM_BANKS and CONFIG_FWU_NUM_IMAGES_PER_BANKS are
set to 2 and 1 respectively. This uses FIP (Firmware Image Package) type image
which contains TF-A, U-Boot and OP-TEE (the OP-TEE is optional).
You can use fiptool to compose the FIP image from those firmware images.
Rebuild SCP firmware
--------------------
Rebuild SCP firmware which supports FWU Multi Bank Update as below::
cd SCP-firmware/
OUT=./build/product/synquacer
ROMFW_FILE=$OUT/scp_romfw/$SCP_BUILD_MODE/bin/scp_romfw.bin
RAMFW_FILE=$OUT/scp_ramfw/$SCP_BUILD_MODE/bin/scp_ramfw.bin
ROMRAMFW_FILE=scp_romramfw_release.bin
make CC=arm-none-eabi-gcc PRODUCT=synquacer MODE=release
tr "\000" "\377" < /dev/zero | dd of=${ROMRAMFW_FILE} bs=1 count=196608
dd if=${ROMFW_FILE} of=${ROMRAMFW_FILE} bs=1 conv=notrunc seek=0
dd if=${RAMFW_FILE} of=${ROMRAMFW_FILE} bs=1 seek=65536
cd ../
And you can get the `scp_romramfw_release.bin` file.
Rebuild OPTEE firmware
----------------------
Rebuild OPTEE to use in new-layout FIP as below::
cd optee_os/
make -j`nproc` PLATFORM=synquacer ARCH=arm \
CROSS_COMPILE64=aarch64-linux-gnu- CFG_ARM64_core=y \
CFG_CRYPTO_WITH_CE=y CFG_CORE_HEAP_SIZE=524288 CFG_CORE_DYN_SHM=y \
CFG_CORE_ARM64_PA_BITS=48 CFG_TEE_CORE_LOG_LEVEL=1 CFG_TEE_TA_LOG_LEVEL=1
cp out/arm-plat-synquacer/core/tee-pager_v2.bin ../arm-trusted-firmware/
The produced `tee-pager_v2.bin` is to be used while building TF-A next.
Rebuild TF-A and FIP
--------------------
Rebuild TF-A which supports FWU Multi Bank Update as below::
cd arm-trusted-firmware/
make CROSS_COMPILE=aarch64-linux-gnu- -j`nproc` PLAT=synquacer \
TRUSTED_BOARD_BOOT=1 SPD=opteed SQ_RESET_TO_BL2=1 GENERATE_COT=1 \
MBEDTLS_DIR=../mbedtls BL32=tee-pager_v2.bin \
BL33=../u-boot/u-boot.bin all fip fiptool
And make a FIP image.::
cp build/synquacer/release/fip.bin SPI_NOR_NEWFIP.fd
tools/fiptool/fiptool update --tb-fw build/synquacer/release/bl2.bin SPI_NOR_NEWFIP.fd
UUIDs for the FWU Multi Bank Update
-----------------------------------
FWU multi-bank update requires some UUIDs. The DeveloperBox platform uses
following UUIDs.
- Location UUID for the FIP image: 17e86d77-41f9-4fd7-87ec-a55df9842de5
- Image type UUID for the FIP image: 10c36d7d-ca52-b843-b7b9-f9d6c501d108
- Image UUID for Bank0 : 5a66a702-99fd-4fef-a392-c26e261a2828
- Image UUID for Bank1 : a8f868a1-6e5c-4757-878d-ce63375ef2c0
These UUIDs are used for making a FWU metadata image.
u-boot$ ./tools/mkfwumdata -i 1 -b 2 \
17e86d77-41f9-4fd7-87ec-a55df9842de5,10c36d7d-ca52-b843-b7b9-f9d6c501d108,5a66a702-99fd-4fef-a392-c26e261a2828,a8f868a1-6e5c-4757-878d-ce63375ef2c0 \
../devbox-fwu-mdata.img
Create Accept & Revert capsules
u-boot$ ./tools/mkeficapsule -A -g 7d6dc310-52ca-43b8-b7b9-f9d6c501d108 NEWFIP_accept.Cap
u-boot$ ./tools/mkeficapsule -R NEWFIP_revert.Cap
Install via flash writer
------------------------
As explained in above section, the new FIP image and the FWU metadata image
can be installed via NOR flash writer.
Once the flasher tool is running we are ready to flash the images.::
Write the FIP image to the Bank-0 & 1 at 6MB and 10MB offset.::
flash rawwrite 600000 180000
flash rawwrite a00000 180000
>> Send SPI_NOR_NEWFIP.fd via XMODEM (Control-A S in minicom) <<
flash rawwrite 500000 1000
flash rawwrite 530000 1000
>> Send devbox-fwu-mdata.img via XMODEM (Control-A S in minicom) <<
And write the new SCP firmware.::
flash write cm3
>> Send scp_romramfw_release.bin via XMODEM (Control-A S in minicom) <<
At last, turn on the DSW 3-4 on the board, and reboot.
Note that if DSW 3-4 is turned off, the DeveloperBox will boot from
the original EDK2 firmware (or non-FWU U-Boot if you already installed).

View file

@ -1,13 +1,13 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/firmware/u-boot,fwu-mdata-sf.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
$id: http://devicetree.org/schemas/firmware/u-boot,fwu-mdata-mtd.yaml#
$schema: http://devicetree.org/meta-schemas/base.yaml#
title: FWU metadata on MTD device without GPT
maintainers:
- Masami Hiramatsu <masami.hiramatsu@linaro.org>
- Jassi Brar <jaswinder.singh@linaro.org>
properties:
compatible:
@ -15,24 +15,101 @@ properties:
- const: u-boot,fwu-mdata-mtd
fwu-mdata-store:
maxItems: 1
description: Phandle of the MTD device which contains the FWU medatata.
$ref: /schemas/types.yaml#/definitions/phandle
description: Phandle of the MTD device which contains the FWU MetaData and Banks.
mdata-offsets:
mdata-parts:
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
minItems: 2
description: Offsets of the primary and secondary FWU metadata in the NOR flash.
maxItems: 2
description: labels of the primary and secondary FWU metadata partitions in the 'fixed-partitions' subnode of the 'jedec,spi-nor' flash device node.
patternProperties:
"fwu-bank[0-9]":
type: object
description: List of FWU mtd-backed banks. Typically two banks.
properties:
id:
$ref: /schemas/types.yaml#/definitions/uint32
description: Index of the bank.
label:
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
minItems: 1
maxItems: 1
description: label of the partition, in the 'fixed-partitions' subnode of the 'jedec,spi-nor' flash device node, that holds this bank.
patternProperties:
"fwu-image[0-9]":
type: object
description: List of images in the FWU mtd-backed bank.
properties:
id:
$ref: /schemas/types.yaml#/definitions/uint32
description: Index of the bank.
offset:
$ref: /schemas/types.yaml#/definitions/uint32
description: Offset, from start of the bank, where the image is located.
size:
$ref: /schemas/types.yaml#/definitions/uint32
description: Size reserved for the image.
uuid:
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
minItems: 1
maxItems: 1
description: UUID of the image.
required:
- id
- offset
- size
- uuid
additionalProperties: false
required:
- id
- label
- fwu-images
additionalProperties: false
required:
- compatible
- fwu-mdata-store
- mdata-offsets
- mdata-parts
- fwu-banks
additionalProperties: false
examples:
- |
fwu-mdata {
compatible = "u-boot,fwu-mdata-mtd";
fwu-mdata-store = <&spi-flash>;
mdata-offsets = <0x500000 0x530000>;
};
fwu-mdata {
compatible = "u-boot,fwu-mdata-mtd";
fwu-mdata-store = <&flash0>;
mdata-parts = "MDATA-Pri", "MDATA-Sec";
fwu-bank0 {
id = <0>;
label = "FIP-Bank0";
fwu-image0 {
id = <0>;
offset = <0x0>;
size = <0x400000>;
uuid = "5a66a702-99fd-4fef-a392-c26e261a2828";
};
};
fwu-bank1 {
id = <1>;
label = "FIP-Bank1";
fwu-image0 {
id = <0>;
offset = <0x0>;
size = <0x400000>;
uuid = "a8f868a1-6e5c-4757-878d-ce63375ef2c0";
};
};
};
...

89
doc/mkfwumdata.1 Normal file
View file

@ -0,0 +1,89 @@
.\" SPDX-License-Identifier: GPL-2.0-or-later
.\" Copyright (C) 2023 Jassi Brar <jaswinder.singh@linaro.org>
.TH MKFWUMDATA 1 2023-04-10 U-Boot
.SH NAME
mkfwumdata \- create FWU metadata image
.
.SH SYNOPSIS
.SY mkfwumdata
.OP \-a activeidx
.OP \-p previousidx
.OP \-g
.BI \-i\~ imagecount
.BI \-b\~ bankcount
.I UUIDs
.I outputimage
.YS
.SY mkfwumdata
.B \-h
.YS
.
.SH DESCRIPTION
.B mkfwumdata
creates metadata info to be used with FWU.
.
.SH OPTIONS
.TP
.B \-h
Print usage information and exit.
.
.TP
.B \-a
Set
.IR activeidx
as the currently active Bank. Default is 0.
.
.TP
.B \-p
Set
.IR previousidx
as the previous active Bank. Default is
.IR activeidx "-1"
or
.IR bankcount "-1,"
whichever is non-negative.
.
.TP
.B \-g
Convert the
.IR UUIDs
as GUIDs before use.
.
.TP
.B \-i
Specify there are
.IR imagecount
images in each bank.
.
.TP
.B \-b
Specify there are a total of
.IR bankcount
banks.
.
.TP
.IR UUIDs
Comma-separated list of UUIDs required to create the metadata :-
location_uuid,image_type_uuid,<images per bank uuid list of all banks>
.
.TP
.IR outputimage
Specify the name of the metadata image file to be created.
.
.SH BUGS
Please report bugs to the
.UR https://\:source\:.denx\:.de/\:u-boot/\:u-boot/\:issues
U-Boot bug tracker
.UE .
.SH EXAMPLES
Create a metadata image with 2 banks and 1 image/bank, BankAct=0, BankPrev=1:
.PP
.EX
.in +4
$ \c
.B mkfwumdata \-a 0 \-p 1 \-b 2 \-i 1 \\\\\&
.in +6
.B 17e86d77-41f9-4fd7-87ec-a55df9842de5,\\\\\&
.B 10c36d7d-ca52-b843-b7b9-f9d6c501d108,\\\\\&
.B 5a66a702-99fd-4fef-a392-c26e261a2828,a8f868a1-6e5c-4757-878d-ce63375ef2c0 \\\\\&
.B fwu-mdata.img

View file

@ -6,6 +6,11 @@ config FWU_MDATA
FWU Metadata partitions reside on the same storage device
which contains the other FWU updatable firmware images.
choice
prompt "Storage Layout Scheme"
depends on FWU_MDATA
default FWU_MDATA_GPT_BLK
config FWU_MDATA_GPT_BLK
bool "FWU Metadata access for GPT partitioned Block devices"
select PARTITION_TYPE_GUID
@ -14,3 +19,13 @@ config FWU_MDATA_GPT_BLK
help
Enable support for accessing FWU Metadata on GPT partitioned
block devices.
config FWU_MDATA_MTD
bool "Raw MTD devices"
depends on MTD
help
Enable support for accessing FWU Metadata on non-partitioned
(or non-GPT partitioned, e.g. partition nodes in devicetree)
MTD devices.
endchoice

View file

@ -6,3 +6,4 @@
obj-$(CONFIG_FWU_MDATA) += fwu-mdata-uclass.o
obj-$(CONFIG_FWU_MDATA_GPT_BLK) += gpt_blk.o
obj-$(CONFIG_FWU_MDATA_MTD) += raw_mtd.o

View file

@ -14,170 +14,39 @@
#include <linux/errno.h>
#include <linux/types.h>
#include <u-boot/crc.h>
/**
* fwu_get_mdata_part_num() - Get the FWU metadata partition numbers
* @dev: FWU metadata device
* @mdata_parts: array for storing the metadata partition numbers
*
* Get the partition numbers on the storage device on which the
* FWU metadata is stored. Two partition numbers will be returned.
* fwu_read_mdata() - Wrapper around fwu_mdata_ops.read_mdata()
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_get_mdata_part_num(struct udevice *dev, uint *mdata_parts)
int fwu_read_mdata(struct udevice *dev, struct fwu_mdata *mdata, bool primary)
{
const struct fwu_mdata_ops *ops = device_get_ops(dev);
if (!ops->get_mdata_part_num) {
log_debug("get_mdata_part_num() method not defined\n");
if (!ops->read_mdata) {
log_debug("read_mdata() method not defined\n");
return -ENOSYS;
}
return ops->get_mdata_part_num(dev, mdata_parts);
return ops->read_mdata(dev, mdata, primary);
}
/**
* fwu_read_mdata_partition() - Read the FWU metadata from a partition
* @dev: FWU metadata device
* @mdata: Copy of the FWU metadata
* @part_num: Partition number from which FWU metadata is to be read
*
* Read the FWU metadata from the specified partition number
* fwu_write_mdata() - Wrapper around fwu_mdata_ops.write_mdata()
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_read_mdata_partition(struct udevice *dev, struct fwu_mdata *mdata,
uint part_num)
int fwu_write_mdata(struct udevice *dev, struct fwu_mdata *mdata, bool primary)
{
const struct fwu_mdata_ops *ops = device_get_ops(dev);
if (!ops->read_mdata_partition) {
log_debug("read_mdata_partition() method not defined\n");
if (!ops->write_mdata) {
log_debug("write_mdata() method not defined\n");
return -ENOSYS;
}
return ops->read_mdata_partition(dev, mdata, part_num);
}
/**
* fwu_write_mdata_partition() - Write the FWU metadata to a partition
* @dev: FWU metadata device
* @mdata: Copy of the FWU metadata
* @part_num: Partition number to which FWU metadata is to be written
*
* Write the FWU metadata to the specified partition number
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_write_mdata_partition(struct udevice *dev, struct fwu_mdata *mdata,
uint part_num)
{
const struct fwu_mdata_ops *ops = device_get_ops(dev);
if (!ops->write_mdata_partition) {
log_debug("write_mdata_partition() method not defined\n");
return -ENOSYS;
}
return ops->write_mdata_partition(dev, mdata, part_num);
}
/**
* fwu_mdata_check() - Check if the FWU metadata is valid
* @dev: FWU metadata device
*
* Validate both copies of the FWU metadata. If one of the copies
* has gone bad, restore it from the other copy.
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_mdata_check(struct udevice *dev)
{
const struct fwu_mdata_ops *ops = device_get_ops(dev);
if (!ops->check_mdata) {
log_debug("check_mdata() method not defined\n");
return -ENOSYS;
}
return ops->check_mdata(dev);
}
/**
* fwu_get_mdata() - Get a FWU metadata copy
* @dev: FWU metadata device
* @mdata: Copy of the FWU metadata
*
* Get a valid copy of the FWU metadata.
*
* Note: This function is to be called first when modifying any fields
* in the metadata. The sequence of calls to modify any field in the
* metadata would be 1) fwu_get_mdata 2) Modify metadata, followed by
* 3) fwu_update_mdata
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_get_mdata(struct udevice *dev, struct fwu_mdata *mdata)
{
const struct fwu_mdata_ops *ops = device_get_ops(dev);
if (!ops->get_mdata) {
log_debug("get_mdata() method not defined\n");
return -ENOSYS;
}
return ops->get_mdata(dev, mdata);
}
/**
* fwu_update_mdata() - Update the FWU metadata
* @dev: FWU metadata device
* @mdata: Copy of the FWU metadata
*
* Update the FWU metadata structure by writing to the
* FWU metadata partitions.
*
* Note: This function is not to be called directly to update the
* metadata fields. The sequence of function calls should be
* 1) fwu_get_mdata() 2) Modify the medata fields 3) fwu_update_mdata()
*
* The sequence of updating the partitions should be, update the
* primary metadata partition (first partition encountered), followed
* by updating the secondary partition. With this update sequence, in
* the rare scenario that the two metadata partitions are valid but do
* not match, maybe due to power outage at the time of updating the
* metadata copies, the secondary partition can be updated from the
* primary.
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_update_mdata(struct udevice *dev, struct fwu_mdata *mdata)
{
void *buf;
const struct fwu_mdata_ops *ops = device_get_ops(dev);
if (!ops->update_mdata) {
log_debug("get_mdata() method not defined\n");
return -ENOSYS;
}
/*
* Calculate the crc32 for the updated FWU metadata
* and put the updated value in the FWU metadata crc32
* field
*/
buf = &mdata->version;
mdata->crc32 = crc32(0, buf, sizeof(*mdata) - sizeof(u32));
return ops->update_mdata(dev, mdata);
return ops->write_mdata(dev, mdata, primary);
}
UCLASS_DRIVER(fwu_mdata) = {

View file

@ -24,38 +24,40 @@ enum {
MDATA_WRITE,
};
static int gpt_get_mdata_partitions(struct blk_desc *desc,
uint mdata_parts[2])
static uint g_mdata_part[2]; /* = {0, 0} to check against uninit parts */
static int gpt_get_mdata_partitions(struct blk_desc *desc)
{
int i, ret;
int i;
u32 nparts;
efi_guid_t part_type_guid;
struct disk_partition info;
const efi_guid_t fwu_mdata_guid = FWU_MDATA_GUID;
/* if primary and secondary partitions already found */
if (g_mdata_part[0] && g_mdata_part[1])
return 0;
nparts = 0;
for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) {
for (i = 1; i < MAX_SEARCH_PARTITIONS && nparts < 2; i++) {
if (part_get_info(desc, i, &info))
continue;
uuid_str_to_bin(info.type_guid, part_type_guid.b,
UUID_STR_FORMAT_GUID);
if (!guidcmp(&fwu_mdata_guid, &part_type_guid)) {
if (nparts < 2)
mdata_parts[nparts] = i;
++nparts;
}
if (!guidcmp(&fwu_mdata_guid, &part_type_guid))
g_mdata_part[nparts++] = i;
}
if (nparts != 2) {
log_debug("Expect two copies of the FWU metadata instead of %d\n",
nparts);
ret = -EINVAL;
} else {
ret = 0;
g_mdata_part[0] = 0;
g_mdata_part[1] = 0;
return -EINVAL;
}
return ret;
return 0;
}
static int gpt_get_mdata_disk_part(struct blk_desc *desc,
@ -123,112 +125,6 @@ static int gpt_read_write_mdata(struct blk_desc *desc,
return 0;
}
static int fwu_gpt_update_mdata(struct udevice *dev, struct fwu_mdata *mdata)
{
int ret;
struct blk_desc *desc;
uint mdata_parts[2];
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
desc = dev_get_uclass_plat(priv->blk_dev);
ret = gpt_get_mdata_partitions(desc, mdata_parts);
if (ret < 0) {
log_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
}
/* First write the primary partition */
ret = gpt_read_write_mdata(desc, mdata, MDATA_WRITE, mdata_parts[0]);
if (ret < 0) {
log_debug("Updating primary FWU metadata partition failed\n");
return ret;
}
/* And now the replica */
ret = gpt_read_write_mdata(desc, mdata, MDATA_WRITE, mdata_parts[1]);
if (ret < 0) {
log_debug("Updating secondary FWU metadata partition failed\n");
return ret;
}
return 0;
}
static int gpt_get_mdata(struct blk_desc *desc, struct fwu_mdata *mdata)
{
int ret;
uint mdata_parts[2];
ret = gpt_get_mdata_partitions(desc, mdata_parts);
if (ret < 0) {
log_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
}
ret = gpt_read_write_mdata(desc, mdata, MDATA_READ, mdata_parts[0]);
if (ret < 0) {
log_debug("Failed to read the FWU metadata from the device\n");
return -EIO;
}
ret = fwu_verify_mdata(mdata, 1);
if (!ret)
return 0;
/*
* Verification of the primary FWU metadata copy failed.
* Try to read the replica.
*/
memset(mdata, '\0', sizeof(struct fwu_mdata));
ret = gpt_read_write_mdata(desc, mdata, MDATA_READ, mdata_parts[1]);
if (ret < 0) {
log_debug("Failed to read the FWU metadata from the device\n");
return -EIO;
}
ret = fwu_verify_mdata(mdata, 0);
if (!ret)
return 0;
/* Both the FWU metadata copies are corrupted. */
return -EIO;
}
static int fwu_gpt_get_mdata(struct udevice *dev, struct fwu_mdata *mdata)
{
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
return gpt_get_mdata(dev_get_uclass_plat(priv->blk_dev), mdata);
}
static int fwu_gpt_get_mdata_partitions(struct udevice *dev, uint *mdata_parts)
{
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
return gpt_get_mdata_partitions(dev_get_uclass_plat(priv->blk_dev),
mdata_parts);
}
static int fwu_gpt_read_mdata_partition(struct udevice *dev,
struct fwu_mdata *mdata, uint part_num)
{
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
return gpt_read_write_mdata(dev_get_uclass_plat(priv->blk_dev),
mdata, MDATA_READ, part_num);
}
static int fwu_gpt_write_mdata_partition(struct udevice *dev,
struct fwu_mdata *mdata, uint part_num)
{
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
return gpt_read_write_mdata(dev_get_uclass_plat(priv->blk_dev),
mdata, MDATA_WRITE, part_num);
}
static int fwu_get_mdata_device(struct udevice *dev, struct udevice **mdata_dev)
{
u32 phandle;
@ -267,12 +163,43 @@ static int fwu_mdata_gpt_blk_probe(struct udevice *dev)
return 0;
}
static int fwu_gpt_read_mdata(struct udevice *dev, struct fwu_mdata *mdata,
bool primary)
{
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
struct blk_desc *desc = dev_get_uclass_plat(priv->blk_dev);
int ret;
ret = gpt_get_mdata_partitions(desc);
if (ret < 0) {
log_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
}
return gpt_read_write_mdata(desc, mdata, MDATA_READ,
primary ? g_mdata_part[0] : g_mdata_part[1]);
}
static int fwu_gpt_write_mdata(struct udevice *dev, struct fwu_mdata *mdata,
bool primary)
{
struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
struct blk_desc *desc = dev_get_uclass_plat(priv->blk_dev);
int ret;
ret = gpt_get_mdata_partitions(desc);
if (ret < 0) {
log_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
}
return gpt_read_write_mdata(desc, mdata, MDATA_WRITE,
primary ? g_mdata_part[0] : g_mdata_part[1]);
}
static const struct fwu_mdata_ops fwu_gpt_blk_ops = {
.get_mdata = fwu_gpt_get_mdata,
.update_mdata = fwu_gpt_update_mdata,
.get_mdata_part_num = fwu_gpt_get_mdata_partitions,
.read_mdata_partition = fwu_gpt_read_mdata_partition,
.write_mdata_partition = fwu_gpt_write_mdata_partition,
.read_mdata = fwu_gpt_read_mdata,
.write_mdata = fwu_gpt_write_mdata,
};
static const struct udevice_id fwu_mdata_ids[] = {

269
drivers/fwu-mdata/raw_mtd.c Normal file
View file

@ -0,0 +1,269 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2023, Linaro Limited
*/
#define LOG_CATEGORY UCLASS_FWU_MDATA
#include <fwu.h>
#include <fwu_mdata.h>
#include <memalign.h>
#include <linux/errno.h>
#include <linux/types.h>
/* Internal helper structure to move data around */
struct fwu_mdata_mtd_priv {
struct mtd_info *mtd;
char pri_label[50];
char sec_label[50];
u32 pri_offset;
u32 sec_offset;
};
enum fwu_mtd_op {
FWU_MTD_READ,
FWU_MTD_WRITE,
};
extern struct fwu_mtd_image_info fwu_mtd_images[];
static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
{
return !do_div(size, mtd->erasesize);
}
static int mtd_io_data(struct mtd_info *mtd, u32 offs, u32 size, void *data,
enum fwu_mtd_op op)
{
struct mtd_oob_ops io_op = {};
u64 lock_len;
size_t len;
void *buf;
int ret;
if (!mtd_is_aligned_with_block_size(mtd, offs)) {
log_err("Offset unaligned with a block (0x%x)\n", mtd->erasesize);
return -EINVAL;
}
/* This will expand erase size to align with the block size */
lock_len = round_up(size, mtd->erasesize);
ret = mtd_unlock(mtd, offs, lock_len);
if (ret && ret != -EOPNOTSUPP)
return ret;
if (op == FWU_MTD_WRITE) {
struct erase_info erase_op = {};
erase_op.mtd = mtd;
erase_op.addr = offs;
erase_op.len = lock_len;
erase_op.scrub = 0;
ret = mtd_erase(mtd, &erase_op);
if (ret)
goto lock;
}
/* Also, expand the write size to align with the write size */
len = round_up(size, mtd->writesize);
buf = memalign(ARCH_DMA_MINALIGN, len);
if (!buf) {
ret = -ENOMEM;
goto lock;
}
memset(buf, 0xff, len);
io_op.mode = MTD_OPS_AUTO_OOB;
io_op.len = len;
io_op.datbuf = buf;
if (op == FWU_MTD_WRITE) {
memcpy(buf, data, size);
ret = mtd_write_oob(mtd, offs, &io_op);
} else {
ret = mtd_read_oob(mtd, offs, &io_op);
if (!ret)
memcpy(data, buf, size);
}
free(buf);
lock:
mtd_lock(mtd, offs, lock_len);
return ret;
}
static int fwu_mtd_read_mdata(struct udevice *dev, struct fwu_mdata *mdata, bool primary)
{
struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
struct mtd_info *mtd = mtd_priv->mtd;
u32 offs = primary ? mtd_priv->pri_offset : mtd_priv->sec_offset;
return mtd_io_data(mtd, offs, sizeof(struct fwu_mdata), mdata, FWU_MTD_READ);
}
static int fwu_mtd_write_mdata(struct udevice *dev, struct fwu_mdata *mdata, bool primary)
{
struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
struct mtd_info *mtd = mtd_priv->mtd;
u32 offs = primary ? mtd_priv->pri_offset : mtd_priv->sec_offset;
return mtd_io_data(mtd, offs, sizeof(struct fwu_mdata), mdata, FWU_MTD_WRITE);
}
static int flash_partition_offset(struct udevice *dev, const char *part_name, fdt_addr_t *offset)
{
ofnode node, parts_node;
fdt_addr_t size = 0;
parts_node = ofnode_by_compatible(dev_ofnode(dev), "fixed-partitions");
node = ofnode_by_prop_value(parts_node, "label", part_name, strlen(part_name) + 1);
if (!ofnode_valid(node)) {
log_err("Warning: Failed to find partition by label <%s>\n", part_name);
return -ENOENT;
}
*offset = ofnode_get_addr_size_index_notrans(node, 0, &size);
return (int)size;
}
static int fwu_mdata_mtd_of_to_plat(struct udevice *dev)
{
struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
const fdt32_t *phandle_p = NULL;
struct udevice *mtd_dev;
struct mtd_info *mtd;
const char *label;
fdt_addr_t offset;
int ret, size;
u32 phandle;
ofnode bank;
int off_img;
/* Find the FWU mdata storage device */
phandle_p = ofnode_get_property(dev_ofnode(dev),
"fwu-mdata-store", &size);
if (!phandle_p) {
log_err("FWU meta data store not defined in device-tree\n");
return -ENOENT;
}
phandle = fdt32_to_cpu(*phandle_p);
ret = device_get_global_by_ofnode(ofnode_get_by_phandle(phandle),
&mtd_dev);
if (ret) {
log_err("FWU: failed to get mtd device\n");
return ret;
}
mtd_probe_devices();
mtd_for_each_device(mtd) {
if (mtd->dev == mtd_dev) {
mtd_priv->mtd = mtd;
log_debug("Found the FWU mdata mtd device %s\n", mtd->name);
break;
}
}
if (!mtd_priv->mtd) {
log_err("Failed to find mtd device by fwu-mdata-store\n");
return -ENODEV;
}
/* Get the offset of primary and secondary mdata */
ret = ofnode_read_string_index(dev_ofnode(dev), "mdata-parts", 0, &label);
if (ret)
return ret;
strncpy(mtd_priv->pri_label, label, 50);
ret = flash_partition_offset(mtd_dev, mtd_priv->pri_label, &offset);
if (ret <= 0)
return ret;
mtd_priv->pri_offset = offset;
ret = ofnode_read_string_index(dev_ofnode(dev), "mdata-parts", 1, &label);
if (ret)
return ret;
strncpy(mtd_priv->sec_label, label, 50);
ret = flash_partition_offset(mtd_dev, mtd_priv->sec_label, &offset);
if (ret <= 0)
return ret;
mtd_priv->sec_offset = offset;
off_img = 0;
ofnode_for_each_subnode(bank, dev_ofnode(dev)) {
int bank_num, bank_offset, bank_size;
const char *bank_name;
ofnode image;
ofnode_read_u32(bank, "id", &bank_num);
bank_name = ofnode_read_string(bank, "label");
bank_size = flash_partition_offset(mtd_dev, bank_name, &offset);
if (bank_size <= 0)
return bank_size;
bank_offset = offset;
log_debug("Bank%d: %s [0x%x - 0x%x]\n",
bank_num, bank_name, bank_offset, bank_offset + bank_size);
ofnode_for_each_subnode(image, bank) {
int image_num, image_offset, image_size;
const char *uuid;
if (off_img == CONFIG_FWU_NUM_BANKS *
CONFIG_FWU_NUM_IMAGES_PER_BANK) {
log_err("DT provides more images than configured!\n");
break;
}
uuid = ofnode_read_string(image, "uuid");
ofnode_read_u32(image, "id", &image_num);
ofnode_read_u32(image, "offset", &image_offset);
ofnode_read_u32(image, "size", &image_size);
fwu_mtd_images[off_img].start = bank_offset + image_offset;
fwu_mtd_images[off_img].size = image_size;
fwu_mtd_images[off_img].bank_num = bank_num;
fwu_mtd_images[off_img].image_num = image_num;
strcpy(fwu_mtd_images[off_img].uuidbuf, uuid);
log_debug("\tImage%d: %s @0x%x\n\n",
image_num, uuid, bank_offset + image_offset);
off_img++;
}
}
return 0;
}
static int fwu_mdata_mtd_probe(struct udevice *dev)
{
/* Ensure the metadata can be read. */
return fwu_get_mdata(NULL);
}
static struct fwu_mdata_ops fwu_mtd_ops = {
.read_mdata = fwu_mtd_read_mdata,
.write_mdata = fwu_mtd_write_mdata,
};
static const struct udevice_id fwu_mdata_ids[] = {
{ .compatible = "u-boot,fwu-mdata-mtd" },
{ }
};
U_BOOT_DRIVER(fwu_mdata_mtd) = {
.name = "fwu-mdata-mtd",
.id = UCLASS_FWU_MDATA,
.of_match = fwu_mdata_ids,
.ops = &fwu_mtd_ops,
.probe = fwu_mdata_mtd_probe,
.of_to_plat = fwu_mdata_mtd_of_to_plat,
.priv_auto = sizeof(struct fwu_mdata_mtd_priv),
};

View file

@ -40,19 +40,29 @@
/* Since U-Boot 64bit PCIe support is limited, disable 64bit MMIO support */
#ifdef CONFIG_FWU_MULTI_BANK_UPDATE
#define DEFAULT_DFU_ALT_INFO
#else
#define DEFAULT_DFU_ALT_INFO "dfu_alt_info=" \
"mtd nor1=u-boot.bin raw 200000 100000;" \
"fip.bin raw 180000 78000;" \
"optee.bin raw 500000 100000\0"
#endif
/* GUIDs for capsule updatable firmware images */
#define DEVELOPERBOX_UBOOT_IMAGE_GUID \
EFI_GUID(0x53a92e83, 0x4ef4, 0x473a, 0x8b, 0x0d, \
0xb5, 0xd8, 0xc7, 0xb2, 0xd6, 0x00)
#ifdef CONFIG_FWU_MULTI_BANK_UPDATE
#define DEVELOPERBOX_FIP_IMAGE_GUID \
EFI_GUID(0x7d6dc310, 0x52ca, 0x43b8, 0xb7, 0xb9, \
0xf9, 0xd6, 0xc5, 0x01, 0xd1, 0x08)
#else
#define DEVELOPERBOX_FIP_IMAGE_GUID \
EFI_GUID(0x880866e9, 0x84ba, 0x4793, 0xa9, 0x08, \
0x33, 0xe0, 0xb9, 0x16, 0xf3, 0x98)
#endif
#define DEVELOPERBOX_OPTEE_IMAGE_GUID \
EFI_GUID(0xc1b629f1, 0xce0e, 0x4894, 0x82, 0xbf, \

View file

@ -8,6 +8,8 @@
#include <blk.h>
#include <efi.h>
#include <mtd.h>
#include <uuid.h>
#include <linux/types.h>
@ -18,83 +20,32 @@ struct fwu_mdata_gpt_blk_priv {
struct udevice *blk_dev;
};
/**
* @mdata_check: check the validity of the FWU metadata partitions
* @get_mdata() - Get a FWU metadata copy
* @update_mdata() - Update the FWU metadata copy
*/
struct fwu_mtd_image_info {
u32 start, size;
int bank_num, image_num;
char uuidbuf[UUID_STR_LEN + 1];
};
struct fwu_mdata_ops {
/**
* check_mdata() - Check if the FWU metadata is valid
* @dev: FWU device
*
* Validate both copies of the FWU metadata. If one of the copies
* has gone bad, restore it from the other copy.
* read_mdata() - Populate the asked FWU metadata copy
* @dev: FWU metadata device
* @mdata: Output FWU mdata read
* @primary: If primary or secondary copy of metadata is to be read
*
* Return: 0 if OK, -ve on error
*/
int (*check_mdata)(struct udevice *dev);
int (*read_mdata)(struct udevice *dev, struct fwu_mdata *mdata, bool primary);
/**
* get_mdata() - Get a FWU metadata copy
* @dev: FWU device
* @mdata: Pointer to FWU metadata
*
* Get a valid copy of the FWU metadata.
* write_mdata() - Write the given FWU metadata copy
* @dev: FWU metadata device
* @mdata: Copy of the FWU metadata to write
* @primary: If primary or secondary copy of metadata is to be written
*
* Return: 0 if OK, -ve on error
*/
int (*get_mdata)(struct udevice *dev, struct fwu_mdata *mdata);
/**
* update_mdata() - Update the FWU metadata
* @dev: FWU device
* @mdata: Copy of the FWU metadata
*
* Update the FWU metadata structure by writing to the
* FWU metadata partitions.
*
* Return: 0 if OK, -ve on error
*/
int (*update_mdata)(struct udevice *dev, struct fwu_mdata *mdata);
/**
* get_mdata_part_num() - Get the FWU metadata partition numbers
* @dev: FWU metadata device
* @mdata_parts: array for storing the metadata partition numbers
*
* Get the partition numbers on the storage device on which the
* FWU metadata is stored. Two partition numbers will be returned.
*
* Return: 0 if OK, -ve on error
*/
int (*get_mdata_part_num)(struct udevice *dev, uint *mdata_parts);
/**
* read_mdata_partition() - Read the FWU metadata from a partition
* @dev: FWU metadata device
* @mdata: Copy of the FWU metadata
* @part_num: Partition number from which FWU metadata is to be read
*
* Read the FWU metadata from the specified partition number
*
* Return: 0 if OK, -ve on error
*/
int (*read_mdata_partition)(struct udevice *dev,
struct fwu_mdata *mdata, uint part_num);
/**
* write_mdata_partition() - Write the FWU metadata to a partition
* @dev: FWU metadata device
* @mdata: Copy of the FWU metadata
* @part_num: Partition number to which FWU metadata is to be written
*
* Write the FWU metadata to the specified partition number
*
* Return: 0 if OK, -ve on error
*/
int (*write_mdata_partition)(struct udevice *dev,
struct fwu_mdata *mdata, uint part_num);
int (*write_mdata)(struct udevice *dev, struct fwu_mdata *mdata, bool primary);
};
#define FWU_MDATA_VERSION 0x1
@ -127,100 +78,25 @@ struct fwu_mdata_ops {
0xe1, 0xfc, 0xed, 0xf1, 0xc6, 0xf8)
/**
* fwu_check_mdata_validity() - Check for validity of the FWU metadata copies
*
* Read both the metadata copies from the storage media, verify their
* checksum, and ascertain that both copies match. If one of the copies
* has gone bad, restore it from the good copy.
*
* Return: 0 if OK, -ve on error
*
* fwu_read_mdata() - Wrapper around fwu_mdata_ops.read_mdata()
*/
int fwu_check_mdata_validity(void);
int fwu_read_mdata(struct udevice *dev, struct fwu_mdata *mdata, bool primary);
/**
* fwu_get_mdata_part_num() - Get the FWU metadata partition numbers
* @dev: FWU metadata device
* @mdata_parts: array for storing the metadata partition numbers
*
* Get the partition numbers on the storage device on which the
* FWU metadata is stored. Two partition numbers will be returned
* through the array.
*
* Return: 0 if OK, -ve on error
*
* fwu_write_mdata() - Wrapper around fwu_mdata_ops.write_mdata()
*/
int fwu_get_mdata_part_num(struct udevice *dev, uint *mdata_parts);
int fwu_write_mdata(struct udevice *dev, struct fwu_mdata *mdata, bool primary);
/**
* fwu_read_mdata_partition() - Read the FWU metadata from a partition
* @dev: FWU metadata device
* @mdata: Copy of the FWU metadata
* @part_num: Partition number from which FWU metadata is to be read
* fwu_get_mdata() - Read, verify and return the FWU metadata
*
* Read the FWU metadata from the specified partition number
* Read both the metadata copies from the storage media, verify their checksum,
* and ascertain that both copies match. If one of the copies has gone bad,
* restore it from the good copy.
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_read_mdata_partition(struct udevice *dev, struct fwu_mdata *mdata,
uint part_num);
/**
* fwu_write_mdata_partition() - Write the FWU metadata to a partition
* @dev: FWU metadata device
* @mdata: Copy of the FWU metadata
* @part_num: Partition number to which FWU metadata is to be written
*
* Write the FWU metadata to the specified partition number
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_write_mdata_partition(struct udevice *dev, struct fwu_mdata *mdata,
uint part_num);
/**
* fwu_get_mdata() - Get a FWU metadata copy
* @dev: FWU metadata device
* @mdata: Copy of the FWU metadata
*
* Get a valid copy of the FWU metadata.
*
* Note: This function is to be called first when modifying any fields
* in the metadata. The sequence of calls to modify any field in the
* metadata would be 1) fwu_get_mdata 2) Modify metadata, followed by
* 3) fwu_update_mdata
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_get_mdata(struct udevice *dev, struct fwu_mdata *mdata);
/**
* fwu_update_mdata() - Update the FWU metadata
* @dev: FWU metadata device
* @mdata: Copy of the FWU metadata
*
* Update the FWU metadata structure by writing to the
* FWU metadata partitions.
*
* Note: This function is not to be called directly to update the
* metadata fields. The sequence of function calls should be
* 1) fwu_get_mdata() 2) Modify the medata fields 3) fwu_update_mdata()
*
* The sequence of updating the partitions should be, update the
* primary metadata partition (first partition encountered), followed
* by updating the secondary partition. With this update sequence, in
* the rare scenario that the two metadata partitions are valid but do
* not match, maybe due to power outage at the time of updating the
* metadata copies, the secondary partition can be updated from the
* primary.
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_update_mdata(struct udevice *dev, struct fwu_mdata *mdata);
int fwu_get_mdata(struct fwu_mdata *mdata);
/**
* fwu_get_active_index() - Get active_index from the FWU metadata
@ -262,18 +138,6 @@ int fwu_set_active_index(uint active_idx);
*/
int fwu_get_image_index(u8 *image_index);
/**
* fwu_mdata_check() - Check if the FWU metadata is valid
* @dev: FWU metadata device
*
* Validate both copies of the FWU metadata. If one of the copies
* has gone bad, restore it from the other copy.
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_mdata_check(struct udevice *dev);
/**
* fwu_revert_boot_index() - Revert the active index in the FWU metadata
*
@ -286,20 +150,6 @@ int fwu_mdata_check(struct udevice *dev);
*/
int fwu_revert_boot_index(void);
/**
* fwu_verify_mdata() - Verify the FWU metadata
* @mdata: FWU metadata structure
* @pri_part: FWU metadata partition is primary or secondary
*
* Verify the FWU metadata by computing the CRC32 for the metadata
* structure and comparing it against the CRC32 value stored as part
* of the structure.
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_verify_mdata(struct fwu_mdata *mdata, bool pri_part);
/**
* fwu_accept_image() - Set the Acceptance bit for the image
* @img_type_id: GUID of the image type for which the accepted bit is to be
@ -409,4 +259,28 @@ u8 fwu_empty_capsule_checks_pass(void);
*/
int fwu_trial_state_ctr_start(void);
/**
* fwu_gen_alt_info_from_mtd() - Parse dfu_alt_info from metadata in mtd
* @buf: Buffer into which the dfu_alt_info is filled
* @len: Maximum characters that can be written in buf
* @mtd: Pointer to underlying MTD device
*
* Parse dfu_alt_info from metadata in mtd. Used for setting the env.
*
* Return: 0 if OK, -ve on error
*/
int fwu_gen_alt_info_from_mtd(char *buf, size_t len, struct mtd_info *mtd);
/**
* fwu_mtd_get_alt_num() - Mapping of fwu_plat_get_alt_num for MTD device
* @image_guid: Image GUID for which DFU alt number needs to be retrieved
* @alt_num: Pointer to the alt_num
* @mtd_dev: Name of mtd device instance
*
* To map fwu_plat_get_alt_num onto mtd based metadata implementation.
*
* Return: 0 if OK, -ve on error
*/
int fwu_mtd_get_alt_num(efi_guid_t *image_guid, u8 *alt_num, const char *mtd_dev);
#endif /* _FWU_H_ */

View file

@ -5,3 +5,4 @@
obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o
obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o
obj-$(CONFIG_FWU_MDATA_MTD) += fwu_mtd.o

View file

@ -15,13 +15,13 @@
#include <linux/errno.h>
#include <linux/types.h>
#include <u-boot/crc.h>
static struct fwu_mdata g_mdata; /* = {0} makes uninit crc32 always invalid */
static struct udevice *g_dev;
static u8 in_trial;
static u8 boottime_check;
#include <linux/errno.h>
#include <linux/types.h>
#include <u-boot/crc.h>
enum {
IMAGE_ACCEPT_SET = 1,
IMAGE_ACCEPT_CLEAR,
@ -33,26 +33,6 @@ enum {
BOTH_PARTS,
};
static int fwu_get_dev_mdata(struct udevice **dev, struct fwu_mdata *mdata)
{
int ret;
ret = uclass_first_device_err(UCLASS_FWU_MDATA, dev);
if (ret) {
log_debug("Cannot find fwu device\n");
return ret;
}
if (!mdata)
return 0;
ret = fwu_get_mdata(*dev, mdata);
if (ret < 0)
log_debug("Unable to get valid FWU metadata\n");
return ret;
}
static int trial_counter_update(u16 *trial_state_ctr)
{
bool delete;
@ -162,133 +142,124 @@ static int fwu_get_image_type_id(u8 *image_index, efi_guid_t *image_type_id)
}
/**
* fwu_verify_mdata() - Verify the FWU metadata
* fwu_sync_mdata() - Update given meta-data partition(s) with the copy provided
* @mdata: FWU metadata structure
* @pri_part: FWU metadata partition is primary or secondary
*
* Verify the FWU metadata by computing the CRC32 for the metadata
* structure and comparing it against the CRC32 value stored as part
* of the structure.
* @part: Bitmask of FWU metadata partitions to be written to
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_verify_mdata(struct fwu_mdata *mdata, bool pri_part)
static int fwu_sync_mdata(struct fwu_mdata *mdata, int part)
{
u32 calc_crc32;
void *buf;
void *buf = &mdata->version;
int err;
buf = &mdata->version;
calc_crc32 = crc32(0, buf, sizeof(*mdata) - sizeof(u32));
if (calc_crc32 != mdata->crc32) {
log_debug("crc32 check failed for %s FWU metadata partition\n",
pri_part ? "primary" : "secondary");
return -EINVAL;
if (part == BOTH_PARTS) {
err = fwu_sync_mdata(mdata, SECONDARY_PART);
if (err)
return err;
part = PRIMARY_PART;
}
/*
* Calculate the crc32 for the updated FWU metadata
* and put the updated value in the FWU metadata crc32
* field
*/
mdata->crc32 = crc32(0, buf, sizeof(*mdata) - sizeof(u32));
err = fwu_write_mdata(g_dev, mdata, part == PRIMARY_PART);
if (err) {
log_err("Unable to write %s mdata\n",
part == PRIMARY_PART ? "primary" : "secondary");
return err;
}
/* update the cached copy of meta-data */
memcpy(&g_mdata, mdata, sizeof(struct fwu_mdata));
return 0;
}
static inline int mdata_crc_check(struct fwu_mdata *mdata)
{
void *buf = &mdata->version;
u32 calc_crc32 = crc32(0, buf, sizeof(*mdata) - sizeof(u32));
return calc_crc32 == mdata->crc32 ? 0 : -EINVAL;
}
/**
* fwu_check_mdata_validity() - Check for validity of the FWU metadata copies
* fwu_get_mdata() - Read, verify and return the FWU metadata
* @mdata: Output FWU metadata read or NULL
*
* Read both the metadata copies from the storage media, verify their checksum,
* and ascertain that both copies match. If one of the copies has gone bad,
* restore it from the good copy.
*
* Return: 0 if OK, -ve on error
*
*/
int fwu_check_mdata_validity(void)
int fwu_get_mdata(struct fwu_mdata *mdata)
{
int ret;
struct udevice *dev;
struct fwu_mdata pri_mdata;
struct fwu_mdata secondary_mdata;
uint mdata_parts[2];
uint valid_partitions, invalid_partitions;
int err;
bool parts_ok[2] = { false };
struct fwu_mdata s, *parts_mdata[2];
ret = fwu_get_dev_mdata(&dev, NULL);
if (ret)
return ret;
parts_mdata[0] = &g_mdata;
parts_mdata[1] = &s;
/*
* Check if the platform has defined its own
* function to check the metadata partitions'
* validity. If so, that takes precedence.
*/
ret = fwu_mdata_check(dev);
if (!ret || ret != -ENOSYS)
return ret;
/* if mdata already read and ready */
err = mdata_crc_check(parts_mdata[0]);
if (!err)
goto ret_mdata;
/* else read, verify and, if needed, fix mdata */
/*
* Two FWU metadata partitions are expected.
* If we don't have two, user needs to create
* them first
*/
valid_partitions = 0;
ret = fwu_get_mdata_part_num(dev, mdata_parts);
if (ret < 0) {
log_debug("Error getting the FWU metadata partitions\n");
return -ENOENT;
for (int i = 0; i < 2; i++) {
parts_ok[i] = false;
err = fwu_read_mdata(g_dev, parts_mdata[i], !i);
if (!err) {
err = mdata_crc_check(parts_mdata[i]);
if (!err)
parts_ok[i] = true;
else
log_debug("mdata : %s crc32 failed\n", i ? "secondary" : "primary");
}
}
ret = fwu_read_mdata_partition(dev, &pri_mdata, mdata_parts[0]);
if (!ret) {
ret = fwu_verify_mdata(&pri_mdata, 1);
if (!ret)
valid_partitions |= PRIMARY_PART;
}
ret = fwu_read_mdata_partition(dev, &secondary_mdata, mdata_parts[1]);
if (!ret) {
ret = fwu_verify_mdata(&secondary_mdata, 0);
if (!ret)
valid_partitions |= SECONDARY_PART;
}
if (valid_partitions == (PRIMARY_PART | SECONDARY_PART)) {
if (parts_ok[0] && parts_ok[1]) {
/*
* Before returning, check that both the
* FWU metadata copies are the same. If not,
* populate the secondary partition from the
* FWU metadata copies are the same.
*/
err = memcmp(parts_mdata[0], parts_mdata[1], sizeof(struct fwu_mdata));
if (!err)
goto ret_mdata;
/*
* If not, populate the secondary partition from the
* primary partition copy.
*/
if (!memcmp(&pri_mdata, &secondary_mdata,
sizeof(struct fwu_mdata))) {
ret = 0;
} else {
log_info("Both FWU metadata copies are valid but do not match.");
log_info(" Restoring the secondary partition from the primary\n");
ret = fwu_write_mdata_partition(dev, &pri_mdata,
mdata_parts[1]);
if (ret)
log_debug("Restoring secondary FWU metadata partition failed\n");
log_info("Both FWU metadata copies are valid but do not match.");
log_info(" Restoring the secondary partition from the primary\n");
parts_ok[1] = false;
}
for (int i = 0; i < 2; i++) {
if (parts_ok[i])
continue;
memcpy(parts_mdata[i], parts_mdata[1 - i], sizeof(struct fwu_mdata));
err = fwu_sync_mdata(parts_mdata[i], i ? SECONDARY_PART : PRIMARY_PART);
if (err) {
log_debug("mdata : %s write failed\n", i ? "secondary" : "primary");
return err;
}
goto out;
}
if (!(valid_partitions & BOTH_PARTS)) {
log_info("Both FWU metadata partitions invalid\n");
ret = -EBADMSG;
goto out;
}
ret_mdata:
if (!err && mdata)
memcpy(mdata, parts_mdata[0], sizeof(struct fwu_mdata));
invalid_partitions = valid_partitions ^ BOTH_PARTS;
ret = fwu_write_mdata_partition(dev,
(invalid_partitions == PRIMARY_PART) ?
&secondary_mdata : &pri_mdata,
(invalid_partitions == PRIMARY_PART) ?
mdata_parts[0] : mdata_parts[1]);
if (ret)
log_debug("Restoring %s FWU metadata partition failed\n",
(invalid_partitions == PRIMARY_PART) ?
"primary" : "secondary");
out:
return ret;
return err;
}
/**
@ -303,19 +274,14 @@ out:
*/
int fwu_get_active_index(uint *active_idx)
{
int ret;
struct udevice *dev;
struct fwu_mdata mdata = { 0 };
ret = fwu_get_dev_mdata(&dev, &mdata);
if (ret)
return ret;
int ret = 0;
struct fwu_mdata *mdata = &g_mdata;
/*
* Found the FWU metadata partition, now read the active_index
* value
*/
*active_idx = mdata.active_index;
*active_idx = mdata->active_index;
if (*active_idx >= CONFIG_FWU_NUM_BANKS) {
log_debug("Active index value read is incorrect\n");
ret = -EINVAL;
@ -336,30 +302,25 @@ int fwu_get_active_index(uint *active_idx)
int fwu_set_active_index(uint active_idx)
{
int ret;
struct udevice *dev;
struct fwu_mdata mdata = { 0 };
struct fwu_mdata *mdata = &g_mdata;
if (active_idx >= CONFIG_FWU_NUM_BANKS) {
log_debug("Invalid active index value\n");
return -EINVAL;
}
ret = fwu_get_dev_mdata(&dev, &mdata);
if (ret)
return ret;
/*
* Update the active index and previous_active_index fields
* in the FWU metadata
*/
mdata.previous_active_index = mdata.active_index;
mdata.active_index = active_idx;
mdata->previous_active_index = mdata->active_index;
mdata->active_index = active_idx;
/*
* Now write this updated FWU metadata to both the
* FWU metadata partitions
*/
ret = fwu_update_mdata(dev, &mdata);
ret = fwu_sync_mdata(mdata, BOTH_PARTS);
if (ret) {
log_debug("Failed to update FWU metadata partitions\n");
ret = -EIO;
@ -389,15 +350,10 @@ int fwu_get_image_index(u8 *image_index)
u8 alt_num;
uint update_bank;
efi_guid_t *image_guid, image_type_id;
struct udevice *dev;
struct fwu_mdata mdata = { 0 };
struct fwu_mdata *mdata = &g_mdata;
struct fwu_image_entry *img_entry;
struct fwu_image_bank_info *img_bank_info;
ret = fwu_get_dev_mdata(&dev, &mdata);
if (ret)
return ret;
ret = fwu_plat_get_update_index(&update_bank);
if (ret) {
log_debug("Failed to get the FWU update bank\n");
@ -418,11 +374,11 @@ int fwu_get_image_index(u8 *image_index)
*/
for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
if (!guidcmp(&image_type_id,
&mdata.img_entry[i].image_type_uuid)) {
img_entry = &mdata.img_entry[i];
&mdata->img_entry[i].image_type_uuid)) {
img_entry = &mdata->img_entry[i];
img_bank_info = &img_entry->img_bank_info[update_bank];
image_guid = &img_bank_info->image_uuid;
ret = fwu_plat_get_alt_num(dev, image_guid, &alt_num);
ret = fwu_plat_get_alt_num(g_dev, image_guid, &alt_num);
if (ret) {
log_debug("alt_num not found for partition with GUID %pUs\n",
image_guid);
@ -436,8 +392,8 @@ int fwu_get_image_index(u8 *image_index)
}
}
log_debug("Partition with the image type %pUs not found\n",
&image_type_id);
log_err("Partition with the image type %pUs not found\n",
&image_type_id);
out:
return ret;
@ -457,26 +413,21 @@ int fwu_revert_boot_index(void)
{
int ret;
u32 cur_active_index;
struct udevice *dev;
struct fwu_mdata mdata = { 0 };
ret = fwu_get_dev_mdata(&dev, &mdata);
if (ret)
return ret;
struct fwu_mdata *mdata = &g_mdata;
/*
* Swap the active index and previous_active_index fields
* in the FWU metadata
*/
cur_active_index = mdata.active_index;
mdata.active_index = mdata.previous_active_index;
mdata.previous_active_index = cur_active_index;
cur_active_index = mdata->active_index;
mdata->active_index = mdata->previous_active_index;
mdata->previous_active_index = cur_active_index;
/*
* Now write this updated FWU metadata to both the
* FWU metadata partitions
*/
ret = fwu_update_mdata(dev, &mdata);
ret = fwu_sync_mdata(mdata, BOTH_PARTS);
if (ret) {
log_debug("Failed to update FWU metadata partitions\n");
ret = -EIO;
@ -503,16 +454,11 @@ int fwu_revert_boot_index(void)
static int fwu_clrset_image_accept(efi_guid_t *img_type_id, u32 bank, u8 action)
{
int ret, i;
struct udevice *dev;
struct fwu_mdata mdata = { 0 };
struct fwu_mdata *mdata = &g_mdata;
struct fwu_image_entry *img_entry;
struct fwu_image_bank_info *img_bank_info;
ret = fwu_get_dev_mdata(&dev, &mdata);
if (ret)
return ret;
img_entry = &mdata.img_entry[0];
img_entry = &mdata->img_entry[0];
for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
if (!guidcmp(&img_entry[i].image_type_uuid, img_type_id)) {
img_bank_info = &img_entry[i].img_bank_info[bank];
@ -521,7 +467,7 @@ static int fwu_clrset_image_accept(efi_guid_t *img_type_id, u32 bank, u8 action)
else
img_bank_info->accepted = 0;
ret = fwu_update_mdata(dev, &mdata);
ret = fwu_sync_mdata(mdata, BOTH_PARTS);
goto out;
}
}
@ -599,6 +545,24 @@ __weak int fwu_plat_get_update_index(uint *update_idx)
return ret;
}
/**
* fwu_plat_get_bootidx() - Get the value of the boot index
* @boot_idx: Boot index value
*
* Get the value of the bank(partition) from which the platform
* has booted. This value is passed to U-Boot from the earlier
* stage bootloader which loads and boots all the relevant
* firmware images
*/
__weak void fwu_plat_get_bootidx(uint *boot_idx)
{
int ret;
ret = fwu_get_active_index(boot_idx);
if (ret < 0)
*boot_idx = 0; /* Dummy value */
}
/**
* fwu_update_checks_pass() - Check if FWU update can be done
*
@ -656,8 +620,6 @@ static int fwu_boottime_checks(void *ctx, struct event *event)
{
int ret;
u32 boot_idx, active_idx;
struct udevice *dev;
struct fwu_mdata mdata = { 0 };
/* Don't have boot time checks on sandbox */
if (IS_ENABLED(CONFIG_SANDBOX)) {
@ -665,9 +627,17 @@ static int fwu_boottime_checks(void *ctx, struct event *event)
return 0;
}
ret = fwu_check_mdata_validity();
if (ret)
return 0;
ret = uclass_first_device_err(UCLASS_FWU_MDATA, &g_dev);
if (ret) {
log_debug("Cannot find fwu device\n");
return ret;
}
ret = fwu_get_mdata(NULL);
if (ret) {
log_debug("Unable to read meta-data\n");
return ret;
}
/*
* Get the Boot Index, i.e. the bank from
@ -703,11 +673,7 @@ static int fwu_boottime_checks(void *ctx, struct event *event)
if (efi_init_obj_list() != EFI_SUCCESS)
return 0;
ret = fwu_get_dev_mdata(&dev, &mdata);
if (ret)
return ret;
in_trial = in_trial_state(&mdata);
in_trial = in_trial_state(&g_mdata);
if (!in_trial || (ret = fwu_trial_count_update()) > 0)
ret = trial_counter_update(NULL);

185
lib/fwu_updates/fwu_mtd.c Normal file
View file

@ -0,0 +1,185 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2023, Linaro Limited
*/
#include <dm.h>
#include <dfu.h>
#include <fwu.h>
#include <fwu_mdata.h>
#include <log.h>
#include <malloc.h>
#include <mtd.h>
#include <uuid.h>
#include <vsprintf.h>
#include <dm/ofnode.h>
struct fwu_mtd_image_info
fwu_mtd_images[CONFIG_FWU_NUM_BANKS * CONFIG_FWU_NUM_IMAGES_PER_BANK];
static struct fwu_mtd_image_info *mtd_img_by_uuid(const char *uuidbuf)
{
int num_images = ARRAY_SIZE(fwu_mtd_images);
for (int i = 0; i < num_images; i++)
if (!strcmp(uuidbuf, fwu_mtd_images[i].uuidbuf))
return &fwu_mtd_images[i];
return NULL;
}
int fwu_mtd_get_alt_num(efi_guid_t *image_id, u8 *alt_num,
const char *mtd_dev)
{
struct fwu_mtd_image_info *mtd_img_info;
char uuidbuf[UUID_STR_LEN + 1];
fdt_addr_t offset, size = 0;
struct dfu_entity *dfu;
int i, nalt, ret;
mtd_probe_devices();
uuid_bin_to_str(image_id->b, uuidbuf, UUID_STR_FORMAT_STD);
mtd_img_info = mtd_img_by_uuid(uuidbuf);
if (!mtd_img_info) {
log_err("%s: Not found partition for image %s\n", __func__, uuidbuf);
return -ENOENT;
}
offset = mtd_img_info->start;
size = mtd_img_info->size;
ret = dfu_init_env_entities(NULL, NULL);
if (ret)
return -ENOENT;
nalt = 0;
list_for_each_entry(dfu, &dfu_list, list)
nalt++;
if (!nalt) {
log_warning("No entities in dfu_alt_info\n");
dfu_free_entities();
return -ENOENT;
}
ret = -ENOENT;
for (i = 0; i < nalt; i++) {
dfu = dfu_get_entity(i);
/* Only MTD RAW access */
if (!dfu || dfu->dev_type != DFU_DEV_MTD ||
dfu->layout != DFU_RAW_ADDR ||
dfu->data.mtd.start != offset ||
dfu->data.mtd.size != size)
continue;
*alt_num = dfu->alt;
ret = 0;
break;
}
dfu_free_entities();
log_debug("%s: %s -> %d\n", __func__, uuidbuf, *alt_num);
return ret;
}
/**
* fwu_plat_get_alt_num() - Get the DFU Alt Num for the image from the platform
* @dev: FWU device
* @image_id: Image GUID for which DFU alt number needs to be retrieved
* @alt_num: Pointer to the alt_num
*
* Get the DFU alt number from the platform for the image specified by the
* image GUID.
*
* Note: This is a weak function and platforms can override this with
* their own implementation for obtaining the alt number value.
*
* Return: 0 if OK, -ve on error
*/
__weak int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_id,
u8 *alt_num)
{
return fwu_mtd_get_alt_num(image_id, alt_num, "nor1");
}
static int gen_image_alt_info(char *buf, size_t len, int sidx,
struct fwu_image_entry *img, struct mtd_info *mtd)
{
char *p = buf, *end = buf + len;
int i;
p += snprintf(p, end - p, "mtd %s", mtd->name);
if (end < p) {
log_err("%s:%d Run out of buffer\n", __func__, __LINE__);
return -E2BIG;
}
/*
* List the image banks in the FWU mdata and search the corresponding
* partition based on partition's uuid.
*/
for (i = 0; i < CONFIG_FWU_NUM_BANKS; i++) {
struct fwu_mtd_image_info *mtd_img_info;
struct fwu_image_bank_info *bank;
char uuidbuf[UUID_STR_LEN + 1];
u32 offset, size;
/* Query a partition by image UUID */
bank = &img->img_bank_info[i];
uuid_bin_to_str(bank->image_uuid.b, uuidbuf, UUID_STR_FORMAT_STD);
mtd_img_info = mtd_img_by_uuid(uuidbuf);
if (!mtd_img_info) {
log_err("%s: Not found partition for image %s\n", __func__, uuidbuf);
break;
}
offset = mtd_img_info->start;
size = mtd_img_info->size;
p += snprintf(p, end - p, "%sbank%d raw %x %x",
i == 0 ? "=" : ";", i, offset, size);
if (end < p) {
log_err("%s:%d Run out of buffer\n", __func__, __LINE__);
return -E2BIG;
}
}
if (i == CONFIG_FWU_NUM_BANKS)
return 0;
return -ENOENT;
}
int fwu_gen_alt_info_from_mtd(char *buf, size_t len, struct mtd_info *mtd)
{
struct fwu_mdata mdata;
int i, l, ret;
ret = fwu_get_mdata(&mdata);
if (ret < 0) {
log_err("Failed to get the FWU mdata.\n");
return ret;
}
for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
ret = gen_image_alt_info(buf, len, i * CONFIG_FWU_NUM_BANKS,
&mdata.img_entry[i], mtd);
if (ret)
break;
l = strlen(buf);
/* Replace the last ';' with '&' if there is another image. */
if (i != CONFIG_FWU_NUM_IMAGES_PER_BANK - 1 && l)
buf[l - 1] = '&';
len -= l;
buf += l;
}
return ret;
}

View file

@ -98,7 +98,7 @@ static int dm_test_fwu_mdata_read(struct unit_test_state *uts)
ut_assertok(populate_mmc_disk_image(uts));
ut_assertok(write_mmc_blk_device(uts));
ut_assertok(fwu_get_mdata(dev, &mdata));
ut_assertok(fwu_get_mdata(&mdata));
ut_asserteq(mdata.version, 0x1);
@ -118,30 +118,14 @@ static int dm_test_fwu_mdata_write(struct unit_test_state *uts)
ut_assertok(uclass_first_device_err(UCLASS_FWU_MDATA, &dev));
ut_assertok(fwu_get_mdata(dev, &mdata));
ut_assertok(fwu_get_mdata(&mdata));
active_idx = (mdata.active_index + 1) % CONFIG_FWU_NUM_BANKS;
ut_assertok(fwu_set_active_index(active_idx));
ut_assertok(fwu_get_mdata(dev, &mdata));
ut_assertok(fwu_get_mdata(&mdata));
ut_asserteq(mdata.active_index, active_idx);
return 0;
}
DM_TEST(dm_test_fwu_mdata_write, UT_TESTF_SCAN_FDT);
static int dm_test_fwu_mdata_check(struct unit_test_state *uts)
{
struct udevice *dev;
ut_assertok(setup_blk_device(uts));
ut_assertok(populate_mmc_disk_image(uts));
ut_assertok(write_mmc_blk_device(uts));
ut_assertok(uclass_first_device_err(UCLASS_FWU_MDATA, &dev));
ut_assertok(fwu_check_mdata_validity());
return 0;
}
DM_TEST(dm_test_fwu_mdata_check, UT_TESTF_SCAN_FDT);

View file

@ -157,4 +157,13 @@ config LUT_SEQUENCE
help
Look Up Table Sequence
config TOOLS_MKFWUMDATA
bool "Build mkfwumdata command"
default y if FWU_MULTI_BANK_UPDATE
help
This command allows users to create a raw image of the FWU
metadata for initial installation of the FWU multi bank
update on the board. The installation method depends on
the platform.
endmenu

View file

@ -250,6 +250,10 @@ HOSTLDLIBS_mkeficapsule += \
$(shell pkg-config --libs uuid 2> /dev/null || echo "-luuid")
hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
mkfwumdata-objs := mkfwumdata.o lib/crc32.o
HOSTLDLIBS_mkfwumdata += -luuid
hostprogs-$(CONFIG_TOOLS_MKFWUMDATA) += mkfwumdata
# We build some files with extra pedantic flags to try to minimize things
# that won't build on some weird host compiler -- though there are lots of
# exceptions for files that aren't complaint.

334
tools/mkfwumdata.c Normal file
View file

@ -0,0 +1,334 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2023, Linaro Limited
*/
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <u-boot/crc.h>
#include <unistd.h>
#include <uuid/uuid.h>
/* This will dynamically allocate the fwu_mdata */
#define CONFIG_FWU_NUM_BANKS 0
#define CONFIG_FWU_NUM_IMAGES_PER_BANK 0
/* Since we can not include fwu.h, redefine version here. */
#define FWU_MDATA_VERSION 1
typedef uint8_t u8;
typedef int16_t s16;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
#include <fwu_mdata.h>
/* TODO: Endianness conversion may be required for some arch. */
static const char *opts_short = "b:i:a:p:gh";
static struct option options[] = {
{"banks", required_argument, NULL, 'b'},
{"images", required_argument, NULL, 'i'},
{"guid", required_argument, NULL, 'g'},
{"active-bank", required_argument, NULL, 'a'},
{"previous-bank", required_argument, NULL, 'p'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0},
};
static void print_usage(void)
{
fprintf(stderr, "Usage: mkfwumdata [options] <UUIDs list> <output file>\n");
fprintf(stderr, "Options:\n"
"\t-i, --images <num> Number of images (mandatory)\n"
"\t-b, --banks <num> Number of banks (mandatory)\n"
"\t-a, --active-bank <num> Active bank (default=0)\n"
"\t-p, --previous-bank <num> Previous active bank (default=active_bank - 1)\n"
"\t-g, --guid Use GUID instead of UUID\n"
"\t-h, --help print a help message\n"
);
fprintf(stderr, " UUIDs list syntax:\n"
"\t <location uuid>,<image type uuid>,<images uuid list>\n"
"\t images uuid list syntax:\n"
"\t img_uuid_00,img_uuid_01...img_uuid_0b,\n"
"\t img_uuid_10,img_uuid_11...img_uuid_1b,\n"
"\t ...,\n"
"\t img_uuid_i0,img_uuid_i1...img_uuid_ib,\n"
"\t where 'b' and 'i' are number of banks and number\n"
"\t of images in a bank respectively.\n"
);
}
struct fwu_mdata_object {
size_t images;
size_t banks;
size_t size;
struct fwu_mdata *mdata;
};
static int previous_bank, active_bank;
static bool __use_guid;
static struct fwu_mdata_object *fwu_alloc_mdata(size_t images, size_t banks)
{
struct fwu_mdata_object *mobj;
mobj = calloc(1, sizeof(*mobj));
if (!mobj)
return NULL;
mobj->size = sizeof(struct fwu_mdata) +
(sizeof(struct fwu_image_entry) +
sizeof(struct fwu_image_bank_info) * banks) * images;
mobj->images = images;
mobj->banks = banks;
mobj->mdata = calloc(1, mobj->size);
if (!mobj->mdata) {
free(mobj);
return NULL;
}
return mobj;
}
static struct fwu_image_entry *
fwu_get_image(struct fwu_mdata_object *mobj, size_t idx)
{
size_t offset;
offset = sizeof(struct fwu_mdata) +
(sizeof(struct fwu_image_entry) +
sizeof(struct fwu_image_bank_info) * mobj->banks) * idx;
return (struct fwu_image_entry *)((char *)mobj->mdata + offset);
}
static struct fwu_image_bank_info *
fwu_get_bank(struct fwu_mdata_object *mobj, size_t img_idx, size_t bnk_idx)
{
size_t offset;
offset = sizeof(struct fwu_mdata) +
(sizeof(struct fwu_image_entry) +
sizeof(struct fwu_image_bank_info) * mobj->banks) * img_idx +
sizeof(struct fwu_image_entry) +
sizeof(struct fwu_image_bank_info) * bnk_idx;
return (struct fwu_image_bank_info *)((char *)mobj->mdata + offset);
}
/**
* convert_uuid_to_guid() - convert UUID to GUID
* @buf: UUID binary
*
* UUID and GUID have the same data structure, but their binary
* formats are different due to the endianness. See lib/uuid.c.
* Since uuid_parse() can handle only UUID, this function must
* be called to get correct data for GUID when parsing a string.
*
* The correct data will be returned in @buf.
*/
static void convert_uuid_to_guid(unsigned char *buf)
{
unsigned char c;
c = buf[0];
buf[0] = buf[3];
buf[3] = c;
c = buf[1];
buf[1] = buf[2];
buf[2] = c;
c = buf[4];
buf[4] = buf[5];
buf[5] = c;
c = buf[6];
buf[6] = buf[7];
buf[7] = c;
}
static int uuid_guid_parse(char *uuidstr, unsigned char *uuid)
{
int ret;
ret = uuid_parse(uuidstr, uuid);
if (ret < 0)
return ret;
if (__use_guid)
convert_uuid_to_guid(uuid);
return ret;
}
static int
fwu_parse_fill_image_uuid(struct fwu_mdata_object *mobj,
size_t idx, char *uuids)
{
struct fwu_image_entry *image = fwu_get_image(mobj, idx);
struct fwu_image_bank_info *bank;
char *p = uuids, *uuid;
int i;
if (!image)
return -ENOENT;
/* Image location UUID */
uuid = strsep(&p, ",");
if (!uuid)
return -EINVAL;
if (strcmp(uuid, "0") &&
uuid_guid_parse(uuid, (unsigned char *)&image->location_uuid) < 0)
return -EINVAL;
/* Image type UUID */
uuid = strsep(&p, ",");
if (!uuid)
return -EINVAL;
if (uuid_guid_parse(uuid, (unsigned char *)&image->image_type_uuid) < 0)
return -EINVAL;
/* Fill bank image-UUID */
for (i = 0; i < mobj->banks; i++) {
bank = fwu_get_bank(mobj, idx, i);
if (!bank)
return -ENOENT;
bank->accepted = 1;
uuid = strsep(&p, ",");
if (!uuid)
return -EINVAL;
if (strcmp(uuid, "0") &&
uuid_guid_parse(uuid, (unsigned char *)&bank->image_uuid) < 0)
return -EINVAL;
}
return 0;
}
/* Caller must ensure that @uuids[] has @mobj->images entries. */
static int fwu_parse_fill_uuids(struct fwu_mdata_object *mobj, char *uuids[])
{
struct fwu_mdata *mdata = mobj->mdata;
int i, ret;
mdata->version = FWU_MDATA_VERSION;
mdata->active_index = active_bank;
mdata->previous_active_index = previous_bank;
for (i = 0; i < mobj->images; i++) {
ret = fwu_parse_fill_image_uuid(mobj, i, uuids[i]);
if (ret < 0)
return ret;
}
mdata->crc32 = crc32(0, (const unsigned char *)&mdata->version,
mobj->size - sizeof(uint32_t));
return 0;
}
static int
fwu_make_mdata(size_t images, size_t banks, char *uuids[], char *output)
{
struct fwu_mdata_object *mobj;
FILE *file;
int ret;
mobj = fwu_alloc_mdata(images, banks);
if (!mobj)
return -ENOMEM;
ret = fwu_parse_fill_uuids(mobj, uuids);
if (ret < 0)
goto done_make;
file = fopen(output, "w");
if (!file) {
ret = -errno;
goto done_make;
}
ret = fwrite(mobj->mdata, mobj->size, 1, file);
if (ret != mobj->size)
ret = -errno;
else
ret = 0;
fclose(file);
done_make:
free(mobj->mdata);
free(mobj);
return ret;
}
int main(int argc, char *argv[])
{
unsigned long banks = 0, images = 0;
int c, ret;
/* Explicitly initialize defaults */
active_bank = 0;
__use_guid = false;
previous_bank = INT_MAX;
do {
c = getopt_long(argc, argv, opts_short, options, NULL);
switch (c) {
case 'h':
print_usage();
return 0;
case 'b':
banks = strtoul(optarg, NULL, 0);
break;
case 'i':
images = strtoul(optarg, NULL, 0);
break;
case 'g':
__use_guid = true;
break;
case 'p':
previous_bank = strtoul(optarg, NULL, 0);
break;
case 'a':
active_bank = strtoul(optarg, NULL, 0);
break;
}
} while (c != -1);
if (!banks || !images) {
fprintf(stderr, "Error: The number of banks and images must not be 0.\n");
return -EINVAL;
}
/* This command takes UUIDs * images and output file. */
if (optind + images + 1 != argc) {
fprintf(stderr, "Error: UUID list or output file is not specified or too much.\n");
print_usage();
return -ERANGE;
}
if (previous_bank == INT_MAX) {
/* set to the earlier bank in round-robin scheme */
previous_bank = active_bank > 0 ? active_bank - 1 : banks - 1;
}
ret = fwu_make_mdata(images, banks, argv + optind, argv[argc - 1]);
if (ret < 0)
fprintf(stderr, "Error: Failed to parse and write image: %s\n",
strerror(-ret));
return ret;
}