《真象还原》读书笔记——第四章 保护模式入门(代码部分)

本文详细介绍了从实模式切换到保护模式的过程,包括打开A20地址线、设置全局描述表(GDT)以及修改控制寄存器CR0的步骤。通过示例代码展示了如何定义GDT描述符、构建GDT以及最终进入保护模式。代码涉及了16位和32位模式的转换,是理解x86体系结构和操作系统启动原理的重要参考。
摘要由CSDN通过智能技术生成

4.3.5 让我们进入保护模式

  1. 打开A20
  2. 加载GDT
  3. 将cr0的PE位置置为1
三个文件代码
1. boot.inc
;---loader 和 kernel ---

LOADER_BASE_ADDR    equ 0x900
LOADER_START_SECTOR equ 0x2
LOADER_STACK_TOP    equ LOADER_BASE_ADDR

;--- gdt描述符属性 ---   
DESC_G_4K           equ 100000000000000000000000b
DESC_D_32           equ  10000000000000000000000b
DESC_L              equ   0000000000000000000000b 

;64位代码标记
DESC_AVL            equ    000000000000000000000b 
DESC_LIMIT_CODE2    equ     11110000000000000000b
DESC_LIMIT_DATA2    equ     DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2   equ      0000000000000000000b 
DESC_P              equ         1000000000000000b 
DESC_DPL_0          equ          000000000000000b 
DESC_DPL_1          equ          010000000000000b 
DESC_DPL_2          equ          100000000000000b 
DESC_DPL_3          equ          110000000000000b
DESC_S_CODE         equ            1000000000000b 
DESC_S_DATA         equ             DESC_S_CODE
DESC_S_sys          equ            0000000000000b 
DESC_TYPE_CODE      equ             100000000000b

;x1,e0,w1,a0,代码段,非一致性
DESC_TYPE_DATA      equ             001000000000b 

;x0,e0,w1,a0,数据段,向上延伸
DESC_CODE_HIGH4     equ           (0x00<<24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
DESC_P + DESC_DPL_0 + DESC_S_CODE + \
DESC_TYPE_CODE + 0x00

DESC_DATA_HIGH4     equ           (0x00<<24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + \
DESC_TYPE_DATA + 0x00

DESC_VIDEO_HIGH4    equ           (0x00<<24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + \
DESC_TYPE_DATA + 0x0b


;---选择子属性---
RPL0    equ 00b 
RPL1    equ 01b 
RPL2    equ 10b 
RPL3    equ 11b 
TI_GDT   equ 000b
TI_LDT   equ 100b



2. mbr.asm 39行别忘了扩大cx改为4,意味着读取4块区域数据

%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
    
    ;===读取硬盘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

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

mov byte[gs:320],'J'
mov byte[gs:322],'M'
mov byte[gs:324],'P'

jmp loader_start 

;------------------------------
;构建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  ;相当于 (CODE_DESC-GDT_BASE)/8+TI_GDT+RPL0
SELECTOR_DATA       equ (0x0002<<3)+TI_GDT+RPL0
SELECTOR_VIDEO      equ (0x0003<<3)+TI_GDT+RPL0 

gdt_ptr             dw GDT_LIMIT
                    dd GDT_BASE

loadermsg           db '2 loader in real.'

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

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

    ;---准备进入保护模式---
    ;---打开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 ;刷新流水线

[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 $


编译文件
nasm 该文件路径/mbr.asm -o 该文件路径/mbr.bin
nasm 该文件路径/loader.asm -o 该文件路径/loader.bin
写入启动盘
;尽管只有762byte,只用了两块512,我个人在这里写入4块。
dd if=该文件路径/mbr.bin of=该文件路径/hd60M.img bs=512 count=1 seek=0 conv=notrunc
dd if=该文件路径/loader.bin of=该文件路径/hd60M.img bs=512 count=4 seek=2 conv=notrubc
运行结果

因为红字绿底是闪耀状态,所以截图两张。
在这里插入图片描述
在这里插入图片描述

更新:
boot.inc

; loader and kernel
LOADER_BASE_ADDR    equ 0x900
LOADER_START_SECTOR equ 0x2

; gdt 属性 
DESC_G_4K   equ 0x800000
DESC_D_32   equ 0x400000
DESC_L      equ 0x000000 ;是否开启64位代码
DESC_AVL    equ 0x000000 ;可用的

DESC_LIMIT_CODE2    equ 0xF0000
DESC_LIMIT_DATA2    equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2   equ 0x00000

DESC_P      equ 0x8000
DESC_DPL_0  equ 0x0000
DESC_DPL_1  equ 0x2000
DESC_DPL_2  equ 0x4000
DESC_DPL_3  equ 0x6000

DESC_S_CODE equ 0x1000
DESC_S_DATA equ DESC_S_CODE ;相同的

DESC_S_SYS  equ 0x0000
DESC_TYPE_CODE  equ 0x800 ;x1 e0 w0 a0
DESC_TYPE_DATA  equ 0x200 ;x0 e0 w1 a0
DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
    DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
    DESC_P + DESC_DPL_0 + DESC_S_CODE + \
    DESC_TYPE_CODE + 0x00

DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
    DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + \
    DESC_P + DESC_DPL_0 + DESC_S_DATA + \
    DESC_TYPE_DATA + 0x00

DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
    DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + \
    DESC_P + DESC_DPL_0 + DESC_S_DATA + \
    DESC_TYPE_DATA + 0x0b

;选择子 属性
RPL0    equ 00b 
RPL1    equ 01b
RPL2    equ 10b
RPL3    equ 11b

TI_GDT  equ 000b
TI_LDT  equ 100b

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

;gdt指针
gdt_ptr dw  GDT_LIMIT
        dd  GDT_BASE
loadermsg   db '2 loader in real.'


loader_start:
    mov sp,LOADER_BASE_ADDR
    mov bx,160
	mov cx,17
	mov si,0
    print_loader:
        mov al,[loadermsg+si]
        mov [gs:bx],al
        inc bx
        mov byte [gs:bx],0x24
        inc bx
        add si,1
    loop print_loader

;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 $

运行结果:
运行结果

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值