FreeBSD启动扇区代码分析(ver5.2.1) 2007-04-27 来源:cnfug 作者: 关键词: FreeBSD代码BSD扇区分析 FreeBSD完整的内核代码在FreeBSD的/sys目录下。其中,FreeBSD 的 Boot Manager代码是 sys/boot/i386/boot0/boot0.s,它是FreeBSD自带的Boot Manager,其功
2007-04-27 来源:cnfug 作者:
关键词: FreeBSD 代码 BSD 扇区 分析
FreeBSD完整的内核代码在FreeBSD的 “/sys”目录下。其中,FreeBSD 的 Boot Manager代码是 sys/boot/i386/boot0/boot0.s,它是FreeBSD自带的Boot Manager,其功能虽然不如Linux的lilo和Grub功能强大,但它只需按一个键就可以引导,界面直观。小小的512字节,可以给你一个简单明 了的启动选择界面,还能记住你上次的选择。以下,就对此代码进行详细分析:
当我们打开计算机电源时,计算机叽叽嘎嘎进行设备和内 存检测过后就读取硬盘或者软盘的引导扇区,这个扇区只有512字节,显然这512字节不能够有多大作用,这512字节的代码被BIOS放在地址从 0x0000:0x7c00开始处。然后直接跳转到0x0000:0x7c00处去执行。以上工作是BIOS 干的,你什么也不用作。操作系统需要通过这个引导扇区代码再装载操作系统的其他部分。 在还没有跳转到这段代码之前,也就是BIOS把磁盘的引导扇区读入到内存之后,其DL和ES、SI寄存器的内容如下: DL:表示启动设备,例如,如果计算机是从软盘启动的则DL=0,若是从IDE的C、D盘(严格来说是物理磁盘一和物理磁盘二,而不是逻辑磁盘分区)启动 的则DL分别为0x80和0x81。如果是从硬盘启动的话,ES:SI是指向BIOS中的硬盘分区表存放的地址。
好了,我们现在已经知道,计算机的BIOS已经把引导扇区的512字节的内容读入到了0:0x7c00处,然后就跳转到0:0x7C00处去执行,也就是执行引导扇区代码,引导扇区代码boot0执行代码dump如下(它很有用,以后我们还不时回头来看):
560)this.style.width=560;''
onmousewheel = ''javascript:return big(this)'' height=517 alt=""
src="http://images.51cto.com/files/uploadimg/20051027/104330627.gif"
width=590 border=0>
上图中的4x16个字节是保留的4个分区信息
下面,我们对FreeBSD启动扇区代码boot0.s进行逐步分析。boot0.s代码如下:
<span><span><span><span>#<br> # Copyright (c) 1998 Robert Nordier<br> # All rights reserved.<br> #<br> # Redistribution and use in source and binary forms are freely<br> # permitted provided that the above copyright notice and this<br> # paragraph and the following disclaimer are duplicated in all<br> # such forms.<br> #<br> # This software is provided "AS IS" and without any express or<br> # implied warranties, including, without limitation, the implied<br> # warranties of merchantability and fitness for a particular<br> # purpose.<br> #<br> 以上的Coyright就不用翻译了。<br> <br> # $FreeBSD: src/sys/boot/i386/boot0/boot0.s,v 1.27 2003/11/20 20:28:18 jhb Exp $<br> 以上供版本管理软件使用<br> <br> <br> # A 512-byte boot manager.<br> <br> .set NHRDRV,0x475 # Number of hard drives<br> .set ORIGIN,0x600 # Execution address<br> .set FAKE,0x800 # Partition entry<br> .set LOAD,0x7c00 # Load address<br> <br> .set PRT_OFF,0x1be # Partition table<br> <br> .set TBL0SZ,0x3 # Table 0 size<br> .set TBL1SZ,0xb # Table 1 size<br> <br> .set MAGIC,0xaa55 # Magic: bootable<br> .set B0MAGIC,0xbb66 # Identification<br> <br> .set KEY_ENTER,0x1c # Enter key scan code<br> .set KEY_F1,0x3b # F1 key scan code<br> .set KEY_1,0x02 # #1 key scan code<br> #<br> # Addresses in the sector of embedded data values.<br> # Accessed with negative offsets from the end of the relocated sector (%ebp).<br> #<br> .set _NXTDRV,-0x48 # Next drive<br> .set _OPT,-0x47 # Default option<br> .set _SETDRV,-0x46 # Drive to force<br> .set _FLAGS,-0x45 # Flags<br> .set _TICKS,-0x44 # Timeout ticks<br> .set _FAKE,0x0 # Fake partition entry<br> .set _MNUOPT,0xc # Menu options<br> <br> <br> 以上是定义相关的参数值,例如“.set NHRDRV,0x475”类似于C语言中的“#define NHRDRV 0x475”<br> <br> .globl start # Entry point<br> .code16 # This runs in real mode<br> <br> #<br> # Initialise segments and registers to known values.<br> # segments start at 0.<br> # The stack is immediately below the address we were loaded to.<br> #<br> start:<br> cld # String ops inc<br> xorw %ax,%ax # Zero<br> movw %ax,%es # Address<br> movw %ax,%ds # data<br> movw %ax,%ss # Set up<br> movw $LOAD,%sp # stack<br> <br> <br> 以上代码:<br> 1)首先使用“cld”指令清除方向标志,使得以下的进行“rep”操作时SI和DI的值递增。<br> 2)使ax清零,并使除代码段cs外的另外两个数据段寄存器es、ds和堆栈段ss清零。当然,此时cs<br> 由于reset或初始上电已经为零了。<br> 3)BIOS已经把引导扇区的512字节的内容读入到了0:0x7c00处,movw $LOAD,%sp 使得堆栈指针指向扇区<br> 代码(或曰本段代码 0:0x7c00)的顶部。虽然堆栈向下生长可能会影响代码的内容,但下面我<br> 们马上就把位于0:7c00处代码移到其他地方去执行。<br> <br> <br> #<br> # Copy this code to the address it was linked for<br> #<br> movw %sp,%si # Source<br> movw $start,%di # Destination<br> movw $0x100,%cx # Word count<br> rep # Relocate<br> movsw # code<br> <br> 把位于0:7c00处的代码搬移到0:0x600处。注意,此时由于代码连接的重定向,$start=0x600。<br> <br> #<br> # Set address for variable space beyond code, and clear it.<br> # Notice that this is also used to point to the values embedded in the block,<br> # by using negative offsets.<br> movw %di,%bp # Address variables<br> movb $0x8,%cl # Words to clear<br> rep # Zero<br> stosw # them<br> <br> 通过以上一段代码的执行,本代码已被搬移到0:0x600处,此时si=di=0x600+0x100,以上代码<br> 把di的值保存到bp,bp此时指向本程序搬移后的未用的空间的首部,且把此bp所指的16字节空间<br> 清零。以上过程如下图所示:<br> <br> ┏>0:0x600 ┏━━━━━┓<br> ┃ ┃ ┃<br> ┃ ┃ 搬 ┃<br> ┃ ┃ 移 ┃<br> ┃ ┃ 之 ┃<br> ┃ ┃ 后 ┃<br> ┃ ┃ 的 ┃<br> ┃ ┃ 代 ┃<br> ┃ ┃ 码 ┃<br> ┃ ┃ ┃<br> ┃ 0:0x7ff ┣━━━━━┫<br> ┃ ┃ 0 ┃ ┃ ┣━━━━━┫以下所称的fake partition entry就是指这里。<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┣━━━━━┫<br> ┃ ┃ 0 ┃<br> ┃ ┗━━━━━┛<br> 0:0x7c00 ┏━━━━━┓ ┛<br> ┃ ┃<br> ┃ 搬 ┃<br> ┃ 移 ┃<br> ┃ 之 ┃<br> ┃ 前 ┃<br> ┃ 的 ┃<br> ┃ 代 ┃<br> ┃ 码 ┃<br> ┃ ┃<br> 0:0x7dff ┗━━━━━┛<br> <br> 图(二)<br> #<br> # Relocate to the new copy of the code.<br> #<br> incb -0xe(%di) # Sector number<br> jmp main-LOAD+ORIGIN # To relocated code<br> <br> 把以上清零的16字节的第二个字节置为1,表示我们已经读取了一个分区。然后跳转到搬<br> 移之后的新代码的main处执行。<br> <br> #<br> # Check what flags were loaded with us, specifically, Use a predefined Drive.<br> # If what the bios gives us is bad, use the ''0'' in the block instead, as well.<br> #<br> main:<br> testb $0x20,_FLAGS(%bp) # Set number drive?<br> jnz main.1 # Yes<br> testb %dl,%dl # Drive number valid?<br> js main.2 # Possibly (0x80 set)<br> main.1:<br> movb _SETDRV(%bp),%dl # Drive number to use<br> 上面说过,BIOS把磁盘的引导扇区读入到内存之后,其dl的内容表示启动设备,但我们安装好FreeBSD<br> 之后,我们可以改变此引导扇区的内容,其中的一个改变就是可以使我们可以“手动指定”我们实际安<br> 装FreeBSD的分区,如果我们希望指定FreeBSD所在的boot分区,那么我们在bp-0x45处的位置<br> (即_FLAGS(%bp)处)的bit 0x20置1,那么上面的“movb _SETDRV(%bp),%dl”一句中movb<br> _SETDRV(%bp),%dl(即bp-0x46)即指向我们“手动指定”FreeBSD所在分区代码,例如,IDE的C、D<br> 盘(严格来说是第一个物理磁盘的第一个和第二个分区)的代码分别为 0x80和0x81。如果没有“手动指<br> 定”启动分区,那么,我们缺省使用机器当前启动的分区,上面说过,机器当前启动的分区代码放在dl中。<br> <br> 因为FreeBSD Boot Manager 不可能安装到软盘(如果从软盘启动则dl为0),所以,使用testb %dl,%dl<br> 来判断驱动器代码是否合法(volid)。<br> <br> 有关_FLAGS(%bp)中其他bit位表示的意义,在随后的代码分析中慢慢给你道来。<br> <br> #<br> # Whatever we decided to use, now store it into the fake<br> # partition entry that lives in the data space above us.<br> #<br> main.2:<br> movb %dl,_FAKE(%bp) # Save drive number<br> callw putn # To new line<br> pushw %dx # Save drive number<br> <br> 以上第一句把FreeBSD启动分区的代码保存到_FAKE(%bp)(bp-0)处,也就是说,上图(二)的bp<br> 处保存的是FreeBSD启动分区的代码(_FAKE=0)。<br> <br> “callw putn”一句在屏幕上打印“回车”和“换行”,“pushw %dx”一句把启动分区<br> 的值压入堆栈。<br> #<br> # Start out with a pointer to the 4th byte of the first table entry<br> # so that after 4 iterations it''s beyond the end of the sector.<br> # and beyond a 256 byte boundary and has overflowed 8 bits (see next comment).<br> # (remember that the table starts 2 bytes earlier than you would expect<br> # as the bootable flag is after it in the block)<br> #<br> movw $(partbl+0x4),%bx # Partition table (+4)<br> xorw %dx,%dx # Item number<br> <br> 以上代码首先把%bx指向分区表partbl的的第四个字节,这里存放的是分区类型,如82表示<br> Linux Native分区83表示Linux Swap 分区,有关分区表的细节请详见本文的尾部。然后dx清零,<br> 此后,dx将作为遍历磁盘分区的列举代号使用。启动分区代码dl的原来的值在上面已经压入<br> 了堆栈保存。<br> <br> #<br> # Loop around on the partition table, printing values until we<br> # pass a 256 byte boundary. The end of loop test is at main.5.<br> #<br> main.3:<br> movb %ch,-0x4(%bx) # Zero active flag (ch == 0)<br> btw %dx,_FLAGS(%bp) # Entry enabled?<br> jnc main.5 # No<br> 上面首先使得第一个分区的活动标志为0,标志它不为活动标志,因为ch的值为0。至<br> 于第二句“btw %dx,_FLAGS(%bp)”中的_FLAGS(%bp)是上面我们说到的“手动指定我们<br> 实际安装FreeBSD的分区代码”。其中的bit 0x20我们在上面已经提到过。_FLAGS(%bp)<br> 中的其他位表示是否我们需要检查相应的磁盘分区。缺省情况下,我们需要检查所有<br> 的磁盘分区。检查磁盘分区看是否有可以启动的磁盘分区,例如,可能磁盘上的某个<br> 分区为WindowsXP或者是Linux等。如果我们没有改变在磁盘上该处的值,则相应的bit<br> 位的值为0,表示所有的分区都要检查(因为此时_FLAGS(%bp)中的值为0),否则,<br> 只针对FLAGS(%bp)中相应的bit位未被设置为1的分区进行检查。<br> <br> 大家知道,FreeBSD Manager启动时可能出现以下的提示:<br> <br> F1 FreeBSD<br> F2 ??<br> F3 BSD<br> F4 ??<br> Default F1<br> <br> 其中,上面的提示中出现了令人讨厌的“??”,为了避免出现“??”的提示,我们可以设置相应的<br> 第一分区和第四分区不检查,就需要正确设置_FLAGS(%bp)中的bit位。设置好后,屏幕可能<br> 出现以下的提示:<br> <br> F1 FreeBSD<br> F2 BSD<br> Default F1<br> <br> #<br> # If any of the entries in the table are<br> # the same as the ''type'' in the slice table entry,<br> # then this is an empty or non bootable partition. Skip it.<br> #<br> movb (%bx),%al # Load type<br> movw $tables,%di # Lookup tables<br> movb $TBL0SZ,%cl # Number of entries<br> repne # Exclude<br> scasb # partition?<br> je main.5 # Yes<br> 我们从上面已经知道起始(%bx)指向的是MBR中分区信息1(16字节)的位置(见图(三)),<br> 以上代码在“忽略的分区类型$tables”中查找看是否本分区是不可启动的或者不合法的分区。<br> 不可启动的或者不合法的分区类型有3($TBL0SZ=3)个,它们是“0x0, 0x5, 0xf”,见下面的<br> $tables处。如果是不可启动的或者不合法的分区类型则跳转到main.5,进行下一轮循环。<br> <br> #<br> # Now scan the table of known types<br> #<br> movb $TBL1SZ,%cl # Number of entries<br> repne # Known<br> scasb # type?<br> jne main.4 # No<br> #<br> # If it matches get the matching element in the<br> # next array. if it doesn''t, we are already<br> # pointing at its first element which points to a "?".<br> #<br> addw $TBL1SZ,%di # Adjust<br> main.4:<br> movb (%di),%cl # Partition<br> addw %cx,%di # description<br> callw putx # Display it<br> 上面检查看所检查的分区类型是否为我们知道的分区类型,知道的分区类型有11($TBL1SZ=0xb)<br> 个,它们是:“0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83,0x9f, 0xa5, 0xa6, 0xa9”,见<br> 下面的$tables处。如果不是以上的类型,则跳转到main.4。那么,(%di)所指的字串是“??”,<br> 如果分区类型是“0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83,0x9f, 0xa5, 0xa6, 0xa9”<br> 之一,则(%di)所指的字串是“DOS”、“Linux”、“FreeBSD”或“BSD”等。<br> 见下面的“os_misc”、“os_dos”、“os_linux”、“os_freebsd”、“os_bsd”等<br> 标记。<br> <br> callw putx调用putx函数,在屏幕上打印:“Fx XXX”。其中XXX为DOS”、“Linux”、<br> “FreeBSD”或“BSD”等。<br> <br> main.5:<br> incw %dx # Next item<br> addb $0x10,%bl # Next entry<br> jnc main.3 # Till done<br> 遍历磁盘分区的举代号dx加1,重复下一轮循环查找。bl加上0x10(0x10=16)表示寻址到下<br> 一个分区信息(加16字节)入口。循环直到255字节边界。<br> <br> #<br> # Passed a 256 byte boundary..<br> # table is finished.<br> # Add one to the drive number and check it is valid,<br> #<br> popw %ax # Drive number<br> subb $0x80-0x1,%al # Does next<br> cmpb NHRDRV,%al # drive exist? (from BIOS?)<br> jb main.6 # Yes<br> “popw %ax”把上面压入堆栈的bx(当前的启动扇区)值弹出到ax中。例如,如果计算机是从软盘<br> 启动的则dl=0,若是从IDE的C、D盘启动的则dl分别为 0x80和0x81。然而,FreeBSD的Boot Manerger不能够<br> 安装到软盘上,所以,dl只能为0x80、0x81,0x82...等。<br> 在计算机的BIOS地址0:0x475处存放的是计算机的硬盘的数目,“subb $0x80-0x1,%al”一句等于“sub<br> $0x79,%al”,例如,即当前驱动器如果是C盘,则al的值是ox80-0x79=1,然后再与计算机的硬盘的数目比<br> 较,如果当前所在硬盘不是最后一个硬盘,则直接跳转到main.6。如果当前所在硬盘是最后一个硬盘,则继<br> 续执行。<br> <br> # If not then if there is only one drive,<br> # Don''t display drive as an option.<br> #<br> decw %ax # Already drive 0?<br> jz main.7 # Yes<br> 如果只有一个硬盘,则直接跳转到main.7,这样,本计算机只有一个硬盘,所以不用显示其他<br> 磁盘相关的提示。<br> <br> # If it was illegal or we cycled through them,<br> # then go back to drive 0.<br> #<br> xorb %al,%al # Drive 0<br> <br> 下面的内容表示多于一个磁盘的情况。此时“al”清0,与磁盘列举相关。<br> #<br> # Whatever drive we selected, make it an ascii digit and save it back<br> # to the "next drive" location in the loaded block in case we<br> # want to save it for next time.<br> # This also is part of the printed drive string so add 0x80 to indicate<br> # end of string.<br> #<br> main.6:<br> addb $''0''|0x80,%al # Save next<br> movb %al,_NXTDRV(%bp) # drive number<br> movw $drive,%di # Display<br> callw putx # item<br> 首先,在_NXTDR(%bp)处置入“0字符高位置1”的字符,以代表第二个驱动器,<br> 然后在屏幕上显示“Fx Drive”,表示更换另外的磁盘启动。注意,在调用<br> putx之前,di中保存的是下面字串“Drive ”的首地址。dl中存放的是当前<br> 遍历的到的可启动的或者合法的分区类型递增序数,al与dl是不同的,al是ASCII码,<br> dl是“Fx”中的x值。<br> <br> #<br> # Now that we''ve printed the drive (if we needed to), display a prompt.<br> # Get ready for the input byt noting the time.<br> #<br> main.7:<br> movw $prompt,%si # Display<br> callw putstr # prompt<br> movb _OPT(%bp),%dl # Display<br> decw %si # default<br> callw putkey # key<br> xorb %ah,%ah # BIOS: Get<br> int $0x1a # system time<br> movw %dx,%di # Ticks when<br> addw _TICKS(%bp),%di # timeout<br> 上面的代码首先在屏幕上打印出字符串“Default: ”,缺省启动的磁盘号放在<br> “_OPT(%bp)”中,这里有个小小的技巧,在执行“decw %si”和“callw putkey”<br> 两句后屏幕会显示“Fx”,x是_OPT(%bp)的ASCII。<br> <br> 然后取得当前的tickes放到%di中,等待用户按键超时的时间从_TICKS(%bp)中取出,<br> 加到当前的tickes即是最后超时时间到的tickes。<br> <br> #<br> # Busy loop, looking for keystrokes but<br> # keeping one eye on the time.<br> #<br> main.8:<br> movb $0x1,%ah # BIOS: Check<br> int $0x16 # for keypress<br> jnz main.11 # Have one<br> xorb %ah,%ah # BIOS: Get<br> int $0x1a # system time<br> cmpw %di,%dx # Timeout?<br> jb main.8 # No<br> 等待用户按下“Fx”键,同时检查当前等待是否超时,如果有用户按键则跳转到main.11,<br> 如果超时时间不到则继续等待。<br> <br> #<br> # If timed out or defaulting, come here.<br> #<br> main.9:<br> movb _OPT(%bp),%al # Load default<br> jmp main.12 # Join common code<br> 超时时间到,此时表示用户使用缺省分区启动,把缺省的启动分区号置入al中,然后跳转<br> 到main.12。<br> <br> #<br> # User''s last try was bad, beep in displeasure.<br> # Since nothing was printed, just continue on as if the user<br> # hadn''t done anything. This gives the effect of the user getting a beep<br> # for all bad keystrokes but no action until either the timeout<br> # occurs or the user hits a good key.<br> #<br> main.10:<br> movb $0x7,%al # Signal<br> callw putchr # error<br> 用户输入错误,只是响铃提示,其他什么也不发生。<br> <br> #<br> # Get the keystroke.<br> #<br> main.11:<br> xorb %ah,%ah # BIOS: Get<br> int $0x16 # keypress<br> movb %ah,%al # Scan code<br> 用户按下了一个键,把键值扫描码放到al中。<br> <br> #<br> # If it''s CR act as if timed out.<br> #<br> cmpb $KEY_ENTER,%al # Enter pressed?<br> je main.9 # Yes<br> 如果用户按下“Enter”键,和超时等同处理,这样,就启动缺省的boot分区。<br> <br> #<br> # Otherwise check if legal<br> # If not ask again.<br> #<br> subb $KEY_F1,%al # Less F1 scan code<br> cmpb $0x4,%al # F1..F5?<br> jna main.12 # Yes<br> subb $(KEY_1 - KEY_F1),%al # Less #1 scan code<br> cmpb $0x4,%al # #1..#5?<br> ja main.10 # No<br> 如果是除“Enter”键外其他的键,则检查是不是F1...F5键,如果不是,<br> 表示输入不合法,跳回到main.10处理。<br> <br> #<br> # We have a selection.<br> # but if it''s a bad selection go back to complain.<br> # The bits in MNUOPT were set when the options were printed.<br> # Anything not printed is not an option.<br> #<br> main.12:<br> cbtw # Option<br> btw %ax,_MNUOPT(%bp) # enabled?<br> jnc main.10 # No<br> 如果是F1...F5键,则检查是否在我们提示的范围内,其中,_MNUOPT(%bp)的相应<br> bit位为1,表示是一个合法的选项,如果不是,跳回到 main.10处理。<br> <br> #<br> # Save the info in the original tables<br> # for rewriting to the disk.<br> #<br> movb %al,_OPT(%bp) # Save option<br> 把我们按下的F1...F5键保存到_OPT(%bp)位置。<br> <br> movw $FAKE,%si # Partition for write<br> movb (%si),%dl # Drive number<br> 把原来的启动分区代码取回到dl中。<br> <br> movw %si,%bx # Partition for read<br> cmpb $0x4,%al # F5 pressed?<br> pushf # Save<br> je main.13 # Yes<br> 如果我们按下的是F5键则直接跳转到main.13处理。<br> <br> shlb $0x4,%al # Point to<br> addw $partbl,%ax # selected<br> xchgw %bx,%ax # partition<br> movb $0x80,(%bx) # Flag active<br> 上面,我们从按键Fx选择中得到图(三)中的我们选择的四个分区信息中的某一分区信息,<br> 上面计算出的bx为我们选择的分区信息的首地址,我们把此选择到的分区信息的第一个<br> 个字节置为0x80表示它是当前的活动分区。<br> #<br> # If not asked to do a write-back (flags 0x40) don''t do one.<br> #<br> main.13:<br> pushw %bx # Save<br> testb $0x40,_FLAGS(%bp) # No updates?<br> jnz main.14 # Yes<br> movw $start,%bx # Data to write<br> movb $0x3,%ah # Write sector<br> callw intx13 # to disk<br> 检查回写标志_FLAGS(%bp)的bit位0x40为,如果设置的是可回写,则把当前选择到的boot<br> 分区作为下次缺省的启动分区。<br> <br> main.14:<br> popw %si # Restore<br> popf # Restore<br> <br> #<br> # If going to next drive, replace drive with selected one.<br> # Remember to un-ascii it. Hey 0x80 is already set, cool!<br> #<br> jne main.15 # If not F5<br> 恢复上面保存的si和标志寄存器的内容。如果不是按键F5,则直接跳转到main.15去执行。<br> <br> movb _NXTDRV(%bp),%dl # Next drive<br> subb $''0'',%dl # number<br> <br> 否则的话,我们选择下一个驱动器作为启动盘。<br> #<br> # load selected bootsector to the LOAD location in RAM.<br> # If it fails to read or isn''t marked bootable, treat it<br> # as a bad selection.<br> # XXX what does %si carry?<br> #<br> main.15:<br> movw $LOAD,%bx # Address for read<br> movb $0x2,%ah # Read sector<br> callw intx13 # from disk<br> jc main.10 # If error<br> 把我们上面选择到的分区读到0x7c00处,就象我们刚刚才加电启动一样,只是活动分区改变<br> 了而已。如果发生读错误则直接跳转到main.10。使用户重新选择启动分区。<br> <br> cmpw $MAGIC,0x1fe(%bx) # Bootable?<br> jne main.10 # No<br> 判断引导扇区的最后两个字节是否是“0x55AA”,如果不是,则表示此扇区是不可引导的,或<br> 不合法的引导扇区则直接跳转到main.10。使用户重新选择启动分区。<br> <br> pushw %si # Save<br> movw $crlf,%si # Leave some<br> callw puts # space<br> popw %si # Restore<br> <br> 打印“回车”和“换行”。<br> <br> jmp *%bx # Invoke bootstrap<br> <br> 跳转到我们选择的引导扇区去执行。整个Boot Manager代码到此执行完毕。<br> <br> #<br> # Display routines<br> #<br> <br> putkey:<br> movb $''F'',%al # Display<br> callw putchr # ''F''<br> movb $''1'',%al # Prepare<br> addb %dl,%al # digit<br> jmp putstr.1 # Display the rest<br> “putkey”函数在屏幕上打印“F1”、“F2”或“F3”等。如果dl为0则打印“F1”,<br> 如果dl为1则打印“F2”,如果dl为3则打印“F3”。和调用“putstr”在屏幕上打印<br> es:si指向的以最高位置1为结束字符的字符串。<br> #<br> # Display the option and note that it is a valid option.<br> # That last point is a bit tricky..<br> #<br> putx: #首先,把_MNUOPT(%bp)的第dx位(bit)置1,表示此菜单选项被显示。然后在<br> 屏幕上打印空格和es:di指向的以最高位置1为结束字符的字符串。<br> btsw %dx,_MNUOPT(%bp) # Enable menu option<br> movw $item,%si # Display<br> callw putkey # key<br> movw %di,%si # Display the rest<br> <br> puts: #调用“putstr”在屏幕上打印es:si指向的以最高位置1为结束字符的字符串。<br> callw putstr # Display string<br> <br> putn: #“putn”打印“回车/换行”后在屏幕上打印es:si指向的以最高位置1为结束字符的字符串。<br> movw $crlf,%si # To next line<br> <br> putstr: #“putstr”在屏幕上打印es:si指向的以最高位置1为结束字符的字符串。<br> lodsb # Get byte<br> testb $0x80,%al # End of string?<br> jnz putstr.2 # Yes<br> putstr.1:<br> callw putchr # Display char<br> jmp putstr # Continue<br> putstr.2:<br> andb $~0x80,%al # Clear MSB<br> <br> putchr:<br> pushw %bx # Save<br> movw $0x7,%bx # Page:attribute<br> movb $0xe,%ah # BIOS: Display<br> int $0x10 # character<br> popw %bx # Restore<br> retw # To caller<br> “putchr”在屏幕上显示“al”中的字符。<br> # One-sector disk I/O routine<br> <br> intx13:<br> movb 0x1(%si),%dh # Load head<br> movw 0x2(%si),%cx # Load cylinder:sector<br> movb $0x1,%al # Sector count<br> pushw %si # Save<br> movw %sp,%di # Save<br> testb $0x80,_FLAGS(%bp) # Use packet interface?<br> jz intx13.1 # No<br> pushl $0x0 # Set the<br> pushl 0x8(%si) # LBA address<br> pushw %es # Set the transfer<br> pushw %bx # buffer address<br> push $0x1 # Block count<br> push $0x10 # Packet size<br> movw %sp,%si # Packet pointer<br> decw %ax # Verify off<br> orb $0x40,%ah # Use disk packet<br> intx13.1:<br> int $0x13 # BIOS: Disk I/O<br> movw %di,%sp # Restore<br> popw %si # Restore<br> retw # To caller<br> <br> # Menu strings<br> <br> item:<br> .ascii " "; .byte '' ''|0x80<br> prompt:<br> .ascii "/nDefault:"; .byte '' ''|0x80<br> crlf:<br> .ascii "/r"; .byte ''/n''|0x80<br> <br> # Partition type tables<br> <br> tables:<br> #<br> # These entries identify invalid or NON BOOT types and partitions.<br> #<br> .byte 0x0, 0x5, 0xf<br> #<br> # These values indicate bootable types we know the names of<br> #<br> .byte 0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83<br> .byte 0x9f, 0xa5, 0xa6, 0xa9<br> #<br> # These are offsets that match the known names above and point to the strings<br> # that will be printed.<br> #<br> .byte os_misc-. # Unknown<br> .byte os_dos-. # DOS<br> .byte os_dos-. # DOS<br> .byte os_dos-. # DOS<br> .byte os_dos-. # Windows<br> .byte os_dos-. # Windows<br> .byte os_dos-. # Windows<br> .byte os_linux-. # Linux<br> .byte os_bsd-. # BSD/OS<br> .byte os_freebsd-. # FreeBSD<br> .byte o <br> (完)</span> </span> </span> </span>