Linux0.11核心讀書筆記/boot/bootsect.s
果凍
QQ:457283
! 本程式完成的主要功能
! 1.bootsect.s從0x7c00處開始執行
! 2.將自己複製到0x90000處
! 3.將setup.s程式從磁碟第2扇區讀取到0x90200處
! 4.將system讀取到0x10000處
! 5.獲取根檔案系統裝置號
! 6.顯示資訊
!
! 程式執行示意圖
! +---+---+---+ 0xA0000
! | | | |
! | | | |
! +---+---+---+
! | | | |
! | | | |
! | | | S |
! | | | |
! | | | |
! +---+---+---+ 0x90200
! | | B | B |
! +---+---+---+ 0x90000
! | | | |
! | | | |
! +---+---+---+
! | | | |
! | | | |
! | | | |
! | | | |
! | | | K |
! | | | |
! | | | |
! | | | |
! +---+---+---+ 0x10000
! | | | |
! | | | |
! | | | |
! | | | |
! +---+---+---+
! | B | | |
! +---+---+---+ 0x7c00
! | | | |
! +---+---+---+ 0x0000
! 1 2 3
! B - bootsect.s程式
! S - setup.s程式
! K - system模組
!
! SYS_SIZE is the number of clicks (16 bytes) to be loaded.
! 0x3000 is 0x30000 bytes = 196kB, more than enough for current
! versions of linux
!
! 編譯連線後system模組的大小
SYSSIZE = 0x3000
!
! bootsect.s (C) 1991 Linus Torvalds
!
! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
! iself out of the way to address 0x90000, and jumps there.
!
! It then loads 'setup' directly after itself (0x90200), and the system
! at 0x10000, using BIOS interrupts.
!
! NOTE! currently system is at most 8*65536 bytes long. This should be no
! problem, even in the future. I want to keep it simple. This 512 kB
! kernel size should be enough, especially as this doesn't contain the
! buffer cache as in minix
!
! The loader has been made as simple as possible, and continuos
! read errors will result in a unbreakable loop. Reboot by hand. It
! loads pretty fast by getting whole sectors at a time whenever possible.
! 定義6個全域性識別符號 程式碼段,資料段,未初始化資料段起始結束地址
.globl begtext, begdata, begbss, endtext, enddata, endbss
! 程式碼段
.text
begtext:
! 資料段
.data
begdata:
! 未初始化資料段
.bss
begbss:
! 程式碼段
.text
SETUPLEN = 4 ! nr of setup-sectors
! setup程式的扇區數
BOOTSEG = 0x07c0 ! original address of boot-sector
! boot-sector的原始段地址
INITSEG = 0x9000 ! we move boot here - out of the way
! 將boot-sector移動到這裡
SETUPSEG = 0x9020 ! setup starts here
! setup程式在這裡開始
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
! system模組載入到0x10000(64K)
ENDSEG = SYSSEG + SYSSIZE ! where to stop loading
! 停止載入的段地址
! ROOT_DEV: 0x000 - same type of floppy as boot.
! 0x301 - first partition on first drive etc
ROOT_DEV = 0x306 ! 裝置號,指根檔案系統是第2個硬碟的第一個分割槽
! 裝置號 = 主裝置號*256 + 次裝置號(=(major<<8)+ minor)
! 主裝置號:1-記憶體,2-磁碟,3-硬碟,4-ttyx,5-tty,6-並口,7-非命名管道
! 0x300 = 3*256 + 0 第1個硬碟
! 0x301 = 3*256 + 1 第1個硬碟,第1個分割槽
! 0x306 = 3*256 + 6 第2個硬碟,第1個分割槽
entry _start ! 程式入口點
_start:
! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
mov ax,#BOOTSEG ! 將資料段DS暫存器設定為0x07C0
mov ds,ax
mov ax,#INITSEG ! 將附加段ES暫存器設定為0x9000
mov es,ax
mov cx,#256 ! 移動數值CX=256字 = 512位元組
sub si,si ! 源地址 ds:si = 0x07c0:0x0000
sub di,di ! 目的地址 es:di = 0x9000:0x0000
rep ! 重複執行,直到CX=0
movw ! 移動一個字
! 上面的程式碼作用是將bootsect自身從位置0x07c00移動到0x90000處,共512個位元組
jmpi go,INITSEG ! 間接跳轉,INITSEG為跳轉到的段地址
! 以下程式碼是從0x90000處執行的
go: mov ax,cs ! 將ds,es和ss設定成0x9000,此時cs跳轉到0x9000了
mov ds,ax
mov es,ax
! put stack at 0x9ff00. ! 將堆疊指標指向0x9ff00(0x9000:0xff00)
mov ss,ax
mov sp,#0xFF00 ! arbitrary value >>512
! 因為0x90000 - 0x90200放置bootsect,0x90200開始放置大約4個扇區的setup
! 程式,因此堆疊指標sp要指向0x200 + 0x200 * 4 + 堆疊大小的位置
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
load_setup:
mov dx,#0x0000 ! drive 0, head 0
! 驅動器0,磁頭0
mov cx,#0x0002 ! sector 2, track 0
! 扇區2,磁軌0
mov bx,#0x0200 ! address = 512, in INITSEG
! 緩衝區偏移量,es在上面的程式碼已經設定成0x9000
mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
! 讀取4個盤扇區到記憶體
int 0x13 ! read it
! 呼叫中斷
jnc ok_load_setup ! ok - continue
! CF沒置位表示讀取成功,跳轉到ok_load_setup
mov dx,#0x0000 ! 復位驅動器和磁頭
mov ax,#0x0000 ! reset the diskette
int 0x13 ! 復位
j load_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標誌置位
! 成功載入setup模組
ok_load_setup:
! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
! Get disk drive parameters, specifically nr of sectors/track
mov dl,#0x00 ! 設定驅動器號為0
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的值,這裡重新改回
! 以上程式碼是讀取磁碟驅動器引數,特別是扇區數量
! 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 = 軟碟機磁碟引數表
! 顯示資訊'Loading system ...回車換行'共24個字元
! Print some inane message
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 ! 寫字串並且移動游標
! ok, we've written the message, now
! 以下程式碼將system模組載入到0x10000處
! we want to load the system (at 0x10000)
mov ax,#SYSSEG
mov es,ax ! segment of 0x010000
call read_it ! 讀取system模組,es為輸入引數
call kill_motor ! 關閉驅動馬達
! 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 (2,28) or /dev/at0 (2,8), depending
! on the number of sectors that the BIOS reports currently.
! Linux中軟碟機的主裝置號是2,次裝置號 = 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驅動器,其裝置號是0x21c
! 同理/dev/at0 (2, 8)指的是1.2M A驅動器,裝置號是0x0208
seg cs
mov ax,root_dev ! 獲取根裝置號
cmp ax,#0 ! 如果不為0跳轉到root_defined,證明已經給定根裝置號
jne root_defined
! 檢測根裝置號
seg cs ! 下面一句程式碼在CS所指的段中
mov bx,sectors ! 取得每磁軌扇區數 15-1.2mb驅動器 18-1.44MB驅動器
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 ! 儲存裝置號
! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
jmpi 0,SETUPSEG ! 跳轉到0x9020:0000(setup.s程式開始處)執行
! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! 本程式結束! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
! This routine loads the system at address 0x10000, making sure
! no 64kB boundaries are crossed. We try to load it as fast as
! possible, loading whole tracks whenever we can.
!
! in: es - starting address segment (normally 0x1000)
!
sread: .word 1+SETUPLEN ! sectors read of current track
! 當前磁軌中已讀扇區數,開始已經讀取1個扇區的bootsect
head: .word 0 ! current head
! 當前磁頭號
track: .word 0 ! current track
! 當前磁軌號
read_it:
mov ax,es
test ax,#0x0fff ! 將es裡的值與0x0fff相與,看是否處於0x1000位置
die: jne die ! es must be at 64kB boundary 不處於0x1000位置則進入死迴圈
xor bx,bx ! bx is starting address within segment
! bx是段內偏移地址
rp_read:
mov ax,es ! 比較es值與ENDSEG,看是否達到段末端,如果不是則跳轉到ok1_read繼續讀取,否則返回
cmp ax,#ENDSEG ! have we loaded all yet?
jb ok1_read
ret
ok1_read:
seg cs ! 設定下面一條指令操作在CS所指的段中
mov ax,sectors ! 讀取每磁軌扇區數
sub ax,sread ! 減掉當前已讀扇區數
mov cx,ax ! cx = 當前未讀扇區數
shl cx,#9 ! cx = cx << 9 = cx * 512位元組 = 共有多少位元組沒有讀取
add cx,bx ! cx = cx + 段內當前偏移量 判斷未讀取位元組數 + 段內偏移是否超過64KB
jnc ok2_read ! 未超過64KB位元組,跳轉到ok2_read執行
je ok2_read
! 以下情況是未讀取位元組數+段內偏移超過了64KB
xor ax,ax ! 清空ax
sub ax,bx ! ax = ax - bx 得到最多還能讀入的位元組數
shr ax,#9 ! 轉換成還需要讀取的扇區數
ok2_read:
call read_track
mov cx,ax ! cx = 上面那條呼叫後已經讀出的扇區數
add ax,sread ! 更新已經讀取的扇區數
seg cs ! 下一條指令操作在CS所指的段中
cmp ax,sectors ! 如果當前磁軌上還有扇區未讀,跳轉到ok3_read
jne ok3_read
mov ax,#1 ! 判斷磁頭號
sub ax,head ! 如果為0磁頭,則再去讀1磁頭面上的扇區資料
jne ok4_read
inc track ! 否則讀下一磁軌
ok4_read:
mov head,ax ! 儲存當前磁頭號
xor ax,ax ! 清除當前磁軌已讀扇區數
ok3_read:
mov sread,ax ! 儲存當前磁軌已讀扇區數
shl cx,#9 ! 上次已讀扇區數*512位元組
add bx,cx ! 調整當前段內資料開始位置
jnc rp_read ! 若小於64KB邊界,跳轉到rp_read繼續讀資料
mov ax,es
add ax,#0x1000 ! 將段基地址調整為指向下一個64KB記憶體開始處
mov es,ax
xor bx,bx ! 清除段內偏移量
jmp rp_read ! 跳轉到rp_read繼續執行
! 讀當前磁軌上指定開始扇區和需要讀扇區數的資料到es:bx開始處
read_track:
! ax,bx,cx,dx如棧
push ax
push bx
push cx
push dx
mov dx,track ! 取得當前磁軌號
mov cx,sread ! 取得當前磁軌上已讀扇區數
inc cx ! cl = 開始讀扇區
mov ch,dl ! ch = 當前磁軌號
mov dx,head ! 取當前詞頭號
mov dh,dl ! dh = 磁頭號
mov dl,#0 ! dl = 驅動器號(0表示當前驅動器A)
and dx,#0x0100 ! 詞頭號不大於1
mov ah,#2 ! ah = 2,讀磁碟扇區功能號
int 0x13 ! 呼叫中斷
jc bad_rt ! 出錯,跳轉到bad_rt
pop dx ! 彈出dx, cx, bx, ax
pop cx
pop bx
pop ax
ret
! 進行驅動器復位操作(磁碟中斷功能號0),再跳轉到read_track重試
bad_rt: mov ax,#0
mov dx,#0
int 0x13
pop dx
pop cx
pop bx
pop ax
jmp read_track
! /*
! * 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 ! A驅動器,關閉FDC,靜止DMA和中斷請求,關閉馬達
outb ! 將al內容輸出到dx指定埠
pop dx
ret
sectors: ! 用來儲存磁軌扇區數
.word 0
msg1:
.byte 13,10 ! 回車,換行ASCII碼
.ascii "Loading system ..."
.byte 13,10,13,10 ! 共24個ASCII碼字元
.org 508 ! 表示下面的語句從508(0x1FC)開始
root_dev: ! 儲存根檔案系統所在的裝置號(init/main.c用到)
.word ROOT_DEV
boot_flag: ! 硬碟有效標識
.word 0xAA55
.text
endtext:
.data
enddata:
.bss
endbss: