清華大學教學內核ucore學習系列(1) bootloader

ucore是清華大學操做系統課程的實驗內核,也是一個開源項目,是不可多得的很是好的操做系統學習資源php

https://github.com/chyyuu/ucore_lab.git, 各位同窗可使用git下載源碼和文檔。git

本文我會對項目中的code/lab1/boot/bootasm.S文件進行徹底註釋。github

 1 #include <asm.h>
asm.h頭文件中包含了一些宏定義,用於定義gdt,gdt是保護模式使用的全局段描述符表,其中存儲着段描述符。
2
3 # Start the CPU: switch to 32-bit protected mode, jump into C. 4 # The BIOS loads this code from the first sector of the hard disk into 5 # memory at physical address 0x7c00 and starts executing in real mode 6 # with %cs=0 %ip=7c00.
此段註釋說明了要完成的目的:啓動保護模式,轉入C函數。
這裏正好說了一下bootasm.S文件的做用。計算機加電後,由BIOS將bootasm.S生成的可執行代碼從硬盤的第一個扇區複製到內存中的物理地址0x7c00處,並開始執行。
此時系統處於實模式。可用內存很少於1M。
7 8 .set PROT_MODE_CSEG, 0x8 # kernel code segment selector
9 .set PROT_MODE_DSEG, 0x10 # kernel data segment selector
這兩個段選擇子的做用實際上是提供了gdt中代碼段和數據段的索引,具體怎麼用的下面用到的時候咱們詳細解釋
10 .set CR0_PE_ON, 0x1 # protected mode enable flag 這個變量也在下面用到的時候解釋

11 12 # start address should be 0:7c00, in real mode, the beginning address of the running bootloader 13 .globl start
14 start: 這兩行代碼至關於定義了C語言中的main函數,start就至關於main,BIOS調用程序時,從這裏開始執行

15 .code16 # Assemble for 16-bit mode
由於如下代碼是在實模式下執行,因此要告訴編譯器使用16位模式編譯。

16 cli # Disable interrupts 17 cld # String operations increment
關中斷,設置字符串操做是遞增方向。cld的做用是將direct flag標誌位清零,it means that instructions that autoincrement the source index and destination index (like MOVS) will increase both of them。

18 19 # Set up the important data segment registers (DS, ES, SS). 20 xorw %ax, %ax # Segment number zero ax寄存器就是eax寄存器的低十六位,使用xorw清零ax,效果至關於movw $0, %ax。 可是好像xorw性能好一些,google了一下沒有獲得好答案

21 movw %ax, %ds # -> Data Segment 22 movw %ax, %es # -> Extra Segment 23 movw %ax, %ss # -> Stack Segment 24
將段選擇子清零
25 # Enable A20: 26 # For backwards compatibility with the earliest PCs, physical 27 # address line 20 is tied low, so that addresses higher than 28 # 1MB wrap around to zero by default. This code undoes this. 準備工做就緒,下面開始動真格的了,激活A20地址位。先翻譯註釋:因爲須要兼容早期pc,物理地址的第20位綁定爲0,因此高於1MB的地址又回到了0x00000.
好了,激活A20後,就能夠訪問全部4G內存了,就可使用保護模式了。

怎麼激活呢,因爲歷史緣由A20地址位由鍵盤控制器芯片8042管理。因此要給8042發命令激活A20
8042有兩個IO端口:0x60和0x64, 激活流程位: 發送0xd1命令到0x64端口 --> 發送0xdf到0x60,done!

29 seta20.1:
30 inb $0x64, %al # Wait for not busy(8042 input buffer empty). 31 testb $0x2, %al 32 jnz seta20.1
發送命令以前,要等待鍵盤輸入緩衝區爲空,這經過8042的狀態寄存器的第2bit來觀察,而狀態寄存器的值能夠讀0x64端口獲得。
上面的指令的意思就是,若是狀態寄存器的第2位爲1,就跳到seta20.1符號處執行,知道第2位爲0,表明緩衝區爲空

33 34 movb $0xd1, %al # 0xd1 -> port 0x64 35 outb %al, $0x64 # 0xd1 means: write data to 8042's P2 port
發送0xd1到0x64端口
36 37 seta20.2: 38 inb $0x64, %al # Wait for not busy(8042 input buffer empty). 39 testb $0x2, %al 40 jnz seta20.2 41 42 movb $0xdf, %al # 0xdf -> port 0x60 43 outb %al, $0x60 # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 1 44
到此,A20激活完成。
45 # Switch from real to protected mode, using a bootstrap GDT 46 # and segment translation that makes virtual addresses 47 # identical to physical addresses, so that the 48 # effective memory map does not change during the switch. 轉入保護模式,這裏須要指定一個臨時的GDT,來翻譯邏輯地址。這裏使用的GDT經過gdtdesc段定義,它翻譯獲得的物理地址和虛擬地址相同,因此轉換過程當中內存映射不會改變

49 lgdt gdtdesc
載入gdt
50 movl %cr0, %eax 51 orl $CR0_PE_ON, %eax 52 movl %eax, %cr0
打開保護模式標誌位,至關於按下了保護模式的開關。cr0寄存器的第0位就是這個開關,經過CR0_PE_ON或cr0寄存器,將第0位置1
53 54 # Jump to next instruction, but in 32-bit code segment. 55 # Switches processor into 32-bit mode. 56 ljmp $PROT_MODE_CSEG, $protcseg 57
因爲上面的代碼已經打開了保護模式了,因此這裏要使用邏輯地址,而不是以前實模式的地址了。
這裏用到了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,因此通過映射後和轉換前的內存映射的物理地址同樣。

58 .code32 # Assemble for 32-bit mode 59 protcseg: 60 # Set up the protected-mode data segment registers 61 movw $PROT_MODE_DSEG, %ax # Our data segment selector 62 movw %ax, %ds # -> DS: Data Segment 63 movw %ax, %es # -> ES: Extra Segment 64 movw %ax, %fs # -> FS 65 movw %ax, %gs # -> GS 66 movw %ax, %ss # -> SS: Stack Segment 67
從新初始化各個段寄存器。 68 # Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00) 69 movl $0x0, %ebp 70 movl $start, %esp 71 call bootmain
棧頂設定在start處,也就是地址0x7c00處,call函數將返回地址入棧,將控制權交給bootmain
72 73 # If bootmain returns (it shouldn't), loop. 74 spin: 75 jmp spin 76 77 # Bootstrap GDT 78 .p2align 2 # force 4 byte alignment 79 gdt: 80 SEG_NULLASM # null seg 81 SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg for bootloader and kernel 82 SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg for bootloader and kernel 83 84 gdtdesc: 85 .word 0x17 # sizeof(gdt) - 1 86 .long gdt # address gdt
相關文章
相關標籤/搜索