在IA32模式下,CPU有两种工作模式:实模式和保护模式。实际上,当CPU加电时是工作在实模式下的,通过某种机制后才进入保护模式。在保护模式下,通过分段机制实现逻辑地址到线性地址的转换,通过分页机制实现线性地址到物理地址的转换。
具体是怎么转换的呢?
1、在了解怎么转换之前,实模式下是怎么寻址的呢?
8086CPU是16位的CPU,拥有16位的寄存器,16位的数据总线,20位的地址总线,寻址空间仅仅为1M(2^20)。一个地址由段和偏移两部分构成,指令操作的直接为物理地址。
物理地址 = 16*段地址 + 偏移地址
80386时代,Intel进入32位的时代,CPU的寻址能力达到了4G,为了与以前兼容,实模式下访问方式类似于8086CPU,而在保护模式下则采用段业机制访问物理内存。
2、保护模式的分段机制
先了解几个概念Descriptor(描述符)、GDT(全局描述符表)、GDTR(全局描述符寄存器)、Selector(选择子)、LDT(局部描述符表)、LDTR(局部描述符寄存器),可以参考此文帮助理解《GDT,LDT,GDTR,LDTR 详解,包你理解透彻》。
以下,为具体的调用模式。
当selector的TI位为0时,表示段描述符在GDT中,过程为1-> 2 -> 3。
当selector的TI位为0时,表示段描述符的LDT中,过程为1' --> 2' --> 3' --> 4' --> 5'
实际上,分段机制采用的思想类似于C语言中的指针和数组访问的思想。TI=0时,GDTR给出GDT的地址,selector选择GDT中某个描述符的具体位置,描述符中记录的才是某个段的32位段地址。例如:jmp cs:offset,实际上是selector <== cs,具体访问的
物理地址 = selector对应描述符中的32位段地址 + offset
3、分页机制
分页机制的开启控制位为,cr0寄存器的PG位,以后详述分页机制。
4、编程实现实模式转入保护模式
;/*
;nasm boot.asm -o boot.com
;*/
;
; 描述符
; usage: Descriptor Base, Limit, Attr
; Base: dd
; Limit: dd (low 20 bits available)
; Attr: dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3
dw %2 & 0FFFFh ; 段界限1
dw %1 & 0FFFFh ; 段基址1
db (%1 >> 16) & 0FFh ; 段基址2
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性1 + 段界限2 + 属性2
db (%1 >> 24) & 0FFh ; 段基址3
%endmacro ; 共 8 字节
DA_DRW EQU 92h ; 存在的可读写数据段属性值
DA_32 EQU 4000h ; 32 位段
DA_C EQU 98h ; 存在的只执行代码段属性值
org 0x0100
jmp LABEL_BEGIN
[section .gdt]
;GDT 段基址, 段界限, 段属性
LABEL_GDT_NULL: Descriptor 0, 0, 0 ;空描述符
LABEL_CODE_DESC: Descriptor 0, Code32Len, DA_C + DA_32 ;非一致代码段
LABEL_VEDIO_DESC: Descriptor 0B8000H, 0FFFFH, DA_DRW;显存
;GDT end
Gdtlen equ $ - LABEL_GDT_NULL
Gdtptr dw Gdtlen; GDTR段界限
dd 0 ;GDTR段基址
;GDT选择子
SelectorCode32 equ LABEL_CODE_DESC - LABEL_GDT_NULL
SelectorVedio equ LABEL_VEDIO_DESC - LABEL_GDT_NULL
;end section .gdt
[section .code16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov es, ax
mov ds, ax
mov ss, ax
mov sp, 0100h
;初始化32位代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_CODE32
mov word [LABEL_CODE_DESC + 2], ax
shr eax, 16
mov byte [LABEL_CODE_DESC + 4], al
mov byte [LABEL_CODE_DESC + 7], ah
;为加载GDTR做准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT_NULL
mov dword[Gdtptr + 2], eax
;加载gdtr
lgdt [Gdtptr]
;关中断
cli
;a20地址线打开
in al, 92h
or al, 00000010b
out 92h, al
;切换保护模式 准备
mov eax, cr0
or eax, 1
mov cr0, eax
;跳转到保护模式
jmp dword SelectorCode32:0
; end of section .code16
[section .code32]
[BITS 32]
LABEL_CODE32:
mov ax, SelectorVedio
mov gs, ax
xor ecx, ecx
mov cx, 20
print:
mov edi, (80*11 +10) ; 11行,10~30列,每个位置两个字节,打印20个‘P’
add edi, ecx
add edi, edi
mov ah, 0ch
mov al, 'P'
mov [gs:edi], ax
dec ecx
jnz print
jmp $
Code32Len equ $ - LABEL_CODE32
;end of .code32
命令nasm boot.asm -o boot.com,可以生成可在dos环境下运行的com文件。从bochs官网下载freedos映像文件,用WinImage工具将boot.com文件加入freedos.img 映像文件中,在bochs中运行freedos,执行boot.com命令。结果如下:
参考 《自己动手写操作系统》 于渊