获取物理内存大小
BIOS 提供的内存相关中断(int 0x15)
基础功能(eax = 0xE801)
- 分别检测低15MB和高 16MB-4GB的内存空间
- 最大支持 4GB 内存检测
高级功能(eax = 0xE820)
- 遍历主机上所有的内存范围
- 获取各个内存范围的详细信息
因此设置中断参数eax=0xE801,调用中断后查看相关寄存器的返回值就可以得到内存容量
- cf –> 成功 0,出错 1
- ax,cx –> 以 1KB 为单位, 表示 15MB 以下的内存容量
- bx,dx –> 以 64 KB 为单位, 表示 16MB 以上的内存容量
汇编小贴士:标志寄存器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| xor eax, eax mov eax, 0xE801 ; 获取物理内存的大小
int 0x15
jc geterr ; CF位为1说明出错了
;eax 以kB为单位 ;ebx y shl eax, 10 ; 15M以下内存容量 shl ebx, 6 ; 16M以上内存容量 shl ebx, 10
add dword [MEM_SIZE], eax add dword [MEM_SIZE], ebx
|
似乎并非真正物理内存?
为什么0xE801获取的内存容量分两部分表示?明明一个32位寄存器可以表示0~4GB大小的任意内存
为什么0xE801获取的内存容量大小少了1MB?
这是由于一些历史原因,80286中的24根地址线最大寻址范围是16MB,80386后为了兼容,在使用int 0x15后,ax寄存器最多表示15MB的内存容量即0x3C00。80386多余16MB的内存容量便要单独返回。
FFF0~FFFF
这个区间的内存可以重新映射,比如映射到外设的存储单元,这个时候FFF0~FFFF
即便存在也无法使用,即“内存黑洞”。当时ISA设备使用15MB以上的地址作为缓冲区(mmp操作),操作系统无法使用15MB~16MB的部分内存。
因此要加上1MB才是真正大小的物理内存,修正代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| mov dword [MEM_SIZE], 0
xor eax, eax mov eax, 0xE801
int 0x15
jc geterr
shl eax, 10 ; eax = eax * 1024
shl ebx, 6 ; ebx = ebx * 64 shl ebx, 10 ; ebx = ebx * 1024
mov ecx, 1 ; ecx修正1M shl ecx, 20
add dword [MEM_SIZE], eax add dword [MEM_SIZE], ebx add dword [MEM_SIZE], ecx
|
反汇编ndisasm -o 0x9000 loader > loader.txt
查看loader.txt
因此设置两个断点在调用函数前后,查看MEM_SIZE即0x9049处内存具体值,可以看到是32MB内存大小
1 2 3 4 5 6 7 8 9 10 11
| <bochs:5> x /1wx 0x9049 [bochs]: 0x0000000000009049 <bogus+ 0>: 0x00000000 <bochs:6> c (0) Breakpoint 2, 0x000000000000905e in ?? () Next at t=15475924 (0) [0x00000000905e] 0000:905e (unk. ctxt): mov esi, 0x0000913c ; 66be3c910000 <bochs:7> x /1wx 0x9049 [bochs]: 0x0000000000009049 <bogus+ 0>: 0x02000000
|
结论
- eax=0xE801返回的是可实际使用的内存容量
- 处理器对内存地址空间做了分段处理
int 0x15进阶功能
BIOS 提供的内存相关中断(int 0x15)
基础功能(eax = 0xE801)
- 分别检测低15MB和高 16MB-4GB的内存空间
- 最大支持 4GB 内存检测
高级功能(eax = 0xE820)
- 遍历主机上所有的内存范围
- 获取各个内存范围的详细信息
int 0x15进阶功能中断参数
- eax = 0xE820 (固定值)
- edx = 0x534D4150 (固定值)
- ebx -> 初始参数必须 0, 终止标志
- ecx ARDS 结构体大小(20 字节)
- es:di ->ARDS结构体数组,每个元素占用20字节
地址范围描述结构(Address Range Descriptor Structure)
1 2 3 4 5 6 7 8
| struct ARDS { unsigned int BaseAddrLow; unsigned int BaseAddrHigh; unsigned int LengthLow; unsigned int LengthHigh; unsigned int Type; };
|
ARDS 结构体中的 Type 成员
- AddressRangeMemory表示这段内存可以被操作系统使用
- AddressRangeReserved表示内存使用中或被保留,操作系统不可使用
- 其它值 - 未定义,保留,可当作AddressRangeMemory 处理
通过ARDS结构体数组可以清晰的知道内存空间情况。
伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| ARDS pARDS[256] = {0}; int count = 0;
es:di = pADRS; ebx = 0;
do{ eax = 0xE820; edx = 0x534D4150; ecx = 20; int 0x15; if(cf == 1){ error(); break; } di += 20; count++; }while(ebx != 0);
|
获取ARDS记录
在 32 位系统中
- ARDS结构体中的BaseAddrHigh和LengthHigh均为 0(因为32位系统低位的32位就够了呀,高位32位用不上)
- 物理内存容量需要通过属性为 1 的内存段计算(说明操作系统可用)
- 计算方式为:
max( BaseAddrLow0 + LengthLow0,BaseAddrLow1 + LengthLow1,BaseAddrLow2 + LengthLow2,...,BaseAddrLowN + LengthLowN )
BaseAddrLow + LengthLow 是一段内存的地址上限
当一片内存可被操作系统使用, 且地址上限最大时, 这个地址上限就是物理内存的大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| ; eax -->0:succeed 1:failed InitSysMemBuf: push edi push ebx push ecx push edx ; 这里加上就是最终代码了 ; call GetMemSize ; 拿到一个物理内存大小
mov edi, MEM_ARDS mov ebx, 0
doloop: mov eax, 0xE820 mov edx, 0x534D4150 mov ecx, 20
int 0x15
jc memerr
cmp dword [edi + 16], 1 ; Type字段和1比较 jne next mov eax, [edi] add eax, [edi + 8]
cmp dword [MEM_SIZE], eax ; 无需更新,跳转继续比较下一个 jnb next
mov dword [MEM_SIZE], eax ; MEM_SIZE作更新为更大值
next: add edi, 20 inc dword [MEM_ADRS_NUM]
cmp ebx, 0 jne doloop jmp memok
memerr: mov eax, 1 mov dword [MEM_SIZE], 0 mov dword [MEM_ADRS_NUM], 0 memok: mov eax, 0
pop edx pop ecx pop ebx pop edi ret
|
bochs单步调试查看对应代码
发现获得的物理内存的大小是0x01ff0000,并不是0x02000000,然后依次查看MEM_ADRS的内存存储的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <bochs:6> x /1wx 0x9049 [bochs]: 0x0000000000009049 <bogus+ 0>: 0x01ff0000 <bochs:7> x /5wx 0x9051 [bochs]: 0x0000000000009051 <bogus+ 0>: 0x00000000 0x00000000 0x0009f000 0x00000000 0x0000000000009061 <bogus+ 16>: 0x00000001 ;TYPE=1说明是操作系统可用内存大小0x0009f000 <bochs:8> x /5wx 0x9051+20 [bochs]: 0x0000000000009065 <bogus+ 0>: 0x0009f000 0x00000000 0x00001000 0x00000000 0x0000000000009075 <bogus+ 16>: 0x00000002 ;TYPE=2说明是操作系统不可使用这段内存 <bochs:9> x /5wx 0x9051+40 [bochs]: 0x0000000000009079 <bogus+ 0>: 0x000e8000 0x00000000 0x00018000 0x00000000 0x0000000000009089 <bogus+ 16>: 0x00000002 ;TYPE=2说明是操作系统不可使用这段内存 <bochs:10> x /5wx 0x9051+60 [bochs]: 0x000000000000908d <bogus+ 0>: 0x00100000 0x00000000 0x01ef0000 0x00000000 0x000000000000909d <bogus+ 16>: 0x00000001 ;TYPE=1说明是操作系统可用内存大小0x01ff0000 <bochs:11> x /5wx 0x9051+80 [bochs]: 0x00000000000090a1 <bogus+ 0>: 0x01ff0000 0x00000000 0x00010000 0x00000000 0x00000000000090b1 <bogus+ 16>: 0x00000003 ;TYPE=3 这块内存大小0x02000000 <bochs:12> x /5wx 0x9051+100 [bochs]: 0x00000000000090b5 <bogus+ 0>: 0xfffc0000 0x00000000 0x00040000 0x00000000 0x00000000000090c5 <bogus+ 16>: 0x00000002 ;TYPE=2说明是操作系统不可使用这段内存 <bochs:13> x /5wx 0x9051+120 [bochs]: 0x00000000000090c9 <bogus+ 0>: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000000090d9 <bogus+ 16>: 0x00000000
|
实验中使用的策略
- 通 过 0xE801 计算物理内存大小
- 通 过 0xE820 获取各个 ARDS 并填入结构体数组
- 根 据 ARDS 结构体数组计算物理内存大小
- 选择计算得到的较大内存容量作为最终结果