最近在研究X86架构保护模式,记录下研究过程,然后写了一个汇编启动程序来验证保护模式下,指令执行方式。
提到保护模式就不得不提 “实模式”,当处理器加电后会从一个地址0xfff0执行一个跳转jmp指令到0x7c00地址处执行启动扇区指令,实模式下段寄存器长度为16位,一次只能寻址到64KB内存。
实模式下寻址方式:
段地址(存在段寄存器)*16+偏移来定位一个内存地址。
mov ax,0x0
mov ds,ax
mov byte [0x100],'a'
以上代码将'a' 写入地址 为ds:0x100的内存区域,那么这个地址为 0x0*16+0x100=0x100
进入保护模式后就不能通过这种方式寻址了。
保护模式寻址方式
段选择子加段内偏移
段段子子同样存放于段寄存器中,它不再是一个地址,而是一个索引,指向全局描述符表的某一项,GDT(全局描述符表)。
GDT 里面存放段描述符,段描述符里存放有段基址。
段描述符格式
以上是Intel手册中的关于全局描述符表和段描述符数据结构。
进入保护模式流程
1.设置GDT
2.设置CR0 ,PE位=1 进入保护模式
3.跳转到保护模式下指令(执行长跳转清空指令流
将汇编代码编译bin格式后,写入USB存储,通过在BIOS设置为USB启动,在电脑屏幕上显示LoadProect加载保护模式成功
保护模式汇编代码 保护模式测试代码
%define CODESEG 0x7c00 ;代码段地址
mov word bx,ds:[gdt_table+CODESEG]
;描述符1 代码段描述符
mov word [bx+8],0x0 ;段长度
mov word [bx+10],0x7c00 ;段基址
mov word [bx+12],0x9a00
mov word [bx+14],0xc0 ;段颗粒度 4KB
;创建#3描述符,保护模式下的堆栈段描述符
mov dword [bx+0x18],0x00007a00
mov dword [bx+0x1c],0x00409600
;GDTR表长度
mov word [cs: gdt_addr+CODESEG],31
lgdt [cs: gdt_addr+CODESEG] ;加载GDTR地址到寄存器
cli ;禁止中断
;开启保护模式
mov eax,cr0
or eax,1
mov cr0,eax
;跳转至保护模式下代码 16段选择子:32位偏移
jmp dword 0x0008:protect_start
bits 32
protect_start:
mov cx,0x10 ;加载数据段
mov ds,cx
call clear_console
;向显示缓冲区写入 LoadProtect 以显示到屏幕上
mov byte ds:[0],'L'
mov word ds:[1],0x7
mov byte ds:[2],'o'
mov word ds:[3],0x7
mov byte ds:[4],'a'
mov word ds:[5],0x7
mov byte ds:[6],'d'
mov word ds:[7],0x7
mov byte ds:[8],'P'
mov word ds:[9],0x7
mov byte ds:[10],'r'
mov word ds:[11],0x7
mov byte ds:[12],'o'
mov word ds:[13],0x7
mov byte ds:[14],'t'
mov word ds:[15],0x7
mov byte ds:[16],'e'
mov word ds:[17],0x7
mov byte ds:[18],'c'
mov word ds:[19],0x7
mov byte ds:[20],'t'
mov word ds:[21],0x7
jmp end
end:
jmp end
;清空输出缓冲区
clear_console:
push eax
push edi
xor eax,eax
mov al,0x0
mov ecx,0x500
mov edi,0xb8000
cld
rep stosb
pop edi
pop eax
ret
gdt_addr dw 0x0;段限长度
gdt_table dd (gdt+CODESEG);GDT的物理地址
gdt:
;描述符0 空描述符
dd 0x0,0x0
;代码段描述符
dd 0x0,0x0
;显示缓冲区段描述符
view_seg:
dw 0xffff ;段限长
dw 0x8000 ;段基址 00-15
db 0x0b ;段基地 16-23
db 0x92;段属性 P-1 DPL-2 S-1 TYPE-4
db 0x40;Segment Limit-4 AVL-1 L-1 DB-1 G-1
db 0x0 ;段基址
times 510-($-$$) db 0
db 0x55,0xaa