STM32 启动地址 + 存储器映射 + RSA 线索:从「为啥不是 0x00」到工程落地
视频引子:STM32 程序为啥从
0x08000000开始跑?CPU 上电却永远从0x00000000取第一条指令——中间靠 BOOT 引脚 + 地址别名(Alias) 搭桥。下面把这条线、相关概念、RSA 数论背景、指标、生产指导、最佳实践、ASCII 心智模型一次性串起来。
0. 一句话心智模型(先看这个)
把 Cortex-M 想象成只认「0 号门牌」取信的邮差,STM32 是把不同仓库在复位瞬间挂到 0 号门牌下的物业:
- CPU 永远敲
0x00000000的门 → 问「栈顶在哪」「复位函数在哪」 - 物业(芯片硬件)看 BOOT0/BOOT1 开关:
- 拨到 Flash →
0x00000000实际连到0x08000000 - 拨到 System Memory → 连到
0x1FFF0000(出厂 ISP BootLoader) - 拨到 SRAM → 连到
0x20000000
- 拨到 Flash →
- 所以「程序从 0x08000000 开始」= Flash 物理基地址;「CPU 从 0x00 开始」= 内核固定取指口,二者不矛盾。
1. 由浅入深:场景串讲
场景 A:上电那一瞬间发生了什么
- 板子通电 / NRST 拉低释放 / 看门狗复位
- 硬件采样 BOOT0、BOOT1(部分系列 BOOT1 复用 PB2)
- 内核硬线动作(无需任何代码):
- 读
0x00000000→ 载入 MSP(主栈指针) - 读
0x00000004→ 载入 PC = Reset_Handler 地址
- 读
- 进入
Reset_Handler(startup_xx.s)→SystemInit()→ 拷.data、清.bss→__main→main()
场景 B:为啥不直接把 Flash 放在 0x00
- Cortex-M 内核是 ARM 通用设计,不绑定 STM32
- 有的芯片 Flash 在 0x00,有的在 0x08000000,有的 SRAM 启动
- ARM 定死:「我复位只认 0x00 向量表」,具体 0x00 背后挂谁是厂商自由
- STM32 选了 Flash 物理基址
0x08000000,再用别名把 0x00 影射过去 → 内核设计统一,厂商灵活
场景 C:你天天写的地址都住哪
0x0000_0000 ── alias(复位时动态挂 Flash/SRAM/BootROM)
0x0800_0000 ── Flash:代码 .text / 向量表 / const / 掉电不丢
0x2000_0000 ── SRAM:.data .bss 栈 堆 全局变量
0x4000_0000 ── 外设:GPIO/USART/TIM/ADC 寄存器(写地址=控硬件)
0xE000_0000 ── 内核外设:NVIC / SysTick / MPU / 调试
操作
GPIOA->ODR = 1本质就是写0x4001080C这类地址。
场景 D:视频里顺带提的 RSA 是啥线
视频标题带 #rsa算法,正文没展开。RSA 不是 STM32 启动的一部分,但同属「地址/数论抽象 + 硬软边界」思维:
- STM32 启动:地址 0x00 是抽象入口,物理介质靠映射切换
- RSA:公钥
(n,e)是抽象锁,私钥d靠「大整数分解难」这个硬边界保安全 - 两者都体现嵌入式/安全工程一句老话:入口可以统一,背后实现可以替换,信任边界要算清楚
2. 核心知识点 + 坑(MD 清单)
2.1 知识点
- 4GB 统一寻址:Cortex-M 把 Flash/SRAM/外设/内核全塞进 0~0xFFFFFFFF 平面地址,不分段。
- 别名区(Alias):
0x00000000不是真存储,是总线开关,复位时按 BOOT 映射到某物理区。 - 向量表前 8 字节:
0x00=初始 MSP,0x04=Reset_Handler;之后 NMI/HardFault/SysTick/IRQ… - BOOT 引脚锁存:电平在复位上升沿采样,运行中改 BOOT 无效,必须复位。
- Flash 物理基址 0x08000000:链接脚本
FLASH_ORIGIN一般设这;程序计数器里的函数地址也是 0x0800xxxx。 - SRAM 基址 0x20000000:栈从高地址往下长,堆往上长,撞车即溢出。
- 外设基址 0x40000000:CMSIS 头文件里
GPIOA_BASE等就是这。 - RSA 三要素:选大素数 p,q → n=pq,φ=(p-1)(q-1),e·d≡1 mod φ;加解密靠欧拉定理
m^(ed)≡m mod n。
2.2 坑(高频)
- ❌ 以为「CPU 从 0x00 取指 = Flash 必须在 0x00 物理烧」→ 实际是别名,Flash 烧在 0x08000000。
- ❌ BOOT0 悬空 → 电平漂,偶尔进 System Memory 变「砖(看起来不跑用户程序)」→ 必须上拉/下拉。
- ❌ 改了 Flash 偏移做 BootLoader(APP 放 0x08004000)却忘改
VTOR→ 中断全飞,HardFault。 - ❌ SRAM 启动调试时,向量表还在 Flash 链接地址 → 不重定位 VTOR 到 SRAM 就开中断,必崩。
- ❌ 栈顶
_estack没 4 字节对齐或超出 SRAM 实大小 → 复位即 HardFault。 - ❌ RSA 用在 STM32 侧还用 512bit/1024bit → 现在 2048bit 起跳;且裸 MCU 做 RSA 慢,要用 CRT/蒙哥马利/硬件 RSA 或转 ECC。
- ❌ 把 RSA 当对称加密用、自己造协议 → 不做填充(PKCS#1 v1.5/OAEP)会被明文攻击。
3. 涉及的算法:RSA 怎么来的(数论脉络)
- 欧几里得算法(求 gcd、扩展欧几里得求逆元)→ 算 d = e⁻¹ mod φ(n)
- 欧拉函数 φ(n):n=pq 时 φ=(p-1)(q-1)
- 欧拉定理:gcd(m,n)=1 则 m^φ(n) ≡ 1 (mod n)
- 推导:ed = 1 + kφ → m^(ed) = m·(mφ)k ≡ m (mod n)
- 安全来源:n 公开,想算 d 得先知道 φ(n),想知 φ(n) 得分解 n=pq;大整数分解(GNFS)目前无多项式时间经典算法。
- 历史:1976 Diffie-Hellman 提公钥思想;1977 Rivest-Shamir-Adleman 发表 RSA;1973 年 Clifford Cocks 在 GCHQ 已秘密做出,1997 才解密。
和 STM32 的关系:MCU 侧 RSA 多用于固件签名验签、安全启动、TLS 握手辅助,不是拿 MCU 跑大数据加密。
4. 关键指标意义
| 指标 | 意义 |
|---|---|
0x08000000 |
Flash 物理基址,决定链接脚本 ORIGIN |
0x00000000 |
内核复位取指口(逻辑地址),不等于物理 0 |
| BOOT0/BOOT1 | 启动介质选择开关,决定别名挂哪 |
| MSP 初值 | 栈顶,决定函数调用/中断现场会不会越界 |
| VTOR | 向量表偏移寄存器,BootLoader 跳 APP 必须重设 |
| RSA 模长 n | 2048bit 现行底线,4096bit 更长寿命;决定抗分解强度 |
| e=65537 | 常用公钥指数,平衡速度与攻防 |
| Flash 等待周期 | 主频 >24/48MHz 要配 LATENCY,否则取指错乱 |
5. 如何指导生产
- 量产板 BOOT0 必须下拉(10k 到 GND),SWD 烧录完即用户 Flash 启动,避免返修机误进 ISP。
- BootLoader + APP 分区:BootLoader 占
0x08000000~0x08003FFF,APP 从0x08004000起;APP 工程改 FLASH_ORIGIN 和 VTOR。 - 安全启动:BootLoader 验 APP 的 RSA/ECDSA 签名,公钥哈希锁芯片 OTP,过不了不跳转。
- 待机唤醒:从 Standby 醒会重新采样 BOOT,BOOT 电路不能靠程序运行时拉高,硬件要定死。
- 产线烧录:ST-LINK 走 SWD 直写 0x08000000;空片救砖用串口 ISP(BOOT0=1 进 System Memory)。
- RSA 密钥:私钥不出厂,公钥或公钥哈希进 OTP/Flash 只读页;固件包带签名,BootLoader 验签。
6. 关联功能
- 中断系统(NVIC)← 向量表位置
- 看门狗/IWDG ← 复位源之一
- SWD/JTAG ← 调试器能改 PC 直接绕开 0x00 启动
- DFU/USART ISP ← System Memory 启动
- RTOS ← 进 main 前 MSP 设置、VTOR 重映射
- 固件升级 OTA ← BootLoader 跳转 + 签名验签(RSA/ECC)
- TLS/MQTTs ← 握手阶段 RSA/ECDHE,MCU 侧常卸载到软件库(mbedTLS)
7. 经典问答(Q&A)
Q1:程序烧在 0x08000000,为啥 CPU 从 0x00 开始?
A:0x00 是内核固定取指逻辑地址;Flash 启动时硬件把 0x00 别名到 0x08000000,CPU 无感。
Q2:能不能把 Flash 链接到 0x00?
A:不能,STM32 Flash 物理基址固定 0x08000000;你可以让 CPU 看到的向量表在 0x00,但二进制烧写位置仍是 0x08 起。
Q3:BOOT0=1 就一定是 ISP 吗?
A:F1/F4 上 BOOT0=1,BOOT1=0 → System Memory(ISP BootLoader);BOOT0=1,BOOT1=1 → SRAM 启动。要看型号参考手册。
Q4:Reset_Handler 之前是谁跑的?
A:纯硬件。复位释放后内核自动读 MSP/PC,没有 C 环境、没有栈变量可用。
Q5:RSA 在 STM32 上跑得动吗?
A:2048bit 验签在 F4/F7/H7 上软件 mbedTLS 几十~几百 ms;加密/签名更慢。大量通信用 ECDSA/ECDHE,或外接 SE(安全元件)。
Q6:为啥向量表第 0 项是栈顶不是指令?
A:Cortex-M 复位不取指令,先取「栈顶」再取「复位函数地址」,这是和老 ARM9 最大区别。
8. 最佳工程实践(反例 vs 正确)
启动与 BOOT
- ❌ BOOT0 悬空,靠内部弱上拉
- ✅ BOOT0 10k 下拉到 GND;需 ISP 的机型用 0Ω+跳帽或 MOS 控制
- ❌ 链接脚本 APP 改 ORIGIN 但 startup.s 里向量表仍默认,VTOR 不挪
- ✅ APP 里
SCB->VTOR = FLASH_BASE + APP_OFFSET;在 Reset_Handler 早期设
栈与内存
- ❌
_estack = 0x20005000抄例程,实际芯片只有 20KB SRAM - ✅ 按 datasheet SRAM 大小算栈顶,留 Heap/栈碰撞余量,开编译时栈使用分析
BootLoader 跳转
- ❌ 直接
((void(*)())app_reset)()不关中断不清外设 - ✅ 关全局中断 → 复位/DeInit 外设 → 设 MSP → 设 VTOR → 跳 Reset_Handler
RSA 使用
- ❌ 自己写
c = m^e mod n朴素幂、无填充、密钥 1024bit - ✅ 用 mbedTLS/libtomcrypt;2048bit+;OAEP 填充;私钥不出厂;BootLoader 只验签不解密大块数据
9. ASCII 心智模型(集成版)
9.1 启动别名开关
Cortex-M CPU
|
固定敲门 0x0000_0000
|
+-------+--------+ BOOT0/BOOT1 采样
| |
Flash启动 SystemMem启动 SRAM启动
0x00 -> 0x08000000 0x00->0x1FFF0000 0x00->0x20000000
| | |
.text 出厂ISP 调试用(需VTOR重设)
Reset_Handler 串口DFU Reset_Handler在SRAM
注意:0x00 这栋「门面房」不存东西,只是把客人领到后面真仓库。
9.2 4GB 地图(简化)
0xFFFFFFFF +-------------------------+
| 内核外设 NVIC/SysTick | 0xE0000000
0x60000000 +-------------------------+
| 外部存储器 FMC/FSMC |
0x40000000 +-------------------------+ 外设寄存器 GPIO/UART/TIM
| SRAM (变量/栈/堆) | 0x20000000
0x08000000 +-------------------------+ Flash (代码/向量表)
| 0x00000000 别名区 |
0x00000000 +-------------------------+
9.3 RSA 信任链(和启动映射对照)
明文 m --e--> 密文 c = m^e mod n (公钥开锁,谁都能做)
密文 c --d--> 明文 m = c^d mod n (私钥解锁,只有持有者)
|
n = p*q 公开
φ(n) 隐藏 <- 分解 n 极难 <- 安全边界
类比:0x00 入口公开,背后挂 Flash 还是 BootROM 由 BOOT 隐藏控制
10. 数据来源
- STM32 启动/BOOT 别名/向量表:CSDN 启动解析、EmbedFire F4 手册章节、微信嵌入式边角料(BOOT 采样与 F1/F4 映射表)
- Cortex-M 复位序列 MSP/PC 自动加载:CSDN 单片机启动流程、ARM Cortex-M 启动问答
- 4GB 存储器映射 0x08/0x20/0x40:CSDN STM32 内存划分、DeepWiki F1xx 总线架构
- RSA 数论来源(欧几里得/欧拉/分解难度):Bristol 数学系 RSA 讲义、南京大学 RSA 前世今生、Rivest 1978 原论文综述
收口:视频那句「STM32 为啥不从 00 开始」的答案是——Flash 物理基址是 0x08000000,但 Cortex-M 内核复位只认 0x00000000,STM32 用 BOOT 引脚控制的地址别名把 0x00 动态挂到 0x08(或 0x1FFF/0x2000)上,既守住了内核统一入口,又给了厂商启动灵活性。RSA 那条线独立看,是同一类「统一接口 + 背后硬边界」的工程哲学:入口可以人人访问,信任靠数学/硬件边界守住。

浙公网安备 33010602011771号