?? linux操作系統內核引導程序詳細剖析.txt
字號:
Linux操作系統內核引導程序詳細剖析
貼出者為 llc
這段程序是Linux操作系統啟動boot程序,其思想原理可參看本人翻譯的《Linux內核
漫游》一篇。
中文注釋:趙炯 gohigh@shtdu.edu.cn www.freedove.com
發信人: seis (矛), 信區: Linux
標 題: Linux操作系統內核引導程序詳細剖析
發信站: BBS 水木清華站 (Fri Feb 2 14:12:43 2001)
這段程序是Linux操作系統啟動boot程序,其思想原理可參看本人翻譯的《Linux內核
漫游》一篇。
中文注釋:趙炯 gohigh@shtdu.edu.cn www.freedove.com
! bootsect.s (c) 1991, 1992 Linus Torvalds 版權所有
! Drew Eckhardt修改過
! Bruce Evans (bde)修改過
!
! bootsect.s 被bios-啟動子程序加載至0x7c00 (31k)處,并將自己
! 移到了地址0x90000 (576k)處,并跳轉至那里。
!
! bde - 不能盲目地跳轉,有些系統可能只有512k的低
! 內存。使用中斷0x12來獲得(系統的)最高內存、等。
!
! 它然后使用BIOS中斷將setup直接加載到自己的后面(0x90200)(576.5k),
! 并將系統加載到地址0x10000處。
!
! 注意! 目前的內核系統最大長度限制為(8*65536-4096)(508k)字節長,即使是在
! 將來這也是沒有問題的。我想讓它保持簡單明了。這樣508k的最大內核長度應該
! 是足夠了,尤其是這里沒有象minix中一樣包含緩沖區高速緩沖(而且尤其是現在
! 內核是壓縮的 :-)
!
! 加載程序已經做的盡量地簡單了,所以持續的讀出錯將導致死循環。只能手工重啟。
! 只要可能,通過一次取得整個磁道,加載過程可以做的很快的。
#include /* 為取得CONFIG_ROOT_RDONLY參數 */
!! config.h中(即autoconf.h中)沒有CONFIG_ROOT_RDONLY定義!!!?
#include
.text
SETUPSECS = 4 ! 默認的setup程序扇區數(setup-sectors)的默認值;
BOOTSEG = 0x7C0 ! bootsect的原始地址;
INITSEG = DEF_INITSEG ! 將bootsect程序移到這個段處(0x9000) - 避開;
SETUPSEG = DEF_SETUPSEG ! 設置程序(setup)從這里開始(0x9020);
SYSSEG = DEF_SYSSEG ! 系統加載至0x1000(65536)(64k)段處;
SYSSIZE = DEF_SYSSIZE ! 系統的大小(0x7F00): 要加載的16字節為一節的數;
!! 以上4個DEF_參數定義在boot.h中:
!! DEF_INITSEG 0x9000
!! DEF_SYSSEG 0x1000
!! DEF_SETUPSEG 0x9020
!! DEF_SYSSIZE 0x7F00 (=32512=31.75k)*16=508k
! ROOT_DEV & SWAP_DEV 現在是由"build"中編制的;
ROOT_DEV = 0
SWAP_DEV = 0
#ifndef SVGA_MODE
#define SVGA_MODE ASK_VGA
#endif
#ifndef RAMDISK
#define RAMDISK 0
#endif
#ifndef CONFIG_ROOT_RDONLY
#define CONFIG_ROOT_RDONLY 1
#endif
! ld86 需要一個入口標識符,這和通常的一樣;
.globl _main
_main:
#if 0 /* 調試程序的異常分支,除非BIOS古怪(比如老的HP機)否則是無害的 */
int 3
#endif
mov ax,#BOOTSEG !! 將ds段寄存器置為0x7C0;
mov ds,ax
mov ax,#INITSEG !! 將es段寄存器置為0x9000;
mov es,ax
mov cx,#256 !! 將cx計數器置為256(要移動256個字, 512字節);
sub si,si !! 源地址 ds:si=0x07C0:0x0000;
sub di,di !! 目的地址es:di=0x9000:0x0000;
cld !! 清方向標志;
rep !! 將這段程序從0x7C0:0(31k)移至0x9000:0(576k)處;
movsw !! 共256個字(512字節)(0x200長);
jmpi go,INITSEG !! 間接跳轉至移動后的本程序go處;
! ax和es現在已經含有INITSEG的值(0x9000);
go: mov di,#0x4000-12 ! 0x4000(16k)是>=bootsect + setup 的長度 +
! + 堆棧的長度 的任意的值;
! 12 是磁盤參數塊的大小 es:di=0x94000-12=592k-12;
! bde - 將0xff00改成了0x4000以從0x6400處使用調試程序(bde)。如果
! 我們檢測過最高內存的話就不用擔心這事了,還有,我的BIOS可以被配置為將wini驅動
表
! 放在內存高端而不是放在向量表中。老式的堆棧區可能會搞亂驅動表;
mov ds,ax ! 置ds數據段為0x9000;
mov ss,ax ! 置堆棧段為0x9000;
mov sp,di ! 置堆棧指針INITSEG:0x4000-12處;
/*
* 許多BIOS的默認磁盤參數表將不能
* 進行扇區數大于在表中指定
* 的最大扇區數( - 在某些情況下
* 這意味著是7個扇區)后面的多扇區的讀操作。
*
* 由于單個扇區的讀操作是很慢的而且當然是沒問題的,
* 我們必須在RAM中(為第一個磁盤)創建新的參數表。
* 我們將把最大扇區數設置為36 - 我們在一個ED 2.88驅動器上所能
* 遇到的最大值。
*
* 此值太高是沒有任何害處的,但是低的話就會有問題了。
*
* 段寄存器是這樣的: ds=es=ss=cs - INITSEG,(=0X9000)
* fs = 0, gs沒有用到。
*/
! 上面執行重復操作(rep)以后,cx為0;
mov fs,cx !! 置fs段寄存器=0;
mov bx,#0x78 ! fs:bx是磁盤參數表的地址;
push ds
seg fs
lds si,(bx) ! ds:si是源地址;
!! 將fs:bx地址所指的指針值放入ds:si中;
mov cl,#6 ! 拷貝12個字節到0x9000:0x4000-12開始處;
cld
push di !! 指針0x9000:0x4000-12處;
rep
movsw
pop di !! di仍指向0x9000:0x4000-12處(參數表開始處);
pop si !! ds => si=INITSEG(=0X9000);
movb 4(di),*36 ! 修正扇區計數值;
seg fs
mov (bx),di !! 修改fs:bx(0000:0x0078)處磁盤參數表的地址為0x9000:0x4000-12;
seg fs
mov 2(bx),es
! 將setup程序所在的扇區(setup-sectors)直接加載到boot塊的后面。!! 0x90200開始處
;
! 注意,es已經設置好了。
! 同樣經過rep循環后cx為0
load_setup:
xor ah,ah ! 復位軟驅(FDC);
xor dl,dl
int 0x13
xor dx,dx ! 驅動器0, 磁頭0;
mov cl,#0x02 ! 從扇區2開始,磁道0;
mov bx,#0x0200 ! 置數據緩沖區地址=es:bx=0x9000:0x200;
! 在INITSEG段中,即0x90200處;
mov ah,#0x02 ! 要調用功能號2(讀操作);
mov al,setup_sects ! 要讀入的扇區數SETUPSECS=4;
! (假釋所有數據都在磁頭0、磁道0);
int 0x13 ! 讀操作;
jnc ok_load_setup ! ok則繼續;
push ax ! 否則顯示出錯信息。保存ah的值(功能號2);
call print_nl !! 打印換行;
mov bp,sp !! bp將作為調用print_hex的參數;
call print_hex !! 打印bp所指的數據;
pop ax
jmp load_setup !! 重試!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!INT 13 - DISK - READ SECTOR(S) INTO MEMORY
!! AH = 02h
!! AL = number of sectors to read (must be nonzero)
!! CH = low eight bits of cylinder number
!! CL = sector number 1-63 (bits 0-5)
!! high two bits of cylinder (bits 6-7, hard disk only)
!! DH = head number
!! DL = drive number (bit 7 set for hard disk)
!! ES:BX -> data buffer
!! Return: CF set on error
!! if AH = 11h (corrected ECC error), AL = burst length
!! CF clear if successful
!! AH = status (see #00234)
!! AL = number of sectors transferred (only valid if CF set for some
!! BIOSes)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ok_load_setup:
! 取得磁盤驅動器參數,特別是每磁道扇區數(nr of sectors/track);
#if 0
! bde - Phoenix BIOS手冊中提到功能0x08只對硬盤起作用。
! 但它對于我的一個BIOS(1987 Award)不起作用。
! 不檢查錯誤碼是致命的錯誤。
xor dl,dl
mov ah,#0x08 ! AH=8用于取得驅動器參數;
int 0x13
xor ch,ch
!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! INT 13 - DISK - GET DRIVE PARAMETERS (PC,XT286,CONV,PS,ESDI,SCSI)
!! AH = 08h
!! DL = drive (bit 7 set for hard disk)
!!Return: CF set on error
!! AH = status (07h) (see #00234)
!! CF clear if successful
!! AH = 00h
!! AL = 00h on at least some BIOSes
!! BL = drive type (AT/PS2 floppies only) (see #00242)
!! CH = low eight bits of maximum cylinder number
!! CL = maximum sector number (bits 5-0)
!! high two bits of maximum cylinder number (bits 7-6)
!! DH = maximum head number
!! DL = number of drives
!! ES:DI -> drive parameter table (floppies only)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#else
! 好象沒有BIOS調用可取得扇區數。如果扇區36可以讀就推測是36個扇區,
! 如果扇區18可讀就推測是18個扇區,如果扇區15可讀就推測是15個扇區,
! 否則推測是9. [36, 18, 15, 9]
mov si,#disksizes ! ds:si->要測試扇區數大小的表;
probe_loop:
lodsb !! ds:si所指的字節 =>al, si=si+1;
cbw ! 擴展為字(word);
mov sectors, ax ! 第一個值是36,最后一個是9;
cmp si,#disksizes+4
jae got_sectors ! 如果所有測試都失敗了,就試9;
xchg ax,cx ! cx = 磁道和扇區(第一次是36=0x0024);
xor dx,dx ! 驅動器0,磁頭0;
xor bl,bl !! 設置緩沖區es:bx = 0x9000:0x0a00(578.5k);
mov bh,setup_sects !! setup_sects = 4 (共2k);
inc bh
shl bh,#1 ! setup后面的地址(es=cs);
mov ax,#0x0201 ! 功能2(讀),1個扇區;
int 0x13
jc probe_loop ! 如果不對,就試用下一個值;
#endif
got_sectors:
! 恢復es
mov ax,#INITSEG
mov es,ax ! es = 0x9000;
! 打印一些無用的信息(換行后,顯示Loading)
mov ah,#0x03 ! 讀光標位置;
xor bh,bh
int 0x10
mov cx,#9
mov bx,#0x0007 ! 頁0,屬性7 (normal);
mov bp,#msg1
mov ax,#0x1301 ! 寫字符串,移動光標;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -