目录
21天 Intel CPU BIOS 学习专栏
第6天:内存管理
内存管理
今天我们将深入探讨内存管理在BIOS中的重要性,以及如何在BIOS初始化过程中正确地初始化和管理内存。内存管理是BIOS启动过程中的关键步骤之一,它确保内存资源在操作系统启动前被正确配置和分配。通过今天的课程,你将了解内存管理的基本概念、主要步骤以及如何编写和测试内存管理模块。
1. 内存管理概述
内存管理是指在计算机启动时,BIOS对内存进行的一系列配置和设置,以确保内存资源能够被操作系统正确使用。这些配置包括初始化内存控制器、配置内存分段和分页机制等。
2. 初始化内存控制器
内存控制器是负责管理内存访问的硬件组件。在初始化过程中,我们需要配置内存控制器,使其能够正确地管理和访问物理内存。
主要步骤
- 初始化内存控制器寄存器:设置内存控制器的各种寄存器,以配置内存的大小、速度和模式。
- 检测内存大小:通过读取内存控制器的状态寄存器,检测系统中可用的物理内存大小。
- 配置内存时序:设置内存时序参数,以优化内存访问性能。
示例代码
c
深色版本
VOID InitializeMemoryController() {
// 初始化内存控制器寄存器
UINT32 mem_ctrl_reg = 0x12345678; // 示例值,实际值取决于具体硬件
WriteMemoryControllerRegister(MEM_CTRL_REG_ADDR, mem_ctrl_reg);
// 检测内存大小
UINT32 total_memory = DetectTotalMemory();
Print(L"Total Memory: %d MB\n", total_memory / (1024 * 1024));
// 配置内存时序
ConfigureMemoryTiming();
}
VOID WriteMemoryControllerRegister(UINT32 address, UINT32 value) {
// 写入内存控制器寄存器
// 示例代码,实际实现取决于具体硬件
*(volatile UINT32*)address = value;
}
UINT32 DetectTotalMemory() {
// 检测总内存大小
// 示例代码,实际实现取决于具体硬件
return 4 * 1024 * 1024 * 1024; // 返回4GB内存
}
VOID ConfigureMemoryTiming() {
// 配置内存时序参数
// 示例代码,实际实现取决于具体硬件
UINT32 timing_reg = 0x87654321; // 示例值,实际值取决于具体硬件
WriteMemoryControllerRegister(TIMING_REG_ADDR, timing_reg);
}
3. 内存分段
内存分段是一种内存管理机制,它将内存划分为多个段,每个段有自己的基地址和长度。在实模式下,内存分段是必须的。
主要步骤
- 设置段寄存器:设置段寄存器(CS, DS, ES, SS, FS, GS)以指向相应的段。
- 配置段描述符表:创建和配置全局描述符表(GDT)或局部描述符表(LDT),以描述各个段的属性。
示例代码
c
深色版本
VOID InitializeMemorySegmentation() {
// 设置段寄存器
AsmWriteCs(0x10); // 设置代码段寄存器
AsmWriteDs(0x10); // 设置数据段寄存器
AsmWriteEs(0x10); // 设置附加段寄存器
AsmWriteSs(0x10); // 设置堆栈段寄存器
// 配置段描述符表
InitializeGdt();
}
VOID InitializeGdt() {
// 初始化全局描述符表
GDT_ENTRY gdt[5]; // 5个段描述符
// 空描述符
memset(&gdt[0], 0, sizeof(GDT_ENTRY));
// 代码段描述符
gdt[1].LimitLow = 0xFFFF;
gdt[1].BaseLow = 0x0000;
gdt[1].BaseMid = 0x00;
gdt[1].Access = 0x9A; // 可执行,只读,特权级0
gdt[1].Flags = 0xCF; // 4KB粒度,长模式
// 数据段描述符
gdt[2].LimitLow = 0xFFFF;
gdt[2].BaseLow = 0x0000;
gdt[2].BaseMid = 0x00;
gdt[2].Access = 0x92; // 可读写,特权级0
gdt[2].Flags = 0xCF; // 4KB粒度,长模式
// 加载GDT
LoadGdt((UINT32)&gdt, sizeof(gdt));
}
VOID LoadGdt(UINT32 gdt_base, UINT16 gdt_limit) {
// 加载全局描述符表
GDT_POINTER gdt_ptr;
gdt_ptr.Limit = gdt_limit - 1;
gdt_ptr.Base = gdt_base;
__asm__ __volatile__(
"lgdt %0"
: : "m"(gdt_ptr)
);
// 更新段寄存器
__asm__ __volatile__(
"ljmp $0x08, $1f\n"
"1:\n"
"mov %ax, %ds\n"
"mov %ax, %es\n"
"mov %ax, %fs\n"
"mov %ax, %gs\n"
"mov %ax, %ss\n"
: : "a"(0x10)
);
}
4. 内存分页
内存分页是一种内存管理机制,它将虚拟地址映射到物理地址,支持大容量内存管理和内存保护。在保护模式下,内存分页是必须的。
主要步骤
- 设置页目录和页表:创建页目录和页表,用于存储虚拟地址到物理地址的映射关系。
- 启用分页:设置控制寄存器CR0,启用分页机制。
示例代码
c
深色版本
VOID InitializeMemoryPaging() {
// 设置页目录和页表
UINT32 page_directory[1024] = {0};
UINT32 page_table[1024] = {0};
// 设置页目录项
page_directory[0] = (UINT32)page_table | 0x003; // 页表基址 | 存储权限
// 设置页表项
for (int i = 0; i < 1024; i++) {
page_table[i] = (i * 4096) | 0x003; // 物理地址 | 存储权限
}
// 设置页目录基址寄存器CR3
AsmWriteCr3((UINT32)page_directory);
// 启用分页
UINT32 cr0 = AsmReadCr0();
cr0 |= 0x80000000; // 设置PG位,启用分页
AsmWriteCr0(cr0);
}
5. 编写内存管理模块
现在,我们将把这些初始化步骤整合到一个完整的BIOS模块中。
-
创建项目目录:
- 在
edk2目录下创建一个新的目录,例如MemoryManagementModule。 - 在
MemoryManagementModule目录下创建MemoryManagementModule.inf文件,内容如下: ini深色版本
[Defines] INF_VERSION = 0x00010005 BASE_NAME = MemoryManagementModule FILE_GUID = 12345678-1234-1234-1234-1234567890AB MODULE_TYPE = UEFI_DRIVER VERSION_STRING = 1.0 UNIVERSAL = TRUE PROCESSOR = IA32 X64 [Sources] MemoryManagementModule.c [Packages] MdePkg/MdePkg.dec MdeModulePkg/MdeModulePkg.dec [LibraryClasses] UefiLib UefiDriverEntryPoint [Protocols] gEfiShellProtocolGuid [FeaturePcd] gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8000000F
- 在
-
编写模块代码:
- 在
MemoryManagementModule目录下创建MemoryManagementModule.c文件,内容如下: c深色版本
#include <Uefi.h> #include <Library/BaseLib.h> #include <Library/UefiLib.h> #include <Library/UefiBootServicesTableLib.h> VOID InitializeMemoryController() { // 初始化内存控制器寄存器 UINT32 mem_ctrl_reg = 0x12345678; // 示例值,实际值取决于具体硬件 WriteMemoryControllerRegister(MEM_CTRL_REG_ADDR, mem_ctrl_reg); // 检测内存大小 UINT32 total_memory = DetectTotalMemory(); Print(L"Total Memory: %d MB\n", total_memory / (1024 * 1024)); // 配置内存时序 ConfigureMemoryTiming(); } VOID WriteMemoryControllerRegister(UINT32 address, UINT32 value) { // 写入内存控制器寄存器 // 示例代码,实际实现取决于具体硬件 *(volatile UINT32*)address = value; } UINT32 DetectTotalMemory() { // 检测总内存大小 // 示例代码,实际实现取决于具体硬件 return 4 * 1024 * 1024 * 1024; // 返回4GB内存 } VOID ConfigureMemoryTiming() { // 配置内存时序参数 // 示例代码,实际实现取决于具体硬件 UINT32 timing_reg = 0x87654321; // 示例值,实际值取决于具体硬件 WriteMemoryControllerRegister(TIMING_REG_ADDR, timing_reg); } VOID InitializeMemorySegmentation() { // 设置段寄存器 AsmWriteCs(0x10); // 设置代码段寄存器 AsmWriteDs(0x10); // 设置数据段寄存器 AsmWriteEs(0x10); // 设置附加段寄存器 AsmWriteSs(0x10); // 设置堆栈段寄存器 // 配置段描述符表 InitializeGdt(); } VOID InitializeGdt() { // 初始化全局描述符表 GDT_ENTRY gdt[5]; // 5个段描述符 // 空描述符 memset(&gdt[0], 0, sizeof(GDT_ENTRY)); // 代码段描述符 gdt[1].LimitLow = 0xFFFF; gdt[1].BaseLow = 0x0000; gdt[1].BaseMid = 0x00; gdt[1].Access = 0x9A; // 可执行,只读,特权级0 gdt[1].Flags = 0xCF; // 4KB粒度,长模式 // 数据段描述符 gdt[2].LimitLow = 0xFFFF; gdt[2].BaseLow = 0x0000; gdt[2].BaseMid = 0x00; gdt[2].Access = 0x92; // 可读写,特权级0 gdt[2].Flags = 0xCF; // 4KB粒度,长模式 // 加载GDT LoadGdt((UINT32)&gdt, sizeof(gdt)); } VOID LoadGdt(UINT32 gdt_base, UINT16 gdt_limit) { // 加载全局描述符表 GDT_POINTER gdt_ptr; gdt_ptr.Limit = gdt_limit - 1; gdt_ptr.Base = gdt_base; __asm__ __volatile__( "lgdt %0" : : "m"(gdt_ptr) ); // 更新段寄存器 __asm__ __volatile__( "ljmp $0x08, $1f\n" "1:\n" "mov %ax, %ds\n" "mov %ax, %es\n" "mov %ax, %fs\n" "mov %ax, %gs\n" "mov %ax, %ss\n" : : "a"(0x10) ); } VOID InitializeMemoryPaging() { // 设置页目录和页表 UINT32 page_directory[1024] = {0}; UINT32 page_table[1024] = {0}; // 设置页目录项 page_directory[0] = (UINT32)page_table | 0x003; // 页表基址 | 存储权限 // 设置页表项 for (int i = 0; i < 1024; i++) { page_table[i] = (i * 4096) | 0x003; // 物理地址 | 存储权限 } // 设置页目录基址寄存器CR3 AsmWriteCr3((UINT32)page_directory); // 启用分页 UINT32 cr0 = AsmReadCr0(); cr0 |= 0x80000000; // 设置PG位,启用分页 AsmWriteCr0(cr0); } VOID MyInitializationFunction() { InitializeMemoryController(); InitializeMemorySegmentation(); InitializeMemoryPaging(); Print(L"Memory Management Initialization Complete\n"); } EFI_STATUS EFIAPI UefiMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { MyInitializationFunction(); return EFI_SUCCESS; }
- 在
-
编译模块:
- 打开命令提示符或终端。
- 导航到
edk2目录。 - 运行以下命令编译模块: sh
深色版本
build -p MemoryManagementModule/MemoryManagementModule.inf
-
运行模块:
- 编译成功后,你可以在
Build目录下找到生成的.efi文件。例如:深色版本
Build\MemoryManagementModule\DEBUG_VS2019x86\X64\MemoryManagementModule.efi - 使用QEMU或其他虚拟机工具运行生成的模块,验证其功能。例如,使用QEMU: sh
深色版本
qemu-system-x86_64 -bios path\to\OVMF.fd -m 2G -hda path\to\mydisk.img -drive file=path\to\Build\MemoryManagementModule\DEBUG_VS2019x86\X64\MemoryManagementModule.efi,format=raw
- 编译成功后,你可以在
作业
-
阅读材料:
- 阅读Intel的官方文档,了解内存控制器和内存管理的详细信息。
- 阅读EDK II的官方文档,了解模块开发的最佳实践。
-
实践任务:
- 编写一个更复杂的内存管理模块,例如支持动态内存分配和释放。
- 编译并测试该模块,记录下遇到的问题和解决方法。
明天的任务
明天我们将继续深入探讨中断处理,了解如何在BIOS中设置中断向量表、编写中断处理程序。准备好迎接新的挑战吧!
395

被折叠的 条评论
为什么被折叠?



