目录
5.1 获取物理内存容量
5.1.1 学习 Linux 获取内存的方法
5.1.2 利用 BIOS中断 0x15 子功能 0xe820 获取内存
5.1.3 利用BIOS中断 0x15 子功能 0xe801 获取内存
5.1.4 利用BIOS中断0x15子功能0x88获取内存
5.1.5 实战内存容量检测
5.1 获取物理内存容量
操作系统是计算机硬件的管家,它不仅要知道自己安装了哪些硬件,还得给出有效得当的管理措施,按照预定的一套管理策略使硬件资源得到合理的运用。
5.1.1 学习 Linux 获取内存的方法
通过调用 BIOS 中断 0x15 实现,分别是 BIOS 中断 0x15 的3个子功能,子功能号要存放到寄存器 EAX 或 AX 中。
- EAX=0xE820:遍历主机上全部内存。
- AX =0xE801: 分别检测低 15MB 和 16MB~4GB 的内存,最大支持 4GB。
- AH=0x88:最多检测出 64MB 内存,实际内存超过此容量也按照 64MB 返回。
BIOS 中断是实模式下的方法,只能在进入保护模式前调用。
5.1.2 利用 BIOS中断 0x15 子功能 0xe820 获取内存
BIOS 中断 0x15 的子功能 0xE820 能够获取系统的内存布局,按照类型属性来划分这片系统内存。
5.1.3 利用BIOS中断 0x15 子功能 0xe801 获取内存
5.1.4 利用BIOS中断0x15子功能0x88获取内存
该方法使用最简单,但功能也最简单,简单到只能识别最大 64MB 的内存。即使内存容量大于 64MB,也只会显示 63MB。
5.1.5 实战内存容量检测
%include "boot.inc"section loader vstart=LOADER_BASE_ADDRLOADER_STACK_TOP equ LOADER_BASE_ADDR;构建gdt及其内部的描述符GDT_BASE: dd 0x00000000 dd 0x00000000CODE_DESC: dd 0x0000FFFF dd DESC_CODE_HIGH4DATA_STACK_DESC: dd 0x0000FFFFdd DESC_DATA_HIGH4VIDEO_DESC: dd 0x80000007 ; limit=(0xbffff-0xb8000)/4k=0x7dd DESC_VIDEO_HIGH4 ; 此时dpl为0GDT_SIZE equ $ - GDT_BASEGDT_LIMIT equ GDT_SIZE - 1 times 60 dq 0 ; 此处预留60个描述符的空位(slot)SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900,; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址total_mem_bytes dd 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址gdt_ptr dw GDT_LIMIT dd GDT_BASE;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节ards_buf times 244 db 0ards_nr dw 0 ;用于记录ards结构体数量loader_start:;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 -------xor ebx, ebx ;第一次调用时,ebx值要为0mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变mov di, ards_buf ;ards结构缓冲区
.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节int 0x15jc .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 ;edx为最大的内存容量,在此先清0
.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用mov eax, [ebx] ;base_add_lowadd eax, [ebx+8] ;length_lowadd ebx, 20 ;指向缓冲区中下一个ARDS结构cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量jge .next_ardsmov edx, eax ;edx为总内存大小
.next_ards:loop .find_max_mem_areajmp .mem_get_ok;------ int 15h ax = E801h 获取内存大小,最大支持4G ------
; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位
; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。
.e820_failed_so_try_e801:mov ax,0xe801int 0x15jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位mov cx,0x400 ;cx和ax值一样,cx用做乘数mul cx shl edx,16and eax,0x0000FFFFor edx,eaxadd edx, 0x100000 ;ax只是15MB,故要加1MBmov esi,edx ;先把低15MB的内存容量存入esi寄存器备份;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量xor eax,eaxmov ax,bx mov ecx, 0x10000 ;0x10000十进制为64KBmul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax.add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可mov edx,esi ;edx为总内存大小jmp .mem_get_ok;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ----------
.e801_failed_so_try88: ;int 15后,ax存入的是以kb为单位的内存容量mov ah, 0x88int 0x15jc .error_hltand eax,0x0000FFFF;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位mul cxshl edx, 16 ;把dx移到高16位or edx, eax ;把积的低16位组合到edx,为32位的积add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB.mem_get_ok:mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。;----------------- 准备进入保护模式 -------------------
;1 打开A20
;2 加载gdt
;3 将cr0的pe位置1;----------------- 打开A20 ----------------in al,0x92or al,0000_0010Bout 0x92,al;----------------- 加载GDT ----------------lgdt [gdt_ptr];----------------- cr0第0位置1 ----------------mov eax, cr0or eax, 0x00000001mov cr0, eaxjmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转,; 这将导致之前做的预测失效,从而起到了刷新的作用。
.error_hlt: ;出错则挂起hlt[bits 32]
p_mode_start:mov ax, SELECTOR_DATAmov ds, axmov es, axmov ss, axmov esp,LOADER_STACK_TOPmov ax, SELECTOR_VIDEOmov gs, axmov byte [gs:160], 'P'jmp $