mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-14 17:07:38 +00:00
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:
commit
5f41ef792c
23 changed files with 1515 additions and 675 deletions
|
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -7,3 +7,4 @@
|
|||
#
|
||||
|
||||
obj-y := developerbox.o
|
||||
obj-$(CONFIG_FWU_MDATA_MTD) += fwu_plat.o
|
||||
|
|
|
@ -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 = {
|
||||
|
|
37
board/socionext/developerbox/fwu_plat.c
Normal file
37
board/socionext/developerbox/fwu_plat.c
Normal 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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; don’t 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).
|
||||
|
|
|
@ -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
89
doc/mkfwumdata.1
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) = {
|
||||
|
|
|
@ -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
269
drivers/fwu-mdata/raw_mtd.c
Normal 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),
|
||||
};
|
|
@ -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, \
|
||||
|
|
228
include/fwu.h
228
include/fwu.h
|
@ -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_ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
185
lib/fwu_updates/fwu_mtd.c
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
334
tools/mkfwumdata.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue