x01.os.18: MBR

硬盘不同于软盘,它是要分区的。这时,mbr(master boot record)便不可少了。安装 os 硬盘的第一扇区,开始有一小段不多于 446 字节的程序,然后是分区表 512-446-2 字节,然后是引导标志 0xAA55 两字节。这一小段程序,便是 mbr 的主体。mbr 首先将其自身复制到 0x0600 处,代码如下:

; 0x7C00 => 0x0600

    mov        si, sp

    push       si

    mov        di, 0x0600

    mov        cx, 0x200

    cld

    rep        movsw

    

这是要给分区的引导代码腾位置。操作系统的引导代码,可能位于软盘,光盘等 Media 中,其代码首先便是 org   0x7C00,所以 mbr 复制自身进行重定位,就必不可少了。

第二步,当然就是寻找活动分区的启动代码了。有这么一句:test  dl, dl。这个 dl 的值,便是 drive number, 由 bios 的 int 0x19 取得。通过 dl 判断设备的类型,作不同的选择。mbr.s 的内容如下:

; ----------------------

; mbr.s (c) 2014 by x01

; ----------------------



P_EntrySize        equ        16        ; 分区表每项为 16 字节

P_PartOffset    equ        0x1BE     ; 分区表在引导扇区的偏移位置

P_BootOffset    equ        0        ; 分区项中引导标志的偏移位置

P_TypeOffset    equ        4        ; 分区项中分区类型的偏移位置

P_LbaOffset        equ        8        ; 分区项中起始扇区 LBA 的偏移位置



; Partition table struct

; ----------------------

; offset    len                    description

;   0         1            状态(80h=可引导,00h=不可引导,其他不合法)

;    1         1            起始磁头号

