1. 程式人生 > >Linux0.11核心讀書筆記/boot/bootsect.s

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: