【UEFI系列】EFI Memory Map内存映射 and type
前言
memory map主要分为两个部分,一个是系统能用的memory,通常allocate的部分,能当作堆栈存放数据存放代码、系统能用的memory。另一部分就是IO空间,一些设备比如PCI设备用的部分(MMio),它们所处的其实是分散的地方,通过映射、映射到连续的一段内存空间。
内存硬件层面:
channel > DIMM > rank > chip > bank > row/column
一个channel槽一般设计为1~4个DIMM口,可供插入DIMM条,DIMM就是我们通常说的内存条。
一个DIMM分为2面,一面为一个Rank,一个Rank有多个chip颗粒(DDR),一个chip颗粒上有多层Bank,每层Bank又有横列column数列row。
这里图上标SPD的实际上是EEPROM芯片,SPD是EEPROM存储的数据里面一项重要数据。
Memory Init
1.CAR: Cache as Ram,UEFI SEC最开始的CPU缓存当内存堆栈用
2.Init Memory Controler,内存插上去之后需要一些初始化和training才能使用。设备需要满足一些时序才能和CPU通信,内存需要满足一些比较复杂的时序。
3.read SPD with SMBUS, record delay of memory,用SMBUS去读内存的SPD,记录一些信息比如延时。但这个信息是需要修正的。
4.memory training, get Timing and Voltage of DIMM。Intel的话,training是在bios上运行的(intel提供的MRC code,发送一些信号,得到反馈,一直重复来调整延时和电压,得到一个比较准确的信息,然后记录下来)。AMD的话,自己有芯片去运行。
5.record memory info of HOB,PEI阶段存到HOB里去,DXE可以获取到。
Memory Management
地址空间在上电的时候回去检测是不是重启的。
如果是重启,有些设置是通过variable保存下来的,这些设置会被放到hob里。不是重启的话(可能是第一次刷板子开机)回去读默认值,再重启一次(这也就是为什么有些平台的CPU风扇开了又关然后又开)。
Hardware Perspective
Memory Map内存分布图(32位)
从上到下(地址从高到低)依次为
MMIo:Memory map IO
TOLUD:Top of Low Usable DRAM(系统可访问物理内存的最高地址边界)
System Memory
PCI memory range涉及到PCI配置表,以0XF开头,具体可以看我另外一篇讲的PCI&PCIe的。
如果是32bit以上(64bit),4G以上的寻址空间,就有更多的内存可以使用。
4G以上一部分也被拿来当作OS可用内存,再reclaim一段TOLUD到0xFFFFFFFF的内存大小(实际上只是获取了大小,数据并不是mmio的内容),再继续4G以上部分的mmio。
- Reclaim 的不是 MMIO 区域,而是被 MMIO“挤掉”的 DRAM
你有比如 8GB 的物理内存,但 CPU 的地址空间从 0x00000000 到 0xFFFFFFFF(4GB)之间被 MMIO 占了一部分。
那么原本应该映射在这段地址上的 DRAM 就没法映射进来。
这部分 DRAM 并没有消失,而是被“挪”到 4GB 以上的地址空间继续使用。 - Reclaim 区域的大小 ≈ MMIO 占用的大小
是的,被 MMIO 占用多少地址空间,就会有多少 DRAM 被挤出去,然后 reclaim 回来。
所以 reclaim 区域的大小 ≈ 0xFFFFFFFF - TOLUD + 1 - Reclaim 出来的那段 DRAM 是可以正常使用的
操作系统通过 BIOS 提供的 e820 表知道哪些地址是可用内存。
那段 reclaim 出来的 DRAM 会被标记为“可用”,可以被 OS 分配给用户进程、内核、缓存等。
如下:
PEI(Hob)
初始化内存,有的Hob要存放内存相关信息,DXE时候去获取。
可以看到绿色部分是HOB部分的(包含一个PHIT HOB头,里面放了物理起始地址和Hob list可用内存范围),hob list的大小是由代码固定好的,HOB一旦创建便不可删除或修改位置,仅支持追加(在最后面)操作。
蓝色部分是初始化好但还没用的,黄色是已经被分配过的地方。
最开始PeiMemoryTop和PeiFreeMemoryTop是一样的,每分配一段内存PeiFreeMemoryTop就会向下,EfiFreeMemoryBottom就会往上来用hob记录内存使用(黄色区域增多,绿色部分增多,蓝色区域减少)。
DXE(Gcd)
初始化驱动阶段。
这里4G以上就是之前讲的64位平台所拥有的内存扩展可用区域。
mmio同之前讲的一样,在4G最高地址。
DXE最开始的时候,会内存服务,GCD:Global Coherency Domain。
去分配一些比较大的空间。
GCD阶段,内存分为四个type:
1.Nonexistent memory:没用过的内存
2.System memory:系统内存
3.Memory mapped I/O:mmio
4.Reserved memory:预留内存(申请了准备用但还没有用的)
红框里的都是OS需要用到的,但是它实际上并不是连续的一块,是动态分配的,通过GCD的这些函数。
函数如图。
以下是函数具体执行。
原本都是Nonexistent memory,之后add成不同的类型:MMIO/Reserved/System memory。
GetMemoryMap
跑完UEFI Boot,从OS来看内存分布(BS是boot service):
可以通过GetMemoryMap来得到这张表,会在ExitBootService之前调用这个函数,会把这些信息传给OS。
因为OS需要知道物理内存是怎么用的,有些内存是不能被bios使用的也会被标注出来。OS需要知道那些内存是它可用的、哪些不可用。
这是函数的参数:
其中MemoryMap的PhysicalStart一段内存空间的地址。
这是每个EFI_MEMORY_DESCRIPTOR的结构:
typedef struct {UINT32 Type; // 内存类型(如代码、数据、保留等)EFI_PHYSICAL_ADDRESS PhysicalStart; // 物理起始地址,理解为寻址空间(32位对应4G寻址空间那种)EFI_VIRTUAL_ADDRESS VirtualStart; // 虚拟起始地址(未映射时为0)UINT64 NumberOfPages; // 页数(每页=4KB),可以看做内存区段大小的一种计量单位UINT64 Attribute; // 属性(可执行、缓存策略等)
} EFI_MEMORY_DESCRIPTOR;
也可以通过memmap指令在shell(还没有执行ExitBootService,可以回到Setup页面)下获得:
(1Page=4K)