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 <marek.matej@espressif.com>
diff --git a/soc/espressif/common/Kconfig.flash b/soc/espressif/common/Kconfig.flash
index 3dd465c..2ed5d3f 100644
--- a/soc/espressif/common/Kconfig.flash
+++ b/soc/espressif/common/Kconfig.flash
@@ -127,4 +127,12 @@
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 6d1b7ec..e563f67 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 <hal/cache_hal.h>
#include <rom/cache.h>
#include <esp_rom_sys.h>
+#include <esp_err.h>
#define MMU_FLASH_MASK (~(CONFIG_MMU_PAGE_SIZE - 1))
-#ifdef CONFIG_BOOTLOADER_MCUBOOT
+#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_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 @@
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 @@
#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 @@
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();
}