《真象还原》读书笔记——第五章 保护模式进阶,向内核迈进(获取物理内存容量)

5.1 获取物理内存容量

5.1.1 学习linux获取内存的方法

BIOS中断0x15的3个子功能:BIOS中断是实模式下的用法,不能在保护模式下使用。若全部失败了就只好让机器挂起了。运行成功再启动保护模式。

  • EAX=0xE820:遍历主机上全部内存
  • AX=0xE801:分别检测出64MB和16MB~4GB的内存,最大支持4GB
  • AH=0x88:最多检测出64MB内存,实际内存超过该容量也按64MB返回

5.1.2 利用BIOS中断0x15子功能0xe820获取内存

返回地址消息,通过地址范围描述符
地址范围描述符结构ARDS
Type字段的含义:
地址范围描述符结构的Type字段
BIOS中断0x15中的0xe820子功能需要的参数。
因为是在32位下工作,所以只用到BaseAddrLow+lengthLow是一片内存区域上限。
BIOS中断0x15子功能0xe820说明
BIOS中断0x15子功能0xe820说明
操作步骤

  1. 填写“调用前输入”中列出的寄存器。
  2. 执行中断,调用 int 0x15
  3. 在CF位为0的情况下,“返回后输出",中对应寄存器会有对应的结果。

5.1.3 利用BIOS中断0x15子功能0xe801获取内存

最大只能识别4GB,对于32位也算可以了。

  • 低于15MB的内存以1KB为单位,数量在AX,CX中记录。
    因此实际内存=AX*1024。AX,CX最大值为0x3c00
  • 16MB~4GB以64KB为单位,数量在BX,DX中记录。
    因此实际内存=AX*64 *1024。
    至于15~16MB的部分是留给ISA设备的。没有入统计。
    BIOS中断0x15子功能0xE801说明
    操作步骤
  1. 将AX寄存器写入0xE801
  2. 执行中断int 0x15
  3. 在CF=0的情况下,”返回后输出“中对应的寄存器会有对应结果。

5.1.4 利用BIOS中断0x15子功能0x88获取内存

即使内存容量大于64MB,也只会显示63MB。因为此中断只会显示1MB之上的内存,所以记得手动加上1MB
BIOS中断0x15子功能0x88说明

  1. 将AX寄存器写入0x88
  2. 执行中断调用int 0x15
  3. 在CF位为0的情况下,”返回后输出“中对应的寄存器便会有对应的结果。

5.1.5 实战内存容量检测

更改的内容:将第44行jmp到load的代码改为:
jmp LOADER_BASE_ADDR+0x300


%include "boot.inc"
section mbr vstart=0x7c00
   mov ax,cs
   mov ds,ax
   mov es,ax
   mov ss,ax
   mov fs,ax
   mov sp,0x7c00
   mov ax,0xb800
   mov gs,ax

   ;清屏
   mov ax,0600h
   mov bx,0700h
   mov cx,0
   mov dx,184fh
   int 10h

   ;输出字符串:MBR
   mov byte[gs:0x00],'1'
   mov byte[gs:0x01],0xA4

   mov byte[gs:0x02],' '
   mov byte[gs:0x03],0xA4

   mov byte[gs:0x04],'M'
   mov byte[gs:0x05],0xA4

   mov byte[gs:0x06],'B'
   mov byte[gs:0x07],0xA4

   mov byte[gs:0x08],'R'
   mov byte[gs:0x09],0xa4


   mov eax,LOADER_START_SECTOR                 ;其实栓去lba地址
   mov bx ,LOADER_BASE_ADDR                    ;写入的地址
   mov cx ,4                                   ;这里的cx就是读取扇区数
   call rd_disk_m_16
mov byte[gs:160],'r'
mov byte[gs:162],'d'
   
   jmp LOADER_BASE_ADDR+0x300
   
   ;===读取硬盘n个扇区===
