概述
DDR 初始化分为两个主要阶段:
- 硬件初始化阶段(Bootloader/固件)- DRAM PHY 训练和初始化
- 软件初始化阶段
第一阶段:DDR 硬件初始化 (Bootloader)
这是在操作系统启动之前由 Bootloader(如 U-Boot、UEFI)或硬件固件完成的初始化,主要用于物理层训练和配置。
1. 电源按序上电
2. 复位稳定释放
- RESET_n 拉低保持 tINIT1 ≥ 200us
- 目的: 强制 DRAM 进入初始化,清除所有内部寄存器和状态机
3. 时钟稳定启振 + CKE 激活
4. 写入 MR 模式寄存器
5. ZQ 阻抗校准
6. 命令/地址训练(CA 训练)
7. 写入均衡训练(Write Leveling)
- 目的: 使每个颗粒的 DQS 上升沿与 CLK 上升沿精确对齐,满足 tDQSS 时序要求
8. DQ/DBI 数据位训练
9. 进入正常读写工作状态
第二阶段:Linux 内核软件初始化
2.1 启动初始化 (start_kernel)
文件位置: init/main.c
start_kernel() {
// ... 前期初始化 ...
setup_arch(&command_line); // 架构相关设置,包含内存检测
mm_core_init(); // 内存管理核心初始化
// ... 其他初始化 ...
}
2.2 架构相关初始化 (setup_arch)
此阶段主要由各个架构的代码实现,通过 zone_sizes_init() 函数完成:
- 调用
free_area_init() 初始化内存管理数据结构
主要文件:
arch/<arch>/kernel/setup.carch/<arch>/mm/init.c
关键函数 zone_sizes_init():
void __init zone_sizes_init(void) {
unsignedlong max_zone_pfns[MAX_NR_ZONES];
// 设置各 zone 的最大 PFN
max_zone_pfns[ZONE_DMA] = min(MAX_DMA_PFN, max_low_pfn);
max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
// ...
free_area_init(max_zone_pfns); // 核心初始化
}
2.3 内存管理核心初始化 (free_area_init)
文件位置: mm/mm_init.c
这是内存初始化的核心阶段,由 free_area_init() 完成:
free_area_init_node() - 初始化每个 NUMA 节点
memmap_init() - 初始化内存映射
- 遍历所有内存区域,为每个物理页面初始化 struct page
调用关系:
setup_arch()
└─> zone_sizes_init()
└─> free_area_init(max_zone_pfns)
├─> free_area_init_node() // 初始化各节点
└─> memmap_init() // 初始化页面映射
2.4 内存管理完成初始化 (mm_core_init)
文件位置: mm/mm_init.c
在 start_kernel() 中调用,完成内存管理系统的最终初始化:
void __init mm_core_init(void) {
build_all_zonelists(NULL); // 构建 zonelist
page_alloc_init_cpuhp(); // 初始化每CPU页面缓存
page_ext_init_flatmem(); // 初始化页面扩展信息
mem_init(); // 释放 memblock,添加内存到伙伴系统
mem_init_print_info(); // 打印内存信息
kmem_cache_init(); // 初始化 slab 分配器
vmalloc_init(); // 初始化 vmalloc 系统
// ...
}
关键函数 mem_init():
2.5 页面分配器后期初始化
文件位置: mm/page_alloc.c
关键函数:
page_alloc_init_late()- 在
kernel_init_freeable() 中调用,此时调度器已就绪
2.5 核心数据结构
2.5.1 pg_data_t (节点数据)
每个 NUMA 节点对应一个 pg_data_t 结构体,包含:
- node_zones[] - 该节点的内存区域数组
- node_mem_map - 该节点的 page 结构体数组
2.5.2 zone (内存区域)
系统将内存划分为不同的区域:
- ZONE_HIGHMEM - 高端内存(32位系统)
每个 zone 包含:
- zone_pgdat - 指向所属的 pg_data_t
2.5.3 struct page
表示一个物理页面的结构体,包含:
2.6 详细软件初始化流程
2.6.1 早期内存管理 (memblock)
系统启动初期使用 memblock 分配器管理内存:
memblock_add() // 添加内存区域
memblock_reserve() // 预留内存区域
memblock_alloc() // 分配内存
2.6.2 节点初始化 (free_area_init_node)
对每个 NUMA 节点执行:
2.6.3 内存区域初始化 (free_area_init_core)
对每个 zone 执行:
- 计算 zone 的 spanned_pages 和 present_pages
2.6.4 页面结构初始化 (memmap_init)
为每个物理页面初始化 struct page:
void __init memmap_init_zone_range(struct zone *zone, ...) {
for (pfn = start_pfn; pfn < end_pfn; pfn++) {
structpage *page = pfn_to_page(pfn);
__init_single_page(page, pfn, zone_idx, nid);
// 初始化页面标志、引用计数等
}
}
2.6.5 内存初始化完成 (mem_init)
void __init mem_init(void) {
// 释放 memblock 分配器
memblock_free_all();
// 将所有可用页面添加到伙伴系统
// 设置内存管理系统就绪标志
}
2.8 关键函数调用链
start_kernel()
└─> setup_arch()
└─> zone_sizes_init()
└─> free_area_init(max_zone_pfns)
├─> free_area_init_node()
│ ├─> calculate_node_totalpages()
│ ├─> alloc_node_mem_map()
│ └─> free_area_init_core()
│ ├─> zone_init_internals()
│ ├─> setup_usemap()
│ └─> init_currently_empty_zone()
└─> memmap_init()
└─> mm_core_init()
├─> build_all_zonelists()
├─> page_alloc_init_cpuhp()
├─> mem_init()
└─> kmem_cache_init()
└─> kernel_init_freeable()
└─> page_alloc_init_late()
2.9 初始化时机
| | | |
|---|
| setup_arch → zone_sizes_init | | |
| | | |
| | | |
| | | |
| | | |
相关文件
内核软件初始化
- mm/page_alloc.c - 页面分配器实现
- include/linux/mm.h - 内存管理头文件
- include/linux/mmzone.h - 内存区域定义
arch/<arch>/mm/init.c
DDR 控制器驱动
- drivers/memory/emif.c - EMIF DDR 控制器驱动
- drivers/memory/jedec_ddr_data.c - JEDEC DDR 时序参数
总结
DDR 初始化是一个从硬件到软件的完整过程:
- 硬件阶段:Bootloader 完成电源、时钟、复位、PHY 训练等物理层初始化
- 软件阶段:Linux 内核建立内存管理系统,从 memblock 早期分配器开始,逐步初始化节点、区域、页面结构,最终建立完整的伙伴系统
两个阶段紧密配合,才能让系统正常高效地使用 DDR 内存。