bootsect啟動代碼分析
Linux0.11內核啟動過程
I、Linux0.11內核啟動過程概述:
當PC的電源打開后,80x86結構的CPU將進入實模式,并從地址0XFFFF0開始自動執行。
PC機的BIOS將執行某些系統的檢測,并在物理地址0處開始初始化中斷向量。
啟動設備(軟驅或硬盤)的第一個扇區(磁盤引導扇區,512字節)讀入到內存的絕對地址0x7C00處,并跳轉到這個地方運行。
Linux0.11啟動代碼目錄boot下文件介紹:
在boot目錄下主要有bootsect.s,head.s,setup.s三個匯編文件,三個匯編文件完成了啟動引導的流程,過程圖如下圖所示:
1、bootsect.s程序:
bootsec.s是磁盤引導塊程序,駐留在磁盤的第一個扇區中(引導扇區,0磁道,0磁頭,第一個扇區)。
2、setup.s程序
setup.s是一個操作系統加載程序,它的主要作用是利用ROM BIOS中斷讀取機器系統數據,并將這些數據保存到0x90000開始的位置。
3、head.s程序
從這里開始,內核完全都是在保護模式下運行了。
II、 boot/bootsect.s啟動內核時在內存中的位置和移動情況:
8086體系結構的計算機在上電后將由BIOS進行系統的自檢。
之后BIOS會將bootsect.s文件讀入內存的0x7C00(31k)處。
然后跳轉到此執行引導扇區的代碼。
這段代碼執行時會將自己移動的內存的0x90000(576k)到0xA0000處一共64K,并把存儲設備中的setup.s文件讀入到內存的0x90200(576.5k)處,system模塊讀入到內存的0x10000(64k)處。啟動引導時內核在內存中的位置和移動后的位置情況如下圖所示:
III 、bootsect.s程序分析
目錄:
bootsect.s文件讀入內存
setup讀入內存及讀取磁盤驅動器參數
打印信息代碼分析
SYSTEM模塊讀入內存
確定使用哪個根文件系統設備
跳轉執行setup
1、bootsect.s文件讀入內存
1.1、代碼分析:
entry start ! 告知連接程序,程序從start 標號開始執行。 start: ! 47--56 行作用是將自身(bootsect)從目前段位置0x07c0(31k) mov ax,#BOOTSEG ! 將ds 段寄存器置為0x7C0; mov ds,ax mov ax,#INITSEG ! 將es 段寄存器置為0x9000; mov es,ax mov cx,#256 ! 移動計數值=256 字; sub si,si ! 源地址 ds:si = 0x07C0:0x0000 sub di,di ! 目的地址 es:di = 0x9000:0x0000 rep ! 重復執行,直到cx = 0 movw ! 移動1 個字; jmpi go,INITSEG ! 間接跳轉。這里INITSEG 指出跳轉到的段地址。
1
2
3
4
5
6
7
8
9
10
11
12
1.2、bootsect.s文件讀入內存示圖
1.3、ds,es和ss端處設置
go: mov ax,cs ! 將ds、es 和ss 都置成移動后代碼所在的段處(0x9000)。 mov ds,ax !由于程序中有堆棧操作(push,pop,call),因此必須設置堆棧。 mov es,ax
1
2
3
1.4、移動堆棧指針程序分析
! put stack at 0x9ff00. ! 將堆棧指針sp 指向0x9ff00(即0x9000:0xff00)處
! 由于代碼段移動過了,所以要重新設置堆棧段的位置。
! sp 只要指向遠大于512 偏移(即地址0x90200)處
! 都可以。因為從0x90200 地址開始處還要放置setup 程序,
! 而此時setup 程序大約為4 個扇區,因此sp 要指向大
! 于(0x200 + 0x200 * 4 + 堆棧大小)處。
mov ss,ax mov sp,#0xFF00 ! arbitrary value >>512
1
2
2、setup讀入內存及讀取磁盤驅動器參數
2.1、磁盤平面圖:
2.2、啟動文件在磁盤中位置示意圖:
2.3、setup讀入內存示意圖:
2.4、setup讀入內存代碼分析
! 用途是利用BIOS 中斷INT 0x13 將setup 模塊從磁盤第2 個扇區
! 開始讀到0x90200 開始處,共讀4 個扇區。如果讀出錯,則復位驅動器,并
! 重試,沒有退路。INT 0x13 的使用方法如下:
! 讀扇區:
! ah = 0x02 讀磁盤扇區到內存;al = 需要讀出的扇區數量;
! ch = 磁道(柱面)號的低8 位; cl = 開始扇區(0-5 位),磁道號高2 位(6-7);
! dh = 磁頭號; dl = 驅動器號(如果是硬盤則要置位7);
! es:bx ??指向數據緩沖區; 如果出錯則CF 標志置位。
mov dx,#0x0000 ! drive 0, head 0 mov cx,#0x0002 ! sector 2, track 0 mov bx,#0x0200 ! address = 512, in INITSEG mov ax,#0x0200+SETUPLEN ! service 2 , 讀4個扇區 int 0x13 ! read it jnc ok_load_setup ! ok - continue mov dx,#0x0000 mov ax,#0x0000 ! reset the diskette int 0x13 j load_setup
1
2
3
4
5
6
7
8
9
10
2.5、setup讀入參數代碼分析
! Get disk drive parameters, specifically nr of sectors/track
! 取磁盤驅動器的參數,特別是每道的扇區數量。
! 取磁盤驅動器參數INT 0x13 調用格式和返回信息如下:
! ah = 0x08 dl = 驅動器號(如果是硬盤則要置位7 為1)。
! 返回信息:
! 如果出錯則CF 置位,并且ah = 狀態碼。
! ah = 0, al = 0, bl = 驅動器類型(AT/PS2)
! ch = 最大磁道號的低8 位,cl = 每磁道最大扇區數(位0-5),最大磁道號高2 位(位6-7)
! dh = 最大磁頭數, dl = 驅動器數量,
! es:di -?? 軟驅磁盤參數表。
ok_load_setup: mov dl,#0x00 mov ax,#0x0800 ! AH=8 is get drive parameters int 0x13 mov ch,#0x00 seg cs ! 表示下一條語句的操作數在cs 段寄存器所指的段中。 mov sectors,cx ! 保存每磁道扇區數。 mov ax,#INITSEG mov es,ax ! 因為上面取磁盤參數中斷改掉了es 的值,這里重新改回。
1
2
3
4
5
6
7
8
9
3、打印信息代碼分析:
! Print some inane message
! 在顯示一些信息(‘Loading system …’回車換行,共24 個字符)。
! BIOS中斷0x10功能號 ah = 0x03,讀光標
!BIOS中斷0x10功能號 ah=0x13,顯示字符
mov ah,#0x03 ! read cursor pos xor bh,bh ! 讀光標位置。 int 0x10 mov cx,#24 ! 共24 個字符。 mov bx,#0x0007 ! page 0, attribute 7 (normal) mov bp,#msg1 ! 指向要顯示的字符串。 mov ax,#0x1301 ! write string, move cursor int 0x10 ! 寫字符串并移動光標。
1
2
3
4
5
6
7
8
9
msg1:
.byte 13,10 ! 回車、換行的ASCII 碼。 .ascii "Loading system ..." .byte 13,10,13,10 ! 共24 個ASCII 碼字符。 .org 508 ! 表示下面語句從地址508(0x1FC)開始,所以root_dev ! 在啟動扇區的第508 開始的2 個字節中。 root_dev: .word ROOT_DEV ! 這里存放根文件系統所在的設備號(init/main.c 中會用)。 boot_flag: .word 0xAA55 ! 硬盤有效標識。
1
2
3
4
5
6
7
8
9
10
4、SYSTEM模塊讀入內存
4.1示圖
4.2 代碼分析
! ok, we’ve written the message, now
! we want to load the system (at 0x10000) ! 現在開始將system 模塊加載到0x10000(64k)處。
SYSSIZE = 0x3000 ! 指編譯連接后system 模塊的大小。參見列表1.2 中第92 的說明。 ! setup 程序從這里開始; SYSSEG = 0x1000 ! system loaded at 0x10000 (65536). ! system 模塊加載到0x10000(64 kB)處; ENDSEG = SYSSEG + SYSSIZE ! where to stop loading !-------------------------------------------------- mov ax,#SYSSEG mov es,ax ! segment of 0x010000 ! es = 存放system 的段地址。 call read_it ! 讀磁盤上system 模塊,es 為輸入參數。 call kill_motor ! 關閉驅動器馬達,這樣就可以知道驅動器的狀態了。
1
2
3
4
5
6
7
8
9
10
11
12
4.3、read_it
//待分析
4.4、關閉軟驅的馬達
! 這個子程序用于關閉軟驅的馬達,這樣我們進入內核后它處于已知狀態,以后也就無須擔心它了。
kill_motor: push dx mov dx,#0x3f2 ! 軟驅控制卡的驅動端口,只寫。 mov al,#0 ! A 驅動器,關閉FDC,禁止DMA 和中斷請求,關閉馬達。 outb ! 將al 中的內容輸出到dx 指定的端口去。 pop dx ret
1
2
3
4
5
6
7
5、確定使用哪個根文件系統設備
5.1、設備號介紹
! ROOT_DEV: 0x000 same type of floppy as boot.
! 根文件系統設備使用與引導時同樣的軟驅設備;
! 0x301 first partition on first drive etc
! 根文件系統設備在第一個硬盤的第一個分區上,等等;
ROOT_DEV = 0x306 ! 指定根文件系統設備是第2 個硬盤的第1 個分區。這是Linux 老式的硬盤命名
! 方式,具體值的含義如下:
! 設備號=主設備號*256 + 次設備號(也即dev_no = (major<<8) + minor )
! (主設備號:1-內存,2-磁盤,3-硬盤,4-ttyx,5-tty,6-并行口,7-非命名管道)
! 0x300 /dev/hd0 代表整個第1 個硬盤;
! 0x301 /dev/hd1 第1 個盤的第1 個分區;
! …
! 0x304 /dev/hd4 第1 個盤的第4 個分區;
! 0x305 /dev/hd5 代表整個第2 個硬盤盤;
! 0x306 /dev/hd6 第2 個盤的第1 個分區;
! …
! 0x309 /dev/hd9 第2 個盤的第4 個分區;
! 從linux 內核0.95 版后已經使用與現在相同的命名方法了。
5.2、內存地址偏移代碼:
.org 508 ! 表示下面語句從地址508(0x1FC)開始,所以root_dev ! 在啟動扇區的第508 開始的2 個字節中。 root_dev: .word ROOT_DEV ! 這里存放根文件系統所在的設備號(init/main.c 中會用)。 boot_flag: .word 0xAA55 ! 硬盤有效標識。
1
2
3
4
5
6
5.3、內存地址偏移如圖所示:
5.4 代碼分析
! After that we check which root-device to use. If the device is
! defined (!= 0), nothing is done and the given device is used.
! Otherwise, either /dev/PS0 (,28) or /dev/at0 (2,8), depending
! on the number of sectors that the BIOS reports currently.
! 此后,我們檢查要使用哪個根文件系統設備(簡稱根設備)。如果已經指定了設備(!=0)
! 就直接使用給定的設備。否則就需要根據BIOS 報告的每磁道扇區數來
! 確定到底使用/dev/PS0 (2,28) 還是 /dev/at0 (2,8)。
! 上面一行中兩個設備文件的含義:
! 在Linux 中軟驅的主設備號是2(參見第43 行的注釋),次設備號 = type*4 + nr,其中
! nr 為0-3 分別對應軟驅A、B、C 或D;type 是軟驅的類型(2??1.2M 或7??1.44M 等)。
! 因為7*4 + 0 = 28,所以 /dev/PS0 (2,28)指的是1.44M A 驅動器,其設備號是0x021c
! 同理 /dev/at0 (2,8)指的是1.2M A 驅動器,其設備號是0x0208。
seg cs mov ax,root_dev ! 將根設備號 cmp ax,#0 jne root_defined seg cs mov bx,sectors ! 取上面第88 行保存的每磁道扇區數。如果sectors=15
1
2
3
4
5
6
! 則說明是1.2Mb 的驅動器;如果sectors=18,則說明是
! 1.44Mb 軟驅。因為是可引導的驅動器,所以肯定是A 驅。
mov ax,#0x0208 ! /dev/ps0 - 1.2Mb cmp bx,#15 ! 判斷每磁道扇區數是否=15 je root_defined ! 如果等于,則ax 中就是引導驅動器的設備號。 mov ax,#0x021c ! /dev/PS0 - 1.44Mb cmp bx,#18 je root_defined undef_root: ! 如果都不一樣,則死循環(死機)。 jmp undef_root root_defined: seg cs mov root_dev,ax ! 將檢查過的設備號保存起來。
1
2
3
4
5
6
7
8
9
10
11
6、跳轉執行setup
! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
! 到此,所有程序都加載完畢,我們就跳轉到被
! 加載在bootsect 后面的setup 程序去。
jmpi 0,SETUPSEG ! 跳轉到0x9020:0000(setup.s 程序的開始處)。
1
Elasticsearch Linux
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。