X86 内存布局分析(Memory map)

本文详细描述了从BIOS启动到OS加载前的计算机物理内存状态,包括低地址内存、高地址内存区域的特性及使用注意事项。特别关注了BIOS数据区和扩展BIOS数据区在内存中的位置与作用,以及如何在实模式和保护模式下进行内存操作。

This article describes the contents of the computer's physical memory at the moment that the BIOS jumps to your bootloader code.

本文主要描述从BIOS代码工作到OS bootloader之前的计算机物理内存分析。

Contents

[hide]


"Low" memory (< 1 MiB)

低地址内存(< 1MB )

When a typical x86 PC boots it will be in Real Mode, with an active BIOS. During the time the CPU remains in Real Mode, IRQ0 (the clock) will fire repeatedly, and the hardware that is used to boot the PC (floppy, hard disk, CD, Network card, USB) will also generate IRQs. This means that during the PC boot process, the Real Mode IVT (see below) must be carefully preserved, because it is being used.

When the IVT is activated by an IRQ, it will call a BIOS routine to handle the IRQ. Bootloaders will also access BIOS functions. This means that the two memory workspaces that the BIOS uses (the BDA and the EBDA) must also be carefully preserved during boot. Also, every time the BIOS handles an IRQ0 (18 times a second), several bytes in the BDA get overwritten by the BIOS -- so do not attempt to store anything there while IRQs are active in Real Mode.

After all the BIOS functions have been called, and your kernel is loaded into memory somewhere, the bootloader or kernel may exit Real Mode forever (often by going into 32bit Protected Mode). If the kernel never uses Real Mode again, then the first 0x500 bytes of memory in the PC may be reused and overwritten. (However, it is very common to temporarily return to Real Mode in order to change the Video Display Mode.)

When the CPU is in Protected Mode, System Management Mode (SMM) is still invisibly active, and cannot be shut off. SMM also seems to use the EBDA. So the EBDA memory area should never be overwritten.

Note: the EBDA is a variable-sized memory area (on different BIOSes). If it exists, it is always immediately below 0xA0000 in memory. It is absolutely guaranteed to be less than 128 KiB in size. It is often 1 KiB. The biggest ones ever actually seen are 8 KiB. You can determine the size of the EBDA by using BIOS function INT 12h, or (often) by examining the word at 0x40E in the BDA (see below). Both of those methods will tell you the location of the bottom of the EBDA.

It should also be noted that your bootloader code is probably loaded and running in memory at physical addresses 0x7C00 through 0x7DFF. So that memory area is likely to also be unusable until execution has been transferred to a second stage bootloader, or to your kernel.

Overview

(all values except KiBs are in hex)

startendsizetypedescription
Low Memory (the first MiB)
00000000000003FF400 (1 KiB)RAM - partially unusable (see above)Real Mode IVT (Interrupt Vector Table)
00000400000004FF100RAM - partially unusable (see above)BDA (BIOS data area)
0000050000007BFF7700 (almost 30 KiB)RAM (guaranteed free for use)Conventional memory
00007C00 (typical location)00007DFF200RAM - partially unusable (see above)Your OS BootSector
00007E000007FFFF7FB00 (481 KiB)RAM (guaranteed free for use)Conventional memory
000800000009FBFF1FC00 (approximately 120 KiB)RAM (free for use, if it exists)Conventional memory
0009FC00 (typical location)0009FFFF400RAM (unusable)EBDA (Extended BIOS Data Area)
000A0000000FFFFF60000various (unusable)ROM Area (384 KiB)

 

BIOS Data Area (BDA)

The BDA is only partially standardized, and almost all the values stored there are completely obsolete and uninteresting. The following is a partial list. See the External Links references below for more detail.

address (size)description
400 (word)IO port for COM1 serial
408 (word)IO port for LPT1 parallel
40E (word)EBDA base address >> 4 (usually!)
410 (word)packed bit flags for detected hardware
449 (byte)Display Mode
463 (2 bytes, taken as a word)base IO port for video
46C (word)# of IRQ0 timer ticks since boot
475 (byte)# of hard disk drives detected
497 (byte)last keyboard LED/Shift key state

 

Extended BIOS Data Area (EBDA)

You may see "maps" of the EBDA if you search the web. However, those maps are for the original IBM BIOS EBDA. They do not apply to any current EBDA, used by any current BIOS. The EBDA area is not standardized. It does contain data that your OS will need, but you must do a bytewise pattern search to find those tables. (See PlugNPlay.)

 

ROM Area

startendsizeregion/exceptiondescription
Standard usage of the ROM Area
000A0000000AFFFF10000video RAMVGA framebuffer (64 KiB)
000B0000000B7FFF8000video RAMVGA text monochrome (32 KiB)
000B8000000BFFFF8000video RAMVGA text color (32 KiB)
000C0000000C7FFF8000ROMVideo BIOS (32 KiB is typical size)
000C8000000EFFFF28000ROMs and unusable spaceMapped hardware & Misc.
000F0000000FFFFF10000ROMMotherboard BIOS (64 KiB is typical size)

 

