linux源代码阅读-系统启动bootsect.s

来源:互联网 发布:唯品会用什么抢购软件 编辑:程序博客网 时间:2024/05/16 11:33

系统启动bootsect.S

一般PC在电源一开时,是由内存中地址FFFF:0000开始执行(这个地址一定在ROM BI

OS中,ROM BIOS一般是在FEOOOhFFFFFh),而此处的内容则是一个jump指令,jump到另一个位於ROM BIOS中的位置,开始执行一系列的动作,包括了检查RAMkeyboard,显示器,软硬磁盘等等,这些动作是由系统测试代码 (POSTsystem test code)来执行的,随着制作BIOS厂商的不同而会有些许差异,但都是大同小异,读者可自行观察自家机器开机时,萤幕上所显示的检查讯息。

紧接着系统测试码之后,控制权会转移给ROM中的启动程序(ROM bootstrap routine),这个程序会将磁盘上的第零轨第零扇区读入内存中(这就是一般所谓的boot sector,如果你曾接触过电脑病毒,就大概听过它的大名),至於被读到内存的哪里呢? --绝对位置07C0:0000(07C00h),这是IBM系列PC的特性。而位在linux开机磁盘的boot sector上的正是linuxbootsect程序,也就是说,bootsect是第一个被读入内存中并执行的程序。现在,我们可以开始来看看到底bootsect做了什么。

 

第一步

  首先,bootsect将它"自己"从被ROM BIOS载入的绝对地址0x7C00处搬到0x90000处,

然后利用一个jmpi(jump indirectly)的指令,跳到新位置的jmpi的下一行去执行,

       movw      $BOOTSEG, %ax               $BOOTSEG           = 0x07C0

       movw       %ax, %ds

       movw       $INITSEG, %ax               #$INITSEG            = DEF_INITSEG=0x0x9000

       movw       %ax, %es

       movw       $256, %cx                #256word s== 512bytes

       subw       %si, %si

       subw       %di, %di

       cld

       rep

       movsw

       ljmp       $INITSEG, $go

 

第二步

  接着,将其他segment registers包括DSESSS都指向0x9000这个位置,与CS看齐

。另外将SPDX指向一任意位移地址( offset ),这个地址等一下会用来存放磁盘参数

(disk para- meter table )

go:   movw      $0x4000-12, %di         # 0x4000 is an arbitrary value >=

                                   # length of bootsect + length of

                                   # setup + room for stack;

                                   # 12 is disk parm size.

       movw       %ax, %ds              # ax and es already contain INITSEG

       movw       %ax, %ss

       movw       %di, %sp              # put stack at INITSEG:0x4000-12.sp=0x94000-1212byte放磁盘参数

 

第三步

  接着利用BIOS中断服务int 13h的第0号功能,重置磁盘控制器,使得刚才的设定发

挥功能。主要是为了一次读扇区数目增加,增加效率。

# Many BIOS's default disk parameter tables will not recognize

# multi-sector reads beyond the maximum sector number specified

# in the default diskette parameter tables - this may mean 7

# sectors in some cases.

#

# Since single sector reads are slow and out of the question,

# we must take care of this by creating new parameter tables

# (for the first disk) in RAM.  We will set the maximum sector

# count to 36 - the most we will encounter on an ED 2.88. 

#

# High doesn't hurt.  Low does.

#

# Segments are as follows: ds = es = ss = cs = INITSEG, fs = 0,

# and gs is unused.

 

       movw       %cx, %fs              # set fs to 0

movw   $0x78, %bx      # fs:bx is parameter table address,磁盘参数已经由BIOS读到这个地址了,就是物理地址0x78

       pushw       %ds

       ldsw           %fs:(%bx), %si              # ds:si is source  ds:si==0x0000:0x78

       movb       $6, %cl                 # copy 12 bytes

       pushw       %di                # di = 0x4000-12.

       rep                        # don't need cld -> done on line 66

       movsw                  #ds:si--àes:di

       popw       %di

       popw       %ds

       movb       $36, 0x4(%di)        # patch sector count,36重置

       movw       %di, %fs:(%bx)

       movw       %es, %fs:2(%bx)

       xorb %ah, %ah        # reset FDC

       xorb %dl, %dl

       int   $0x13      

                                

第四步

  完成重置磁盘控制器之后,bootsect就从磁盘上读入紧邻着bootsectsetup程序,

也就是setup.S,此读入动作是利用BIOS中断服务int 13h的第2号功能。setupimage

会读入至程序所指定的内存绝对地址0x90200处,也就是在内存中紧邻着bootsect 所在

的位置。待setupimage读入内存后,利用BIOS中断服务int 13h的第2号功能读取目前

