21天 Intel CPU BIOS 学习专栏第六天(6)--内存管理

目录

21天 Intel CPU BIOS 学习专栏

第6天:内存管理

内存管理

1. 内存管理概述

2. 初始化内存控制器

主要步骤

示例代码

3. 内存分段

主要步骤

示例代码

4. 内存分页

主要步骤

示例代码

5. 编写内存管理模块

作业

明天的任务


21天 Intel CPU BIOS 学习专栏

第6天:内存管理

内存管理

今天我们将深入探讨内存管理在BIOS中的重要性,以及如何在BIOS初始化过程中正确地初始化和管理内存。内存管理是BIOS启动过程中的关键步骤之一,它确保内存资源在操作系统启动前被正确配置和分配。通过今天的课程,你将了解内存管理的基本概念、主要步骤以及如何编写和测试内存管理模块。

1. 内存管理概述

内存管理是指在计算机启动时,BIOS对内存进行的一系列配置和设置,以确保内存资源能够被操作系统正确使用。这些配置包括初始化内存控制器、配置内存分段和分页机制等。

2. 初始化内存控制器

内存控制器是负责管理内存访问的硬件组件。在初始化过程中,我们需要配置内存控制器,使其能够正确地管理和访问物理内存。

主要步骤
  1. 初始化内存控制器寄存器:设置内存控制器的各种寄存器,以配置内存的大小、速度和模式。
  2. 检测内存大小:通过读取内存控制器的状态寄存器,检测系统中可用的物理内存大小。
  3. 配置内存时序:设置内存时序参数,以优化内存访问性能。
示例代码
 

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. 内存分段

内存分段是一种内存管理机制,它将内存划分为多个段,每个段有自己的基地址和长度。在实模式下,内存分段是必须的。

主要步骤
  1. 设置段寄存器:设置段寄存器(CS, DS, ES, SS, FS, GS)以指向相应的段。
  2. 配置段描述符表:创建和配置全局描述符表(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. 内存分页

内存分页是一种内存管理机制,它将虚拟地址映射到物理地址,支持大容量内存管理和内存保护。在保护模式下,内存分页是必须的。

主要步骤
  1. 设置页目录和页表:创建页目录和页表,用于存储虚拟地址到物理地址的映射关系。
  2. 启用分页:设置控制寄存器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模块中。

  1. 创建项目目录

    • 在 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
  2. 编写模块代码

    • 在 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;
      }
  3. 编译模块

    • 打开命令提示符或终端。
    • 导航到 edk2 目录。
    • 运行以下命令编译模块:
       sh 

      深色版本

      build -p MemoryManagementModule/MemoryManagementModule.inf
  4. 运行模块

    • 编译成功后,你可以在 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

作业

  1. 阅读材料

    • 阅读Intel的官方文档,了解内存控制器和内存管理的详细信息。
    • 阅读EDK II的官方文档,了解模块开发的最佳实践。
  2. 实践任务

    • 编写一个更复杂的内存管理模块,例如支持动态内存分配和释放。
    • 编译并测试该模块,记录下遇到的问题和解决方法。

明天的任务

明天我们将继续深入探讨中断处理,了解如何在BIOS中设置中断向量表、编写中断处理程序。准备好迎接新的挑战吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值