文章目录
-
- 完整的内存映射层次
-
- [1. 第一层:硬件固定映射(固定不可变)](#1. 第一层:硬件固定映射(固定不可变))
- [2. 第二层:启动时的初始映射(由启动代码配置)](#2. 第二层:启动时的初始映射(由启动代码配置))
- [3. 第三层:MMU虚拟地址映射(软件配置)](#3. 第三层:MMU虚拟地址映射(软件配置))
- 详细流程分析
- 实际启动过程分析
- 为什么需要这样设计?
-
- [1. 灵活性](#1. 灵活性)
- [2. 内存保护](#2. 内存保护)
- [3. 内存别名](#3. 内存别名)
- 总结:三层映射关系
SRAM和DDR的映射也有MMU完成吗?
答案是:不完全是 ,MMU主要负责的是虚拟地址到物理地址的映射,而SRAM和DDR的物理地址分配是由更底层的硬件机制决定的。
完整的内存映射层次
1. 第一层:硬件固定映射(固定不可变)
这是物理世界的连接方式:
c
/* ARM64的物理地址空间布局(硬件设计时决定) */
#define PHYS_SRAM_BASE 0x00000000 // 物理SRAM起始
#define PHYS_SRAM_SIZE 0x04000000 // 64MB
#define PHYS_DDR_BASE 0x40000000 // 物理DDR起始
#define PHYS_DDR_SIZE 0xC0000000 // 3GB
#define PHYS_PERIPH_BASE 0x80000000 // 外设起始
// 这个映射是硬件连接时就确定的:
// SRAM芯片的片选信号 → 连接到地址线A[31:0]=0x0
// DDR芯片的片选信号 → 连接到地址线A[31:0]=0x40000000
2. 第二层:启动时的初始映射(由启动代码配置)
在MMU启用前,CPU通过物理地址直接访问内存:
assembly
/* 启动阶段的物理地址访问 */
_start:
// 1. 复位后CPU从物理地址0x0开始执行(就是SRAM)
ldr r0, =0x0 // 物理地址0x0 -> 访问SRAM
ldr r1, [r0] // 从SRAM读取
// 2. 初始化DDR控制器后,可以访问DDR
ldr r0, =0x40000000 // 物理地址0x40000000 -> 访问DDR
ldr r1, [r0] // 从DDR读取
// 3. 访问外设
ldr r0, =0x80000000 // 物理地址0x80000000 -> 访问GPIO
str r1, [r0] // 写入GPIO
3. 第三层:MMU虚拟地址映射(软件配置)
MMU启用后,虚拟地址 → 物理地址:
c
/* MMU页表配置示例 */
// 虚拟地址空间布局(软件决定)
#define VA_SRAM_BASE 0xFFFF000000000000 // 高端虚拟地址
#define VA_DDR_BASE 0xFFFF000040000000
#define VA_PERIPH_BASE 0xFFFF000080000000
// 建立映射关系(在页表中)
void setup_mmu_mappings(void) {
// 虚拟地址 → 物理地址 映射
// VA_SRAM_BASE → PHYS_SRAM_BASE
// VA_DDR_BASE → PHYS_DDR_BASE
// VA_PERIPH_BASE → PHYS_PERIPH_BASE
// 也可以有多个虚拟地址映射到同一个物理地址
// 0x00000000 (虚拟) → PHYS_SRAM_BASE (物理)
// 0x20000000 (虚拟) → PHYS_SRAM_BASE (物理) ← 别名映射
}
详细流程分析
阶段1:复位后的硬件固定映射
assembly
CPU视图 硬件连接
┌─────────┐ ┌─────────┐
│ 0x0 │ ←───────────┤ SRAM │
│ │ 片选信号 │ 芯片 │
│0x40000000│ ←───────────┤ DDR │
│ │ 片选信号 │ 控制器 │
│0x80000000│ ←───────────┤ 外设 │
│ │ 片选信号 │ 总线 │
└─────────┘ └─────────┘
物理地址空间 物理设备
这个映射由芯片的Memory Controller(内存控制器) 完成,通过芯片的引脚连接 和内部地址解码逻辑实现。
阶段2:MMU启用后的虚拟映射
c
/*
MMU映射关系:
┌─────────────────┐ ┌─────────────────┐
│ 虚拟地址空间 │ │ 物理地址空间 │
│ │ │ │
│ 0x0000_0000 │ ──────→│ 0x0000_0000 │
│ (用户空间) │ │ (SRAM) │
│ │ │ │
│ 0x4000_0000 │ ──────→│ 0x4000_0000 │
│ (用户空间) │ │ (DDR) │
│ │ │ │
│ 0xFFFF_8000_0000│ ──────→│ 0x0000_0000 │
│ (内核空间) │ │ (SRAM) │
│ │ │ │
│ 0xFFFF_C000_0000│ ──────→│ 0x4000_0000 │
│ (内核空间) │ │ (DDR) │
└─────────────────┘ └─────────────────┘
*/
实际启动过程分析
步骤1:芯片复位(物理地址访问)
c
/*
复位向量表位于物理地址0x0(SRAM)
┌─────────────────┐
│ 0x0: reset_vector │
│ 0x8: undef_vector │
│ 0x10: svc_vector │
│ ... │
└─────────────────┘
CPU直接通过物理地址访问SRAM
*/
步骤2:初始化DDR(仍然使用物理地址)
assembly
init_ddr:
// 配置DDR控制器(通过外设总线)
ldr x0, =0x80000000 // 外设基址(物理)
bl ddr_init
// 测试DDR访问
ldr x0, =0x40000000 // DDR物理地址
ldr x1, =0x12345678
str x1, [x0] // 写入DDR
ldr x2, [x0] // 读取验证
步骤3:配置MMU(建立虚拟映射)
c
void setup_virtual_memory(void) {
// 创建页表
uint64_t *ttbr0 = (uint64_t *)PHYS_SRAM_BASE + 0x1000;
// 映射SRAM: 虚拟地址0x0和0xFFFF000000000000都映射到物理0x0
map_page(ttbr0, 0x0, PHYS_SRAM_BASE, 64*1024*1024);
map_page(ttbr0, 0xFFFF000000000000, PHYS_SRAM_BASE, 64*1024*1024);
// 映射DDR: 虚拟地址0x40000000和0xFFFF000040000000映射到物理0x40000000
map_page(ttbr0, 0x40000000, PHYS_DDR_BASE, 1*1024*1024*1024);
map_page(ttbr0, 0xFFFF000040000000, PHYS_DDR_BASE, 1*1024*1024*1024);
// 启用MMU
set_ttbr0(ttbr0);
enable_mmu();
}
步骤4:MMU启用后的访问
c
// MMU启用后,所有地址都经过转换
uint32_t read_sram(void) {
// 这两个虚拟地址都映射到同一个物理SRAM
uint32_t *addr1 = (uint32_t *)0x0; // 用户空间虚拟地址
uint32_t *addr2 = (uint32_t *)0xFFFF000000000000; // 内核空间虚拟地址
// 实际访问的是物理SRAM的同一个位置
return *addr1; // == *addr2
}
为什么需要这样设计?
1. 灵活性
c
// 可以通过MMU重新映射
void remap_for_security(void) {
// 将SRAM映射到随机地址,增加安全性
uint64_t random_base = get_random_address();
map_page(ttbr0, random_base, PHYS_SRAM_BASE, SRAM_SIZE);
// 旧的映射可以删除
unmap_page(ttbr0, 0x0);
}
2. 内存保护
c
// 设置不同的访问权限
void setup_memory_protection(void) {
// SRAM: 可执行、可读写(用于代码)
map_page_attr(VA_SRAM, PA_SRAM, SIZE_64MB,
AP_RW | XN_DISABLE);
// DDR数据区: 可读写,不可执行(防代码注入)
map_page_attr(VA_DDR_DATA, PA_DDR_DATA, SIZE_1GB,
AP_RW | XN_ENABLE);
// 外设: 只允许特权模式访问
map_page_attr(VA_PERIPH, PA_PERIPH, SIZE_256MB,
AP_PRIV_RW | USER_NO_ACCESS);
}
3. 内存别名
c
// 同一个物理内存可以映射到多个虚拟地址
void setup_aliases(void) {
// 方式1:写时复制(Copy-on-Write)
map_page(ttbr0, VA_PROCESS1, PA_SHARED, SIZE_4KB, RO); // 只读
map_page(ttbr0, VA_PROCESS2, PA_SHARED, SIZE_4KB, RO); // 只读
// 方式2:DMA缓冲区的不同视图
map_page(ttbr0, VA_CPU, PA_DMA_BUF, SIZE_4KB, CACHE_WB); // 缓存使能
map_page(ttbr0, VA_DEVICE, PA_DMA_BUF, SIZE_4KB, DEVICE_MEM); // 非缓存
}
总结:三层映射关系
关键点:
- SRAM在0x0,DDR在0x40000000是硬件设计:由芯片引脚和内存控制器固定
- MMU不决定这个物理布局:只负责虚拟到物理的映射
- 启动初期没有MMU:CPU直接使用物理地址
- MMU启用后:可以重新映射这些物理区域到任意的虚拟地址