從%cs=0 $pc=0x7c00
,bootloader的首地址進入後
首先清理環境:包括將flag置0和將段寄存器置0
.code16
cli
cld
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
開啓A20:通過將鍵盤控制器上的A20線置於高電位,全部32條地址線可用, 可以訪問4G的內存空間。
爲什麼是A20呢?這是一個歷史問題。8086是用SEG:OFFSET這樣的模式分段的,所以,它能表示的最大內存是FFFF:FFFF,即10FFEFh。但8086只有20位的地址總線,智能尋址到1MB,如果試圖訪問超過1MB的地址會回捲(wrap),就是重新從地址零開始尋址。而到了80386時,就可以訪問1MB以上的內存了,就不會再回卷,需要向上兼容,使用8042鍵盤控制器來控制第20個(從零開始數)地址位,這就是A20地址線,如果不被打開,第20個地址爲將會總是零。
所以爲了訪問所有的內存,需要開啓A20,開機時它默認是關閉的。
seta20.1: # 等待8042鍵盤控制器不忙
inb $0x64, %al #
testb $0x2, %al #
jnz seta20.1 #
movb $0xd1, %al # 發送寫8042輸出端口的指令
outb %al, $0x64 #
seta20.1: # 等待8042鍵盤控制器不忙
inb $0x64, %al #
testb $0x2, %al #
jnz seta20.1 #
movb $0xdf, %al # 打開A20
outb %al, $0x60 #
初始化GDT表:將gdtdesc指示的6字節加載到寄存器gdtr,一個簡單的GDT表和其描述符已經靜態儲存在引導區中,載入即可。
lgdt gdtdesc
進入保護模式:通過將cr0寄存器PE位置1便開啓了保護模式
movl %cr0, %eax
orl $CR0_PE_ON, %eax
movl %eax, %cr0
通過長跳轉更新cs的基地址(進入32位保護模式)
ljmp $PROT_MODE_CSEG, $protcseg
.code32
protcseg:
設置段寄存器,並建立堆棧
movw $PROT_MODE_DSEG, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
movl $0x0, %ebp
movl $start, %esp
轉到保護模式完成,進入boot主方法
call bootmain
Q:在實模式下,16位的寄存器(8086)需要用到 “SEG:OFFSET”這種方法才能達到1MB的尋址能力,現在我們有32位寄存器(80386),尋址空間達到了4GB,是否此段值就被拋棄了?
A:沒有,在32位中仍然用“SEG:OFFSET”的形式來表示,只不過保護模式下“段”的概念發生了根本性的變化。實模式下,段值可以看作是地址的一部分,段值爲XXXXh表示以XXXX0h開始的一段內存。而保護模式下,如果段值仍然由原來16位的cs、ds等寄存器表示,但此時它僅僅變成了一個索引,這個索引指向一個GDT的數據結構的表項(描述符),表項中詳細定義了段的起始地址、界限、屬性等內容。
關於“保護模式”的幾點理解: