esp-idf/components/esp_system/port/image_process.c

245 lines
8.0 KiB
C
Raw Normal View History

/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_types.h"
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_image_format.h"
#include "esp_app_format.h"
#include "esp_flash_partitions.h"
#include "hal/cache_hal.h"
#include "hal/cache_ll.h"
#include "hal/mmu_hal.h"
#include "hal/mmu_ll.h"
#include "soc/soc.h"
#include "soc/soc_caps.h"
#include "soc/ext_mem_defs.h"
#include "esp_private/image_process.h"
#include "esp_private/esp_cache_esp32_private.h"
/**
* ESP32 bootloader size is not enough, not enable this feature for now
*/
#define IMAGE_PROCESS_SUPPORTED_TARGETS (!CONFIG_IDF_TARGET_ESP32)
#if CONFIG_IDF_TARGET_ESP32
#define MMAP_MMU_SIZE 0x320000
#elif CONFIG_IDF_TARGET_ESP32S2
#define MMAP_MMU_SIZE (SOC_DRAM0_CACHE_ADDRESS_HIGH - SOC_DRAM0_CACHE_ADDRESS_LOW)
#else
#define MMAP_MMU_SIZE (SOC_DRAM_FLASH_ADDRESS_HIGH - SOC_DRAM_FLASH_ADDRESS_LOW)
#endif
#if CONFIG_IDF_TARGET_ESP32
#define FLASH_READ_VADDR (SOC_DROM_LOW + MMAP_MMU_SIZE)
#else
#define FLASH_READ_VADDR (SOC_DROM_LOW + MMAP_MMU_SIZE - CONFIG_MMU_PAGE_SIZE)
#endif
#define MMU_FLASH_MASK (~(CONFIG_MMU_PAGE_SIZE - 1))
/**
* @brief Image process driver
*/
struct image_process_driver_s {
/**
* @brief Process segments
*
* @param[in] data image meta data
*
* @return
* - ESP_OK
* - ESP_ERR_INVALID_ARG: invalid argument
* - ESP_ERR_INVALID_STATE: invalid state
*/
esp_err_t (*process_segments)(esp_image_metadata_t *data);
};
const static char *TAG = "image_process";
static uint32_t s_current_read_mapping = UINT32_MAX;
static uint32_t s_flash_drom_paddr_start = 0;
static uint32_t s_flash_irom_paddr_start = 0;
static esp_err_t process_segments(esp_image_metadata_t *data);
static image_process_driver_t s_image_process_driver = {
process_segments,
};
static esp_err_t flash_read(size_t src_addr, void *dest, size_t size)
{
if (src_addr & 3) {
ESP_EARLY_LOGE(TAG, "flash_read src_addr 0x%x not 4-byte aligned", src_addr);
return ESP_ERR_INVALID_ARG;
}
if (size & 3) {
ESP_EARLY_LOGE(TAG, "flash_read size 0x%x not 4-byte aligned", size);
return ESP_ERR_INVALID_ARG;
}
if ((intptr_t)dest & 3) {
ESP_EARLY_LOGE(TAG, "flash_read dest 0x%x not 4-byte aligned", (intptr_t)dest);
return ESP_ERR_INVALID_ARG;
}
uint32_t *dest_words = (uint32_t *)dest;
for (size_t word = 0; word < size / 4; word++) {
uint32_t word_src = src_addr + word * 4; /* Read this offset from flash */
uint32_t map_at = word_src & MMU_FLASH_MASK; /* Map this 64KB block from flash */
uint32_t *map_ptr;
/* Move the 64KB mmu mapping window to fit map_at */
if (map_at != s_current_read_mapping) {
cache_hal_suspend(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
uint32_t actual_mapped_len = 0;
mmu_hal_map_region(0, MMU_TARGET_FLASH0, FLASH_READ_VADDR, map_at, CONFIG_MMU_PAGE_SIZE - 1, &actual_mapped_len);
s_current_read_mapping = map_at;
ESP_EARLY_LOGD(TAG, "starting from paddr=0x%" PRIx32 " and vaddr=0x%" PRIx32 ", 0x%" PRIx32 " bytes are mapped", map_at, FLASH_READ_VADDR, actual_mapped_len);
#if CONFIG_IDF_TARGET_ESP32
cache_sync();
#else
cache_hal_invalidate_addr(FLASH_READ_VADDR, actual_mapped_len);
#endif
cache_hal_resume(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
}
map_ptr = (uint32_t *)(FLASH_READ_VADDR + (word_src - map_at));
dest_words[word] = *map_ptr;
}
return ESP_OK;
}
#if IMAGE_PROCESS_SUPPORTED_TARGETS
static esp_err_t process_image_header(esp_image_metadata_t *data, uint32_t part_offset)
{
bzero(data, sizeof(esp_image_metadata_t));
data->start_addr = part_offset;
ESP_RETURN_ON_ERROR_ISR(flash_read(data->start_addr, &data->image, sizeof(esp_image_header_t)), TAG, "failed to read image");
data->image_len = sizeof(esp_image_header_t);
ESP_EARLY_LOGD(TAG, "reading image header=0x%"PRIx32" image_len=0x%"PRIx32" image.segment_count=0x%x", data->start_addr, data->image_len, data->image.segment_count);
return ESP_OK;
}
#endif
static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segment_header_t *header, esp_image_metadata_t *metadata, int *cnt)
{
/* read segment header */
ESP_RETURN_ON_ERROR_ISR(flash_read(flash_addr, header, sizeof(esp_image_segment_header_t)), TAG, "failed to do flash read");
intptr_t load_addr = header->load_addr;
uint32_t data_len = header->data_len;
uint32_t data_addr = flash_addr + sizeof(esp_image_segment_header_t);
#if SOC_MMU_DI_VADDR_SHARED
#if CONFIG_SPIRAM_FLASH_LOAD_TO_PSRAM
if (load_addr >= SOC_DRAM_PSRAM_ADDRESS_LOW && load_addr < SOC_DRAM_PSRAM_ADDRESS_HIGH) {
if (*cnt == 0) {
s_flash_drom_paddr_start = data_addr;
} else if (*cnt == 1) {
s_flash_irom_paddr_start = data_addr;
}
(*cnt)++;
}
#else
if (load_addr >= SOC_DRAM_FLASH_ADDRESS_LOW && load_addr < SOC_DRAM_FLASH_ADDRESS_HIGH) {
if (*cnt == 0) {
s_flash_drom_paddr_start = data_addr;
} else if (*cnt == 1) {
s_flash_irom_paddr_start = data_addr;
}
(*cnt)++;
}
#endif
#else
if (load_addr >= SOC_IRAM_FLASH_ADDRESS_LOW && load_addr < SOC_IRAM_FLASH_ADDRESS_HIGH) {
s_flash_drom_paddr_start = data_addr;
(*cnt)++;
}
if (load_addr >= SOC_DRAM_FLASH_ADDRESS_LOW && load_addr < SOC_DRAM_FLASH_ADDRESS_HIGH) {
s_flash_irom_paddr_start = data_addr;
(*cnt)++;
}
#endif
ESP_EARLY_LOGD(TAG, "load_addr: %x, data_len: %x, flash_addr: 0x%x, data_addr: %x", load_addr, data_len, flash_addr, data_addr);
if (data_len % 4 != 0) {
ESP_RETURN_ON_FALSE_ISR(false, ESP_ERR_INVALID_STATE, TAG, "unaligned segment length 0x%"PRIx32, data_len);
}
return ESP_OK;
}
static esp_err_t process_segments(esp_image_metadata_t *data)
{
uint32_t start_segments = data->start_addr + data->image_len;
uint32_t next_addr = start_segments;
int cnt = 0;
for (int i = 0; i < data->image.segment_count; i++) {
esp_image_segment_header_t *header = &data->segments[i];
ESP_EARLY_LOGD(TAG, "loading segment header %d at offset 0x%"PRIx32, i, next_addr);
ESP_RETURN_ON_ERROR_ISR(process_segment(i, next_addr, header, data, &cnt), TAG, "failed to process segment");
next_addr += sizeof(esp_image_segment_header_t);
data->segment_data[i] = next_addr;
next_addr += header->data_len;
}
assert(cnt == 2);
uint32_t end_addr = next_addr;
if (end_addr < data->start_addr) {
return ESP_FAIL;
}
data->image_len += end_addr - start_segments;
return ESP_OK;
}
void image_process_get_flash_segments_info(uint32_t *out_drom_paddr_start, uint32_t *out_irom_paddr_start)
{
assert(out_drom_paddr_start && out_irom_paddr_start);
*out_drom_paddr_start = s_flash_drom_paddr_start;
*out_irom_paddr_start = s_flash_irom_paddr_start;
}
esp_err_t image_process(void)
{
#if IMAGE_PROCESS_SUPPORTED_TARGETS
esp_err_t ret = ESP_FAIL;
/**
* We use the MMU_LL_END_DROM_ENTRY_ID mmu entry as a map page for app to find the boot partition
* This depends on 2nd bootloader to set the entry
*/
uint32_t paddr_base = mmu_ll_entry_id_to_paddr_base(0, MMU_LL_END_DROM_ENTRY_ID);
uint32_t part_offset = paddr_base;
esp_image_metadata_t image_data = {0};
ret = process_image_header(&image_data, part_offset);
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "failed to process image header");
abort();
}
ret = s_image_process_driver.process_segments(&image_data);
if (ret != ESP_OK) {
ESP_EARLY_LOGE(TAG, "failed to process segments");
return ESP_FAIL;
}
mmu_ll_set_entry_invalid(0, MMU_LL_END_DROM_ENTRY_ID);
#else
(void)s_image_process_driver;
#endif
return ESP_OK;
}