磁盘的参数。

xorw       %dx, %dx              # drive 0, head 0

       movb       $0x02, %cl              # sector 2, track 0

       movw       $0x0200, %bx        # address = 512, in INITSEG 0x90200

       movb       $0x02, %ah              # service 2, "read sector(s)"

       movb       setup_sects, %al  # (assume all on head 0, track 0) setup_sects=4,读取的扇区数

       int    $0x13                     # read itsetup.S位于内存物理地址0x90200

       jnc       ok_load_setup           # ok – continue

       #读盘错误处理

pushw       %ax               # dump error code  出错处理,ax得到错误码

       call       print_nl

       movw       %sp, %bp

       call       print_hex

       popw       %ax

       jmp       load_setup

 

ok_load_setup:

# Get disk drive parameters, specifically number of sectors/track.

 

# It seems that there is no BIOS call to get the number of sectors.

# Guess 36 sectors if sector 36 can be read, 18 sectors if sector 18

# can be read, 15 if sector 15 can be read.  Otherwise guess 9.

 

movw    $disksizes, %si        # table of sizes to try   在文件末尾定义了数据disksizes:    .byte  36, 18, 15, 9

probe_loop:

       lodsb            (AL)<-((SI)),(SI)<-(SI)+1

       cbtw                            # extend to wordal->ax

       movw       %ax, sectors   sectors:   .word  0

       cmpw       $disksizes+4, %si

       jae       got_sectors              #jae不低于,或者高于或者等于,或进位位为0则转移If all else fails, try 9。就是3618159都测试过了,就跳到got_sectors

      

       xchgw       %cx, %ax              # cx = track and sector

       xorw       %dx, %dx              # drive 0, head 0

       xorb        %bl, %bl

       movb       setup_sects, %bh

       incb        %bh

       shlb        %bh               # address after setup (es = cs)

       movw       $0x0201, %ax        # service 2, 1 sector

       int    $0x13

       jc       probe_loop              # try next value,就是从0头,0磁道,读取第361815号扇区,看能否成功,如果成功就跳到got_sectors,否则就再测试下一个数。把内容读到setup.S后面的内存中,这些扇区内容没有什么意义,只是为了测试sectors/track

 

第五步

  再来,就要读入真正linuxkernel了,也就是你可以在linux的根目录下看到的"v

mlinuz" 。在读入前,将会先呼叫BIOS中断服务int 10h 的第3号功能,读取游标位置,

之后再呼叫BIOS 中断服务int 10h的第13h号功能,在萤幕上输出字串"Loading",这个

字串在boot linux时都会首先被看到,相信大家应该觉得很眼熟吧。

got_sectors:

       movw       $INITSEG, %ax

       movw       %ax, %es              # set up es

       movb       $0x03, %ah              # read cursor pos

       xorb        %bh, %bh

       int    $0x10       #调用10号中断,功能3,读取光标的位置

 

       movw       $9, %cx

       movw       $0x0007, %bx        # page 0, attribute 7 (normal)

       movw    $msg1, %bp

       movw    $0x1301, %ax        # write string, move cursor

       int    $0x10                     # tell the user we're loading..在光标处打印msg1的内容,msg1’loading…..’

movw       $SYSSEG, %ax        # ok, we've written the message, now ,$SYSSEG=0x1000

       movw       %ax, %es              # we want to load system (at 0x10000)

       call  read_it       #读取vmlinux0x10000,大小为0x7f00,会已最快的速度去读,方法就是用前面测试的sectors/track,一次读完整个磁道。

       call       kill_motor

       call       print_nl

 

第六步

  接下来做的事是检查root device,之后就仿照一开始的方法,利用indirect jump

 跳至刚刚已读入的setup部份

 

# 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, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8)

# depending on the number of sectors we pretend to know we have.

 

       movw       root_dev, %ax

       orw %ax, %ax

       jne       root_defined

      

       movw       sectors, %bx

       movw       $0x0208, %ax        # /dev/ps0 - 1.2Mb

       cmpw       $15, %bx

       je       root_defined

      

       movb       $0x1c, %al              # /dev/PS0 - 1.44Mb

       cmpw       $18, %bx

       je       root_defined

      

       movb       $0x20, %al              # /dev/fd0H2880 - 2.88Mb

       cmpw       $36, %bx

       je       root_defined

      

       movb       $0, %al                 # /dev/fd0 - autodetect

root_defined:

       movw       %ax, root_dev

 

# After that (everything loaded), we jump to the setup-routine

# loaded directly after the bootblock:

 

       ljmp       $SETUPSEG, $0  jmp to 0x90200

原创粉丝点击