linux2.4 啟動程式碼head.S分析
32位啟動程式碼,暫時不考慮SMP的情況。關鍵程式碼分析
頁目錄表的起始地址在0x101000,由於目前仍然處於真實模式,地址都是
實體地址
開始啟動核心
startup_32:
清方向標誌位
cld
用核心資料段的地址來初始化ds,es,fs,gs暫存器
巨集__KERNEL_DS在segment.h中有定義,對於i386體系結構來說__KERNEL_DS=0x18
movl $(__KERNEL_DS),%eax
movl %eax,%ds
movl %eax,%es
movl %eax,%fs
movl %eax,%gs
初始化頁表,由於程式中實用的符號的地址都是虛擬地址,所以$pg0 - __PAGE_OFFSET就是pg0的實體地址
movl $pg0-__PAGE_OFFSET,%edi
頁表項的值:該頁在記憶體中,使用者可寫
movl $007,%eax
進行初始化頁表
2: stosl
索引值加1
add $0x1000,%eax
初始化pg0和pg1兩張表
cmp $empty_zero_page-__PAGE_OFFSET,%edi
jne 2b
3:
頁目錄表的實體地址:$swapper_pg_dir-__PAGE_OFFSET
起始於0x101000
movl $swapper_pg_dir-__PAGE_OFFSET,%eax
頁目錄表的實體地址存入cr3暫存器中
movl %eax,%cr3
開啟分頁機制,重置cr0控制暫存器
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0
這樣做只是為了重新整理指令流水線,486之後採用了2條流水線,保證運算元是虛擬地址
這樣才能平穩過渡到保護模式
jmp 1f
1:
movl $1f,%eax
jmp *%eax
1:
初始化堆疊指標暫存器,核心堆疊結構:
task_union+核心資料段。task_union佔用8k
lss stack_start,%esp
初始化eax暫存器為0
xorl %eax,%eax
將為初始化資料段的其實地址存入edi暫存器中
movl $ SYMBOL_NAME(__bss_start),%edi
將核心映像的結束地址存入ecx中。
movl $ SYMBOL_NAME(_end),%ecx
將offset存入ecx中。
subl %edi,%ecx
對這段記憶體區域進行初始化操作,初始化為0
rep
stosb
設定中斷描述符表
call setup_idt
利用push/pop指令初始化eflags暫存器
pushl $0
popfl
將第三張頁表的首地址存入edi暫存器中
movl $ SYMBOL_NAME(empty_zero_page),%edi
一共需要初始化4k的記憶體。
前2k記憶體存放引導引數,後2kb記憶體存放命令列引數
movl $512,%ecx
cld
rep
movsl
將後2kb初始化為0
xorl %eax,%eax
movl $512,%ecx
rep
stosl
設定中斷描述符表子程式
setup_idt:
將預設中斷處理函式的有效地址放入edx暫存器中
lea ignore_int,%edx
初始化中斷門描述符
中斷處理程式入口地址放在0-15位
movl $(__KERNEL_CS << 16),%eax
核心程式碼段選擇符存放在16-31位
movw %dx,%ax
movw $0x8E00,%dx
將中斷描述符表的地址存入edi中
lea SYMBOL_NAME(idt_table),%edi
設定256項中斷描述符表項
mov $256,%ecx
rp_sidt:
設定表項,一個表項佔8位元組
movl %eax,(%edi)
movl %edx,4(%edi)
addl $8,%edi
dec %ecx
jne rp_sidt
ret
定義核心棧
ENTRY(stack_start)
.long SYMBOL_NAME(init_task_union)+8192
.long __KERNEL_DS
int_msg:
.asciz "Unknown interrupt, stack: %p %p %p %p/n"
ALIGN
定義預設中斷處理過程,僅僅列印"Unknown interrupt, stack: %p %p %p %p/n"
ignore_int:
cld
movl $(__KERNEL_DS),%eax
movl %eax,%ds
movl %eax,%es
pushl 12(%esp)
pushl 12(%esp)
pushl 12(%esp)
pushl 12(%esp)
pushl $int_msg
call SYMBOL_NAME(printk)
1: hlt
jmp 1b
定義中斷描述符表表項數量
#define IDT_ENTRIES 256
定義頁目錄表,首先定一個了2張頁表,用來對映核心記憶體空間
分別用於核心和使用者區使用,並且對映到相同的實體地址空間(0-8M),
但是不能通過使用者地址空間的虛擬地址來訪問核心空間,這樣做的原因是保證真實模式到保護模式的平穩過渡。
.org 0x1000
ENTRY(swapper_pg_dir)
.long 0x00102007
.long 0x00103007
.fill BOOT_USER_PGD_PTRS-2,4,0
.long 0x00102007
.long 0x00103007
.fill BOOT_KERNEL_PGD_PTRS-2,4,0
第一張頁表
由於在進入starup_32之前,reamponline.S已經將核心程式碼段設定成從1M開始了。真實模式 flush_instr:ljmpl $__KERNEL_CS, $0x00100000 符號地址在真實模式下:cs:offset
.org 0x2000
ENTRY(pg0)
第二張頁表
.org 0x3000
ENTRY(pg1)
兩張頁表對映8M空間
.org 0x4000
ENTRY(empty_zero_page)
定義全域性表述符表,因為linux採用的是分頁機制,所以在全域性描述符表中設定4個表項,
簡化分段到分頁的地址轉換,這時虛擬地址空間和線性地址空間是一樣的,都能表示4G
空間。在虛擬空間中,核心起始地址和使用者地址空間起始位置相同。
ENTRY(gdt_table)
.quad 0x0000000000000000 空描述表項,一般不用
.quad 0x0000000000000000 同上
.quad 0x00cf9a000000ffff 核心程式碼段
.quad 0x00cf92000000ffff 核心資料段
.quad 0x00cffa000000ffff 使用者程式碼段
.quad 0x00cff2000000ffff 使用者資料段