rd_disk_m_16:
   mov esi, eax
   mov di,cx

   ;第一步,读取扇区
   mov dx,0x1f2
   mov al,cl 
   out dx,al 

   mov eax,esi
   ;第二步,将LBA地址存入0x1f3~0x1f6

   ;lba地址7~0写入端口0x1f3
   mov dx,0x1f3 
   out dx,al

   ;lba地址15-8写入端口0x1f4
   mov cl,8
   shr eax,cl
   mov dx,0x1f4
   out dx,al
   
   ;lba地址32~16写入端口0x1f5
   shr eax,cl
   mov dx,0x1f5
   out dx,al
   
   ;lba14~27
   shr eax,cl
   and al,0x0f 
   or al,0xe0      ;设置7-4位1110,表示lba模式。
   mov dx,0x1f6
   out dx,al
   
   ;第3步:向0x1f7端口写入命令,0x20
   mov dx,0x1f7
   mov al,0x20
   out dx,al
   
   ;第4步:检测硬盘状态
.not_ready:
   nop
   in al,dx 
   and al,0x88 
   cmp al,0x08 
   jnz .not_ready

   ;第5步:从0x1f0端口读数据
   mov ax,di
   mov dx,256
   mul dx
   mov cx,ax
   ;di为要读取的扇区数,一个扇区512字节,每读取一个字;共需要di*512/2次,所以是256
   mov dx,0x1f0 
   
.go_on_read:
   in ax,dx
   mov [bx],ax
   add bx,2
   loop .go_on_read
   ret

times 510-($-$$) db 0
db 0x55,0xaa

让调整数据结构为了让total_mem_bytes在0xb00位置上。

%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR

;------------------------------
;构建gdt及其内部的描述符
GDT_BASE:           dd 0x00000000
                    dd 0x00000000

CODE_DESC:          dd 0x0000FFFF
                    dd DESC_CODE_HIGH4

DATA_STACK_DESK:    dd 0x0000FFFF
                    dd DESC_DATA_HIGH4

VIDEO_DESC:         dd 0x80000007               ;limit=(0xbffff-0xb8000)/4k=0x7
                    dd DESC_VIDEO_HIGH4         ;此时dp1为0

GDT_SIZE            equ $-GDT_BASE
GDT_LIMIT           equ GDT_SIZE-1
times 60 dq 0

SELECTOR_CODE       equ (0x0001<<3)+TI_GDT+RPL0
SELECTOR_DATA       equ (0x0002<<3)+TI_GDT+RPL0
SELECTOR_VIDEO      equ (0x0003<<3)+TI_GDT+RPL0 

total_mem_bytes     dd  0;存放内存大小

gdt_ptr             dw GDT_LIMIT
                    dd GDT_BASE

;------- 记录 ARDS结构体数 ----------
ards_buf            times   244 db 0
ards_nr             dw      0

;------------------------------

mov byte[gs:480],'G'
mov byte[gs:482],'D'
mov byte[gs:484],'T'

loader_start: ;打印字符串
mov byte[gs:640],'L'
mov byte[gs:642],'O'
mov byte[gs:644],'A'
mov byte[gs:646],'D'
mov byte[gs:648],'E'
mov byte[gs:650],'R'
mov byte[gs:652],'_'
mov byte[gs:654],'S'
mov byte[gs:656],'T'
mov byte[gs:658],'A'
mov byte[gs:660],'R'
mov byte[gs:662],'T'

;    mov sp,LOADER_BASE_ADDR
;    mov bp,loadermsg
;    mov cx,17
;    mov ax,0x1301
;    mov bx,0x001f
;    mov dx,0x1800
;    int 0x10

    ;--- 获取内存布局 ---
    xor ebx, ebx
    mov edx, 0x534d4150
    mov di, ards_buf

.e820_mem_get_loop:
    mov eax, 0x0000e820
    mov ecx,20
    int 0x15
    jc .e820_failed_so_try_e801

    add di,cx
    inc word [ards_nr]
    cmp ebx, 0
jnz .e820_mem_get_loop
    mov cx, [ards_nr]
    mov ebx, ards_buf
    xor edx,edx

.find_max_mem_area:
    mov eax,[ebx]
    add eax,[ebx+8]
    add ebx, 20
    cmp edx,eax

    jge .next_ards
    mov edx,eax
.next_ards:
    loop .find_max_mem_area
    jmp .mem_get_ok

;------ int 15h ax=E801和------

