当前位置:Linux教程 - Linux - Minix代码分析之一:引导扇区代码

Minix代码分析之一:引导扇区代码



         Minix代码分析之一:引导扇区代码

    作者: 聪聪



    1.1 计算机加电
    当我们打开计算机电源时,计算机叽叽嘎嘎进行设备和内存检测过后就读取硬盘或者软盘的引导扇区,这个扇区只有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中的硬盘分区表存放的地址。

    1.2 执行引导扇区代码
    好了,我们现在已经知道,计算机的BIOS已经把引导扇区的512字节的内容读入到了0:0x7c00处,然后就跳转到0:0x7C00处去执行,也就是执行引导扇区代码,其引导扇区代码见后面。下面对引导扇区代码进行解释。

    引导扇区代码的执行过程如下:

    1.2.1 BIOS在把引导扇区装载到地址0:0x7C00处后,引导扇区代码首先设置正确的堆栈。其堆栈内容如下图所示:
    堆栈 说明
       
    si  
    es 如果是硬盘的话ES:SI为指向BIOS的硬盘分区表地址
    dx <-0:0x7c04,其中,dl是启动设备号码(0=软盘A,1=软盘b,0x80=硬盘C,0x81=硬盘D等)bp也指向此处的地址
    00H <-0:0x7c02(本程序会在此处填入boot分区的偏移)
    00H <-0:0x7c00(本程序会在此处填入每个柱面的扇区数)
      以下为引导扇区代码(即此程序代码)

    1.2.2 调用BIOS中断:ah=0x08,int 0x13得到磁盘驱动器参数。

    其BIOS中断调用ah=0x08,int 0x13说明如下:
    中断调用ah=0x08,int 0x13返回后,在以下寄存器返回以下信息:

    DL:本机软盘驱动器的数目
    DH:最大磁头号(或说磁面数目)。0表示有1个磁面,1表示有2个磁面
    CH:存放10位磁道柱面数的低8位(高2位在CL的D7、D6中)。1表示有1个柱面,2表示有2个柱面,依次类推。
    CL:0~5位存放每磁道的扇区数目。6和7位表示10位磁道柱面数的高2位。
    AX=0
    BH=0
    BL表示驱动器类型:
    1=360K 5.25
    2=1.2M 5.25
    3=720K 3.5
    4=1.44M 3.5
    ES:SI 指向软盘参数表

    错误信息:
    若产生错误,进位标志CF=1,AH存放错误信息码。
    1.2.3 把以上得到的磁盘参数分别放到parameters处相应的位置,磁盘参数占11字节的空间。
    1.2.4 根据上面得到的磁盘参数调用BIOS中断ah=0x02H,int 13H来读取第1扇区,且把它存放在地址0:0x1000处。
    1.2.5 跳转到0:0x1000处执行装载kernel、mm、fs、net等代码。
    1.3 引导扇区代码

    以下为引导扇区代码:(其中,洋文我一字未动,中文是我添加的,以后同)
    ! Bootblock 1.2 - Minix boot block. Author: Kees J. Bot
    ! Floppy sensing code: Guy Helmer
    ! When the PC is powered on, it will try to read the first sector of floppy
    ! disk 0 into address 0x7C00. If this fails due to the absence of flexible
    ! magnetic media, it will read the master boot record from the first sector
    ! of the hard disk. This sector not only contains executable code, but also
    ! the partition table of the hard disk. When executed, it will select the
    ! active partition and load the first sector of that into address 0x7C00.
    ! This file contains the code that is eventually read from either the floppy
    ! disk, or the hard disk partition. It is just smart enough to load the
    ! secondary boot code from the boot device into memory at address 0x10000 and
    ! execute that. The disk addresses for this secondary boot code are patched
    ! into this code by installboot as 24-bit sector numbers and 8-bit sector
    ! counts above enddata upwards. The secondary boot code is in turn smart
    ! enough to load the different parts of the Minix kernel into memory and
    ! execute them to finally get Minix started.
    !
    ! Kees J. Bot - 91/12/21:
    ! Adapted Guy Helmers code and added hard disk support for my boot monitor
    ! package.
    !

    LOADOFF = 0x7C00 ! 0x0000:LOADOFF is where this code is loaded
    !对了,以上的宏定义是指本代码存放的位置了

    BOOTSEG = 0x1000 ! Secondary boot code segment.
    !以上的宏定义表示第二个BOOT代码的位置。

    BOOTOFF = 0x0030 ! Offset into secondary boot above header
    BUFFER = 0x0600 ! First free memory
    DSKBASE = 0x1E ! Floppy disk parameter vector
    DSKPARSIZE = 11 ! 11 bytes of floppy parameters
    SECTORS = 4 ! Offset into parameters to sectors per track
    LOWSEC = 8 ! Offset of logical first sector in partition
    ! table
    ! Variables addressed using bp register
    device = 0 ! The boot device
    lowsec = 2 ! Offset of boot partition within drive
    secpcyl = 6 ! Sectors per cylinder = heads * sectors

    .define begtext, begdata, begbss, endtext, enddata, endbss, _main
    .data
    begdata:
    .bss
    begbss:
    .text
    begtext:
    _main:
    ! Start boot procedure.
    !此处被放在0:0x7c00处,这是Minix执行的第一个语句。由于上面已经讲得很清楚了
    !而且有洋文注释,以下不再祥述。
    boot:
    !设置正确的数据段
    xor ax, ax ! ax = 0x0000, the vector segment
    mov ds, ax
    cli ! Ignore interrupts while setting stack
    mov ss, ax ! ss = ds = vector segment

    !栈底为0:0x7c00
    mov sp, #LOADOFF ! Usual place for a bootstrap stack
    sti


    !上面已经解释过了,在栈底留下两个字的空间以供传递参数给加载代码(Minix代码分析(2)马上就会讲到)
    push ax
    push ax ! Push a zero lowsec(bp)


    !临时在堆栈中保存启动设备(dl)
    push dx ! Boot device in dl will be device(bp)
    !bp指向此处,以后用bp指针改变本栈的内容
    mov bp, sp ! Using var(bp) is one byte cheaper then var.


    !把BIOS的硬磁盘参数表指针(es:si)保存在栈中。
    push es
    push si ! es:si = partition table entry if hard disk


    !为了以后适当改变parameter处的磁盘参数表,现在则正确的设置di
    mov di, #LOADOFF+parameters ! char (*di)[DSKPARSIZE] = parameters;


    !判断启动设备是硬盘还是软盘
    testb dl, dl ! Winchester disks if dl >= 0x80
    jge floppy


    winchester:

    !启动设备是硬盘。
    ! Get the offset of the first sector of the boot partition from the partition
    ! table. The table is found at es:si, the lowsec parameter at offset LOWSEC.
    !对了,我们已经说过,es:si已经指向了硬盘的磁盘参数表,以下ax里面存放的就会是分区表中
    !活动分区的第一个逻辑扇区的偏移
    eseg
    les ax, LOWSEC(si) ! es:ax = LOWSEC+2(si):LOWSEC(si)

    ! 把引导盘的逻辑第一个扇区的偏移放到栈中,也就是说改变栈的内容,(注意上面讲到过的BP的值)
    mov lowsec+0(bp), ax ! Low 16 bits of partitions first sector
    mov lowsec+2(bp), es ! High 16 bits of partitions first sector


    ! Get the drive parameters, the number of sectors is bluntly written into the
    ! floppy disk parameters.
    !对了,上面已经说过的,取得磁盘参数
    movb ah, #0x08 ! Code for drive parameters
    int 0x13 ! dl still contains drive


    !取得每扇区的扇区数目
    andb cl, #0x3F ! cl = max sector number (1-origin)
    movb SECTORS(di), cl ! Number of sectors per track


    !bh是最大的磁头数目(或者说磁面数目),因为我们已经说过,
    !“0表示有1个磁面,1表示有2个磁面”
    incb dh ! dh = 1 + max head number (0-origin)


    !跳转去装载启动(BOOT)代码。
    jmp loadboot



    ! Floppy:
    ! Execute three read tests to determine the drive type. Test for each floppy
    ! type by reading the last sector on the first track. If it fails, try a type
    ! that has less sectors. Therefore we start with 1.44M (18 sectors) then 1.2M
    ! (15 sectors) ending with 720K/360K (both 9 sectors). (The floppy parameters
    ! of the last two are equal, apart from the motor start time. This saves us
    ! the rather painful "try to read track 41" test.)
    !如果启动设备是软盘,则从floppy处开始执行。以下一句是在
    !di上加上一部分偏移以指向下一组参数。
    !为了得到正确的磁盘容量,只有一组一组参数试,首先判断软
    !盘是不是1.44M的,再判断是不是1.2M的以此类推
    next: add di, #DSKPARSIZE ! Next set of parameters
    !我们知道,中断矢量0x1e(DSKBASE=0x1e)处的地址所指的是软盘
    !参数表,而中断矢量n的位置是0:4*n处,所以要
    !乘以4,例如中断矢量int 10h入口是在地址0:40h处。以下改变软磁
    !盘参数表指向我们定义的位置处。
    floppy: mov DSKBASE*4+0, di ! Load offset of disk parameters

    mov DSKBASE*4+2, ds ! Load segment of disk parameters
    !得到参数后复位驱动器。
    xorb ah, ah ! Reset drive
    int 0x13

    !cl里面为每磁道的扇区数目。
    movb cl, SECTORS(di) ! cl = number of last sector on track

    !不用对720K/360K磁盘进行测试了。
    cmp di, #LOADOFF+dsdd3 ! No need to do the last 720K/360K test
    jz success

    ! Try to read the last sector on track 0
    !以下试读取磁道0的最后一个扇区,如果成功说明所选的参数是对的。
    mov es, lowsec(bp) ! es = vector segment (lowsec = 0)
    mov bx, #BUFFER ! es:bx buffer = 0x0000:0x0600
    mov ax, #0x0201 ! Read sector, #sectors = 1
    xorb ch, ch ! Track 0, last sector
    xorb dh, dh ! Drive dl, head 0
    int 0x13
    jb next ! Error, try the next floppy type

    !得到正确的磁盘参数
    success:movb dh, #2 ! Load number of heads for multiply
    ! Number of sectors is still in cl
    loadboot:
    ! Load the secondary boot code from the boot device
    ! 从启动设备中装载第二个启动代码
    ! al=cl=每磁道多少扇区
    movb al, cl ! al = cl = sectors per track
    mulb dh ! dh = heads, ax = heads * sectors
    ! ax里面保存的是每柱面多少扇区数,然后放入到栈底
    mov secpcyl(bp), ax ! Sectors per cylinder = heads * sectors

    !装载第二个启动代码于0x10000:0处(第二个boot代码下一篇文档中马上就要讲到)
    !即装载第一扇区于 es:bx = 10000:0处
    mov ax, #BOOTSEG ! Segment to load secondary boot code into
    mov es, ax
    xor bx, bx ! Load first sector at es:bx = BOOTSEG:0x0000
     
    ! 启动代码的开始地址放入si,供以后跳转用
    mov si, #LOADOFF+addresses ! Start of the boot code addresses

    !int 13h,ah = 2(读扇区)说明:
    ! 用这个功能将从磁盘上把一个或更多个扇区读进存储器.因为这是一个低级功能,
    ! 在一个操作中读取的全部扇区必须在同一磁道上(就是说,要有相同的磁头号和磁道
    ! 柱面号或磁道号).BIOS不能自动地从一条磁道末尾切换到另一磁道开始,因此,用
    ! 户必须把把跨多条磁道的读操作分为若干条单磁道读操作。

    !入口参数:
    ! ah = 02H 指出读扇区功能调用号。
    ! AL 置要读的扇区数目,不允许读磁道末端以外的数值,也不允许使该寄存器为0
    ! DL 驱动器代码,0和1表示软盘,80和81识别硬盘
    ! DH 所读磁盘的磁头号,0和1是软盘磁头,0到15H代表XT或AT机的硬盘,其他的
    ! 磁盘也可能有不同的磁头号
    ! CH 识别10位开始磁道柱面号(或软盘的磁道号)的低8位。CL寄存器的6-7为存放其高
    ! 2位。对于320K/360K软盘,磁道号的范围是0-39,;于1.2M软盘,磁道号是0-79;
    ! 对于硬盘,其值是0-1023
    ! CL 其6-7位识别10位开始磁道柱面号(或软盘的磁道号)的高2位。0-5位放入所读的起始
    ! 扇区号。对于320K/360K软盘,取值范围是1-8或1-9;于1.2M软盘,取值是1-15;对于
    ! 硬盘,其值是1-17,需注意,扇区号是从1而不是0开始。
    ! ES:BX 置存放从磁盘上读出数据的存储器容量。这个存储器容量应该是能容纳02H功能调用读
    ! 的所有扇区数据。因此,用户在调用这个功能之前必须了解所读扇区的大小和数量。

    !出口参数:
    ! ES:BX 所读的数据的存储区域的指针。如果读入若干扇区,这些扇区的数据会依次排列

    !错误信息:
    ! 如果产生错误,进位标志 CF = 1,错误信息放在AH中
    ! AH 存放错误代码。

    load:

    ! 取得下一个扇区数:低16位,在addresses处到底存放的是什么内容,目前倘不清楚,只知道它
    !是由程序installboot在此处赋了一些值,等我分析完installboot程序后再补以下这段内容。
    !但是有一点是很清楚的,就是以下程序装载boot的第二部分,本代码装载完boot的第二部分代码
    !后就跳转到那里去执行。
    mov ax, 1(si) ! Get next sector number: low 16 bits
    movb dl, 3(si) ! Bits 16-23 for your 8GB disk
    xorb dh, dh ! dx:ax = sector within partition
    add ax, lowsec+0(bp)
    adc dx, lowsec+2(bp)! dx:ax = sector within drive
    div secpcyl(bp) ! ax = cylinder, dx = sector within cylinder
    xchg ax, dx ! ax = sector within cylinder, dx = cylinder
    movb ch, dl ! ch = low 8 bits of cylinder
    divb SECTORS(di) ! al = head, ah = sector (0-origin)
    xorb dl, dl ! About to shift bits 8-9 of cylinder into dl
    shr dx, #1
    shr dx, #1 ! dl[6..7] = high cylinder
    orb dl, ah ! dl[0..5] = sector (0-origin)
    movb cl, dl ! cl[0..5] = sector, cl[6..7] = high cyl
    incb cl ! cl[0..5] = sector (1-origin)
    movb dh, al ! dh = al = head
    movb dl, device(bp) ! dl = device to read
    movb al, SECTORS(di) ! Sectors per track - Sector number (0-origin)
    subb al, ah ! = Sectors left on this track
    cmpb al, (si) ! Compare with # sectors to read
    jbe read ! Can read past the end of a cylinder?
    movb al, (si) ! (si) < sectors left on this track
    read: push ax ! Save al = sectors to read
    movb ah, #2 ! Code for disk read (all registers in use now!)
    int 0x13 ! Call the BIOS for a read
    pop cx ! Restore al in cl
    jb error ! Jump on disk read error

    movb al, cl ! Restore al = sectors read
    addb bh, al ! bx += 2 * al * 256 (add bytes read)
    addb bh, al ! es:bx = where next sector must be read
    add 1(si), ax ! Update address by sectors read
    adcb 3(si), ah ! Don forget bits 16-23 (add ah = 0)
    subb (si), al ! Decrement sector count by sectors read
    jnz load ! Not all sectors have been read

    add si, #4 ! Next (address, count) pair
    cmpb ah, (si) ! Done when no sectors to read
    jnz load ! Read next chunk of secondary boot code
    done:
    ! Call secondary boot, assuming a long a.out header (48 bytes). The a.out
    ! header is usually short (32 bytes), but secondary boot has two entry points:
    ! One at offset 0 for the long, and one at offset 16 for the short header.
    ! Parameters passed in registers are:
    !
    ! dl = Boot-device.
    ! es:si = Partition table entry if hard disk.
    !
    pop si ! Restore es:si = partition table entry
    pop es ! dl is still loaded
    jmpf BOOTOFF, BOOTSEG ! jmp to sec. boot (skipping header).

    ! Read error: print message, hang forever

    error:
    ! mov si, #LOADOFF+errno+1 ! Uncomment this at disaster time
    !prnum: movb al, ah ! Error number in ah
    ! andb al, #0x0F ! Low 4 bits
    ! cmpb al, #10 ! A-F?
    ! jb digit ! 0-9!
    ! addb al, #7 ! A - :
    !digit: addb (si), al ! Modify in string
    ! dec si
    ! movb cl, #4 ! Next 4 bits
    ! shrb ah, cl
    ! jnz prnum ! Again if digit > 0
    mov si, #LOADOFF+rderr ! String to print
    print: lodsb ! al = *si++ is char to be printed
    movb ah, #14 ! 14 = print char
    mov bx, #0x0001 ! Page 0, foreground color
    int 0x10 ! Call BIOS VIDEO_IO
    cmp si, #LOADOFF+errend ! End of string reached?
    jb print
    !在此无限循环
    ! Hang forever waiting for CTRL-ALT-DEL
    hang: jmp hang

    .data
    rderr: .ascii "Read error "
    !errno: .ascii "00 "
    errend:

    parameters:
    ! Floppy disk parameters sorted down by sectors per track. (The format gap
    ! length params of the 3.5" disks might be wrong, but that won matter.)
    !以下为磁盘参数表,其最终结果还的通过调用BIOS中断来修正部分内容。
    !此磁盘参数表的目的是为了正确地读取低级磁盘I/O而设。

    ! 1.44M 3.5"
    dshd3: .data1 0xAF, 0x02, 25, 2, 18/*每磁道的扇区数目*/,
    0x1B, 0xFF, 0x54, 0xF6, 15, 8

    ! 1.2M 5.25"
    dshd5: .data1 0xDF, 0x02, 25, 2, 15, 0x1B, 0xFF, 0x54, 0xF6, 15, 8

    ! 720K 3.5", also used for 360K 5.25"
    dsdd3: .data1 0xDF, 0x02, 25, 2, 9, 0x2A, 0xFF, 0x50, 0xF6, 15, 8

    ! Just for completeness, here are the real 360K params.
    !dsdd5: .data1 0xDF, 0x02, 25, 2, 9, 0x2A, 0xFF, 0x54, 0xF6, 15, 3

    .text
    endtext:
    .data
    enddata:
    addresses:
    ! The space below this is for disk addresses for a 62K secondary boot
    ! program (worst case, i.e. file is fragmented). It should be enough.
    .bss
    endbss:
    发布人:Kevin 来自:CandyLinux