Skip to content

Commit 8f7fded

Browse files
committed
feat(mmu): read flash through cache-mmu
1 parent afbe9ff commit 8f7fded

File tree

11 files changed

+403
-2
lines changed

11 files changed

+403
-2
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ endif()
1818

1919
set(srcs
2020
src/cache.c
21+
src/mmu.c
2122
src/flash.c
2223
src/mem_utils.c
2324
src/rom_wrappers.c

include/esp-stub-lib/mmu.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0 OR MIT
5+
*/
6+
7+
#pragma once
8+
9+
#include <stdint.h>
10+
11+
#include <esp-stub-lib/bit_utils.h>
12+
13+
#define STUB_MMU_PAGE_SIZE 0x10000U /* 64KB */
14+
#define STUB_MMU_PAGE_SHIFT 16
15+
16+
#ifdef __cplusplus
17+
extern "C" {
18+
#endif // __cplusplus
19+
20+
/**
21+
* @brief Map a flash physical address range into the CPU virtual address space.
22+
*
23+
* Temporarily programs MMU entries so the CPU can read flash through cache.
24+
* Only one mapping can be active at a time. The caller must call
25+
* stub_lib_flash_munmap() before creating a new mapping.
26+
*
27+
* @param flash_paddr Flash physical address (page-aligned internally).
28+
* @param size Number of bytes to map (rounded up to page boundary).
29+
* @param[out] out_vaddr On success, receives the virtual address corresponding
30+
* to flash_paddr (including sub-page offset).
31+
*
32+
* @return 0 on success, or a negative error code.
33+
*/
34+
int stub_lib_flash_mmap(uint32_t flash_paddr, uint32_t size, const void **out_vaddr);
35+
36+
/**
37+
* @brief Unmap a previously mapped flash region and restore MMU state.
38+
*/
39+
void stub_lib_flash_munmap(void);
40+
41+
/**
42+
* @brief Read data from flash, automatically handling encrypted flash.
43+
*
44+
* When flash encryption is enabled, reads through the cache via MMU mapping
45+
* to get decrypted data. Otherwise, reads directly via SPI.
46+
*
47+
* @param addr Flash physical address.
48+
* @param buffer Destination buffer.
49+
* @param size Number of bytes to read.
50+
*
51+
* @return 0 on success, or a negative error code.
52+
*/
53+
int stub_lib_flash_read(uint32_t addr, void *buffer, uint32_t size);
54+
55+
#ifdef __cplusplus
56+
}
57+
#endif // __cplusplus

src/mmu.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0 OR MIT
5+
*/
6+
7+
#include <stddef.h>
8+
#include <string.h>
9+
10+
#include <esp-stub-lib/bit_utils.h>
11+
#include <esp-stub-lib/cache.h>
12+
#include <esp-stub-lib/flash.h>
13+
#include <esp-stub-lib/log.h>
14+
#include <esp-stub-lib/security.h>
15+
16+
#include <target/mmu.h>
17+
18+
#define STUB_MMAP_MAX_PAGES 8 // TODO: check if 8 pages is enough
19+
20+
static struct {
21+
uint32_t vaddr_base;
22+
uint32_t entry_start;
23+
uint32_t page_count;
24+
uint32_t saved_entries[STUB_MMAP_MAX_PAGES];
25+
} s_mmap_state;
26+
27+
int stub_lib_flash_mmap(uint32_t flash_paddr, uint32_t size, const void **out_vaddr)
28+
{
29+
uint32_t aligned = ALIGN_DOWN(flash_paddr, STUB_MMU_PAGE_SIZE);
30+
uint32_t offset = flash_paddr - aligned;
31+
uint32_t map_size = size + offset;
32+
uint32_t page_count = (map_size + STUB_MMU_PAGE_SIZE - 1) >> STUB_MMU_PAGE_SHIFT;
33+
34+
STUB_LOGD("aligned: %x, offset: %x, map_size: %x, page_count: %d\n", aligned, offset, map_size, page_count);
35+
36+
if (page_count == 0 || page_count > STUB_MMAP_MAX_PAGES) {
37+
STUB_LOGE("invalid page_count: %d\n", page_count);
38+
return -1;
39+
}
40+
41+
uint32_t drom_start = stub_target_mmu_get_drom_entry_start();
42+
uint32_t drom_end = stub_target_mmu_get_drom_entry_end();
43+
uint32_t drom_count = drom_end - drom_start;
44+
if (page_count > drom_count) {
45+
STUB_LOGE("invalid page_count: %d, drom_count: %d\n", page_count, drom_count);
46+
return -1;
47+
}
48+
49+
/* Use the last N entries of the DROM region (least likely to conflict) */
50+
uint32_t entry_start = drom_end - page_count;
51+
uint32_t drom_vaddr = stub_target_mmu_get_drom_vaddr();
52+
uint32_t vaddr_base = drom_vaddr + (entry_start - drom_start) * STUB_MMU_PAGE_SIZE;
53+
54+
STUB_LOGD("drom_start: %d, drom_end: %d, entry_start: %d, vaddr_base: %x\n",
55+
drom_start,
56+
drom_end,
57+
entry_start,
58+
vaddr_base);
59+
60+
uint32_t autoload = stub_lib_cache_suspend();
61+
62+
/* Save existing MMU entries */
63+
for (uint32_t i = 0; i < page_count; i++)
64+
s_mmap_state.saved_entries[i] = stub_target_mmu_read_entry(entry_start + i);
65+
66+
/* Write new flash mappings */
67+
uint32_t flash_page = aligned >> STUB_MMU_PAGE_SHIFT;
68+
for (uint32_t i = 0; i < page_count; i++)
69+
stub_target_mmu_write_entry(entry_start + i, flash_page + i);
70+
71+
s_mmap_state.vaddr_base = vaddr_base;
72+
s_mmap_state.entry_start = entry_start;
73+
s_mmap_state.page_count = page_count;
74+
75+
stub_lib_cache_resume(autoload);
76+
77+
/* Invalidate cache for the mapped region */
78+
uint32_t caps = stub_lib_cache_get_caps();
79+
if (caps & STUB_CACHE_CAP_HAS_INVALIDATE_ADDR)
80+
stub_lib_cache_invalidate_addr(vaddr_base, page_count * STUB_MMU_PAGE_SIZE);
81+
else
82+
stub_lib_cache_invalidate_all();
83+
84+
*out_vaddr = (const void *)(vaddr_base + offset);
85+
86+
STUB_LOGD("mmap: 0x%x is mapped to 0x%x\n", flash_paddr, *out_vaddr);
87+
88+
return 0;
89+
}
90+
91+
void stub_lib_flash_munmap(void)
92+
{
93+
if (s_mmap_state.page_count == 0)
94+
return;
95+
96+
uint32_t autoload = stub_lib_cache_suspend();
97+
98+
/* Restore saved MMU entries */
99+
for (uint32_t i = 0; i < s_mmap_state.page_count; i++)
100+
stub_target_mmu_restore_entry(s_mmap_state.entry_start + i, s_mmap_state.saved_entries[i]);
101+
102+
stub_lib_cache_resume(autoload);
103+
104+
/* Invalidate stale cache lines */
105+
uint32_t caps = stub_lib_cache_get_caps();
106+
if (caps & STUB_CACHE_CAP_HAS_INVALIDATE_ADDR)
107+
stub_lib_cache_invalidate_addr(s_mmap_state.vaddr_base, s_mmap_state.page_count * STUB_MMU_PAGE_SIZE);
108+
else
109+
stub_lib_cache_invalidate_all();
110+
111+
s_mmap_state.page_count = 0;
112+
}
113+
114+
int stub_lib_flash_read(uint32_t addr, void *buffer, uint32_t size)
115+
{
116+
// if (!stub_lib_security_flash_is_encrypted())
117+
if (0)
118+
return stub_lib_flash_read_buff(addr, buffer, size);
119+
120+
/* Encrypted flash: read through cache via MMU mapping */
121+
const void *vaddr;
122+
int rc = stub_lib_flash_mmap(addr, size, &vaddr);
123+
if (rc != 0) {
124+
STUB_LOGE("mmap failed (%d) @ %x\n", rc, addr);
125+
return rc;
126+
}
127+
128+
memcpy(buffer, vaddr, size);
129+
stub_lib_flash_munmap();
130+
131+
return 0;
132+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0 OR MIT
5+
*/
6+
7+
#pragma once
8+
9+
#include <stdint.h>
10+
11+
#include <esp-stub-lib/mmu.h>
12+
13+
/**
14+
* @brief Get the DROM virtual address base for mmap operations.
15+
*
16+
* @return Start of the DROM virtual address region (e.g. SOC_DROM_LOW).
17+
*/
18+
uint32_t stub_target_mmu_get_drom_vaddr(void);
19+
20+
/**
21+
* @brief Get the first MMU entry index in the DROM region.
22+
*
23+
* On chips with partitioned MMU tables (e.g. ESP32 where entries 0–63 are
24+
* DROM, 64–127 are IRAM0, etc.), this returns the first DROM entry index.
25+
* On chips with unified IROM/DROM tables, this typically returns 0.
26+
*
27+
* @return First DROM entry index (e.g. SOC_MMU_DROM0_PAGES_START).
28+
*/
29+
uint32_t stub_target_mmu_get_drom_entry_start(void);
30+
31+
/**
32+
* @brief Get the one-past-last MMU entry index in the DROM region.
33+
*
34+
* @return One past the last DROM entry index (e.g. SOC_MMU_DROM0_PAGES_END).
35+
*/
36+
uint32_t stub_target_mmu_get_drom_entry_end(void);
37+
38+
/**
39+
* @brief Write a single MMU entry mapping a flash page.
40+
*
41+
* The implementation must add the chip-specific valid bit(s).
42+
*
43+
* @param entry_id MMU entry index.
44+
* @param flash_page_num Flash page number (paddr >> STUB_MMU_PAGE_SHIFT).
45+
*/
46+
void stub_target_mmu_write_entry(uint32_t entry_id, uint32_t flash_page_num);
47+
48+
/**
49+
* @brief Read a single MMU entry (raw value for save/restore).
50+
*
51+
* @param entry_id MMU entry index.
52+
* @return Raw MMU entry value.
53+
*/
54+
uint32_t stub_target_mmu_read_entry(uint32_t entry_id);
55+
56+
/**
57+
* @brief Set a single MMU entry to invalid.
58+
*
59+
* @param entry_id MMU entry index.
60+
*/
61+
void stub_target_mmu_set_entry_invalid(uint32_t entry_id);
62+
63+
/**
64+
* @brief Restore a single MMU entry from a previously saved raw value.
65+
*
66+
* Unlike write_entry, this writes the raw value as-is (no valid bit added).
67+
*
68+
* @param entry_id MMU entry index.
69+
* @param raw_value Raw value previously returned by stub_target_mmu_read_entry().
70+
*/
71+
void stub_target_mmu_restore_entry(uint32_t entry_id, uint32_t raw_value);

src/target/common/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
set(common_srcs
22
src/aes_xts.c
33
src/cache.c
4+
src/mmu.c
45
src/flash.c
56
src/flash_4byte.c
67
src/mem_utils.c

src/target/common/src/mmu.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0 OR MIT
5+
*/
6+
7+
#include <stdint.h>
8+
9+
#include <target/mmu.h>
10+
11+
uint32_t __attribute__((weak)) stub_target_mmu_get_drom_vaddr(void)
12+
{
13+
return 0;
14+
}
15+
16+
uint32_t __attribute__((weak)) stub_target_mmu_get_drom_entry_start(void)
17+
{
18+
return 0;
19+
}
20+
21+
uint32_t __attribute__((weak)) stub_target_mmu_get_drom_entry_end(void)
22+
{
23+
return 0;
24+
}
25+
26+
void __attribute__((weak)) stub_target_mmu_write_entry(uint32_t entry_id, uint32_t flash_page_num)
27+
{
28+
(void)entry_id;
29+
(void)flash_page_num;
30+
}
31+
32+
uint32_t __attribute__((weak)) stub_target_mmu_read_entry(uint32_t entry_id)
33+
{
34+
(void)entry_id;
35+
return 0;
36+
}
37+
38+
void __attribute__((weak)) stub_target_mmu_set_entry_invalid(uint32_t entry_id)
39+
{
40+
(void)entry_id;
41+
}
42+
43+
void __attribute__((weak)) stub_target_mmu_restore_entry(uint32_t entry_id, uint32_t raw_value)
44+
{
45+
(void)entry_id;
46+
(void)raw_value;
47+
}

src/target/esp32/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
set(srcs
22
src/cache.c
3+
src/mmu.c
34
src/flash.c
45
src/sha256.c
56
src/trax.c

src/target/esp32/include/soc/dport_reg.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@
2525

2626
/* CTRL1 bus mask: bits [5:0] — each bit disables one bus when set */
2727
#define DPORT_CACHE_BUS_MASK 0x3fU
28+
#define DPORT_PRO_CACHE_MASK_DROM0 BIT(4)
29+
30+
/* PRO CPU flash MMU table (memory-mapped, 384 entries) */
31+
#define DPORT_PRO_FLASH_MMU_TABLE ((volatile uint32_t *)0x3FF10000)

src/target/esp32/include/soc/ext_mem_defs.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@
88

99
#include <esp-stub-lib/bit_utils.h>
1010

11-
#define SOC_MMU_ENTRY_NUM 384
12-
#define SOC_MMU_INVALID BIT(8)
11+
#define SOC_MMU_ENTRY_NUM 384
12+
#define SOC_MMU_INVALID BIT(8)
13+
14+
#define SOC_MMU_DROM0_PAGES_START 0
15+
#define SOC_MMU_DROM0_PAGES_END 64

0 commit comments

Comments
 (0)