.e820_failed_so_try_e801:
    mov ax,0xe801
    int 0x15
    jc .e801_failed_so_try88

    mov cx,0x400
    mul cx
    shl edx,16
    and eax,0x0000FFFF
    or edx,eax
    add edx, 0x100000
    mov esi,edx

    xor eax,eax
    mov ax,bx
    mov ecx,0x10000
    mul ecx

    add esi,eax
    mov edx,esi
    jmp .mem_get_ok

;------ int 15h ah=0x88 ------
.e801_failed_so_try88:
    mov ah,0x88
    int 0x15
    jc .error_hlt
    and eax,0x0000FFFF

    mov cx,0x400
    mul cx
    shl edx,16
    or edx,eax
    add edx,0x100000

.mem_get_ok:
    mov [total_mem_bytes],edx



    ;---准备进入保护模式---
    ;---打开A20---
    in al,0x92
    or al,0000_0010B
    out 0x92,al
   
    ;---加载GDT---
    lgdt [gdt_ptr]
    
    ;---cr0第0个位置1
    mov eax,cr0 
    or eax,0x00000001
    mov cr0,eax 

    jmp dword SELECTOR_CODE:p_mode_start ;刷新流水线
.error_hlt
hlt
[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:800], 'P'
    jmp $

按照书中的调试方法进行调试。
得到结果:
32MB
更新:
loader.asm

%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 60 dq 0   ;预留60个描述符的位子

;选择子
SELECTOR_CODE equ  (0x0001<<3) + TI_GDT + RPL0
SELECTOR_DATA equ  (0x0002<<3) + TI_GDT + RPL0
SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0

total_mem_bytes dd 0 ;4 保存内存容量
;gdt指针
gdt_ptr dw  GDT_LIMIT ;6
        dd  GDT_BASE
ards_buf times 244 db 0 ;244 ards结构体缓存
ards_nr dw 0    ;2 ards结构体数量,一共 4+6+244+2 = 256字节

loader_start:
;==== 读取内存数
    ;int 15h eax,ebx,es:di,ecx,edx
    xor ebx,ebx  ;ARDS后续值,第一次为0
    mov edx,0x534d4150  ;固定为签名标记
    mov di,ards_buf     ;ards结构缓冲区
    .e820_mem_get_loop:
        mov eax,0x0000e820
        mov ecx,20      ;ards 地址范围描述符结构是20字节
        int 0x15

        jc .e820_failed_so_try_e801 ;若cf为1,则发生错误,常识0xe801

        add di,cx   ;cx是读到的结构缓冲区中数据的字节数
        inc word [ards_nr]
        cmp ebx,0   ;如果是0说明这是最后一个ARDS结构
        jnz .e820_mem_get_loop

        ;找出(base_add_low + length_low)的最大值,也就是内存容量
        mov cx,[ards_nr]
        mov ebx,ards_buf
        xor edx,edx
        .find_max_mem_area:
            mov eax,[ebx]
            add eax,[ebx+8]
            add ebx,20  ;ebx存放 ARDS缓存内容地址
            cmp edx,eax ;比较 edx 与 eax大小
            jge .next_ards
            mov edx,eax
            .next_ards:
            loop .find_max_mem_area
        jmp .mem_get_ok


    ;0x15 0xe801
    .e820_failed_so_try_e801:
        mov ax,0xe801
        int 0x15
        jc .e801_failed_so_try88
        ;1 先算出低15MB的内存
        mov cx,0x400;ax KB单位2^10 ,0x400-> 1024 
        mul cx
        shl edx,16
        and eax,0x0000FFFF
        or edx,eax 
        add edx, 0x100000 ;补上1MB
        mov esi,edx ;先放在esi中
        ;2 算>16MB的内存 bx 64KB为单位
        xor eax,eax
        mov ax,bx
        mov ecx,0x10000 ;将64KB单位转为byte
        mul ecx
        add esi,eax ;<=15 + >16

        jmp .mem_get_ok


    .e801_failed_so_try88:
        mov ah,0x88
        int 0x15
        jc .error_hlt
        and eax,0x0000FFFF ;ax*1024+1MB
        mov cx,0x400
        mul cx          ;ax*1024->dx,ax
        shl edx,16
        or edx,eax
        add edx,0x100000 ;+1MB
        
    .mem_get_ok:
        mov [total_mem_bytes],edx 
    .error_hlt:
    mov byte [gs:160],'2'
    mov byte [gs:162],' '
    mov byte [gs:164],'0'
    mov byte [gs:166],'x'
    mov byte [gs:168],'1'
    mov byte [gs:170],'5'
