linux0.11源碼分析1 bootsect.s檔案分析
從開機加電到執行main函數的過程 由於linux0.11系統當時儲存在磁碟片上,所以其加電過程主要目的就是從啟動盤載入作業系統程式,完成執行main函數的準備工作。 從開機到mian函數的啟動共分三部分:
第一部分是啟動bios,準備
實模式下的中斷向量表和中斷服務程式。 第二部分是從啟動盤載入作業系統到記憶體 第三部分為執行32位的main函數做過渡工作
第一部分 從硬體來看,Intel將所有的80x86系列的CPU的硬體都設計為加電進入16位實模式運行,加電後指向0xFFFF0位置 BIOS程式在記憶體最開始的位置用1kb的記憶體空間(0x00400~0x004FF)構建中斷向量表。 在緊挨著的地方用256位元組的記憶體空間構建BIOS資料區(0x00400~0x004FF),在56kb以後的位置(0x0E2CE)載入了8kb左右的中斷向量表相應的中斷服務程式。
第二部分
對於linux0.11系統而言,電腦分三批逐次載入作業系統的代碼: 第一批由BIOS中斷int 0x19把第一扇區bootsect的內容載入到記憶體。 第二批和第三批在 bootsect的指揮下,分別把其後的四個扇區和隨後的240個扇區的內容載入至記憶體
對於int 0x19中斷向量所指向的中斷服務程式將軟碟機0號磁頭對應盤面的0磁軌1扇區的內容拷貝至記憶體0x07C00(這個地址屬於DRAM地區)處。該磁軌扇區存放的是bootsect的代碼。 實模式下的最大定址記憶體只有1MB,所以需要規劃記憶體。
bootsect複製自身的作業碼如下:
entry start ! 表示程式的進入點start:mov ax,#BOOTSEG mov ds,ax mov ax,#INITSEG mov es,ax mov cx,#256 sub si,si sub di,di rep movwjmpi go,INITSEG
首先,程式進入start這個進入點,然後將開機磁區被bios載入的位置(即BOOTSEG)進行儲存在ds寄存器中,將要移動的新位置的地址(INITSEG)存入cx寄存器,下面的2條sub指令是分別使其為0x0000,cx為256,並且操作是movw,說明是字複製操作,則總共移動512個位元組。(這裡需要瞭解movw這條指令的執行方式,見本人另一部落格的筆記)
接下來執行的代碼
jmpi go,INITSEG go: mov ax,cs mov ds,ax mov es,ax ! put stack at 0x9ff00 mov ss,ax mov sp,#0xFF00 ! arbitrary value >>512
複製完畢後,執行jmpi指令,其意思是跳轉到go:INITSEG, CS的值變為 INITSEG,IP的值變為從INITSEG到go:mov ax,cs的位移。
然後將DS、ES、SS進行調整,把cs當前的值賦給他們,棧頂指標sp指向位移地址為0xFF00處。 注意:SS和SP構成了棧資料在記憶體中的位置。
- 將setup程式載入進記憶體
需要藉助BIOS提供的中斷0x13中斷指向的中斷服務程式來完成。int 0x13把指定的扇區的代碼載入到記憶體的指定位置。
int 0x13是直接磁碟服務,具體的參數和用法參考如此
代碼如下:
mov dx,#0x0000 ! drive 0, head 0 mov cx,#0x0002 ! sector 2, track 0 mov bx,#0x0200 ! address = 512, in INITSEG 存放位移地址512 mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors SETUPLEN是被載入的扇區數,對應參數ALint 0x13 ! read it 指向的是磁碟服務程式,根據AH的值對應於02,是讀扇區功能,AL是扇區數jnc ok_load_setup ! ok - continue 如果cf標誌為為0,則跳轉mov dx,#0x0000 mov ax,#0x0000 ! reset the diskette 不為0則重設int 0x13 。 繼續中斷j load_setup 。返回load_setup位置
前面4個mov是先對參數進行設定,注意第4個mov,給定了AH=02,表明是功能02H讀扇區。AL是扇區數。
然後調用int 0x13中斷,進入中斷服務程式,讀取將磁碟片從第二扇區開始的四個扇區載入至
ES:BX代表的是緩衝區地址,所以bx位移量為512,es的值還是當前段。 載入第三部分代碼
bootsect藉著BIOS中斷int 0x13,將240個扇區的system模組載入進記憶體 載入工作由bootsect調用read it子程式完成,這個子程式將磁碟片第6扇區開始的240個扇區的system模組載入進記憶體的SYSSEG(0x10000)處往後的120kb空間中。
代碼如下:
ok_load_setup:! Get disk drive parameters, specifically nr of sectors/track mov dl,#0x00 。DL=磁碟機mov ax,#0x0800 ! AH=8 is get drive parameters AH=08表示讀取磁碟機參數int 0x13 。中斷13向量mov ch,#0x00 。ch=柱面seg cs 。表示下一條指令將使用段超越mov sectors,cx mov ax,#INITSEGmov es,ax! Print some inane message mov ah,#0x03 ! read cursor pos AH=03是入口參數,在文本座標下,讀取游標各種資訊xor bh,bh 。bh是顯示頁碼int 0x10mov cx,#24 !CH為游標的起始行,CL為游標的終止行mov bx,#0x0007 ! page 0, attribute 7 (normal) 。bx是顯示頁面mov bp,#msg1 。ES:BP=顯示字串的地址,msg1在源碼中該檔案的最後面幾行位置mov ax,#0x1301 ! write string, move cursor !AH=13表示顯示字串,AL=1表示字串只顯含字元,顯示後,游標位置改變。int 0x10 !顯示服務的中斷,進行螢幕寫操作
上述就進行了讀取磁碟機參數, 並且列印載入系統的文字資訊。調用int 0x10中斷。
接著,調用read_it函數進行讀取扇區內容,代碼如下:
read_it: mov ax,es test ax,#0x0fff 。測試ax中的某有位是否為0,如果為0,則ax為0,將zf置1,否則置0die: jne die ! es must be at 64kB boundary zf不等於0則跳轉 xor bx,bx ! bx is starting address within segment 。異或指令rp_read: mov ax,es cmp ax,#ENDSEG ! have we loaded all yet? 。對兩數進行相減,進行比較 jb ok1_read 。判斷2數大小 retok1_read: seg cs mov ax,sectors sub ax,sread mov cx,ax shl cx,#9 add cx,bx jnc ok2_read je ok2_read xor ax,ax sub ax,bx shr ax,#9ok2_read: call read_track mov cx,ax add ax,sread seg cs cmp ax,sectors jne ok3_read mov ax,#1 sub ax,head jne ok4_read inc trackok4_read: mov head,ax xor ax,axok3_read: mov sread,ax shl cx,#9 add bx,cx jnc rp_read mov ax,es add ax,#0x1000 mov es,ax xor bx,bx jmp rp_read
調用kill_motor函數
/** This procedure turns off the floppy drive motor, so* that we enter the kernel in a known state, and* don't have to worry about it later.*/kill_motor: push dx mov dx,#0x3f2 mov al,#0 outb pop dx ret
接下來還有一段代碼是確定根檔案系統號的。
通過執行 jmpi 0,SETUPSEG
將跳轉到0x90200處,即setup程式載入的位置,CS:IP指向setup程式的第一條指令。
現在,整個bootsect程式的任務全部完成了