📄 00000050.htm
字号:
<HTML><HEAD> <TITLE>BBS水木清华站∶精华区</TITLE></HEAD><BODY><CENTER><H1>BBS水木清华站∶精华区</H1></CENTER>发信人: seis (矛), 信区: Linux <BR>标 题: Linux操作系统内核引导程序详细剖析 <BR>发信站: BBS 水木清华站 (Fri Feb 2 14:12:43 2001) <BR> <BR> <BR> 这段程序是Linux操作系统启动boot程序,其思想原理可参看本人翻译的《Linux内核 <BR>漫游》一篇。 <BR> <BR>中文注释:赵炯 <A HREF="mailto:gohigh@shtdu.edu.cn">gohigh@shtdu.edu.cn</A> www.freedove.com <BR> <BR> <BR>! bootsect.s (c) 1991, 1992 Linus Torvalds 版权所有 <BR>! Drew Eckhardt修改过 <BR>! Bruce Evans (bde)修改过 <BR>! <BR>! bootsect.s 被bios-启动子程序加载至0x7c00 (31k)处,并将自己 <BR>! 移到了地址0x90000 (576k)处,并跳转至那里。 <BR>! <BR>! bde - 不能盲目地跳转,有些系统可能只有512k的低 <BR>! 内存。使用中断0x12来获得(系统的)最高内存、等。 <BR>! <BR>! 它然后使用BIOS中断将setup直接加载到自己的后面(0x90200)(576.5k), <BR>! 并将系统加载到地址0x10000处。 <BR>! <BR>! 注意! 目前的内核系统最大长度限制为(8*65536-4096)(508k)字节长,即使是在 <BR>! 将来这也是没有问题的。我想让它保持简单明了。这样508k的最大内核长度应该 <BR>! 是足够了,尤其是这里没有象minix中一样包含缓冲区高速缓冲(而且尤其是现在 <BR>! 内核是压缩的 :-) <BR>! <BR>! 加载程序已经做的尽量地简单了,所以持续的读出错将导致死循环。只能手工重启。 <BR>! 只要可能,通过一次取得整个磁道,加载过程可以做的很快的。 <BR> <BR>#include /* 为取得CONFIG_ROOT_RDONLY参数 */ <BR>!! config.h中(即autoconf.h中)没有CONFIG_ROOT_RDONLY定义!!!? <BR> <BR>#include <BR> <BR>.text <BR> <BR>SETUPSECS = 4 ! 默认的setup程序扇区数(setup-sectors)的默认值; <BR> <BR>BOOTSEG = 0x7C0 ! bootsect的原始地址; <BR> <BR>INITSEG = DEF_INITSEG ! 将bootsect程序移到这个段处(0x9000) - 避开; <BR>SETUPSEG = DEF_SETUPSEG ! 设置程序(setup)从这里开始(0x9020); <BR>SYSSEG = DEF_SYSSEG ! 系统加载至0x1000(65536)(64k)段处; <BR>SYSSIZE = DEF_SYSSIZE ! 系统的大小(0x7F00): 要加载的16字节为一节的数; <BR>!! 以上4个DEF_参数定义在boot.h中: <BR>!! DEF_INITSEG 0x9000 <BR>!! DEF_SYSSEG 0x1000 <BR>!! DEF_SETUPSEG 0x9020 <BR>!! DEF_SYSSIZE 0x7F00 (=32512=31.75k)*16=508k <BR> <BR>! ROOT_DEV & SWAP_DEV 现在是由"build"中编制的; <BR>ROOT_DEV = 0 <BR>SWAP_DEV = 0 <BR>#ifndef SVGA_MODE <BR>#define SVGA_MODE ASK_VGA <BR>#endif <BR>#ifndef RAMDISK <BR>#define RAMDISK 0 <BR>#endif <BR>#ifndef CONFIG_ROOT_RDONLY <BR>#define CONFIG_ROOT_RDONLY 1 <BR>#endif <BR> <BR>! ld86 需要一个入口标识符,这和通常的一样; <BR>.globl _main <BR>_main: <BR>#if 0 /* 调试程序的异常分支,除非BIOS古怪(比如老的HP机)否则是无害的 */ <BR>int 3 <BR>#endif <BR>mov ax,#BOOTSEG !! 将ds段寄存器置为0x7C0; <BR>mov ds,ax <BR>mov ax,#INITSEG !! 将es段寄存器置为0x9000; <BR>mov es,ax <BR>mov cx,#256 !! 将cx计数器置为256(要移动256个字, 512字节); <BR>sub si,si !! 源地址 ds:si=0x07C0:0x0000; <BR>sub di,di !! 目的地址es:di=0x9000:0x0000; <BR>cld !! 清方向标志; <BR>rep !! 将这段程序从0x7C0:0(31k)移至0x9000:0(576k)处; <BR>movsw !! 共256个字(512字节)(0x200长); <BR>jmpi go,INITSEG !! 间接跳转至移动后的本程序go处; <BR> <BR>! ax和es现在已经含有INITSEG的值(0x9000); <BR> <BR>go: mov di,#0x4000-12 ! 0x4000(16k)是>=bootsect + setup 的长度 + <BR>! + 堆栈的长度 的任意的值; <BR>! 12 是磁盘参数块的大小 es:di=0x94000-12=592k-12; <BR> <BR>! bde - 将0xff00改成了0x4000以从0x6400处使用调试程序(bde)。如果 <BR>! 我们检测过最高内存的话就不用担心这事了,还有,我的BIOS可以被配置为将wini驱动 <BR>表 <BR>! 放在内存高端而不是放在向量表中。老式的堆栈区可能会搞乱驱动表; <BR> <BR>mov ds,ax ! 置ds数据段为0x9000; <BR>mov ss,ax ! 置堆栈段为0x9000; <BR>mov sp,di ! 置堆栈指针INITSEG:0x4000-12处; <BR>/* <BR>* 许多BIOS的默认磁盘参数表将不能 <BR>* 进行扇区数大于在表中指定 <BR>* 的最大扇区数( - 在某些情况下 <BR>* 这意味着是7个扇区)后面的多扇区的读操作。 <BR>* <BR>* 由于单个扇区的读操作是很慢的而且当然是没问题的, <BR>* 我们必须在RAM中(为第一个磁盘)创建新的参数表。 <BR>* 我们将把最大扇区数设置为36 - 我们在一个ED 2.88驱动器上所能 <BR>* 遇到的最大值。 <BR>* <BR>* 此值太高是没有任何害处的,但是低的话就会有问题了。 <BR>* <BR>* 段寄存器是这样的: ds=es=ss=cs - INITSEG,(=0X9000) <BR>* fs = 0, gs没有用到。 <BR>*/ <BR> <BR>! 上面执行重复操作(rep)以后,cx为0; <BR> <BR>mov fs,cx !! 置fs段寄存器=0; <BR>mov bx,#0x78 ! fs:bx是磁盘参数表的地址; <BR>push ds <BR>seg fs <BR>lds si,(bx) ! ds:si是源地址; <BR>!! 将fs:bx地址所指的指针值放入ds:si中; <BR>mov cl,#6 ! 拷贝12个字节到0x9000:0x4000-12开始处; <BR>cld <BR>push di !! 指针0x9000:0x4000-12处; <BR> <BR>rep <BR>movsw <BR> <BR>pop di !! di仍指向0x9000:0x4000-12处(参数表开始处); <BR>pop si !! ds => si=INITSEG(=0X9000); <BR> <BR>movb 4(di),*36 ! 修正扇区计数值; <BR> <BR>seg fs <BR>mov (bx),di !! 修改fs:bx(0000:0x0078)处磁盘参数表的地址为0x9000:0x4000-12; <BR>seg fs <BR>mov 2(bx),es <BR> <BR>! 将setup程序所在的扇区(setup-sectors)直接加载到boot块的后面。!! 0x90200开始处 <BR>; <BR>! 注意,es已经设置好了。 <BR>! 同样经过rep循环后cx为0 <BR> <BR>load_setup: <BR>xor ah,ah ! 复位软驱(FDC); <BR>xor dl,dl <BR>int 0x13 <BR> <BR>xor dx,dx ! 驱动器0, 磁头0; <BR>mov cl,#0x02 ! 从扇区2开始,磁道0; <BR>mov bx,#0x0200 ! 置数据缓冲区地址=es:bx=0x9000:0x200; <BR>! 在INITSEG段中,即0x90200处; <BR>mov ah,#0x02 ! 要调用功能号2(读操作); <BR>mov al,setup_sects ! 要读入的扇区数SETUPSECS=4; <BR>! (假释所有数据都在磁头0、磁道0); <BR>int 0x13 ! 读操作; <BR>jnc ok_load_setup ! ok则继续; <BR> <BR>push ax ! 否则显示出错信息。保存ah的值(功能号2); <BR>call print_nl !! 打印换行; <BR>mov bp,sp !! bp将作为调用print_hex的参数; <BR>call print_hex !! 打印bp所指的数据; <BR>pop ax <BR> <BR>jmp load_setup !! 重试! <BR> <BR>!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! <BR>!!INT 13 - DISK - READ SECTOR(S) INTO MEMORY <BR>!! AH = 02h <BR>!! AL = number of sectors to read (must be nonzero) <BR>!! CH = low eight bits of cylinder number <BR>!! CL = sector number 1-63 (bits 0-5) <BR>!! high two bits of cylinder (bits 6-7, hard disk only) <BR>!! DH = head number <BR>!! DL = drive number (bit 7 set for hard disk) <BR>!! ES:BX -> data buffer <BR>!! Return: CF set on error <BR>!! if AH = 11h (corrected ECC error), AL = burst length <BR>!! CF clear if successful <BR>!! AH = status (see #00234) <BR>!! AL = number of sectors transferred (only valid if CF set for some <BR>!! BIOSes) <BR>!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! <BR> <BR> <BR>ok_load_setup: <BR> <BR>! 取得磁盘驱动器参数,特别是每磁道扇区数(nr of sectors/track); <BR> <BR>#if 0 <BR> <BR>! bde - Phoenix BIOS手册中提到功能0x08只对硬盘起作用。 <BR>! 但它对于我的一个BIOS(1987 Award)不起作用。 <BR>! 不检查错误码是致命的错误。 <BR> <BR>xor dl,dl <BR>mov ah,#0x08 ! AH=8用于取得驱动器参数; <BR>int 0x13 <BR>xor ch,ch <BR> <BR>!!!!!!!!!!!!!!!!!!!!!!!!!!! <BR>!! INT 13 - DISK - GET DRIVE PARAMETERS (PC,XT286,CONV,PS,ESDI,SCSI) <BR>!! AH = 08h <BR>!! DL = drive (bit 7 set for hard disk) <BR>!!Return: CF set on error <BR>!! AH = status (07h) (see #00234) <BR>!! CF clear if successful <BR>!! AH = 00h <BR>!! AL = 00h on at least some BIOSes <BR>!! BL = drive type (AT/PS2 floppies only) (see #00242) <BR>!! CH = low eight bits of maximum cylinder number <BR>!! CL = maximum sector number (bits 5-0) <BR>!! high two bits of maximum cylinder number (bits 7-6) <BR>!! DH = maximum head number <BR>!! DL = number of drives <BR>!! ES:DI -> drive parameter table (floppies only) <BR>!!!!!!!!!!!!!!!!!!!!!!!!!!!! <BR> <BR>#else <BR> <BR>! 好象没有BIOS调用可取得扇区数。如果扇区36可以读就推测是36个扇区, <BR>! 如果扇区18可读就推测是18个扇区,如果扇区15可读就推测是15个扇区, <BR>! 否则推测是9. [36, 18, 15, 9] <BR> <BR>mov si,#disksizes ! ds:si->要测试扇区数大小的表; <BR> <BR>probe_loop: <BR>lodsb !! ds:si所指的字节 =>al, si=si+1; <BR>cbw ! 扩展为字(word); <BR>mov sectors, ax ! 第一个值是36,最后一个是9; <BR>cmp si,#disksizes+4 <BR>jae got_sectors ! 如果所有测试都失败了,就试9; <BR>xchg ax,cx ! cx = 磁道和扇区(第一次是36=0x0024); <BR>xor dx,dx ! 驱动器0,磁头0; <BR>xor bl,bl !! 设置缓冲区es:bx = 0x9000:0x0a00(578.5k); <BR>mov bh,setup_sects !! setup_sects = 4 (共2k); <BR>inc bh <BR>shl bh,#1 ! setup后面的地址(es=cs); <BR>mov ax,#0x0201 ! 功能2(读),1个扇区; <BR>int 0x13 <BR>jc probe_loop ! 如果不对,就试用下一个值; <BR> <BR>#endif <BR> <BR>got_sectors: <BR> <BR>! 恢复es <BR> <BR>mov ax,#INITSEG <BR>mov es,ax ! es = 0x9000; <BR> <BR>! 打印一些无用的信息(换行后,显示Loading) <BR> <BR>mov ah,#0x03 ! 读光标位置; <BR>xor bh,bh <BR>int 0x10 <BR> <BR>mov cx,#9 <BR>mov bx,#0x0007 ! 页0,属性7 (normal); <BR>mov bp,#msg1 <BR>mov ax,#0x1301 ! 写字符串,移动光标; <BR>int 0x10 <BR> <BR>! ok, 我们已经显示出了信息,现在 <BR>! 我们要加载系统了(到0x10000处)(64k处) <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -