From efe19295a59dae28c1af813486fe88f34378a980 Mon Sep 17 00:00:00 2001 From: Aleksandar Gerasimovski Date: Fri, 10 Dec 2021 11:07:53 +0100 Subject: [PATCH] km: common: implement field fail-safe u-boot update MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This patch provides possibility for field fail-safe u-boot updates. The implementation can be used on all pg-wcom boards that are booting from parallel NOR flash. When used in a board design, provided check_for_uboot_update function will start new u-boot at defined location if updateduboot envvar is set to yes. With this implementation it is expected that factory programmed u-boot will always stay as it is, and optionally new u-boot can be safely programmed by embedded software when the unit is rolled out on the field. It is expected check_for_uboot_update to be called early in execution before relocation (*_f) once SoC is basically initialized and environment can be read, with this possibilities to not be able to fix a u-boot bug by a u-boot update are reduced to minimum. Signed-off-by: Aleksandar Gerasimovski Reviewed-by: Priyanka Jain --- board/keymile/Kconfig | 32 +++++++++++++++++++++ board/keymile/README | 18 ++++++++++++ board/keymile/common/common.c | 53 +++++++++++++++++++++++++++++++++++ board/keymile/common/common.h | 1 + 4 files changed, 104 insertions(+) create mode 100644 board/keymile/README diff --git a/board/keymile/Kconfig b/board/keymile/Kconfig index 7dd8213ab5..863c07db47 100644 --- a/board/keymile/Kconfig +++ b/board/keymile/Kconfig @@ -130,6 +130,38 @@ config SYS_IVM_EEPROM_PAGE_LEN help Page size of inventory in EEPROM. +config PG_WCOM_UBOOT_UPDATE_SUPPORTED + bool "Enable U-boot Field Fail-Safe Update Functionality" + default n + help + Indicates that field fail-safe u-boot update is supported. + This functionality works only for designs that are booting + from parallel NOR flash. + +config PG_WCOM_UBOOT_BOOTPACKAGE + bool "U-boot Is Part Of Factory Boot-Package Image" + default n + help + Indicates that u-boot will be a part of the factory programmed + boot-package image. + Has to be set for original u-boot programmed at factory. + +config PG_WCOM_UBOOT_UPDATE_TEXT_BASE + hex "Text Base For U-boot Programmed Outside Factory" + default 0xFFFFFFFF + help + Text base of an updated u-boot that is not factory programmed but + later when the unit is rolled out on the field. + Has to be set for original u-boot programmed at factory. + +config PG_WCOM_UBOOT_UPDATE + bool "U-boot Is Part Of Factory Boot-Package Image" + default n + help + Indicates that u-boot will be a part of the embedded software and + programmed at field. + Has to be set for updated u-boot version programmed at field. + source "board/keymile/km83xx/Kconfig" source "board/keymile/kmcent2/Kconfig" source "board/keymile/km_arm/Kconfig" diff --git a/board/keymile/README b/board/keymile/README new file mode 100644 index 0000000000..4e5cfb142a --- /dev/null +++ b/board/keymile/README @@ -0,0 +1,18 @@ +Field Fail-Save U-boot Update +----------------------------- +Field Fail-Save u-boot update is a feature that allows save u-boot update +of FOX and XMC products that are rolled out in the field. + +The feature is initially implemented for designs based on LS102x SoC, but in +theory can be used on all designs that are booting from parallel NOR flash. + +The implementation expects redundant (secondary) u-boot image on a predefined +location in the NOR flash, u-boot execution will be transferred to the redundant  +(secondary) u-boot and redundant u-boot will be started if 'updateduboot' envvar +is set to 'yes'. +Update logic check_for_uboot_update() has to be invoked from the design early +before relocation just after SoC initialization, e.g from board_early_init_f or +misc_init_f functions. +By design it is expected that primary u-boot image is burned in the factory and +never updated, and in case u-boot update is required it can flashed and started +from secondary u-boot location. diff --git a/board/keymile/common/common.c b/board/keymile/common/common.c index ff07260194..3999f48719 100644 --- a/board/keymile/common/common.c +++ b/board/keymile/common/common.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #if defined(CONFIG_POST) #include "post.h" @@ -76,6 +78,57 @@ int set_km_env(void) return 0; } +#if CONFIG_IS_ENABLED(PG_WCOM_UBOOT_UPDATE_SUPPORTED) +#if ((!CONFIG_IS_ENABLED(PG_WCOM_UBOOT_BOOTPACKAGE) && \ + !CONFIG_IS_ENABLED(PG_WCOM_UBOOT_UPDATE)) || \ + (CONFIG_IS_ENABLED(PG_WCOM_UBOOT_BOOTPACKAGE) && \ + CONFIG_IS_ENABLED(PG_WCOM_UBOOT_UPDATE))) +#error "It has to be either bootpackage or update u-boot image!" +#endif +void check_for_uboot_update(void) +{ + void (*uboot_update_entry)(void) = + (void (*)(void)) CONFIG_PG_WCOM_UBOOT_UPDATE_TEXT_BASE; + char *isupdated = env_get("updateduboot"); + ulong bootcount = bootcount_load(); + ulong ebootcount = 0; + + if (IS_ENABLED(CONFIG_PG_WCOM_UBOOT_BOOTPACKAGE)) { + /* + * When running in factory burned u-boot move to the updated + * u-boot version only if updateduboot envvar is set to 'yes' + * and bootcount limit is not exceeded. + * Board must be able to start in factory bootloader mode! + */ + if (isupdated && !strncmp(isupdated, "yes", 3) && + bootcount <= CONFIG_BOOTCOUNT_BOOTLIMIT) { + printf("Check update: update detected, "); + printf("starting new image @%08x ...\n", + CONFIG_PG_WCOM_UBOOT_UPDATE_TEXT_BASE); + ebootcount = early_bootcount_load(); + if (ebootcount <= CONFIG_BOOTCOUNT_BOOTLIMIT) { + early_bootcount_store(++ebootcount); + uboot_update_entry(); + } else { + printf("Check update: warning: "); + printf("early bootcount exceeded (%lu)\n", + ebootcount); + } + } + printf("Check update: starting factory image @%08x ...\n", + CONFIG_SYS_TEXT_BASE); + } else if (IS_ENABLED(CONFIG_PG_WCOM_UBOOT_UPDATE)) { + /* + * When running in field updated u-boot, make sure that + * bootcount limit is never exceeded. Must never happen! + */ + WARN_ON(bootcount > CONFIG_BOOTCOUNT_BOOTLIMIT); + printf("Check update: updated u-boot starting @%08x ...\n", + CONFIG_SYS_TEXT_BASE); + } +} +#endif + #if defined(CONFIG_SYS_I2C_INIT_BOARD) static void i2c_write_start_seq(void) { diff --git a/board/keymile/common/common.h b/board/keymile/common/common.h index fc14728f9b..d16c82487b 100644 --- a/board/keymile/common/common.h +++ b/board/keymile/common/common.h @@ -135,6 +135,7 @@ int set_km_env(void); ulong early_bootcount_load(void); void early_bootcount_store(ulong ebootcount); +void check_for_uboot_update(void); #define DELAY_ABORT_SEQ 62 /* @200kHz 9 clocks = 44us, 62us is ok */ #define DELAY_HALF_PERIOD (500 / (CONFIG_SYS_I2C_SPEED / 1000)) -- 2.39.5