;加载gdtr和idtr
;在这里禁用中断,因为无法在转换到保护模式之前位于实模式且idt已被载入的情况下处理中断
cli
lgdt fword ptr [_GDTregister]
lidt fword ptr [_IDTregister]
; We have to stamp the segment portion of any real-mode far pointer withthe corresponding selector values before we go protected.
mov si,offset _ScreenStart
mov word ptr [si+2],VideoSelector
mov si,offset _vp
mov word ptr [si+2],VideoSelector
; 开启保护模式和分页机制
mov eax,cr0
; 如果是第一次开启保护模式,那么无需开启分页机制,因为osloader已经做好一切
; 如果代码是返回保护模式,分页表已经设置完毕,同样无需开启
or dx,dx
jz only_prot
or eax,PROT_MODE + ENABLE_PAGING
mov cr0,eax
; 接下来代码中的JMP必须是双字对齐,为了避免触发一个i386的硬件bug
; 否则有可能使得预取队列混乱
ALIGN 4
jmp flush
only_prot:
or eax,PROT_MODE
mov cr0,eax
; 刷新预取队列
ALIGN 4
jmp flush
flush:
; 将寄存器CS作为SU模块的代码选择子
push SuCodeSelector
push offset cs:restart
retf
; 将寄存器DS和SS作为SU模块的保护模式数据选择子.
restart:
mov ax,SuDataSelector
mov ds,ax
mov ss,ax
; 加载LDT为0,因为从未被使用.
xor bx,bx
lldt bx
; 加载任务寄存器
or dx,dx
jnz epp10
mov bx,TSS_Selector
ltr bx
epp10:
ret;返回之后介绍的su.asm中的模块
_EnableProtectPaging endp
… …
public _RealMode
_RealMode proc near
; 转换到实模式
sgdt fword ptr [_GDTregister]
sidt fword ptr [_IDTregister]
push [saveDS] ; 将saveDS入栈,方便之后的跳转
mov ax,SuDataSelector
mov es,ax
mov fs,ax
mov gs,ax
mov eax,cr0
and eax, not (ENABLE_PAGING + PROT_MODE)
mov cr0,eax
; 刷新流水线
jmp far ptr here
here:
; Flush TLB
; We don't know where the page directory is, since it wasallocated in the osloader.
; So we don't want to clear out cr3,but we DO want to flush the TLB....
mov eax,cr3
nop ; Fill - Ensure 13 non-page split
nop ; accesses before CR3 load
nop
nop
mov cr3,eax
; 转换为实模式地址
; 此处需要一个远跳转而不是指令retf,因为retf不能正确地重置访问权限为CS
db 0EAh ; JMP FAR PTR
dw offset _TEXT:rmode ; 2000:rmode
dw 02000h
rmode:
pop ax
mov ds,ax
mov ss,ax
; Stamp video pointers for real-mode use
mov si,offset _ScreenStart
mov word ptr [si+2],0b800h
mov si,offset _vp
mov word ptr [si+2],0b800h
lidt fword ptr [_IDTregisterZero]
sti
ret
_RealMode endp
Ntldr 的实模式代码首先获得控制,它的任务是,完成需在16 位模式下执行的初始化工作,例如清除键盘缓冲区,然后为切换到保护模式做好基本的环境准备,之后将处理器切换到保护模式(32 位模式)下,这样它就可以访问整个32 位地址空间了。最后它将控制权交给os loader。
; _TransferToLoader ;该子程序将控制权交给osloader
public _TransferToLoader
_TransferToLoader proc near
mov ebx,dword ptr [esp+2] ; 获取入口点参数
xor eax,eax
mov ax,[saveDS]
; 设置osloader的堆栈
mov cx,KeDataSelector
mov ss,cx
mov esp,LOADER_STACK
; 加载ds和es作为内核数据选择子
mov ds,cx
mov es,cx
; 设置指向文件系统和引导上下文记录的指针
shl eax,4
xor ecx,ecx
mov cx,offset _BootRecord
add eax,ecx
push eax
push 1010h ; 压入假的返回地址
; 将一个48位的地址传给loader的入口点
db OVERRIDE
push KeCodeSelector
push ebx
; 将控制权交还OS loader
db OVERRIDE
retf
_TransferToLoader endp