現在我們就可以深入研究一下自舉過程中不可缺少的匯編語言函數了。
Setup()函數可以在/usr/src/linux-2.4.2/arch/i386/boot/setup.S文件中找到。
Setup()函數代碼是在完整的內核自舉程序加載以后,才會跳到相應的函數代碼處。在內核文件中,其偏移地址是0x200。這使得自舉程序很容易找到這段代碼,并將其拷貝到起始物理地址為0x00090200的內存中。
這個Setup()文函數到底是做什么用的?在計算機時里,內核要正確地操作所有硬件就必需首先要檢測到它們,并且以一種有序的方式進行初始化。Setup()函數初始化所有的硬件設備,從而為內核操作它創造了一個環境。
但是,前面我們不是已經提到過BIOS會檢測所有的硬件嗎?雖然BIOS初始化了所有的硬件,但是Linux內核并不放心,它還要以自己的方式對所有的硬件進行初始化。Linux內核之所以要設計成這樣,是為了增強可移植性和穩定性。這也是Linux內核要優于很多目前可用的Unix和類Unix內核的原因之一,并且也使得它在很多方面表現的非常出眾。
Setup()函數主要完成以下任務:
(1)首先是檢測系統可用內存的總量,它是通過BIOS程序來完成檢測的;
(2)設置鍵盤重復延遲時間和重復速度;
(3)檢測視頻卡;
(4)重新初始化硬盤控制器和硬盤參數;
(5)檢測一個MCA;
(6)檢測一個PS/2定點設備(鼠標總線);
(7)檢測高級電源管理器(APM)BIOS支持;
(8)檢測內核在內存中的位置,如果在低地址0x00010000,就將其移到高地址0x00001000,如在高地址則不做任何移動;
(9)設置設備中斷描述表(IDT)和全局描述表(GDT);
(10)如已經有了浮點單位(FPU),則重置之;
(11)重新調用程序中斷控制器;
(12)通過設置cr0狀態寄存器的PE位,把CPU從“實模式”切換到“保護模式”;
(13)跳轉到stratup_32( )匯編語言函數。
第一個stratup_32( )函數做什么
在啟動過程中要用到兩個stratup_32( )函數,雖然它們都是匯編語言函數,但是卻是兩個完全不同的函數。我們這里所說的函數包含在/usr/src/linux-2.4.2/arch/i386/boot/compressed/head.S文件里。
Setup()文件執行后,這個函數就被加載到物理地址為0x00100000或者物理地址為0x00001000的內存中(取決于內核是載入高或者低內存)。
當執行這個函數時,會執行以下的操作:
(1)初始化段寄存器和一個臨時棧。
(2)內核中沒有初始化氖?荻加?填充。它是通過symbols _edata和 _end來識別的。
(3)執行decompress_kernel( )函數。這個函數用于對Linux內核解壓縮。這個時候,屏幕上將顯示“Uncompressing Linux……”信息。解壓縮完成后,就會顯示“OK, booting the kernel”信息。現在有一個問題,就是解完壓縮的內核被放置在什么位置?答案是如果Linux內核被加載低地址,那么解壓縮的內核將被置于物理地址為0x00100000的地方。如果在高地址,則內核會被先解壓到一個臨時緩沖區中,待完成后再將其加載到物理地址為0x00100000的地方。
(4)最后,跳轉到物理地址為0x00100000的地方執行。
到此為止,代碼執行操作就由另外一個startup_32( )函數來接管。也就是說,第二個startup_32( )函數接管了啟動過程。