;====

;1 打开 A20
    in al,0x92
    or al,0x02
    out 0x92,al 

;2 加载 GDT
lgdt [gdt_ptr]

;3 cr0 pe置1
mov eax,cr0
or eax,0x01
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:320],'3'
    mov byte [gs:322],' '
    mov byte [gs:324],'P'
    mov byte [gs:326],'r'
    mov byte [gs:328],'o'
    mov byte [gs:330],'t'
    mov byte [gs:332],'e'
    mov byte [gs:334],'c'
    mov byte [gs:336],'t'
    mov byte [gs:338],'i'
    mov byte [gs:340],'o'
    mov byte [gs:342],'n'
    mov byte [gs:344],' '
    mov byte [gs:346],'m'
    mov byte [gs:348],'o'
    mov byte [gs:350],'d'
    mov byte [gs:352],'e'
    mov byte [gs:354],'.'


    jmp $

mbr.asm

%include "boot.inc"
section MBR vstart=0x7c00
	mov ax,cs
	mov ds,ax
	mov es,ax
	mov ss,ax
	mov fs,ax
	mov sp,0x7c00 
	mov ax,0xb800 
	mov gs,ax 

;使用 int 0x10 功能号 0x06 清屏
	mov ah,0x06
	mov al,0x00		;上卷行数 0->全部
	mov bl,0x00
	mov bh,0x07
	mov cx,0x0000 ;左上( 0, 0)
	mov dx,0x184f ;右下(80,25)
	int 0x10

; ---------- 在显存中输入信息 ---------
; 输出背景为 黄 色,前景 红 色,并且跳动的字符串 "HELLO WORLD"
	mov bx,0
	mov cx,14
	mov si,0
print:
	mov al,[s+si]
	mov [gs:bx],al
	inc bx
	mov byte [gs:bx],0x24
	inc bx
	add si,1
loop print
	
	mov eax, LOADER_START_SECTOR
	mov bx, LOADER_BASE_ADDR
	mov cx, 4

	call rd_disk_m_16
	jmp LOADER_BASE_ADDR+0x300

rd_disk_m_16:
	mov esi,eax
	mov di,cx

	;指定扇区数0x1f2 8bit
	mov dx,0x1f2
	mov al,cl 
	out dx,al

	mov eax,esi 
	;LBA 0-7 8bit
	mov dx,0x1f3
	out dx,al 
	;LBA 8-15 8bit
	mov cl,8
	shr eax,cl
	mov dx,0x1f4
	out dx,al
	;LBA 16-23
	shr eax,cl 
	mov dx,0x1f5
	out dx,al 
	;LBA Device | 24-27
	shr eax,cl 
	and al,0000_1111b
	or  al,1110_0000b ;LBA, DEV主盘
	mov dx,0x1f6
	out dx,al 

	;写入命令
	mov dx,0x1f7
	mov al,0x20
	out dx,al 

	;检测硬盘状态
.not_ready:
	nop ;相当于个停顿
	in al,dx 
	and al,1000_1000b ;忙/准备好
	cmp al,0000_1000b
jnz .not_ready

	;读数据
	mov ax,di
	mov dx,256 ;512/2
	mul dx 
	mov cx,ax ;一共ax个字节

	mov dx,0x1f0
.read_data:
	in ax,dx 
	mov [bx],ax 
	add bx,2
	loop .read_data
ret



s db "1 MBR_start!!!"
times 510 - ($-$$) db 0
db 0x55, 0xaa

运行结果:
在这里插入图片描述在这里插入图片描述
2024/2/23记录
loader.asm

%include "boot.inc"

section loader vstart=LOADER_BASE_ADDR

loader_start:
    
    call def_print_loader
    call def_read_card_num
    call def_open_A20
    call def_loader_GDT_set_PE
    jmp dword SELECTOR_CODE:p_mode_start


