mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
248 lines
8.1 KiB
C
248 lines
8.1 KiB
C
/*
|
|
* 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);
|
|
}
|
|
|
|
mmu_ll_set_entry_invalid(0, MMU_LL_END_DROM_ENTRY_ID);
|
|
s_current_read_mapping = UINT32_MAX;
|
|
|
|
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;
|
|
}
|