进入32-bit保护模式需要进行以下几个步骤:
使用cli指令可以屏蔽所有可屏蔽中断
在设计GDT那一节,我们设计了GDT描述符,用来确定GDT,而使用lgdt可以将GDT描述符导入GDT寄存器(GDTR,6个字节)中,之后GDTR就可以告诉CPU,GDT的各个参数为多少.
CR0是CPU的控制寄存器,其结构如下图
其中bit0,即PE(Protected Mode Enable)负责使能保护模式.
进入保护模式运行的前提是,“进入GDT中的代码段”,因此使用jmp CODESEG:某标签即可进入GDT中的代码段.
将所有段寄存器置为GDT中的数据段DATASEG
将栈底和栈顶更新为32-bit模式下的栈底和栈顶
本节保护模式下第一个有用的指令为32-bit模式下的VGA打印函数.
[bits 16]
switch_to_pm:
cli; 1. 禁止所有中断
lgdt [gdt_descriptor] ; 2. 载入GDT描述符, 注意GDT描述符有6个字节
mov eax, cr0
or eax, 0x1 ; 3. 使能保护模式,CR0的bit0
mov cr0, eax
jmp CODE_SEG:init_pm ; 4. 跳转到另一个段
[bits 32]
init_pm: ;现在处于32bit指令模式
; 5. 更新段寄存器内的值
mov ax, DATA_SEG
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000 ; 6. 在空闲区域设置栈
mov esp, ebp
call BEGIN_PM ; 7. 调用一个标签,该标签包含32bit保护模式下第一个有用的指令
32bit-main.asm 主程序
[org 0x7C00]
mov bp, 0x9000; 设置栈
mov sp, bp
mov bx, MSG_REAL_MODE
call print
call switch_to_pm
jmp $ ; 事实上,不会执行到这里
%include "../05-bootsector-functions-strings/boot_sect_print.asm"
%include "../09-32bit-gdt/32bit-gdt.asm"
%include "../08-32bit-print/32bit-print.asm"
%include "32bit-switch.asm"
[bits 32]
BEGIN_PM: ; 切换到保护模式之后就会执行这个标签
mov ebx, MSG_PORT_MODE
call print_string_pm ; 记得这个函数会将内容打印到屏幕左上角
jmp $
MSG_REAL_MODE db "Started in 16-bit real mode", 0
MSG_PORT_MODE db "Loaded 32-bit protected mode", 0
times 510 - ($ - $$) db 0
dw 0xAA55