"Upper" Memory (> 1 MiB)

The region of RAM above 1 MiB is not standardized, well-defined, or contiguous. There are likely to be regions of it that contain memory mapped hardware, that nothing but a device driver should ever access. There are likely to be regions of it that contain ACPI tables which your initialization code will probably want to read, and that then can be overwritten and reused. Some ACPI areas cannot be "reclaimed" this way. Some of the computer's RAM may extend above 4 GiB.

Use the BIOS function INT 15h, EAX=0xE820 to get a reliable map of Upper Memory.

 

startendsizeregion/exceptiondescription
High Memory
00100000003FFFFF00300000RAM -- guaranteed free for use1Extended memory
0040000000EFFFFF00900000 (if it all exists)RAM -- free for useExtended memory
00F0000000FFFFFF100000Possible memory mapped hardwareISA Memory Hole 15-16MB (only with ISA bus?)
01000000 ???????? ???????? (whatever exists)RAM -- free for useMore Extended memory
C000000 (sometimes)FFFFFFF4000000various (unusable except by drivers)PnP NVRAM?, LAPIC, BIOS, ...
10000000 (possible mem above 4 GiB) ???????? ???????? (whatever exists)RAM -- free for use (PAE/64bit)More Extended memory

1: Free for use except that your bootloader (ie. GRUB) may have loaded your "modules" here, and you don't want to overwrite those.

 

Comments

See Also

External Links