LOADER_STACK_TOP    equ LOADER_BASE_ADDR
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   60 dq   0;预留60个描述符的空位
;选择子
SELECTOR_CODE   equ (0x0001 << 3) + TI_GDT + RPL0
SELECTOR_DATA   equ (0x0002 << 3) + TI_GDT + RPL0
SELECTOR_VIDEO  equ (0x0003 << 3) + TI_GDT + RPL0

;gdt 指针
gdt_ptr dw  GDT_LIMIT
        dd  GDT_BASE


;===读取内存数
total_mem_bytes     dd  0;4 保存内存容量
ards_buf    times   244 db 0
ards_nr     dw      0

def_read_card_num:
    xor ebx,ebx
    mov edx,0x534d4150
    mov di,ards_buf
.e820_mem_get_loop:
    mov eax,0x0000e820
    mov ecx,20
    int 0x15

    jc .e820_failed_so_try_e801
    add di,cx
    inc word[ards_nr]
    cmp ebx,0
    jnz .e820_mem_get_loop
    ;找出(base_add_low + length_low)的最大值,也就是内存容量
    mov cx,[ards_nr]
    mov ebx,ards_buf
    xor edx,edx
.find_max_mem_area:
    mov eax,[ebx]
    add eax,[ebx+8]
    add ebx,20
    cmp edx,eax
    jge .next_ards
    mov edx,eax
.next_ards:
    loop .find_max_mem_area
    jmp .mem_get_ok
;0x15 0xe801
.e820_failed_so_try_e801:
    mov ax,0xe801
    int 0x15
    jc .e801_failed_so_try88
    ;1 先算出低15MB的内存
    mov cx,0x400 ;ax KB单位2^10, 0x400->1024
    mul cx
    shl edx,16
    and eax,0x0000FFFF
    or edx,eax
    add edx,0x100000
    mov esi,edx
    ;2 算 > 16MB 的内存 bx 64KB位单位
    xor eax,eax
    mov ax,bx
    mov ecx,0x10000;将64KB单位转为byte
    mul ecx
    add esi,eax ; <=15 + >16
    jmp .mem_get_ok
.e801_failed_so_try88:
    mov ah,0x88
    int 0x15
    jc .error_hlt
    and eax,0x0000FFFF
    mov cx,0x400
    mul cx
    shl edx,16
    or edx,eax
    add edx,0x10000 ;+1MB

.mem_get_ok:
    mov [total_mem_bytes], edx
.error_hlt:
    mov byte [gs:480],'['
    mov byte [gs:482],'4'
    mov byte [gs:484],']'
    mov byte [gs:486],' '
    
    mov al,[total_mem_bytes+3]
    add al,'0'
    mov byte [gs:488],'0'
    mov byte [gs:490],al
    
    mov al,[total_mem_bytes+2]
    add al,'0'
    mov byte [gs:492],'0'
    mov byte [gs:494],al
    
    mov al,[total_mem_bytes+1]
    add al,'0'
    mov byte [gs:496],'0'
    mov byte [gs:498],al
    
    mov al,[total_mem_bytes+0]
    add al,'0'
    mov byte [gs:500],'0'
    mov byte [gs:502],al
    ret



def_print_loader:
    mov bx,160
    mov cx,loader_str_len
    mov si,0
print_loader_loop:
    mov al, [loader_str + si]
    mov [gs:bx],al
    inc bx
    mov al, [loader_str_color]
    mov [gs:bx],al
    inc bx
    add si,1
    loop print_loader_loop
    ret
loader_str db '[2] This is loader code'
loader_str_len equ $-loader_str
loader_str_color db 0000_0111b

def_open_A20:
    in al,0x92
    or al,0x02
    out 0x92,al
    ret

def_loader_GDT_set_PE:
    lgdt[gdt_ptr]
    
    mov eax,cr0
    or eax,0x01
    mov cr0,eax
    ret

    [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:320],'['
    mov byte [gs:322],'3'
    mov byte [gs:324],']'
    mov byte [gs:326],' '
    mov byte [gs:328],'3'
    mov byte [gs:330],'2'
    mov byte [gs:332],' '
    mov byte [gs:334],'i'
    mov byte [gs:336],'n'
    mov byte [gs:338],'t'
    mov byte [gs:340],'o'
    jmp $

运行截图:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值