zephyr/soc/espressif/common/loader.c
Sylvio Alves a7a4583e15 soc: esp32: loader: skip non-valid segment after the last valid one
Some ESP32 images may not end with a segment whose
load_addr is 0xFFFFFFFF, especially if the flash was not fully
erased or the image tool does not write an explicit end marker.
This can cause the loader to process leftover or unrelated data as
additional segments, resulting in boot failures.

Update the IS_LAST() macro to treat any segment not matching
a valid memory region as the end of the segment list.
This ensures only valid segments are loaded and any trailing
invalid data is safely skipped.

Signed-off-by: Sylvio Alves <sylvio.alves@espressif.com>
2025-07-09 17:13:54 -05:00

326 lines
10 KiB
C

/*
* Copyright (c) 2024-2025 Espressif Systems (Shanghai) Co., Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <soc.h>
#include <hal/mmu_hal.h>
#include <hal/mmu_types.h>
#include <hal/cache_types.h>
#include <hal/cache_ll.h>
#include <hal/cache_hal.h>
#include <rom/cache.h>
#include <esp_rom_sys.h>
#include <esp_err.h>
#include <esp_app_format.h>
#include <zephyr/storage/flash_map.h>
#include <esp_rom_uart.h>
#include <esp_flash.h>
#include <esp_log.h>
#include <bootloader_clock.h>
#include <bootloader_common.h>
#include <esp_cpu.h>
#include <zephyr/linker/linker-defs.h>
#include <kernel_internal.h>
#if CONFIG_SOC_SERIES_ESP32C6
#include <soc/hp_apm_reg.h>
#include <soc/lp_apm_reg.h>
#include <soc/lp_apm0_reg.h>
#include <soc/pcr_reg.h>
#endif /* CONFIG_SOC_SERIES_ESP32C6 */
#include <esp_flash_internal.h>
#include <bootloader_flash.h>
#include <bootloader_flash_priv.h>
#include <hal/efuse_ll.h>
#include <hal/efuse_hal.h>
#include <hal/wdt_hal.h>
#include <soc/chip_revision.h>
#include <soc/rtc.h>
#ifndef CONFIG_SOC_SERIES_ESP32
#include <soc/assist_debug_reg.h>
#include <soc/system_reg.h>
#endif
#include "hw_init.h"
#include "soc_init.h"
#include "soc_random.h"
#if defined(CONFIG_SOC_ESP32S3_APPCPU) || defined(CONFIG_SOC_ESP32_APPCPU)
#error "APPCPU does not need this file!"
#endif
#define TAG "boot"
#define CHECKSUM_ALIGN 16
#define IS_PADD(o) (o.load_addr == 0)
#define IS_DRAM(o) (o.load_addr >= SOC_DRAM_LOW && o.load_addr < SOC_DRAM_HIGH)
#define IS_IRAM(o) (o.load_addr >= SOC_IRAM_LOW && o.load_addr < SOC_IRAM_HIGH)
#define IS_IROM(o) (o.load_addr >= SOC_IROM_LOW && o.load_addr < SOC_IROM_HIGH)
#define IS_DROM(o) (o.load_addr >= SOC_DROM_LOW && o.load_addr < SOC_DROM_HIGH)
#ifdef SOC_RTC_MEM_SUPPORTED
#define IS_RTC(o) (o.load_addr >= SOC_RTC_DRAM_LOW && o.load_addr < SOC_RTC_DRAM_HIGH)
#else
#define IS_RTC(o) 0
#endif
#define IS_SRAM(o) (IS_IRAM(o) || IS_DRAM(o))
#define IS_MMAP(o) (IS_IROM(o) || IS_DROM(o))
#define IS_LAST(o) \
(!IS_IROM(o) && !IS_DROM(o) && !IS_IRAM(o) && !IS_DRAM(o) && !IS_PADD(o) && !IS_RTC(o))
#define HDR_ATTR __attribute__((section(".entry_addr"))) __attribute__((used))
#if !defined(CONFIG_SOC_ESP32_APPCPU) && !defined(CONFIG_SOC_ESP32S3_APPCPU)
#define PART_OFFSET FIXED_PARTITION_OFFSET(slot0_partition)
#else
#define PART_OFFSET FIXED_PARTITION_OFFSET(slot0_appcpu_partition)
#endif
void __start(void);
static HDR_ATTR void (*_entry_point)(void) = &__start;
esp_image_header_t WORD_ALIGNED_ATTR 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;
#ifndef CONFIG_MCUBOOT
extern uint32_t _libc_heap_size;
static uint32_t libc_heap_size = (uint32_t)&_libc_heap_size;
static struct rom_segments map = {
.irom_map_addr = (uint32_t)&_image_irom_vaddr,
.irom_flash_offset = PART_OFFSET + (uint32_t)&_image_irom_start,
.irom_size = (uint32_t)&_image_irom_size,
.drom_map_addr = ((uint32_t)&_image_drom_vaddr),
.drom_flash_offset = PART_OFFSET + (uint32_t)&_image_drom_start,
.drom_size = (uint32_t)&_image_drom_size,
};
void map_rom_segments(int core, struct rom_segments *map)
{
uint32_t app_irom_vaddr_align = map->irom_map_addr & MMU_FLASH_MASK;
uint32_t app_irom_start_align = map->irom_flash_offset & MMU_FLASH_MASK;
uint32_t app_drom_vaddr_align = map->drom_map_addr & MMU_FLASH_MASK;
uint32_t app_drom_start_align = map->drom_flash_offset & MMU_FLASH_MASK;
/* Traverse segments to fix flash offset changes due to post-build processing */
#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;
offset += sizeof(esp_image_header_t);
while (segments++ < 16) {
if (esp_rom_flash_read(offset, &segment_hdr,
sizeof(esp_image_segment_header_t), true) != 0) {
ESP_EARLY_LOGE(TAG, "Failed to read segment header at %x", offset);
abort();
}
if (IS_LAST(segment_hdr)) {
/* Total segment count = (segments - 1) */
break;
}
ESP_EARLY_LOGI(TAG, "%s: lma 0x%08x vma 0x%08x len 0x%-6x (%u)",
IS_LAST(segment_hdr) ? "???" :
IS_MMAP(segment_hdr) ?
IS_IROM(segment_hdr) ? "IMAP" : "DMAP" :
IS_DRAM(segment_hdr) ? "DRAM" :
IS_RTC(segment_hdr) ? "RTC" : "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 == map->drom_map_addr) {
map->drom_flash_offset = offset + sizeof(esp_image_segment_header_t);
app_drom_start_align = map->drom_flash_offset & MMU_FLASH_MASK;
}
if (segment_hdr.load_addr == map->irom_map_addr) {
map->irom_flash_offset = offset + sizeof(esp_image_segment_header_t);
app_irom_start_align = map->irom_flash_offset & MMU_FLASH_MASK;
}
if (IS_SRAM(segment_hdr) || IS_RTC(segment_hdr)) {
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) {
ESP_EARLY_LOGE(TAG, "Error parsing segments");
abort();
}
ESP_EARLY_LOGI(TAG, "Image with %d segments", segments - 1);
#endif /* !CONFIG_BOOTLOADER_MCUBOOT */
#if CONFIG_SOC_SERIES_ESP32
Cache_Read_Disable(core);
Cache_Flush(core);
#else
cache_hal_disable(CACHE_TYPE_ALL);
#endif /* CONFIG_SOC_SERIES_ESP32 */
/* Clear the MMU entries that are already set up,
* so the new app only has the mappings it creates.
*/
if (core == 0) {
mmu_hal_unmap_all();
}
#if CONFIG_SOC_SERIES_ESP32
int rc = 0;
uint32_t drom_page_count =
(map->drom_size + CONFIG_MMU_PAGE_SIZE - 1) / CONFIG_MMU_PAGE_SIZE;
rc |= cache_flash_mmu_set(core, 0, app_drom_vaddr_align, app_drom_start_align, 64,
drom_page_count);
uint32_t irom_page_count =
(map->irom_size + CONFIG_MMU_PAGE_SIZE - 1) / CONFIG_MMU_PAGE_SIZE;
rc |= cache_flash_mmu_set(core, 0, app_irom_vaddr_align, app_irom_start_align, 64,
irom_page_count);
if (rc != 0) {
ESP_EARLY_LOGE(TAG, "Failed to setup flash cache (e=0x%X). Aborting!", rc);
abort();
}
#else
uint32_t actual_mapped_len = 0;
mmu_hal_map_region(core, MMU_TARGET_FLASH0, app_drom_vaddr_align, app_drom_start_align,
map->drom_size, &actual_mapped_len);
mmu_hal_map_region(core, MMU_TARGET_FLASH0, app_irom_vaddr_align, app_irom_start_align,
map->irom_size, &actual_mapped_len);
#endif /* CONFIG_SOC_SERIES_ESP32 */
/* ----------------------Enable corresponding buses---------------- */
cache_bus_mask_t bus_mask;
bus_mask = cache_ll_l1_get_bus(core, app_drom_vaddr_align, map->drom_size);
cache_ll_l1_enable_bus(core, bus_mask);
bus_mask = cache_ll_l1_get_bus(core, app_irom_vaddr_align, map->irom_size);
cache_ll_l1_enable_bus(core, bus_mask);
#if CONFIG_MP_MAX_NUM_CPUS > 1
bus_mask = cache_ll_l1_get_bus(1, app_drom_vaddr_align, map->drom_size);
cache_ll_l1_enable_bus(1, bus_mask);
bus_mask = cache_ll_l1_get_bus(1, app_irom_vaddr_align, map->irom_size);
cache_ll_l1_enable_bus(1, bus_mask);
#endif
/* ----------------------Enable Cache---------------- */
#if CONFIG_SOC_SERIES_ESP32
/* Application will need to do Cache_Flush(1) and Cache_Read_Enable(1) */
Cache_Read_Enable(core);
#else
cache_hal_enable(CACHE_TYPE_ALL);
#endif /* CONFIG_SOC_SERIES_ESP32 */
#if !defined(CONFIG_SOC_SERIES_ESP32) && !defined(CONFIG_SOC_SERIES_ESP32S2)
/* Configure the Cache MMU size for instruction and rodata in flash. */
uint32_t cache_mmu_irom_size =
((map->irom_size + CONFIG_MMU_PAGE_SIZE - 1) / CONFIG_MMU_PAGE_SIZE) *
sizeof(uint32_t);
/* Split the cache usage by the segment sizes */
Cache_Set_IDROM_MMU_Size(cache_mmu_irom_size, CACHE_DROM_MMU_MAX_END - cache_mmu_irom_size);
#endif
}
#endif /* !CONFIG_MCUBOOT */
void __start(void)
{
#ifdef CONFIG_RISCV_GP
__asm__ __volatile__("la t0, _vector_table\n"
"csrw mtvec, t0\n");
/* Disable normal interrupts. */
csr_read_clear(mstatus, MSTATUS_MIE);
/* 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");
z_bss_zero();
#else /* xtensa */
extern uint32_t _init_start;
/* Move the exception vector table to IRAM. */
__asm__ __volatile__("wsr %0, vecbase" : : "r"(&_init_start));
z_bss_zero();
__asm__ __volatile__("" : : "g"(&__bss_start) : "memory");
/* Disable normal interrupts. */
__asm__ __volatile__("wsr %0, PS" : : "r"(PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE));
/* Initialize the architecture CPU pointer. Some of the
* initialization code wants a valid arch_current_thread() before
* arch_kernel_init() is invoked.
*/
__asm__ __volatile__("wsr.MISC0 %0; rsync" : : "r"(&_kernel.cpus[0]));
#endif /* CONFIG_RISCV_GP */
/* Initialize hardware only during 1st boot */
#if defined(CONFIG_MCUBOOT) || defined(CONFIG_ESP_SIMPLE_BOOT)
if (hardware_init()) {
ESP_EARLY_LOGE(TAG, "HW init failed, aborting");
abort();
}
#endif
#if defined(CONFIG_ESP_SIMPLE_BOOT) || defined(CONFIG_BOOTLOADER_MCUBOOT)
map_rom_segments(0, &map);
/* Show map segments continue using same log format as during MCUboot phase */
ESP_EARLY_LOGI(TAG, "%s segment: paddr=%08xh, vaddr=%08xh, size=%05Xh (%6d) map", "IROM",
map.irom_flash_offset, map.irom_map_addr, map.irom_size, map.irom_size);
ESP_EARLY_LOGI(TAG, "%s segment: paddr=%08xh, vaddr=%08xh, size=%05Xh (%6d) map", "DROM",
map.drom_flash_offset, map.drom_map_addr, map.drom_size, map.drom_size);
esp_rom_uart_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM);
/* Disable RNG entropy source as it was already used */
soc_random_disable();
/* Disable glitch detection as it can be falsely triggered by EMI interference */
ana_clock_glitch_reset_config(false);
ESP_EARLY_LOGI(TAG, "libc heap size %d kB.", libc_heap_size / 1024);
__esp_platform_app_start();
#endif /* CONFIG_ESP_SIMPLE_BOOT || CONFIG_BOOTLOADER_MCUBOOT */
#if defined(CONFIG_MCUBOOT)
__esp_platform_mcuboot_start();
#endif
}