操作系统真相还原第五章(1)------获取内存容量

本文介绍了在32位环境下,通过中断号15的子功能0xE820和0xE801来获取内存容量的方法。ARDS结构用于描述内存区域,而BIOS按照内存类型返回信息。代码示例展示了如何遍历ARDS结构找到最大内存区域,并转换为字节单位。此外,还提到了A20门的开启和保护模式的启动过程。
摘要由CSDN通过智能技术生成

1.地址范围描述符结构(ARDS)
在这里插入图片描述
type字段
描述内存的类型
在这里插入图片描述
我们在32位环境下工作,所以在ARDS结构属性中,我们只用到低32位属性,BaseAddrLow+LengthLow 是一片内存的上限。
特别注意!!!
正常情况下,不会出现较大的内存区域不可用的情况,这意味着此返回的ARDS结构中,BaseAddrLow+LengthLow最大值就是主板配置的物理内存容量。
2.利用子功能0xE820(中断号15)获取内存容量
系统中内存各部分的类型属性不同,BIOS按照类型属性划分这片系统内存,因此查询呈迭代式(每次BIOS只返回一种类型的内存信息)。
在这里插入图片描述
3.利用子功能0xE801(中断号15)获取内存容量
在这里插入图片描述
在这里插入图片描述
只有在实际物理内存容量大于16时,BX才有用,否则只有AX有值。
实际物理内存和实际检测内存大小为什么差1M?
历史遗留问题,为了兼容老的ISA设备,1M作为缓冲区
然后就是(AX和CX),(BX和DX)
在这里插入图片描述
其英文描述完全相同,再结合后面代码解释
在这里插入图片描述
可知其应该是两者应该一模一样的。
4.利用子功能0xE88(中断号15)获取内存容量
在这里插入图片描述
5.代码
Loader.S

%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start
GDT_BASE: dd 0x00000000
		  dd 0x00000000

CODE_DESC: dd 0x0000FFFF
		   dd DESC_CODE_HIGH4

DATA_STACK_DESC: dd 0x0000FFFF
				 dd DESC_DATA_HIGH4

VIDEO_DESC: dd 0x80000007
			dd DESC_VIDEO_HIGH4

GDT_SIZE equ $ - GDT_BASE
GDT_LIMIT equ GDT_SIZE - 1
times 59 dq 0
times 5 db 0

total_mem_bytes dd 0	; save memory capacity

; gdt's pointer
gdt_ptr dw GDT_LIMIT
		dd GDT_BASE

ards_buf times 244 db 0 ; buffer_size
ards_nr dw 0 ; buffer_num

SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0
SELECTOR_DATA equ (0x0002 << 3) + TI_GDT + RPL0
SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0
loader_start:
;int 15h eax=0000E820h,edx=534D4150h('SMAP'),获取内存布局
    xor ebx,ebx         ;第一次调用时,ebx为0
    mov edx,0x534d4150  ;edx标志值
    mov di,ards_buf     ;ards结构缓冲区
.e820_mem_get_loop:     ;执行int 0x15,eax值变为上面edx的值
    mov eax,0x0000e820
;所以每次执行int前都要更新为子功能号
    mov ecx,20
    int 0x15
    jc .e820_failed_so_try_e801
;若cf位为1则有错误发生,尝试0xe801子功能
    
    add di,cx           ;使di增加20字节指向缓冲区中的新的ARDS结构位置
    inc word [ards_nr]  ;记录ARDS数量
    cmp ebx,0           ;若ebx为0且cf不为1,这说明ards全部返回
    jnz .e820_mem_get_loop
;在所有ards结构中
;找出(base_add_low + length_low)的最大值,即内存的容量
    mov cx,[ards_nr]
;遍历每一个ARDS结构体,循环次数为ARDS的数目
    mov ebx,ards_buf
    xor edx,edx
.find_max_mem_area:
;无需判断type是否为1,最大内存块一定可被使用
    mov eax,[ebx]
    add eax,[ebx+8]
    add ebx,20
    cmp edx,eax
;冒泡排序,找出最大,edx寄存器始终是最大的内存容量

    jge .next_ards
    mov edx,eax
.next_ards:
    loop .find_max_mem_area
    jmp .mem_get_ok
;E801获取内存大小,最大支持4G
;返回后,ax cx 值一样,以KB为单位,bx dx值一样,以64kb为单位
;在ax和cx寄存器中为低16MB,在bx和dx寄存器中为16MB-4GB
.e820_failed_so_try_e801:
    mov ax,0xe801
    int 0x15
    jc .e801_failed_so_try88    ;若当前e801方法失败,就尝试0x88方法
;1算出低15MB的内存
;ax和cx是以KB为单位的内存数量,将其转换为byte为单位
    mov cx,0x400
    mul cx
    shl edx,16
    and eax,0x000FFFF
    or  edx,eax
    add edx,0x100000
    mov esi,edx
;2 再将16MB以上的内存转换为byte为单位
;寄存器bx和dx中是以64KB为单位的内存数量
    xor eax,eax
    mov ax,bx
    mov ecx,0x10000
    mul ecx

    add esi,eax
;由于此方法只能测出4GB以内的内存,故32位eax足够了
;edx肯定为0,只有eax便可
    mov edx,esi
    jmp .mem_get_ok
.e801_failed_so_try88:
;int 15后,ax存入的是KB为单位的内存容量
    mov ah,0x88
    int 0x15
    jc .error_hlt
    add eax,0x0000FFFF
;16位乘法,被乘数ax,积在32位。积的高16位在dx中,积的低16位在ax中
    mov cx,0x400
    
    mul cx
    shl edx,16
    or edx,eax
    add edx,0x100000
.error_hlt:
    hlt    
.mem_get_ok:
    mov [total_mem_bytes],edx
    

; 1 打开A20 gate
; 2 加载gdt
; 3 将cr0 的 pe位置1
    
    in al,0x92                 ;端口号0x92 中 第1位变成1即可
    or al,0000_0010b
    out 0x92,al
   
    
    lgdt [gdt_ptr]
    
    
    mov eax,cr0                ;cr0寄存器第0位设置位1
    or  eax,0x00000001              
    mov cr0,eax
      
;-------------------------------- 已经打开保护模式 ---------------------------------------
    jmp dword SELECTOR_CODE:p_mode_start                       ;刷新流水线
 
 [bits 32]
 p_mode_start: 
    mov ax,SELECTOR_DATA
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov esp,LOADER_STACK_TOP
    mov ax,SELECTOR_VIDEO
    mov gs,ax
    
    mov byte [gs:160],'P'
    
    jmp $

然后在运行方面在啰嗦几句。如果代码,你是自己敲得,然后运行起来发现0xb00处大小并不是32M而是0,那很可能是ards_buf位置出错了不在0xb00(前面定义的数据不是200字节)
可以调试一下。
在这里插入图片描述
输入 xp /200 0xb00,(查询0xb00之后的200字节)
当然你的位置可能也会小,将0xb00改小一下就行,不出意外的话,应该在着附近。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值