;    2         1            起始扇区号(仅用了低 6 位,高 2 位为起始柱面号的第 8-9 位

;    3         1            起始柱面号的低 8 位

;    4         1            分区类型(System ID)

;    5         1            结束磁头号

;    6         1            结束扇区号(仅用了低 6 位,高 2 位为结束柱面号的第 8-9 位

;    7         1            结束柱面号的低 8 位

;    8         4            起始扇区的 LBA

;    12         4            扇区数目



boot:

    xor        ax, ax

    mov        ds, ax

    mov        es, ax

    cli

    mov        ss, ax        ; ds = es = ss = 0, 段偏移地址直接映射为物理地址

    mov        sp, 0x7C00

    sti



    ; 0x7C00 => 0x0600

    mov        si, sp

    push    si

    mov        di, 0x0600

    mov        cx, 0x200

    cld

    rep        movsw



    jmp        0:0x0600 + active



active:

    test    dl, dl        ; BIOS int 19h => dl = drive_nr

                        ; SF <- MSB(dl): most significact bit, 1 is -, 0 is +

    jns        nextdisk



    mov        si, 0x0600 + P_PartOffset

    

find:

    cmp        byte [si + P_TypeOffset], 0        ; if not equal 0 then can use

    jz        nextpart

    test    byte [si + P_BootOffset], 0x80    ; 0x80: bootable

    jz        nextpart



loadpart:

    call    load

    jc        error

    ret                ; goto secondary boot => 0x0000:0x7C00

    

nextpart:

    add        si, P_EntrySize

    cmp        si, 0x0600 + P_PartOffset + 4 * P_EntrySize

    jb        find

    

    call    print

    db        "No active partition\0"

    jmp        reboot

    

nextdisk:

    inc        dl

    test    dl, dl

    js        nexthd

    int        0x11        ; get active drive info

    shl        ax, 2        ; floppy info at al[6-7], but after shl at ah[0-1]

    and     ah, 3

    cmp        dl, ah        ; dl <= ah then floppy exist

    ja        nextdisk

    call    loadfloppy

    jc        nextdisk

    ret

    

nexthd:

    call    loadfloppy



error:

    jc        handle_err

    ret



; load floppy 0 sector

loadfloppy:

    mov        si, 0x0600 + zero - P_LbaOffset

    

; load hd boot

load:

    mov        di, 3

    

retry:

    push    dx    ; dl = old drive_nr protect

    

    push    es

    push    di

    

    ; Get disk info(int 0x13: ah=0x8)

    ; ch: cyl low 8 bit, cl[6-7]: cyl high 2 bit

    ; cl[0-5]: sectors per track

    ; dh: heads, dl: drive_nr

    mov        ah, 0x08

    int        0x13

    

    pop        di

    pop        es

    

    and        cl, 0x3F    ; sectors base 1

    inc        dh            ; heads base 0, so inc

    

    mov        al, cl        ; al = cl = sectors per track

    mul        dh            ; ax = cyl sectors = heads * sectors_per_track

    mov        bx, ax

    

    mov        ax, word [si + P_LbaOffset + 0]

    mov        dx, word [si + P_LbaOffset + 2]    ; dx:ax = secondary boot offset

    

    cmp        dx, (0x16390 * 0xFF * 0x3F - 0xFF) >> 0x10

    jae        bigdisk

    

    div        bx        ; /: ax is cyl_nr, mod: dx is cyl_offset

    xchg    ax, dx

    mov        ch, dl    ; ch is cyl_nr low 8 bit

    

    div        cl        ; /: al is head_nr, mod: ah is track offset(base 0)

    xor        dl, dl

    shr        dx, 2    ; dl[6-7] = cyl_nr high 2 bit

    or        dl, ah    ; dl low 5 bit store track offset

    mov        cl, dl    ; cl high 2 bit is cyl high 2 bit, cl low 5 bit is track offset

    inc        cl        ; base 1

    pop        dx        ; old drive_nr

    mov        dh, al    ; head_nr => dh

    mov        bx, 0x7C00    ; es:bx is load addr

    mov        ax, 0x0201    ; read disk sector entry_point parameter

    int        0x13        ; read 1 sector to es:bx

    jmp        read_check

    

bigdisk:

    mov        bx, dx

    pop        dx

    push    si

    mov        si, 0x0600 + dap_offset    ; dap: disk addr packet, for up 8G

    mov        word [si + 8], ax        ; low 

    mov        word [si + 0xA], bx        ; high

    mov        ah, 0x42                ; extend read parameter

    int        0x13

    pop        si

    

read_check:

    jnc        read_ok

    cmp        ah, 0x80    ; timeout

    je        read_bad

    dec        di

    jl        read_bad

    xor        ah, ah

    int        0x13

    jnc        retry

    

read_bad:

    stc

    ret        ; have a bug, need clear zero

    

read_ok:

    cmp        dword [0x7C00 + 510], 0xAA55

    jne        notboot

    ret

    

notboot:

    call    print

    db        "Not bootable\0"

    jmp        reboot

    

handle_err:

    mov        si, 0x7C00 + err_nr + 1        ; err_nr low bit

    

print_num:

    mov        al, ah    ; ah: int 0x13 error code

    and        al, 0x0F

    cmp        al, 0xA

    jb        digit

    add        al, 0x7

    

digit:

    add        byte [si], al    ; err_nr low bit is '0', after add is correct number

    dec        si                ; err_nr high bit

    mov        cl, 4

    shr        ah, cl

    jnz        print_num

    call    print

    db        "Read error"

    

err_nr:

    db        "00\0"

    

reboot:

    call     print

    db        "Hit any key reboot.\0"

    xor        ah, ah

    int        0x16    ; wait key

    call    print

    db        "\r\n\0"

    int        0x19    ; reboot

    

print:

    pop        si        ; ip => get string addr



print_next:

    lodsb            ; al = *si++

    test    al, al

    jz        print_done

    mov        ah, 0x0E

    mov        bx, 0x0001

    int        0x10

    jmp        print_next

    

print_done:

    jmp        si

    

dap_offset:

    PacketSize        db    0x10

    Reserved        db    0x0

    BlockCount        dw    0x1

    BufferOffset    dw    0x7C00

    BufferSegment    dw    0x0

    BlockNumLow        dd    0x0

zero:

    BlockNumHigh    dd    0x0

    

times 510-($-$$)     db    0

BootFlag            dw    0xAA55

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    
View Code

后面加了两句引导标志,是为了快速检验一下运行效果,实际是不需要的。minix 通过 installboot 程序来完成组装与添加。检验方法如下:

1.编译: nasm -o mbr mbr.s

2.写入硬盘:dd if=mbr of=c.img bs=512 count=1 conv=notrunc

其中,c.img 是硬盘映像。配置 bochs 后运行,可看到因找不到活动分区而停止。当然,这又是另一个问题,就不多说了。

 

你可能感兴趣的:(OS)