Skip to content

Commit

Permalink
Merge branch 'feature/mmu_page_size_from_app_bin_v5.4' into 'release/…
Browse files Browse the repository at this point in the history
…v5.4'

feat(bootloader): add support to use MMU page size from app binary (v5.4)

See merge request espressif/esp-idf!35070
  • Loading branch information
Jiang Jiang Jian committed Nov 29, 2024
2 parents 236affb + 9d7a4e5 commit 20bd6ad
Show file tree
Hide file tree
Showing 17 changed files with 238 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -13,15 +13,19 @@
#include <spi_flash_mmap.h> /* including in bootloader for error values */
#include "sdkconfig.h"
#include "bootloader_flash.h"
#include "soc/ext_mem_defs.h"

#ifdef __cplusplus
extern "C" {
#endif

#define FLASH_SECTOR_SIZE 0x1000
#define FLASH_BLOCK_SIZE 0x10000

#define MMAP_ALIGNED_MASK (SPI_FLASH_MMU_PAGE_SIZE - 1)
#define MMU_FLASH_MASK (~(SPI_FLASH_MMU_PAGE_SIZE - 1))
#define MMU_FLASH_MASK_FROM_VAL(PAGE_SZ) (~((PAGE_SZ) - 1))
#define MMU_DROM_END_ENTRY_VADDR_FROM_VAL(PAGE_SZ) (SOC_DRAM_FLASH_ADDRESS_HIGH - (PAGE_SZ))

/**
* MMU mapping must always be in the unit of a SPI_FLASH_MMU_PAGE_SIZE
Expand Down Expand Up @@ -89,7 +93,7 @@ uint32_t bootloader_mmap_get_free_pages(void);
* @param length - Length of data to map.
*
* @return Pointer to mapped data memory (at src_addr), or NULL
* if an allocation error occured.
* if an allocation error occurred.
*/
const void *bootloader_mmap(uint32_t src_addr, uint32_t size);

Expand Down
1 change: 1 addition & 0 deletions components/bootloader_support/include/esp_image_format.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ typedef struct {
uint32_t image_len; /* Length of image on flash, in bytes */
uint8_t image_digest[32]; /* appended SHA-256 digest */
uint32_t secure_version; /* secure version for anti-rollback, it is covered by sha256 (set if CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK=y) */
uint32_t mmu_page_size; /* Flash MMU page size per binary header */
} esp_image_metadata_t;

typedef enum {
Expand Down
27 changes: 18 additions & 9 deletions components/bootloader_support/src/bootloader_utility.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "esp_rom_spiflash.h"

#include "soc/soc.h"
#include "soc/soc_caps.h"
#include "soc/rtc.h"
#include "soc/efuse_periph.h"
#include "soc/rtc_periph.h"
Expand Down Expand Up @@ -66,7 +67,7 @@ static void set_cache_and_start_app(uint32_t drom_addr,
uint32_t irom_addr,
uint32_t irom_load_addr,
uint32_t irom_size,
uint32_t entry_addr);
const esp_image_metadata_t *data);

esp_err_t bootloader_common_read_otadata(const esp_partition_pos_t *ota_info, esp_ota_select_entry_t *two_otadata)
{
Expand Down Expand Up @@ -789,7 +790,7 @@ static void unpack_load_app(const esp_image_metadata_t *data)
rom_addr[1],
rom_load_addr[1],
rom_size[1],
data->image.entry_addr);
data);
}

#else //!SOC_MMU_DI_VADDR_SHARED
Expand Down Expand Up @@ -834,7 +835,7 @@ static void unpack_load_app(const esp_image_metadata_t *data)
irom_addr,
irom_load_addr,
irom_size,
data->image.entry_addr);
data);
}
#endif //#if SOC_MMU_DI_VADDR_SHARED

