ucore是清華大學作業系統課程的實驗核心,也是一個開源項目,是不可多得的非常好的作業系統學習資源
https://github.com/chyyuu/ucore_lab.git, 各位同學可以使用git下載源碼和文檔。
本文我會對項目中的code/lab1/boot/bootasm.S檔案進行完全注釋。
asm.h標頭檔中包含了一些宏定義,用於定義gdt,gdt是保護模式使用的全域段描述符表,其中儲存著段描述符。
# Start the switch to -bit protected mode, jump # The BIOS loads this code from the first sector of the hard disk # memory at physical address 0x7c00 starts executing # with %cs=此段注釋說明了要完成的目的:啟動保護模式,轉入C函數。
這裡正好說了一下bootasm.S檔案的作用。電腦加電後,由BIOS將bootasm.S產生的可執行代碼從硬碟的第一個扇區複製到記憶體中的物理地址0x7c00處,並開始執行。
此時系統處於實模式。可用記憶體不多於1M。 這兩個段選擇子的作用其實是提供了gdt中程式碼片段和資料區段的索引,具體怎麼用的下面用到的時候我們詳細解釋
# start address should be :7c00, 這兩行代碼相當於定義了C語言中的main函數,start就相當於main,BIOS調用程式時,從這裡開始執行
因為以下代碼是在實模式下執行,所以要告訴編譯器使用16位元模式編譯。
關中斷,設定字串操作是遞增方向。cld的作用是將direct flag標誌位清零,it means that instructions that autoincrement the source index and destination index (like MOVS) will increase both of them。
將段選擇子清零
# Enable # address line 好了,啟用A20後,就可以訪問所有4G記憶體了,就可以使用保護模式了。
怎麼啟用呢,由於曆史原因A20地址位由鍵盤控制器晶片8042管理。所以要給8042發命令啟用A20
8042有兩個IO連接埠:0x60和0x64, 啟用流程位: 發送0xd1命令到0x64連接埠 --> 發送0xdf到0x60,done!
inb $0x64, %al # for busy( seta20.發送命令之前,要等待鍵盤輸入緩衝區為空白,這通過8042的狀態寄存器的第2bit來觀察,而狀態寄存器的值可以讀0x64連接埠得到。
上面的指令的意思就是,如果狀態寄存器的第2位為1,就跳到seta20.1符號處執行,知道第2位為0,代表緩衝區為空白
outb %al, $0x64 # 0xd1 write data to 發送0xd1到0x64連接埠 s A20 bit(the bit) to
到此,A20啟用完成。
# # effective memory map does
載入gdt
開啟保護模式標誌位,相當於按下了保護模式的開關。cr0寄存器的第0位就是這個開關,通過CR0_PE_ON或cr0寄存器,將第0位置1
# Jump to next instruction, but # Switches processor 由於上面的代碼已經開啟了保護模式了,所以這裡要使用邏輯地址,而不是之前實模式的地址了。
這裡用到了PROT_MODE_CSEG, 他的值是0x8。根據段選擇子的格式定義,0x8就翻譯成:
INDEX TI CPL
0000 0000 0000 1 00 0
INDEX代表GDT中的索引,TI代表使用GDTR中的GDT, CPL代表處於特權級。
PROT_MODE_CSEG選擇子選擇了GDT中的第1個段描述符。這裡使用的gdt就是變數gdt,下面可以看到gdt的第1個段描述符的基地址是0x0000,所以經過映射後和轉換前的記憶體映射的物理地址一樣。
.code32 # Assemble for movw %ax, %ds # -> movw %ax, %es # -> movw %ax, %ss # ->
重新初始化各個段寄存器。 # Set up the stack pointer C. The stack region is from 棧頂設定在start處,也就是地址0x7c00處,call函數將返回地址入棧,將控制權交給bootmain # If bootmain returns (it shouldn