- Linux內核精析
- 鄭阿奇主編
- 1213字
- 2019-01-01 07:31:56
2.2 實模式setup階段
setup用于體系結構相關的硬件的初始化工作。在32位x86平臺,setup的入口點是arch\x86\boot\header.s中的_start,Boot Loader把setup代碼加載到0x07c00上的某處后,將DS寄存器設置為該處的地址,之后再跳轉到DS:200把控制器交由_start,最后跳轉到start_of_setup。相關代碼在arch\x86\boot\header.s中的定義如下:
.section ".inittext", "ax" start_of_setup: #ifdef SAFE_RESET_DISK_CONTROLLER # 復位磁盤控制器 movw $0x0000, %ax # 復位磁盤控制器 movb $0x80, %dl # 所有的磁盤 int $0x13 # endif # Force %es = %ds movw %ds, %ax movw %ax, %es cld # 如果%ss無效,則重新計算該堆棧指針 movw %ss, %dx cmpw %ax, %dx # %ds == %ss? movw %sp, %dx je 2f # 如果%ss無效,則建立新堆棧 movw $_end, %dx testb $CAN_USE_HEAP, loadflags jz 1f movw heap_end_ptr, %dx 1: addw $STACK_SIZE, %dx jnc 2f xorw %dx, %dx # 防止死循環(huán) 2: # %dx應該指向堆棧空間的棧頂 andw $~3, %dx # 雙字對齊 jnz 3f movw $0xfffc, %dx # 確保%dx非零 3: movw %ax, %ss movzwl %dx, %esp # 清除%esp的上半部 sti # 獲取工作堆棧 pushw %ds pushw $6f lretw 6: cmpl $0x5a5aaa55, setup_sig jne setup_bad # 清空bss movw $_bss_start, %di movw $_end+3, %cx xorl %eax, %eax subw %di, %cx shrw $2, %cx rep; stosl # 跳轉到C代碼 call main
在該代碼中,Boot Loader跳轉到該段代碼時,DS已經(jīng)設置為setup加載的基地址,最后跳轉到C代碼的main函數(shù)。main函數(shù)在arch\x86\boot\main.c文件中的定義如下:
void main(void) { /* 首先把第一個扇區(qū)的參數(shù)復制到boot_params中 */ copy_boot_params(); /* 堆棧結束檢查 */ if (boot_params.hdr.loadflags & CAN_USE_HEAP) { heap_end = (char *)(boot_params.hdr.heap_end_ptr +0x200-STACK_SIZE); } else { puts("WARNING: Ancient bootloader, some functionality " "may be limited!\n"); } /* 確保有適合CPU的支持 */ if (validate_cpu()) { puts("Unable to boot-please use a kernel appropriate" "for your CPU.\n"); die(); } /* 通知BIOS將要運行的CPU模式 */ set_bios_mode(); /* 檢測內存的布局 */ detect_memory(); /* 設置鍵盤重復率 */ keyboard_set_repeat(); /* 設置視頻模式 */ set_video(); /* 查詢MCA信息 */ query_mca(); #ifdef CONFIG_X86_VOYAGER query_voyager(); #endif query_ist(); /* 查詢APM信息 */ #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) query_apm_bios(); #endif /* 查詢EDD信息 */ #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) query_edd(); #endif /* 進入保護模式 */ go_to_protected_mode(); }
該函數(shù)首先通過copy_boot_params()函數(shù)把位于第一個扇區(qū)的參數(shù)復制到boot_params中,該變量位于setup的數(shù)據(jù)段,之后設置系統(tǒng)硬件的相關參數(shù),最后調用go_to_protected_mode()函數(shù)進入保護模式。對于copy_boot_params()函數(shù)的定義如下:
[main-> copy_boot_params] static void copy_boot_params(void) { struct old_cmdline { u16 cl_magic; u16 cl_offset; }; const struct old_cmdline * const oldcmd = (const struct old_cmdline *)OLD_CL_ADDRESS; BUILD_BUG_ON(sizeof boot_params != 4096); /* 把hdr結構中的參數(shù)復制到boot_params.hdr中 */ memcpy(&boot_params.hdr, &hdr, sizeof hdr); … }
該函數(shù)的主要操作是調用memcpy(&boot_params.hdr, &hdr, sizeof hdr)把hdr結構 中 的 參 數(shù) 復 制 到boot_params.hdr中。回 到main() 函 數(shù) 最 后 調 用 的go_to_protected_mode()函數(shù)在文件arch\x86\boot\Pm.c中的定義如下:
[main->go_to_protected_mode] void go_to_protected_mode(void) { /* 在離開實模式前,需要禁用中斷 */ realmode_switch_hook(); /* 將內核/setup移到其最后的區(qū)域 */ move_kernel_around(); /* 開啟A20門 */ if (enable_a20()) { puts("A20 gate not responding, unable to boot...\n"); die(); } /* 復位處理器 */ reset_coprocessor(); /* 屏蔽PIC的所有中斷 */ mask_all_interrupts(); /* 開始進入保護模式 */ setup_idt(); setup_gdt(); //跳轉至code32_start執(zhí)行 protected_mode_jump(boot_params.hdr.code32_start, (u32)&boot_params + (ds() << 4)); }
該函數(shù)首先調用realmode_switch_hook()函數(shù),之后進行復位處理器及屏蔽PIC中斷等操作,最后調用protected_mode_jump()開啟CR0寄存器的PE位進入保護模式,并跳轉到code32_start執(zhí)行。參數(shù)boot_params.hdr.code32_start在header.s中的定義如下:
… code32_start: # 32位代碼的起始地址 #ifndef_BIG_KERNEL_ .long 0x1000 # 0x1000 = zImage默認地址 #else .long 0x100000 # 0x100000 =大內核的默認地址(bzImage) #endif …
從該段代碼可以看出,程序將跳轉到0x1000或0x100000處繼續(xù)執(zhí)行;參數(shù)(u32)&boot_params + (ds() << 4) 為boot_params的線性地址,由于此時仍為實模式,因此線性地址為段地址左移4 位(乘以16)再加上偏移。protected_mode_jump在arch\x86\boot \pmjump.S中的定義如下:
/* * void protected_mode_jump(u32 entrypoint, u32 bootparams); */ protected_mode_jump: movl %edx, %esi # 指向boot_params表 movl %eax, 2f # 2f處放置將要跳轉的指令 … movl %cr0, %edx orb $1, %dl # 保護模式(PE)位 movl %edx, %cr0 jmp 1f 1: movw %cx, %ds movw %cx, %es movw %cx, %fs movw %cx, %gs movw %cx, %ss # 跳轉至32位入口 .byte 0x66, 0xea # 跳轉指令操作碼 2: .long 0 # 偏移 .word _BOOT_CS # 段 .size protected_mode_jump, .-protected_mode_jump
由于arch\x86\kernel\head_32.S是編譯到內核vmlinux中的,然后vmlinux經(jīng)objcopy處理再經(jīng)過壓縮,最后被當做數(shù)據(jù)段的內容和arch\x86\boot\compressed\head_32.0鏈接,因此執(zhí)行arch\x86\boot\compressed\head_32.S中的startup_32,將數(shù)據(jù)段中的壓縮內核解壓后,跳轉至arch\x86\kernel\head_32.S的startup_32。
- Hands-On Internet of Things with MQTT
- Project 2007項目管理實用詳解
- Go Machine Learning Projects
- 手把手教你玩轉RPA:基于UiPath和Blue Prism
- UTM(統(tǒng)一威脅管理)技術概論
- Visual C# 2008開發(fā)技術實例詳解
- 大數(shù)據(jù)安全與隱私保護
- AWS Administration Cookbook
- 完全掌握AutoCAD 2008中文版:綜合篇
- 人工智能技術入門
- 網(wǎng)絡服務器搭建與管理
- 中國戰(zhàn)略性新興產(chǎn)業(yè)研究與發(fā)展·數(shù)控系統(tǒng)
- 智能+:制造業(yè)的智能化轉型
- 分布式Java應用
- Practical Network Automation