diff options
Diffstat (limited to 'chip/stm32/dfu_bootmanager_main.c')
-rw-r--r-- | chip/stm32/dfu_bootmanager_main.c | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/chip/stm32/dfu_bootmanager_main.c b/chip/stm32/dfu_bootmanager_main.c new file mode 100644 index 0000000000..462dd08b60 --- /dev/null +++ b/chip/stm32/dfu_bootmanager_main.c @@ -0,0 +1,181 @@ +/* Copyright 2022 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * DFU Boot Manager Main for STM32 + * + * When the Boot Manager Main is enabled, the RO application skips the + * common runtime and setup. This reduces the flash size and avoids clock, + * interrupt, and setup steps which conflict with the built in Boot Loaders + * while minimizing the Flash Size. + * + * The Boot Manager Main will perform self checks of the Flash and backup + * memory. Based on these results it will boot into the DFU or RW Application. + */ + +#include "clock.h" +#include "dfu_bootmanager_shared.h" +#include "flash.h" +#include "registers.h" +#include "task.h" + +#ifdef CONFIG_DFU_BOOTMANAGER_MAX_REBOOT_COUNT +#if CONFIG_DFU_BOOTMANAGER_MAX_REBOOT_COUNT <= 0 || \ + CONFIG_DFU_BOOTMANAGER_MAX_REBOOT_COUNT > DFU_BOOTMANAGER_VALUE_DFU +#error "Max reboot count is out of range" +#endif +#endif /* CONFIG_DFU_BOOTMANAGER_MAX_REBOOT_COUNT */ + +/* + * Checks if the RW region is valid by reading the first 8 bytes of flash, it + * should not start with an erased block. + * + * The DFU boot manager should not jump into the RW region if it contains + * invalid code as the EC is be unstable. A check will be performed to validate + * the start of the RW region to verify that it contains valid data. + * DFU programmers should erase this section of flash first and at this point, + * the EC will no longer be able to jump into the RW application. + * + * The normal DFU programming sequence programming will work, but by + * splitting into the following sequence we can protect against additional + * failures. + * + * 1. Erase the first RW flash section. This will lock the EC out of RW. + * 2. Update the remaining flash. Erase, program, and read back flash to + * to verify the operation was successful. Regions of the flash which + * are difficult to repair if an error occurs should be programmed next. + * 3. Program the first RW flash section and exit DFU mode if verification is + * successful. + * + * @return 1 if erased, 0 if not erased + */ +static int rw_is_empty(void) +{ + return crec_flash_is_erased(CONFIG_RW_MEM_OFF, 8); +} + +/* + * Reads the backup registers. This will trigger a jump to DFU if either + * the application has requested it or if the reboot counter indicates + * the device is likely in a bad state. A counter recording the number + * of reboots will be incremented. + * + * @returns True if the backup memory region indicates we should boot into DFU. + */ +static bool backup_boot_checks(void) +{ + uint8_t value; + + if (dfu_bootmanager_backup_read(&value)) { + /* Value stored is not valid, set it to a valid value. */ + dfu_bootmanager_backup_write(DFU_BOOTMANAGER_VALUE_CLEAR); + return false; + } + if (value == DFU_BOOTMANAGER_VALUE_DFU) + return true; +#ifdef CONFIG_DFU_BOOTMANAGER_MAX_REBOOT_COUNT + if (value >= CONFIG_DFU_BOOTMANAGER_MAX_REBOOT_COUNT) + return true; + /* Increment the reboot loop counter. */ + value++; + dfu_bootmanager_backup_write(value); +#endif /* CONFIG_DFU_BOOTMANAGER_MAX_REBOOT_COUNT */ + return false; +} + +/* + * Performs the minimal set of initialization required for the boot manager. + * The main application region or DFU boot loader have different prerequisites, + * any configurations that are enabled either need to be benign with both + * images or disabled prior to the jumps. + */ +static void dfu_bootmanager_init(void) +{ + /* enable clock on Power module */ +#ifndef CHIP_FAMILY_STM32H7 +#ifdef CHIP_FAMILY_STM32L4 + STM32_RCC_APB1ENR1 |= STM32_RCC_PWREN; +#else + STM32_RCC_APB1ENR |= STM32_RCC_PWREN; +#endif +#endif +#if defined(CHIP_FAMILY_STM32F4) + /* enable backup registers */ + STM32_RCC_AHB1ENR |= STM32_RCC_AHB1ENR_BKPSRAMEN; +#elif defined(CHIP_FAMILY_STM32H7) + /* enable backup registers */ + STM32_RCC_AHB4ENR |= BIT(28); +#elif defined(CHIP_FAMILY_STM32L4) + /* enable RTC APB clock */ + STM32_RCC_APB1ENR1 |= STM32_RCC_APB1ENR1_RTCAPBEN; +#else + /* enable backup registers */ + STM32_RCC_APB1ENR |= BIT(27); +#endif + /* Delay 1 APB clock cycle after the clock is enabled */ + clock_wait_bus_cycles(BUS_APB, 1); + /* Enable access to RCC CSR register and RTC backup registers */ + STM32_PWR_CR |= BIT(8); +} + +static void jump_to_rw(void) +{ + void (*addr)(void); + + addr = (void (*)(void)) (*((uint32_t *) (CONFIG_PROGRAM_MEMORY_BASE + + CONFIG_RW_MEM_OFF + 4))); + + addr(); +} + +static void jump_to_dfu(void) +{ + void (*addr)(void); + + addr = (void (*)(void)) (*((uint32_t *) (STM32_DFU_BASE + 4))); + + /* Clear the scratchpad. */ + dfu_bootmanager_backup_write(DFU_BOOTMANAGER_VALUE_CLEAR); + addr(); +} + +/* + * DFU Boot Manager main. It'll check if the RW region is not fully programmed + * or if the backup memory indicates we should reboot into DFU. + */ +int main(void) +{ + dfu_bootmanager_init(); + + if (rw_is_empty() || backup_boot_checks()) + jump_to_dfu(); + jump_to_rw(); + + return 0; +} + +/* + * The RW application will replace the vector table and exception handlers + * shortly after the jump. If the application is corrupt and fails before + * this, the only action that can be done is jumping into DFU mode. + */ +void exception_panic(void) +{ + dfu_bootmanager_enter_dfu(); +} + +/* + * Function stubs which are required by bkpdata.c and system.c: + * Interrupts are always disabled in the Boot Manager so we do not + * need to worry about concurrent access. + */ + +void task_clear_pending_irq(int irq) {} +void interrupt_disable(void) {} +void mutex_lock(mutex_t *mtx) {} +void mutex_unlock(mutex_t *mtx) {} + +bool in_interrupt_context(void) +{ + return false; +} |