<think> 我们正在讨论UEFI Shell下的内存映射(memory map)问题。根据之前的对话历史,用户已经询问了如何在EFI Shell下分配内存,现在转向了解内存映射的相关信息。我将结合提供的引用内容来构建回答。 根据引用[1],内存映射主要分为两部分:系统可用的内存(用于分配、存放数据和代码)和IO空间(如PCI设备的MMIO区域)。引用[3]说明,UEFI函数返回的内存映射包含所有连续内存块的描述符,包括通过AllocatePages/AllocatePool分配的内存以及固件使用的内存块。 在EFI Shell中,我们可以通过以下方式获取和操作内存映射: 1. **获取内存映射**:使用`memmap`命令或调用UEFI Boot Services的`GetMemoryMap()`函数。 2. **内存映射结构**:每个内存描述符包含: - 物理起始地址 - 内存块大小 - 内存类型(如EfiLoaderData) - 属性(如是否可执行) 3. **关键用途**: - 操作系统引导时需要完整的内存映射 - 调试内存分配问题 - 确保不覆盖保留内存区域 操作步骤: 1. **Shell命令**: ```shell # 显示当前内存映射 memmap ``` 输出示例: ``` Type Start End # Pages Attributes RT_Data 0000000000000000 0000000000000FFF 0000000000000001 800000000000000F BS_Code 0000000000001000 0000000000001FFF 0000000000000001 800000000000000F ... ``` 2. **编程获取**: ```c EFI_STATUS Status; UINTN MemoryMapSize = 0; UINTN MapKey; UINTN DescriptorSize; UINT32 DescriptorVersion; EFI_MEMORY_DESCRIPTOR *MemoryMap = NULL; // 首次获取大小 Status = gBS->GetMemoryMap( &MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion ); // 分配缓冲区 MemoryMapSize += 2 * DescriptorSize; // 安全裕量 Status = gBS->AllocatePool(EfiLoaderData, MemoryMapSize, (VOID**)&MemoryMap); // 实际获取 Status = gBS->GetMemoryMap( &MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion ); // 遍历描述符 UINTN NumEntries = MemoryMapSize / DescriptorSize; for (UINTN i = 0; i < NumEntries; i++) { EFI_MEMORY_DESCRIPTOR *Desc = (EFI_MEMORY_DESCRIPTOR*)((UINT8*)MemoryMap + i*DescriptorSize); Print(L"Type:%d Addr:0x%lx Pages:%ld\n", Desc->Type, Desc->PhysicalStart, Desc->NumberOfPages); } // 使用后释放 gBS->FreePool(MemoryMap); ``` 3. **内存类型详解**(部分): | 类型值 | 宏定义 | 说明 | |--------|-----------------------|--------------------------| | 1 | `EfiLoaderCode` | UEFI加载器代码 | | 2 | `EfiLoaderData` | UEFI加载器数据 | | 3 | `EfiBootServicesCode` | Boot Services代码 | | 4 | `EfiBootServicesData` | Boot Services数据 | | 5 | `EfiRuntimeServicesCode`| Runtime Services代码 | | 6 | `EfiRuntimeServicesData`| Runtime Services数据 | | 7 | `EfiConventionalMemory`| 可用物理内存 | | ... | ... | ... | 4. **注意事项**: - 调用`ExitBootServices()`后,Boot Services类型内存将失效[^1] - 内存映射可能因平台而异(如ARM与x86差异) - 操作系统启动时会合并UEFI内存映射与ACPI表(引用[2]) --- ### §§ 相关问题§§ 1. 如何解析UEFI内存描述符中的属性字段? 2. `ExitBootServices()`调用后内存映射会发生什么变化? 3. ACPI内存区域(如`EfiACPIReclaimMemory`)在内存映射中如何表示? 4. 为什么操作系统需要同时处理UEFI内存映射和E820映射? </think> ### UEFI Shell 内存映射详解 在 UEFI Shell 环境下,**内存映射(Memory Map)** 描述了整个物理内存空间的布局,包括可用内存区域、固件保留区、设备 MMIO 空间等。其核心功能是通过 `EFI_BOOT_SERVICES.GetMemoryMap()` 获取,结构如下: #### 1. **内存映射核心结构** ```c typedef struct { UINT32 Type; // 内存类型(见下表) EFI_PHYSICAL_ADDRESS PhysicalStart; // 物理起始地址 EFI_VIRTUAL_ADDRESS VirtualStart; // 虚拟起始地址(通常为0) UINT64 NumberOfPages; // 占用的页数 UINT64 Attribute; // 内存属性(如可执行、可写等) } EFI_MEMORY_DESCRIPTOR; ``` #### 2. **内存类型分类(部分)** | 类型值 | 宏定义 | 说明 | |--------|-----------------------|--------------------------| | 1 | `EfiLoaderCode` | UEFI加载器代码 | | 2 | `EfiLoaderData` | UEFI加载器数据 | | 3 | `EfiBootServicesCode` | Boot Services代码 | | 4 | `EfiBootServicesData` | Boot Services数据 | | 5 | `EfiRuntimeServicesCode`| Runtime Services代码 | | 6 | `EfiRuntimeServicesData`| Runtime Services数据 | | 7 | `EfiConventionalMemory`| 可用物理内存 | | 8 | `EfiUnusableMemory` | 损坏/不可用内存 | | 9 | `EfiACPIReclaimMemory`| ACPI可回收内存 | | 10 | `EfiACPIMemoryNVS` | ACPI NVS保留内存 | | 11 | `EfiMemoryMappedIO` | 设备MMIO空间 | [^1][^3] #### 3. **Shell 下操作内存映射 ##### (1) 查看完整内存映射 ```shell # 显示所有内存区域及其类型 memmap -b # -b 参数显示详细属性 ``` 输出示例: ``` Type Start End # Pages Attributes 07 0000000000000000 000000000009EFFF 0000009F 000000000000000F 04 000000000009F000 000000000009FFFF 00000001 800000000000000F ... ``` - **Type 07**:`EfiConventionalMemory`(可用内存) - **Type 04**:`EfiBootServicesData`(启动服务数据) ##### (2) 获取特定内存类型信息 ```shell # 显示所有可用物理内存(Type=07) memmap -t 7 ``` #### 4. **编程获取内存映射(C 示例)** ```c #include <Uefi.h> EFI_STATUS GetMemoryMap() { UINTN MemoryMapSize = 0; EFI_MEMORY_DESCRIPTOR *MemoryMap = NULL; UINTN MapKey, DescriptorSize; UINT32 DescriptorVersion; // 首次调用获取所需缓冲区大小 gBS->GetMemoryMap( &MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion ); // 分配缓冲区 gBS->AllocatePool(EfiLoaderData, MemoryMapSize, (VOID**)&MemoryMap); // 实际获取内存映射 EFI_STATUS Status = gBS->GetMemoryMap( &MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion ); // 遍历描述符 UINTN NumEntries = MemoryMapSize / DescriptorSize; for (UINTN i = 0; i < NumEntries; i++) { EFI_MEMORY_DESCRIPTOR *Desc = (EFI_MEMORY_DESCRIPTOR*)((UINT8*)MemoryMap + i*DescriptorSize); Print(L"Type:%d Addr:0x%lx Pages:%ld\n", Desc->Type, Desc->PhysicalStart, Desc->NumberOfPages); } gBS->FreePool(MemoryMap); return Status; } ``` #### 5. **关键用途** 1. **操作系统引导** 内核通过内存映射获取可用物理内存范围(如 Linux 使用 `efi_memmap` 初始化内存管理)[^2][^4]。 2. **安全隔离** 区分代码区(不可写)与数据区(不可执行),例如: ```c if (Desc->Attribute & EFI_MEMORY_XP) { Print(L"此区域不可执行!"); } ``` 3. **资源预留** 保留 ACPI/MMIO 区域避免被分配(如 `EfiMemoryMappedIO`)[^1]。 #### 6. **注意事项** - **动态变化性**:调用 `AllocatePool()`/`FreePool()` 会改变内存映射,需重新获取。 - **OS 接管时机**:`ExitBootServices()` 后,`EfiBootServices*` 类型内存将失效[^1]。 - **对齐要求**:内存描述符按 `DescriptorSize` 对齐,遍历时需手动计算偏移。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值