diff --git a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi index 9f9837b33b..9957646a46 100644 --- a/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi +++ b/arch/arm/dts/synquacer-sc2a11-developerbox-u-boot.dtsi @@ -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"; + }; + }; + }; }; }; diff --git a/board/socionext/developerbox/Makefile b/board/socionext/developerbox/Makefile index 4a46de995a..1acd067a7e 100644 --- a/board/socionext/developerbox/Makefile +++ b/board/socionext/developerbox/Makefile @@ -7,3 +7,4 @@ # obj-y := developerbox.o +obj-$(CONFIG_FWU_MDATA_MTD) += fwu_plat.o diff --git a/board/socionext/developerbox/developerbox.c b/board/socionext/developerbox/developerbox.c index d92e1d0962..204e5a41a5 100644 --- a/board/socionext/developerbox/developerbox.c +++ b/board/socionext/developerbox/developerbox.c @@ -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 = { diff --git a/board/socionext/developerbox/fwu_plat.c b/board/socionext/developerbox/fwu_plat.c new file mode 100644 index 0000000000..e724e702bd --- /dev/null +++ b/board/socionext/developerbox/fwu_plat.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023, Linaro Limited + */ + +#include +#include +#include +#include +#include + +#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); +} diff --git a/cmd/fwu_mdata.c b/cmd/fwu_mdata.c index f04af27de6..5ecda455df 100644 --- a/cmd/fwu_mdata.c +++ b/cmd/fwu_mdata.c @@ -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; diff --git a/configs/synquacer_developerbox_defconfig b/configs/synquacer_developerbox_defconfig index 68f7bacf02..8e7236b572 100644 --- a/configs/synquacer_developerbox_defconfig +++ b/configs/synquacer_developerbox_defconfig @@ -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 diff --git a/doc/board/socionext/developerbox.rst b/doc/board/socionext/developerbox.rst index 2d943c23be..aa7080e26c 100644 --- a/doc/board/socionext/developerbox.rst +++ b/doc/board/socionext/developerbox.rst @@ -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). diff --git a/doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml b/doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml index 4f5404f999..6a22aeea30 100644 --- a/doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml +++ b/doc/device-tree-bindings/firmware/fwu-mdata-mtd.yaml @@ -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 + - Jassi Brar 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"; + }; + }; + }; +... diff --git a/doc/mkfwumdata.1 b/doc/mkfwumdata.1 new file mode 100644 index 0000000000..7dd718b26e --- /dev/null +++ b/doc/mkfwumdata.1 @@ -0,0 +1,89 @@ +.\" SPDX-License-Identifier: GPL-2.0-or-later +.\" Copyright (C) 2023 Jassi Brar +.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, +. +.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 diff --git a/drivers/fwu-mdata/Kconfig b/drivers/fwu-mdata/Kconfig index 36c4479a59..42736a5e43 100644 --- a/drivers/fwu-mdata/Kconfig +++ b/drivers/fwu-mdata/Kconfig @@ -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 diff --git a/drivers/fwu-mdata/Makefile b/drivers/fwu-mdata/Makefile index 3fee64c10c..06c49747ba 100644 --- a/drivers/fwu-mdata/Makefile +++ b/drivers/fwu-mdata/Makefile @@ -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 diff --git a/drivers/fwu-mdata/fwu-mdata-uclass.c b/drivers/fwu-mdata/fwu-mdata-uclass.c index b477e9603f..0a8edaaa41 100644 --- a/drivers/fwu-mdata/fwu-mdata-uclass.c +++ b/drivers/fwu-mdata/fwu-mdata-uclass.c @@ -14,170 +14,39 @@ #include #include -#include /** - * 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) = { diff --git a/drivers/fwu-mdata/gpt_blk.c b/drivers/fwu-mdata/gpt_blk.c index d35ce49c5c..c7284916c4 100644 --- a/drivers/fwu-mdata/gpt_blk.c +++ b/drivers/fwu-mdata/gpt_blk.c @@ -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[] = { diff --git a/drivers/fwu-mdata/raw_mtd.c b/drivers/fwu-mdata/raw_mtd.c new file mode 100644 index 0000000000..17e4517973 --- /dev/null +++ b/drivers/fwu-mdata/raw_mtd.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023, Linaro Limited + */ + +#define LOG_CATEGORY UCLASS_FWU_MDATA + +#include +#include +#include + +#include +#include + +/* 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), +}; diff --git a/include/configs/synquacer.h b/include/configs/synquacer.h index 8f44c6f66a..cd7359c2f8 100644 --- a/include/configs/synquacer.h +++ b/include/configs/synquacer.h @@ -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, \ diff --git a/include/fwu.h b/include/fwu.h index 0919ced812..ac5c5de870 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -8,6 +8,8 @@ #include #include +#include +#include #include @@ -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_ */ diff --git a/lib/fwu_updates/Makefile b/lib/fwu_updates/Makefile index 1993088e5b..c9e3c06b48 100644 --- a/lib/fwu_updates/Makefile +++ b/lib/fwu_updates/Makefile @@ -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 diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 3b1785e7b1..cd5c3b6477 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -15,13 +15,13 @@ #include #include +#include + +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 -#include -#include - 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); diff --git a/lib/fwu_updates/fwu_mtd.c b/lib/fwu_updates/fwu_mtd.c new file mode 100644 index 0000000000..b73111ae24 --- /dev/null +++ b/lib/fwu_updates/fwu_mtd.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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; +} diff --git a/test/dm/fwu_mdata.c b/test/dm/fwu_mdata.c index b179a65c15..8b5c83ef4e 100644 --- a/test/dm/fwu_mdata.c +++ b/test/dm/fwu_mdata.c @@ -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); diff --git a/tools/Kconfig b/tools/Kconfig index 539708f277..6e23f44d55 100644 --- a/tools/Kconfig +++ b/tools/Kconfig @@ -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 diff --git a/tools/Makefile b/tools/Makefile index 38699b069d..1e3fce0b1c 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -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. diff --git a/tools/mkfwumdata.c b/tools/mkfwumdata.c new file mode 100644 index 0000000000..9732a8ddc5 --- /dev/null +++ b/tools/mkfwumdata.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 + +/* 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] \n"); + fprintf(stderr, "Options:\n" + "\t-i, --images Number of images (mandatory)\n" + "\t-b, --banks Number of banks (mandatory)\n" + "\t-a, --active-bank Active bank (default=0)\n" + "\t-p, --previous-bank 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 ,,\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; +}