Expand All @@ -859,9 +860,11 @@ static void set_cache_and_start_app(
uint32_t irom_addr,
uint32_t irom_load_addr,
uint32_t irom_size,
uint32_t entry_addr)
const esp_image_metadata_t *data)
{
int rc __attribute__((unused));
const uint32_t entry_addr = data->image.entry_addr;
const uint32_t mmu_page_size = data->mmu_page_size;

ESP_EARLY_LOGD(TAG, "configure drom and irom and start");
//-----------------------Disable Cache to do the mapping---------
Expand All @@ -871,12 +874,18 @@ static void set_cache_and_start_app(
#else
cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
#endif

#if SOC_MMU_PAGE_SIZE_CONFIGURABLE
// re-configure MMU page size
mmu_ll_set_page_size(0, mmu_page_size);
#endif //SOC_MMU_PAGE_SIZE_CONFIGURABLE

//reset MMU table first
mmu_hal_unmap_all();

//-----------------------MAP DROM--------------------------
uint32_t drom_load_addr_aligned = drom_load_addr & MMU_FLASH_MASK;
uint32_t drom_addr_aligned = drom_addr & MMU_FLASH_MASK;
uint32_t drom_load_addr_aligned = drom_load_addr & MMU_FLASH_MASK_FROM_VAL(mmu_page_size);
uint32_t drom_addr_aligned = drom_addr & MMU_FLASH_MASK_FROM_VAL(mmu_page_size);
ESP_EARLY_LOGV(TAG, "rodata starts from paddr=0x%08" PRIx32 ", vaddr=0x%08" PRIx32 ", size=0x%" PRIx32, drom_addr, drom_load_addr, drom_size);
//The addr is aligned, so we add the mask off length to the size, to make sure the corresponding buses are enabled.
drom_size = (drom_load_addr - drom_load_addr_aligned) + drom_size;
Expand All @@ -894,13 +903,13 @@ static void set_cache_and_start_app(
ESP_EARLY_LOGV(TAG, "after mapping rodata, starting from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", drom_addr_aligned, drom_load_addr_aligned, actual_mapped_len);
}
//we use the MMU_LL_END_DROM_ENTRY_ID mmu entry as a map page for app to find the boot partition
mmu_hal_map_region(0, MMU_TARGET_FLASH0, MMU_LL_END_DROM_ENTRY_VADDR, drom_addr_aligned, CONFIG_MMU_PAGE_SIZE, &actual_mapped_len);
mmu_hal_map_region(0, MMU_TARGET_FLASH0, MMU_DROM_END_ENTRY_VADDR_FROM_VAL(mmu_page_size), drom_addr_aligned, mmu_page_size, &actual_mapped_len);
ESP_EARLY_LOGV(TAG, "mapped one page of the rodata, from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", drom_addr_aligned, MMU_LL_END_DROM_ENTRY_VADDR, actual_mapped_len);
#endif

//-----------------------MAP IROM--------------------------
uint32_t irom_load_addr_aligned = irom_load_addr & MMU_FLASH_MASK;
uint32_t irom_addr_aligned = irom_addr & MMU_FLASH_MASK;
uint32_t irom_load_addr_aligned = irom_load_addr & MMU_FLASH_MASK_FROM_VAL(mmu_page_size);
uint32_t irom_addr_aligned = irom_addr & MMU_FLASH_MASK_FROM_VAL(mmu_page_size);
ESP_EARLY_LOGV(TAG, "text starts from paddr=0x%08" PRIx32 ", vaddr=0x%08" PRIx32 ", size=0x%" PRIx32, irom_addr, irom_load_addr, irom_size);
//The addr is aligned, so we add the mask off length to the size, to make sure the corresponding buses are enabled.
irom_size = (irom_load_addr - irom_load_addr_aligned) + irom_size;
Expand Down
37 changes: 32 additions & 5 deletions components/bootloader_support/src/esp_image_format.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "bootloader_memory_utils.h"
#include "soc/soc_caps.h"
#include "hal/cache_ll.h"
#include "spi_flash_mmap.h"

#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))

Expand Down Expand Up @@ -77,7 +78,7 @@ static esp_err_t process_segment_data(int segment, intptr_t load_addr, uint32_t
static esp_err_t verify_image_header(uint32_t src_addr, const esp_image_header_t *image, bool silent);

/* Verify a segment header */
static esp_err_t verify_segment_header(int index, const esp_image_segment_header_t *segment, uint32_t segment_data_offs, bool silent);
static esp_err_t verify_segment_header(int index, const esp_image_segment_header_t *segment, uint32_t segment_data_offs, esp_image_metadata_t *metadata, bool silent);

/* Log-and-fail macro for use in esp_image_load */
#define FAIL_LOAD(...) do { \
Expand Down Expand Up @@ -559,7 +560,7 @@ static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segme

ESP_LOGV(TAG, "segment data length 0x%"PRIx32" data starts 0x%"PRIx32, data_len, data_addr);

CHECK_ERR(verify_segment_header(index, header, data_addr, silent));
CHECK_ERR(verify_segment_header(index, header, data_addr, metadata, silent));

if (data_len % 4 != 0) {
FAIL_LOAD("unaligned segment length 0x%"PRIx32, data_len);
Expand Down Expand Up @@ -748,7 +749,7 @@ static esp_err_t process_segment_data(int segment, intptr_t load_addr, uint32_t
return ESP_OK;
}

static esp_err_t verify_segment_header(int index, const esp_image_segment_header_t *segment, uint32_t segment_data_offs, bool silent)
static esp_err_t verify_segment_header(int index, const esp_image_segment_header_t *segment, uint32_t segment_data_offs, esp_image_metadata_t *metadata, bool silent)
{
if ((segment->data_len & 3) != 0
|| segment->data_len >= SIXTEEN_MB) {
Expand All @@ -761,13 +762,39 @@ static esp_err_t verify_segment_header(int index, const esp_image_segment_header
uint32_t load_addr = segment->load_addr;
bool map_segment = should_map(load_addr);

#if SOC_MMU_PAGE_SIZE_CONFIGURABLE
/* ESP APP descriptor is present in the DROM segment #0 */
if (index == 0 && metadata->start_addr != ESP_BOOTLOADER_OFFSET) {
const esp_app_desc_t *app_desc = (const esp_app_desc_t *)bootloader_mmap(segment_data_offs, sizeof(esp_app_desc_t));
if (!app_desc || app_desc->magic_word != ESP_APP_DESC_MAGIC_WORD) {
ESP_LOGE(TAG, "Failed to fetch app description header!");
return ESP_FAIL;
}

// Convert from log base 2 number to actual size while handling legacy image case (value 0)
metadata->mmu_page_size = (app_desc->mmu_page_size > 0) ? (1UL << app_desc->mmu_page_size) : SPI_FLASH_MMU_PAGE_SIZE;
if (metadata->mmu_page_size != SPI_FLASH_MMU_PAGE_SIZE) {
ESP_LOGI(TAG, "MMU page size mismatch, configured: 0x%x, found: 0x%"PRIx32, SPI_FLASH_MMU_PAGE_SIZE, metadata->mmu_page_size);
}
bootloader_munmap(app_desc);
} else if (index == 0 && metadata->start_addr == ESP_BOOTLOADER_OFFSET) {
// Bootloader always uses the default MMU page size
metadata->mmu_page_size = SPI_FLASH_MMU_PAGE_SIZE;
}
#else // SOC_MMU_PAGE_SIZE_CONFIGURABLE
metadata->mmu_page_size = SPI_FLASH_MMU_PAGE_SIZE;
#endif // !SOC_MMU_PAGE_SIZE_CONFIGURABLE

const int mmu_page_size = metadata->mmu_page_size;
ESP_LOGV(TAG, "MMU page size 0x%x", mmu_page_size);

/* Check that flash cache mapped segment aligns correctly from flash to its mapped address,
relative to the 64KB page mapping size.
relative to the MMU page mapping size.
*/
ESP_LOGV(TAG, "segment %d map_segment %d segment_data_offs 0x%"PRIx32" load_addr 0x%"PRIx32,
index, map_segment, segment_data_offs, load_addr);
if (map_segment
&& ((segment_data_offs % SPI_FLASH_MMU_PAGE_SIZE) != (load_addr % SPI_FLASH_MMU_PAGE_SIZE))) {
&& ((segment_data_offs % mmu_page_size) != (load_addr % mmu_page_size))) {
if (!silent) {
ESP_LOGE(TAG, "Segment %d load address 0x%08"PRIx32", doesn't match data 0x%08"PRIx32,
index, load_addr, segment_data_offs);
Expand Down
1 change: 1 addition & 0 deletions components/esp_app_format/esp_app_desc.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const __attribute__((weak)) __attribute__((section(".rodata_desc"))) esp_app_de
#endif
.min_efuse_blk_rev_full = CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL,
.max_efuse_blk_rev_full = CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL,
.mmu_page_size = 31 - __builtin_clz(CONFIG_MMU_PAGE_SIZE),
};

#ifndef CONFIG_APP_EXCLUDE_PROJECT_VER_VAR
Expand Down
4 changes: 3 additions & 1 deletion components/esp_app_format/include/esp_app_desc.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ typedef struct {
uint8_t app_elf_sha256[32]; /*!< sha256 of elf file */
uint16_t min_efuse_blk_rev_full; /*!< Minimal eFuse block revision supported by image, in format: major * 100 + minor */
uint16_t max_efuse_blk_rev_full; /*!< Maximal eFuse block revision supported by image, in format: major * 100 + minor */
uint32_t reserv2[19]; /*!< reserv2 */
uint8_t mmu_page_size; /*!< MMU page size in log base 2 format */
uint8_t reserv3[3]; /*!< reserv3 */
uint32_t reserv2[18]; /*!< reserv2 */
} esp_app_desc_t;

/** @cond */
Expand Down
9 changes: 9 additions & 0 deletions tools/test_apps/system/.build-test-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ tools/test_apps/system/memprot:
temporary: true
reason: the other targets are not tested yet

tools/test_apps/system/mmu_page_size:
enable:
- if: IDF_TARGET in ["esp32c6", "esp32h2"]
reason: Coverage for two targets with configurable MMU page size is sufficient
depends_components:
- esp_app_format
- bootloader_support
- esp_mm

tools/test_apps/system/no_embedded_paths:
enable:
- if: IDF_TARGET in ["esp32", "esp32c3", "esp32s2"]
Expand Down
7 changes: 7 additions & 0 deletions tools/test_apps/system/mmu_page_size/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main)
project(test_mmu_page_size)
5 changes: 5 additions & 0 deletions tools/test_apps/system/mmu_page_size/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |

This test app ensures that bootloader can support configurable MMU page size as per the application binary header.
This test tries to boot the application with different MMU page sizes and checks if the application is able to boot successfully.
51 changes: 51 additions & 0 deletions tools/test_apps/system/mmu_page_size/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import logging
import os

import pytest
from _pytest.fixtures import FixtureRequest
from _pytest.monkeypatch import MonkeyPatch
from pytest_embedded_idf.app import FlashFile
from pytest_embedded_idf.serial import IdfSerial


# This is a custom IdfSerial class to support custom functionality
# which is required only for this test
class FlashBootloader(IdfSerial):
def bootloader_flash(self, binary_path: str) -> None:
"""
Flash bootloader.
:return: None
"""
logging.info('Flashing bootloader')
bootloader_path = os.path.join(binary_path, 'bootloader', 'bootloader.bin')
logging.info(bootloader_path)
offs = int(self.app.sdkconfig.get('BOOTLOADER_OFFSET_IN_FLASH', 0))
logging.info('bootloader offset is {0}'.format(hex(offs)))
prev_flash_files = self.app.flash_files
flash_files = []
flash_files.append(
FlashFile(
offs,
bootloader_path,
False,
)
)
self.app.flash_files = flash_files
self.flash()
# Restore self.app.flash files to original value
self.app.flash_files = prev_flash_files


@pytest.fixture(scope='module')
def monkeypatch_module(request: FixtureRequest) -> MonkeyPatch:
mp = MonkeyPatch()
request.addfinalizer(mp.undo)
return mp


@pytest.fixture(scope='module', autouse=True)
def replace_dut_class(monkeypatch_module: MonkeyPatch) -> None:
monkeypatch_module.setattr('pytest_embedded_idf.IdfSerial', FlashBootloader)
2 changes: 2 additions & 0 deletions tools/test_apps/system/mmu_page_size/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRCS "main.c"
PRIV_REQUIRES unity esp_partition)
40 changes: 40 additions & 0 deletions tools/test_apps/system/mmu_page_size/main/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#include "unity.h"
#include "esp_partition.h"

#define SZ 4096

void app_main(void)
{
printf("App is running\n");

// Perform some partition and memory map related operations
char src_p_1[32] = "Test data pattern 123456789";
char buf[32];

// Find storage partition
const esp_partition_t* partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
TEST_ASSERT_NOT_NULL(partition);

esp_partition_mmap_handle_t handle1;
const void *ptr1;
TEST_ESP_OK(esp_partition_mmap(partition, 0, SZ, ESP_PARTITION_MMAP_DATA, &ptr1, &handle1));
TEST_ESP_OK(esp_partition_erase_range(partition, 0, SZ));
TEST_ESP_OK(esp_partition_write(partition, 0, src_p_1, sizeof(src_p_1)));
memcpy(buf, ptr1, sizeof(buf));

TEST_ASSERT_EQUAL(0, memcmp(buf, src_p_1, sizeof(buf)));
esp_partition_munmap(handle1);

printf("Partition test done\n");
}
6 changes: 6 additions & 0 deletions tools/test_apps/system/mmu_page_size/partitions.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 1M,
storage, data, fat, , 64K,
Loading

0 comments on commit 20bd6ad

Please sign in to comment.