Linux-0.11 boot目錄head.s詳解
模塊簡介
在head.s中,操作系統(tǒng)主要做了如下幾件事:
- 重新設置中斷描述符和全局描述符
- 檢查A20地址線是否開啟
- 檢查數學協(xié)處理器
- 初始化頁表并開啟分頁
- 跳轉到main函數執(zhí)行
過程詳解
重新設置IDT和GDT
在setup.s中我們已經設置過了IDT和GDT, 為什么還要再設置一遍?
因為setup.s中設置的IDT和GDT后面會被覆蓋,因此在head.S中會重新設置一遍。
startup_32:
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
lss stack_start,%esp
call setup_idt !設置中斷
call setup_gdt !設置全局描述符表
movl $0x10,%eax # reload all the segment registers
mov %ax,%ds # after changing gdt. CS was already
mov %ax,%es # reloaded in 'setup_gdt'
mov %ax,%fs
mov %ax,%gs
lss stack_start,%esp
中斷門描述符的格式如下所示:
檢查A20地址線是否開啟
下面用于檢測A20地址線是否已經開啟。
xorl %eax,%eax
1: incl %eax # check that A20 really IS enabled
movl %eax,0x000000 # loop forever if it isn't
cmpl %eax,0x100000
je 1b
檢查數學協(xié)處理器
下面用于檢查數學協(xié)處理器芯片是否存在
movl %cr0,%eax # check math chip
andl $0x80000011,%eax # Save PG,PE,ET
/* "orl $0x10020,%eax" here for 486 might be good */
orl $2,%eax # set MP
movl %eax,%cr0
call check_x87
jmp after_page_tables
/*
* We depend on ET to be correct. This checks for 287/387.
*/
check_x87:
fninit !向協(xié)處理發(fā)出初始化命令
fstsw %ax !取協(xié)處理器狀態(tài)字到ax寄存器中
cmpb $0,%al
je 1f /* no coprocessor: have to set bits */
movl %cr0,%eax
xorl $6,%eax /* reset MP, set EM */
movl %eax,%cr0
ret
初始化頁表并開啟分頁
下面這里將進行頁表的安裝,安裝的過程參考下面這張圖:
after_page_tables:
pushl $0 # These are the parameters to main :-)
pushl $0
pushl $0
pushl $L6 # return address for main, if it decides to.
pushl $main
jmp setup_paging
setup_paging:
movl $1024*5,%ecx /* 5 pages - pg_dir+4 page tables */
xorl %eax,%eax
xorl %edi,%edi /* pg_dir is at 0x000 */
cld;rep;stosl
movl $pg0+7,pg_dir /* set present bit/user r/w */
movl $pg1+7,pg_dir+4 /* --------- " " --------- */
movl $pg2+7,pg_dir+8 /* --------- " " --------- */
movl $pg3+7,pg_dir+12 /* --------- " " --------- */
movl $pg3+4092,%edi
movl $0xfff007,%eax /* 16Mb - 4096 + 7 (r/w user,p) */
std
1: stosl /* fill pages backwards - more efficient :-) */
subl $0x1000,%eax
jge 1b
cld
xorl %eax,%eax !設置頁目錄表基址寄存器cr3的值
movl %eax,%cr3
movl %cr0,%eax !設置啟動使用分頁處理
orl $0x80000000,%eax
movl %eax,%cr0 /* set paging (PG) bit */
ret /* this also flushes prefetch-queue */
跳轉到main函數執(zhí)行
在setup_paging執(zhí)行完畢之后,會通過ret返回,ret指令會將棧頂的內容彈出到PC指針中去執(zhí)行。此時esp指向的位置存放的是main函數的地址。因此接下來會執(zhí)行main函數。
注意到在將main入棧時,還一同入棧了一些其他參數
pushl $0 # These are the parameters to main :-)
pushl $0
pushl $0
pushl $L6
這里就需要回顧一下c語言的調用規(guī)約,如下圖所示:
因此這里可以得到L6是main函數的返回值。立即數0,0,0將會被作為main函數的入參。
接下來再看下面的代碼就很清晰了,實際就是在建立好頁表的映射關系后,就開始跳轉到main函數去執(zhí)行了(init/main.c)。
after_page_tables:
pushl $0 # These are the parameters to main :-)
pushl $0
pushl $0
pushl $L6 # return address for main, if it decides to.
pushl $main
jmp setup_paging
setup_paging:
...
ret
Q & A
setup_paging在建立頁表時會將head.s的部分代碼覆蓋,怎么保證不會把正在執(zhí)行的代碼覆蓋?
可以通過反匯編查看一下system模塊的內存分布
objdump -d tools/system
如下所示:
00000000 <pg_dir>:
0: b8 10 00 00 00 mov $0x10,%eax
5: 8e d8 mov %eax,%ds
...
0000005a <check_x87>:
5a: db e3 fninit
5c: 9b df e0 fstsw %ax
5f: 3c 00 cmp $0x0,%al
...
00000071 <setup_idt>:
71: 8d 15 28 54 00 00 lea 0x5428,%edx
77: b8 00 00 08 00 mov $0x80000,%eax
...
0000008e <rp_sidt>:
8e: 89 07 mov %eax,(%edi)
90: 89 57 04 mov %edx,0x4(%edi)
...
000000a1 <setup_gdt>:
a1: 0f 01 15 b2 54 00 00 lgdtl 0x54b2
a8: c3 ret
...
00001000 <pg0>:
...
00002000 <pg1>:
...
00003000 <pg2>:
...
00004000 <pg3>:
...
00005000 <tmp_floppy_area>:
...
00005400 <after_page_tables>:
5400: 6a 00 push $0x0
5402: 6a 00 push $0x0
...
00005412 <L6>:
5412: eb fe jmp 5412 <L6>
00005414 <int_msg>:
5414: 55 push %ebp
5415: 6e outsb %ds:(%esi),(%dx)
...
00005428 <ignore_int>:
5428: 50 push %eax
5429: 51 push %ecx
...
0000544e <setup_paging>:
544e: b9 00 14 00 00 mov $0x1400,%ecx
5453: 31 c0 xor %eax,%eax
5455: 31 ff xor
...
000054aa <idt_descr>:
54aa: ff 07 incl (%edi)
54ac: b8 54 00 00 00 mov $0x54,%eax
...
000054b2 <gdt_descr>:
54b2: ff 07 incl (%edi)
54b4: b8 .byte 0xb8
54b5: 5c pop %esp
...
000054b8 <idt>:
...
00005cb8 <gdt>:
...
5cc0: ff 0f decl (%edi)
可以看到代碼標號setup_page的起始地址是0000544e,而內存頁表和頁目錄表的地址范圍是0x0000-0x5000。因此當程序執(zhí)行到setup_page時,將建立頁目錄表和頁表, 這將會覆蓋0x0000-0x5000的部分代碼,即pg_dir,check_x87,setup_idt,rp_sidt,setup_gdt, 并不會覆蓋到setup_page的代碼,head.s在代碼的分布計算上確實是費了一番功夫。文章來源:http://www.zghlxwxcb.cn/news/detail-461982.html
文中如有表達不正確之處,歡迎大家與我交流, 微信codebuilding.文章來源地址http://www.zghlxwxcb.cn/news/detail-461982.html
到了這里,關于Linux-0.11 boot目錄head.s詳解的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!