From 43e3088c2d7df553cb9bccdeb6a341d59cf54400 Mon Sep 17 00:00:00 2001 From: Marek Matej Date: Wed, 14 Feb 2024 18:45:17 +0100 Subject: [PATCH] soc: espressif: Simple boot support Add simplistic booting method which allows to load without 2nd stage bootloader. Update common architecture loader to support all build scenarios. - simple boot: using single binary application without bootloader - mcuboot: zephyr port of MCUboot - application: loaded by the MCUboot Signed-off-by: Marek Matej --- soc/espressif/common/Kconfig.flash | 8 ++ soc/espressif/common/loader.c | 178 +++++++++++++++++++++++------ 2 files changed, 152 insertions(+), 34 deletions(-) diff --git a/soc/espressif/common/Kconfig.flash b/soc/espressif/common/Kconfig.flash index 3dd465c6868..2ed5d3f1b65 100644 --- a/soc/espressif/common/Kconfig.flash +++ b/soc/espressif/common/Kconfig.flash @@ -127,4 +127,12 @@ config SPI_FLASH_HPM_ENABLE This option is invisible, and will be selected automatically when ``ESPTOOLPY_FLASHFREQ_120M`` is selected. +config ESP_SIMPLE_BOOT + bool "Simple Boot method" + default y if !BOOTLOADER_MCUBOOT + help + The Simple Boot is a method of booting that doesn't depend on a + 2nd stage bootloader. Please note that some of the bootloader features + are not available using simple boot, such secure boot and OTA. + endif # SOC_FAMILY_ESPRESSIF_ESP32 diff --git a/soc/espressif/common/loader.c b/soc/espressif/common/loader.c index 6d1b7ecba0e..e563f6757ec 100644 --- a/soc/espressif/common/loader.c +++ b/soc/espressif/common/loader.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Espressif Systems (Shanghai) Co., Ltd. + * Copyright (c) 2024 Espressif Systems (Shanghai) Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,29 +12,64 @@ #include #include #include +#include #define MMU_FLASH_MASK (~(CONFIG_MMU_PAGE_SIZE - 1)) -#ifdef CONFIG_BOOTLOADER_MCUBOOT +#include #include #include "esp_rom_uart.h" +#include "esp_flash.h" +#include "esp_log.h" +#include "bootloader_init.h" + +#define TAG "boot" + +#define CHECKSUM_ALIGN 16 +#define IS_PADD(addr) (addr == 0) +#define IS_DRAM(addr) (addr >= SOC_DRAM_LOW && addr < SOC_DRAM_HIGH) +#define IS_IRAM(addr) (addr >= SOC_IRAM_LOW && addr < SOC_IRAM_HIGH) +#define IS_IROM(addr) (addr >= SOC_IROM_LOW && addr < SOC_IROM_HIGH) +#define IS_DROM(addr) (addr >= SOC_DROM_LOW && addr < SOC_DROM_HIGH) +#define IS_SRAM(addr) (IS_IRAM(addr) || IS_DRAM(addr)) +#define IS_MMAP(addr) (IS_IROM(addr) || IS_DROM(addr)) +#define IS_NONE(addr) (!IS_IROM(addr) && !IS_DROM(addr) \ + && !IS_IRAM(addr) && !IS_DRAM(addr) && !IS_PADD(addr)) #define BOOT_LOG_INF(_fmt, ...) \ ets_printf("[" CONFIG_SOC_SERIES "] [INF] " _fmt "\n\r", ##__VA_ARGS__) +#define BOOT_LOG_ERR(_fmt, ...) \ + ets_printf("[" CONFIG_SOC_SERIES "] [ERR] " _fmt "\n\r", ##__VA_ARGS__) #define HDR_ATTR __attribute__((section(".entry_addr"))) __attribute__((used)) void __start(void); - static HDR_ATTR void (*_entry_point)(void) = &__start; +extern esp_image_header_t bootloader_image_hdr; extern uint32_t _image_irom_start, _image_irom_size, _image_irom_vaddr; extern uint32_t _image_drom_start, _image_drom_size, _image_drom_vaddr; + +static uint32_t _app_irom_start = (FIXED_PARTITION_OFFSET(slot0_partition) + + (uint32_t)&_image_irom_start); +static uint32_t _app_irom_size = (uint32_t)&_image_irom_size; +static uint32_t _app_irom_vaddr = ((uint32_t)&_image_irom_vaddr); + +static uint32_t _app_drom_start = (FIXED_PARTITION_OFFSET(slot0_partition) + + (uint32_t)&_image_drom_start); +static uint32_t _app_drom_size = (uint32_t)&_image_drom_size; +static uint32_t _app_drom_vaddr = ((uint32_t)&_image_drom_vaddr); + +#ifndef CONFIG_BOOTLOADER_MCUBOOT +static esp_err_t spi_flash_read(uint32_t address, void *buffer, size_t length) +{ + return esp_flash_read(NULL, buffer, address, length); +} #endif /* CONFIG_BOOTLOADER_MCUBOOT */ void map_rom_segments(uint32_t app_drom_start, uint32_t app_drom_vaddr, - uint32_t app_drom_size, uint32_t app_irom_start, - uint32_t app_irom_vaddr, uint32_t app_irom_size) + uint32_t app_drom_size, uint32_t app_irom_start, + uint32_t app_irom_vaddr, uint32_t app_irom_size) { uint32_t app_irom_start_aligned = app_irom_start & MMU_FLASH_MASK; uint32_t app_irom_vaddr_aligned = app_irom_vaddr & MMU_FLASH_MASK; @@ -43,12 +78,74 @@ void map_rom_segments(uint32_t app_drom_start, uint32_t app_drom_vaddr, uint32_t app_drom_vaddr_aligned = app_drom_vaddr & MMU_FLASH_MASK; uint32_t actual_mapped_len = 0; +#ifndef CONFIG_BOOTLOADER_MCUBOOT + esp_image_segment_header_t WORD_ALIGNED_ATTR segment_hdr; + size_t offset = FIXED_PARTITION_OFFSET(boot_partition); + bool checksum = false; + unsigned int segments = 0; + unsigned int ram_segments = 0; + + /* Using already fetched bootloader image header from bootloader_init */ + offset += sizeof(esp_image_header_t); + + while (segments++ < 16) { + + if (spi_flash_read(offset, &segment_hdr, + sizeof(esp_image_segment_header_t)) != ESP_OK) { + BOOT_LOG_ERR("Failed to read segment header at %x", offset); + abort(); + } + + /* TODO: Find better end-of-segment detection */ + if (IS_NONE(segment_hdr.load_addr)) { + /* Total segment count = (segments - 1) */ + break; + } + + BOOT_LOG_INF("%s: lma 0x%08x vma 0x%08x len 0x%-6x (%u)", + IS_NONE(segment_hdr.load_addr) ? "???" : + IS_MMAP(segment_hdr.load_addr) ? + IS_IROM(segment_hdr.load_addr) ? "IMAP" : "DMAP" : + IS_PADD(segment_hdr.load_addr) ? "padd" : + IS_DRAM(segment_hdr.load_addr) ? "DRAM" : "IRAM", + offset + sizeof(esp_image_segment_header_t), + segment_hdr.load_addr, segment_hdr.data_len, segment_hdr.data_len); + + /* Fix drom and irom produced be the linker, as it could + * be invalidated by the elf2image and flash load offset + */ + if (segment_hdr.load_addr == _app_drom_vaddr) { + app_drom_start = offset + sizeof(esp_image_segment_header_t); + app_drom_start_aligned = app_drom_start & MMU_FLASH_MASK; + } + if (segment_hdr.load_addr == _app_irom_vaddr) { + app_irom_start = offset + sizeof(esp_image_segment_header_t); + app_irom_start_aligned = app_irom_start & MMU_FLASH_MASK; + } + if (IS_SRAM(segment_hdr.load_addr)) { + ram_segments++; + } + offset += sizeof(esp_image_segment_header_t) + segment_hdr.data_len; + + if (ram_segments == bootloader_image_hdr.segment_count && !checksum) { + offset += (CHECKSUM_ALIGN - 1) - (offset % CHECKSUM_ALIGN) + 1; + checksum = true; + } + } + if (segments == 0 || segments == 16) { + BOOT_LOG_ERR("Error parsing segments"); + abort(); + } + + BOOT_LOG_INF("Image with %d segments", segments - 1); +#endif /* !CONFIG_BOOTLOADER_MCUBOOT */ + #if CONFIG_SOC_SERIES_ESP32 Cache_Read_Disable(0); Cache_Flush(0); #else cache_hal_disable(CACHE_TYPE_ALL); -#endif +#endif /* CONFIG_SOC_SERIES_ESP32 */ /* Clear the MMU entries that are already set up, * so the new app only has the mappings it creates. @@ -58,31 +155,33 @@ void map_rom_segments(uint32_t app_drom_start, uint32_t app_drom_vaddr, #if CONFIG_SOC_SERIES_ESP32 int rc = 0; uint32_t drom_page_count = - (app_drom_size + CONFIG_MMU_PAGE_SIZE - 1) / CONFIG_MMU_PAGE_SIZE; + (app_drom_size + CONFIG_MMU_PAGE_SIZE - 1) / CONFIG_MMU_PAGE_SIZE; rc |= cache_flash_mmu_set(0, 0, app_drom_vaddr_aligned, - app_drom_start_aligned, 64, drom_page_count); + app_drom_start_aligned, 64, drom_page_count); rc |= cache_flash_mmu_set(1, 0, app_drom_vaddr_aligned, - app_drom_start_aligned, 64, drom_page_count); + app_drom_start_aligned, 64, drom_page_count); uint32_t irom_page_count = - (app_irom_size + CONFIG_MMU_PAGE_SIZE - 1) / CONFIG_MMU_PAGE_SIZE; + (app_irom_size + CONFIG_MMU_PAGE_SIZE - 1) / CONFIG_MMU_PAGE_SIZE; rc |= cache_flash_mmu_set(0, 0, app_irom_vaddr_aligned, - app_irom_start_aligned, 64, irom_page_count); + app_irom_start_aligned, 64, irom_page_count); rc |= cache_flash_mmu_set(1, 0, app_irom_vaddr_aligned, - app_irom_start_aligned, 64, irom_page_count); + app_irom_start_aligned, 64, irom_page_count); if (rc != 0) { - esp_rom_printf("Failed to setup XIP, aborting\n"); + BOOT_LOG_ERR("Failed to setup XIP, aborting"); abort(); } #else - mmu_hal_map_region(0, MMU_TARGET_FLASH0, app_drom_vaddr_aligned, app_drom_start_aligned, + mmu_hal_map_region(0, MMU_TARGET_FLASH0, + app_drom_vaddr_aligned, app_drom_start_aligned, app_drom_size, &actual_mapped_len); - mmu_hal_map_region(0, MMU_TARGET_FLASH0, app_irom_vaddr_aligned, app_irom_start_aligned, + mmu_hal_map_region(0, MMU_TARGET_FLASH0, + app_irom_vaddr_aligned, app_irom_start_aligned, app_irom_size, &actual_mapped_len); -#endif +#endif /* CONFIG_SOC_SERIES_ESP32 */ /* ----------------------Enable corresponding buses---------------- */ cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, app_drom_vaddr_aligned, app_drom_size); @@ -103,31 +202,42 @@ void map_rom_segments(uint32_t app_drom_start, uint32_t app_drom_vaddr, Cache_Read_Enable(0); #else cache_hal_enable(CACHE_TYPE_ALL); -#endif +#endif /* CONFIG_SOC_SERIES_ESP32 */ + + /* Show map segments continue using same log format as during MCUboot phase */ + BOOT_LOG_INF("DROM segment: paddr=%08xh, vaddr=%08xh, size=%05Xh (%6d) map", + app_drom_start_aligned, app_drom_vaddr_aligned, + app_drom_size, app_drom_size); + BOOT_LOG_INF("IROM segment: paddr=%08xh, vaddr=%08xh, size=%05Xh (%6d) map\r\n", + app_irom_start_aligned, app_irom_vaddr_aligned, + app_irom_size, app_irom_size); + esp_rom_uart_tx_wait_idle(0); } void __start(void) { -#ifdef CONFIG_BOOTLOADER_MCUBOOT - size_t _partition_offset = FIXED_PARTITION_OFFSET(slot0_partition); - uint32_t _app_irom_start = (_partition_offset + (uint32_t)&_image_irom_start); - uint32_t _app_irom_size = (uint32_t)&_image_irom_size; - uint32_t _app_irom_vaddr = ((uint32_t)&_image_irom_vaddr); +#ifdef CONFIG_RISCV_GP + /* Configure the global pointer register + * (This should be the first thing startup does, as any other piece of code could be + * relaxed by the linker to access something relative to __global_pointer$) + */ + __asm__ __volatile__(".option push\n" + ".option norelax\n" + "la gp, __global_pointer$\n" + ".option pop"); +#endif /* CONFIG_RISCV_GP */ - uint32_t _app_drom_start = (_partition_offset + (uint32_t)&_image_drom_start); - uint32_t _app_drom_size = (uint32_t)&_image_drom_size; - uint32_t _app_drom_vaddr = ((uint32_t)&_image_drom_vaddr); - uint32_t actual_mapped_len = 0; +#ifndef CONFIG_BOOTLOADER_MCUBOOT + /* Init fundamental components */ + if (bootloader_init()) { + BOOT_LOG_ERR("HW init failed, aborting"); + abort(); + } +#endif +#ifndef CONFIG_MCUBOOT map_rom_segments(_app_drom_start, _app_drom_vaddr, _app_drom_size, - _app_irom_start, _app_irom_vaddr, _app_irom_size); - - /* Show map segments continue using same log format as during MCUboot phase */ - BOOT_LOG_INF("DROM segment: paddr=%08Xh, vaddr=%08Xh, size=%05Xh (%6d) map", - _app_drom_start, _app_drom_vaddr, _app_drom_size, _app_drom_size); - BOOT_LOG_INF("IROM segment: paddr=%08Xh, vaddr=%08Xh, size=%05Xh (%6d) map\r\n", - _app_irom_start, _app_irom_vaddr, _app_irom_size, _app_irom_size); - esp_rom_uart_tx_wait_idle(0); + _app_irom_start, _app_irom_vaddr, _app_irom_size); #endif __esp_platform_start(); }