Chinaunix

标题: FreeBSD7.0 /sys/boot/i386/mbr/mbr.s源代码分析 [打印本页]

作者: 雨丝风片    时间: 2008-06-27 16:35
标题: FreeBSD7.0 /sys/boot/i386/mbr/mbr.s源代码分析
分析数据采集自bochs+FreeBSD7.0环境,分析过程中参考了Luonix Luo和qiuhanty关于boot0.s的分析文章。


#
# Copyright (c) 1999 Robert Nordier
# All rights reserved.
#
# Redistribution and use in source and binary forms are freely
# permitted provided that the above copyright notice and this
# paragraph and the following disclaimer are duplicated in all
# such forms.
#
# This software is provided "AS IS" and without any express or
# implied warranties, including, without limitation, the implied
# warranties of merchantability and fitness for a particular
# purpose.
#

# $FreeBSD: src/sys/boot/i386/mbr/mbr.s,v 1.7 2004/08/28 08:39:35 yar Exp $

# A 512 byte MBR boot manager that simply boots the active partition.

        .set LOAD,0x7c00        # Load address
        .set EXEC,0x600         # Execution address
        .set PT_OFF,0x1be       # Partition table
        .set MAGIC,0xaa55       # Magic: bootable
        .set FL_PACKET,0x80     # Flag: try EDD

        .set NHRDRV,0x475       # Number of hard drives

        .globl start            # Entry point
        .code16

#
# Setup the segment registers for flat addressing and setup the stack.
#
                                                    # 在/sys/boot/i386/mbr/Makefile中定义的LDFLAGS包含了
                                                    # "-e start -Ttext ${ORG}",即指定程序入口为"start",
                                                    # 并指定.text section的起始为止在绝对地址ORG处,而ORG
                                                    # 在同一Makefile中被定义为0x600,因此,出现在汇编指令
                                                    # 中的"$start"即表示0x600。这部分引导代码是被bios加载到
                                                    # 0x7c00的,所以此处"start:"标号对应的"cld"指令的地址
                                                    # 就是0x7c00,这是第一条引导指令。此时,cpu主要寄存器的
                                                    # 内容如下:
                                                    #    rax: 0x00000000:0000aa55 rcx: 0x00000000:00000000
                                                    #    rdx: 0x00000000:00000080 rbx: 0x00000000:00000000
                                                    #    rsp: 0x00000000:0000ffda rbp: 0x00000000:00000000
                                                    #    rsi: 0x00000000:ffff0000 rdi: 0x00000000:0008ffac
                                                    #    r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
                                                    #    r10: 0x00000000:00000000 r11: 0x00000000:00000000
                                                    #    r12: 0x00000000:00000000 r13: 0x00000000:00000000
                                                    #    r14: 0x00000000:00000000 r15: 0x00000000:00000000
                                                    #    rip: 0x00000000:00007c00
                                                    #    eflags 0x00000082
start:  cld                     # String ops inc    # 保证后续字符串操作为增向。
        xorw %ax,%ax            # Zero
        movw %ax,%es            # Address
        movw %ax,%ds            #  data
        movw %ax,%ss            # Set up
        movw $LOAD,%sp          #  stack            # 将ax、es、ds、ss清0,并将sp指向"LOAD",LOAD在本文中被
                                                    # 定义为0x7c00,这是bios加载引导扇区的地址,也即"start:"
                                                    # 标号处的"cld"指令的地址。此时,cpu主要寄存器的内容如下:
                                                    #    rax: 0x00000000:00000000 rcx: 0x00000000:00000000
                                                    #    rdx: 0x00000000:00000080 rbx: 0x00000000:00000000
                                                    #    rsp: 0x00000000:00007c00 rbp: 0x00000000:00000000
                                                    #    rsi: 0x00000000:ffff0000 rdi: 0x00000000:0008ffac
                                                    #    r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
                                                    #    r10: 0x00000000:00000000 r11: 0x00000000:00000000
                                                    #    r12: 0x00000000:00000000 r13: 0x00000000:00000000
                                                    #    r14: 0x00000000:00000000 r15: 0x00000000:00000000
                                                    #    rip: 0x00000000:00007c0c
                                                    #    eflags 0x00000046

#
# Relocate ourself to a lower address so that we are out of the way when
# we load in the bootstrap from the partition to boot.
#
        movw $main-EXEC+LOAD,%si        # Source
        movw $main,%di                  # Destination
        movw $0x200-(main-start),%cx    # Byte count
        rep                     # Relocate
        movsb                   #  code             # 由于bios将本段引导代码的入口加载至0x7c00,而本段代码链接时
                                                    # 指定的绝对入口地址是0x600,因此须将本段代码拷贝至0x600处才能
                                                    # 正确执行后续与地址相关的指令。下面的jmp指令是在0x7c00区域
                                                    # 执行的最后一条指令,jmp之后就开始在0x600区域继续执行新建的
                                                    # 引导代码复本了,而在0x600区域执行的第一条语句就是"main:"标号
                                                    # 对应的xorw指令,因此0x7c00区域中从"start:"到"main:"之间的
                                                    # 内容是不用拷贝到0x600的。EXEC在本文件中定义为0x600,这是
                                                    # "start:"标号的链接地址,LOAD则是bios对本段引导代码的初始加载
                                                    # 位置,即0x7c00,因此,main-EXEC+LOAD就是0x7c00加上"main:"距
                                                    # "start:"的偏移,即"xorw %si,%si"指令在0x7c00区域的地址,引导
                                                    # 代码的拷贝从这里开始。拷贝的目的地则直接使用"main:"标号的链接
                                                    # 地址,即"xorw %si,%si"指令在0x600区域的地址。在计算需要拷贝的
                                                    # 字节数时需要从引导扇区尺寸512字节(0x200)中减去
                                                    # "main:"和"start:" 之间的内容。执行实际拷贝指令之前cpu主要
                                                    # 寄存器的内容如下:
                                                    #    rax: 0x00000000:00000000 rcx: 0x00000000:000001e6
                                                    #    rdx: 0x00000000:00000080 rbx: 0x00000000:00000000
                                                    #    rsp: 0x00000000:00007c00 rbp: 0x00000000:00000000
                                                    #    rsi: 0x00000000:ffff7c1a rdi: 0x00000000:0008061a
                                                    #    r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
                                                    #    r10: 0x00000000:00000000 r11: 0x00000000:00000000
                                                    #    r12: 0x00000000:00000000 r13: 0x00000000:00000000
                                                    #    r14: 0x00000000:00000000 r15: 0x00000000:00000000
                                                    #    rip: 0x00000000:00007c15
                                                    #    eflags 0x00000046
                                                    # 此时,源地址是0x7c1a,目的地址是0x061a,分别是0x7c00和0x600
                                                    # 偏移0x1a的位置,这正是"main:"和"start:"之间的距离。拷贝完成
                                                    # 之后cpu主要寄存器的内容如下:
                                                    #    rax: 0x00000000:00000000 rcx: 0x00000000:00000000
                                                    #    rdx: 0x00000000:00000080 rbx: 0x00000000:00000000
                                                    #    rsp: 0x00000000:00007c00 rbp: 0x00000000:00000000
                                                    #    rsi: 0x00000000:ffff7e00 rdi: 0x00000000:00080800
                                                    #    r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
                                                    #    r10: 0x00000000:00000000 r11: 0x00000000:00000000
                                                    #    r12: 0x00000000:00000000 r13: 0x00000000:00000000
                                                    #    r14: 0x00000000:00000000 r15: 0x00000000:00000000
                                                    #    rip: 0x00000000:00007c17
                                                    #    eflags 0x00000046
#
# Jump to the relocated code.
#
        jmp main-LOAD+EXEC      # To relocated code # 跳转至0x600区域中"main:"标号对应的指令位置,这是在0x7c00
                                                    # 区域执行的最后一条指令。跳转完成之后cpu主要寄存器的内容如下:
                                                    #    rax: 0x00000000:00000000 rcx: 0x00000000:00000000
                                                    #    rdx: 0x00000000:00000080 rbx: 0x00000000:00000000
                                                    #    rsp: 0x00000000:00007c00 rbp: 0x00000000:00000000
                                                    #    rsi: 0x00000000:ffff7e00 rdi: 0x00000000:00080800
                                                    #    r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
                                                    #    r10: 0x00000000:00000000 r11: 0x00000000:00000000
                                                    #    r12: 0x00000000:00000000 r13: 0x00000000:00000000
                                                    #    r14: 0x00000000:00000000 r15: 0x00000000:00000000
                                                    #    rip: 0x00000000:0000061a
                                                    #    eflags 0x00000046
#
# Scan the partition table looking for an active entry.  Note that %ch is
# zero from the repeated string instruction above.  We save the offset of
# the active partition in %si and scan the entire table to ensure that only
# one partition is marked active.
#
main:   xorw %si,%si            # No active partition
        movw $partbl,%bx        # Partition table
        movb $0x4,%cl           # Number of entries # 首先清除si寄存器。然后将位于本段引导扇区代码尾端的分区表
                                                    # 的地址写入bx寄存器,将分区表的条目数写入cl。此时cpu主要
                                                    # 寄存器的内容如下:
                                                    #    rax: 0x00000000:00000000 rcx: 0x00000000:00000004
                                                    #    rdx: 0x00000000:00000080 rbx: 0x00000000:000007be
                                                    #    rsp: 0x00000000:00007c00 rbp: 0x00000000:00000000
                                                    #    rsi: 0x00000000:ffff0000 rdi: 0x00000000:00080800
                                                    #    r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
                                                    #    r10: 0x00000000:00000000 r11: 0x00000000:00000000
                                                    #    r12: 0x00000000:00000000 r13: 0x00000000:00000000
                                                    #    r14: 0x00000000:00000000 r15: 0x00000000:00000000
                                                    #    rip: 0x00000000:00000621
                                                    #    eflags 0x00000046
                                                    # 此处写入bx的分区表地址是0x7be,这是距引导扇区尾端66字节
                                                    # 的地方,分区表一共四个条目,因此cl的值是4。每个条目占用
                                                    # 16字节,共64字节,最后的2个字节是用来存放幻数0xaa55的。
                                                    # 参见本文末尾对"partbl:"的定义。
                                                    # 此时,0x7be之后66个字节的内容如下:
                                                    #    0x7be : 0x80 0x01 0x01 0x00 0xa5 0x0f 0xff 0xff
                                                    #    0x7c6 : 0x3f 0x00 0x00 0x00 0xa1 0xff 0x7f 0x00
                                                    #    0x7ce : 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
                                                    #    0x7d6 : 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
                                                    #    0x7de : 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
                                                    #    0x7e6 : 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
                                                    #    0x7ee : 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
                                                    #    0x7f6 : 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
                                                    #    0x7fe : 0x55 0xaa
main.1: cmpb %ch,(%bx)       # Null entry?          # 通过分区条目第一字节判断该分区是否为活动分区,0x80为是,0为否
        je main.2            # Yes                  # 此分区非活动分区,准备查询下一分区
        jg err_pt            # If 0x1..0x7f         # 分区条目第一字节的合法值只能是0x80或0,否则报错
        testw %si,%si        # Active already found?    # 通过si是否为0判断是否已找到一个活动分区
        jnz err_pt           # Yes                  # 只能有一个活动分区,否则报错
        movw %bx,%si         # Point to active      # 让si指向活动分区表条目
                                                    # 对于示例情况,分区表第一条目第一字节为0x80,为活动分区,
                                                    # 因此在这里对si进行设置,此时cpu主要寄存器的内容如下:
                                                    #    rax: 0x00000000:00000000 rcx: 0x00000000:00000004
                                                    #    rdx: 0x00000000:00000080 rbx: 0x00000000:000007be
                                                    #    rsp: 0x00000000:00007c00 rbp: 0x00000000:00000000
                                                    #    rsi: 0x00000000:ffff07be rdi: 0x00000000:00080800
                                                    #    r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
                                                    #    r10: 0x00000000:00000000 r11: 0x00000000:00000000
                                                    #    r12: 0x00000000:00000000 r13: 0x00000000:00000000
                                                    #    r14: 0x00000000:00000000 r15: 0x00000000:00000000
                                                    #    rip: 0x00000000:0000062d
                                                    #    eflags 0x00000046

main.2: addb $0x10,%bl       # Till                 # 让bx指向下一个分区表条目
        loop main.1          #  done                # "main.1:"一共执行4次,依次分析分区表中的4个条目
        testw %si,%si        # Active found?        # 判断是否找到了一个活动分区(有多个活动分区的情况已在前面报错)
        jnz main.3           # Yes                  # 找到活动分区则跳转至"main.3"
        int $0x18            # BIOS: Diskless boot  # 没有找到活动分区则尝试无盘引导
#
# Ok, we've found a possible active partition.  Check to see that the drive
# is a valid hard drive number.
#
main.3: cmpb $0x80,%dl       # Drive valid?         # dl中存放的是bios传入的引导设备编号,从0x80开始表示硬盘
        jb main.4            # No                   # 若bios传入的不是硬盘则将之前找到的活动分区的第一字节存入dl
        movb NHRDRV,%dh      # Calculate the highest    # bios将硬盘数目保存在NHRDRV,即0x475处,此处将其写入dh
        addb $0x80,%dh       #  drive number available  # 硬盘号从0x80开始,加上硬盘数目即得到最大可能硬盘号的上限
        cmpb %dh,%dl         # Within range?        # 判断bios传入的硬盘号是否在合法范围之内
        jb main.5            # Yes                  # 硬盘及分区有效,准备读入对应扇区
main.4: movb (%si),%dl       # Load drive           # 若bios传入的不是硬盘或者硬盘号非法则将之前找到的活动分区的
                                                    # 第一字节存入dl
#
# Ok, now that we have a valid drive and partition entry, load the CHS from
# the partition entry and read the sector from the disk.
#
                                                    # 执行至此已经找到了一个有效的硬盘分区入口,此时cpu主要
                                                    # 寄存器的内容如下:
                                                    #    rax: 0x00000000:00000000 rcx: 0x00000000:00000000
                                                    #    rdx: 0x00000000:00008180 rbx: 0x00000000:000007fe
                                                    #    rsp: 0x00000000:00007c00 rbp: 0x00000000:00000000
                                                    #    rsi: 0x00000000:ffff07be rdi: 0x00000000:00080800
                                                    #    r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
                                                    #    r10: 0x00000000:00000000 r11: 0x00000000:00000000
                                                    #    r12: 0x00000000:00000000 r13: 0x00000000:00000000
                                                    #    r14: 0x00000000:00000000 r15: 0x00000000:00000000
                                                    #    rip: 0x00000000:0000064a
                                                    #    eflags 0x00000097
                                                    # 此时si指向的是之前找到的有效分区条目
main.5: movw %sp,%di         # Save stack pointer   # 将sp保存到di
        movb 0x1(%si),%dh    # Load head            # 分区条目第1(从0开始计算)字节存放的是磁头号,将其存入dh
        movw 0x2(%si),%cx    # Load cylinder:sector # 分区条目第2、3字节存放的是柱面号和扇区号,其中,第2字节的
                                                    # 低6位存储扇区号,第2字节的高2位和第3字节的8位共同存储
                                                    # 柱面号。之前找到的有效分区条目的起始地址是0x7be,它的前
                                                    # 4个字节的内容如下:
                                                    #    0x00000000000007be : 0x80 0x01 0x01 0x00
                                                    # 表示的就是第0柱面第1磁头第1扇区。此处将这两个字节的内容
                                                    # 存入cx,此时cpu主要寄存器的内容如下:
                                                    #    rax: 0x00000000:00000000 rcx: 0x00000000:00000001
                                                    #    rdx: 0x00000000:00000180 rbx: 0x00000000:000007fe
                                                    #    rsp: 0x00000000:00007c00 rbp: 0x00000000:00000000
                                                    #    rsi: 0x00000000:ffff07be rdi: 0x00000000:00087c00
                                                    #    r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
                                                    #    r10: 0x00000000:00000000 r11: 0x00000000:00000000
                                                    #    r12: 0x00000000:00000000 r13: 0x00000000:00000000
                                                    #    r14: 0x00000000:00000000 r15: 0x00000000:00000000
                                                    #    rip: 0x00000000:00000652
                                                    #    eflags 0x00000097
        movw $LOAD,%bx       # Transfer buffer      # 把bios初始加载引导代码的地址0x7c00赋给bx
                                                    # 至此已基本完成使用0x13中断的0x02功能读入磁盘扇区的准备,
                                                    # 即cx存放柱面号和扇区号,dh存放磁头号,dl存放磁盘号,
                                                    # es:bx指向用于存放读入的扇区数据的缓冲区地址。不过在决定
                                                    # 使用0x13中断的0x02功能之前,此处要先尝试是否能使用0x13
                                                    # 中断的0x42功能读入相应的扇区数据。在无法使用0x42功能的
                                                    # 情况下才会到"main.7:"去使用0x02功能。
        testb $FL_PACKET,flags      # Try EDD?      # 判断是否需要尝试BIOS Enhanced Disk Drive Services。
                                                    # "flags:"标号在本文尾端定义,位于紧邻分区表之前的一个字节,
                                                    # 内容为FLAGS,这个宏在/sys/boot/i386/mbr/Makefile中定义为
                                                    # BOOT_MBR_FLAGS,即0x80。BOOT_MBR_FLAGS在本文开始处定义为0x80。
        jz main.7            # No.                  # 若上述标志的设置决定不去判断能否使用EDD功能读入扇区,则跳转
                                                    # 至"main.7"使用0x13中断的0x02功能读入扇区。由于到此为止的
                                                    # 判断尚未对cx和bx进行压栈,所以可以直接跳转至"main.7:",
                                                    # 而后续判断中发现无法使用EDD功能时,由于已经对cx、bx进行了
                                                    # 压栈和修改,则需跳转至"main.6:"先恢复cx、bx的内容再到
                                                    # "main.7:"处使用0x13中断的0x02功能。
        pushw %cx            # Save %cx             # 测试是否支持EDD扩展功能,中断号0x13,入参ah存放功能号0x41,
        pushw %bx            # Save %bx             # bx存放幻数0x55aa。部分返回结果需要写入cx,因此先保存cx、bx。
        movw $0x55aa,%bx     # Magic
        movb $0x41,%ah       # BIOS: EDD extensions
        int $0x13            #  present?
        jc main.6            # No.                  # CF为1表示不支持
        cmpw $0xaa55,%bx     # Magic ok?            # 回写至bx的幻数应为0xaa55
        jne main.6           # No.                  # 幻数不正确则跳转至"main.6:"
        testb $0x1,%cl       # Packet mode present? # 返回值cl第1位表示是否使用packet结构访问设备
        jz main.6            # No.                  # 不使用packet结构则跳转至"main.6:"
        popw %bx             # Restore %bx          # 恢复bx。此时cpu主要寄存器的内容如下:
                                                    #    rax: 0x00000000:00003000 rcx: 0x00000000:00000007
                                                    #    rdx: 0x00000000:00000180 rbx: 0x00000000:00007c00
                                                    #    rsp: 0x00000000:00007bfe rbp: 0x00000000:00000000
                                                    #    rsi: 0x00000000:ffff07be rdi: 0x00000000:00087c00
                                                    #    r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
                                                    #    r10: 0x00000000:00000000 r11: 0x00000000:00000000
                                                    #    r12: 0x00000000:00000000 r13: 0x00000000:00000000
                                                    #    r14: 0x00000000:00000000 r15: 0x00000000:00000000
                                                    #    rip: 0x00000000:00000673
                                                    #    eflags 0x00000002
        pushl $0x0           # Set the LBA
        pushl 0x8(%si)       #  address
        pushw %es            # Set the address of
        pushw %bx            #  the transfer buffer
        pushw $0x1           # Read 1 sector
        pushw $0x10          # Packet length        # 准备使用0x13中断的0x42功能以扩展方式从磁盘读入扇区,
                                            # 这一组压栈指令就是在栈上构造磁盘地址包(Disk Address Packet)。
                                            # 压栈完成之后,cpu主要寄存器的内容如下:
                                            #    rax: 0x00000000:00004200 rcx: 0x00000000:00000007
                                            #    rdx: 0x00000000:00000180 rbx: 0x00000000:00007c00
                                            #    rsp: 0x00000000:00007bee rbp: 0x00000000:00000000
                                            #    rsi: 0x00000000:ffff7bee rdi: 0x00000000:00087c00
                                            #    r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
                                            #    r10: 0x00000000:00000000 r11: 0x00000000:00000000
                                            #    r12: 0x00000000:00000000 r13: 0x00000000:00000000
                                            #    r14: 0x00000000:00000000 r15: 0x00000000:00000000
                                            #    rip: 0x00000000:00000685
                                            #    eflags 0x00000002
                                            # sp寄存器指向0x7bee,此时栈顶附近16字节的内容如下:
                                            #    0x0000000000007bee : 0x10 0x00 0x01 0x00 0x00 0x7c 0x00 0x00
                                            #    0x0000000000007bf6 : 0x3f 0x00 0x00 0x00 0x00 0x00 0x00 0x00
                                            # 第0字节表示DAP的大小,此处为0x10,即16字节,第1字节未使用,应填0,
                                            # 这两个字节是由"pushw $0x10"指令压入的。第2字节表示需要读入的扇区数目,
                                            # 此处为0x01,只读入1个扇区,第3字节未使用,应填0,这两个字节是由
                                            # "pushw $0x1"指令压入的。第4到7字节是以"segment: offset"的形式给出的
                                            # 用于存放读入的扇区数据的缓冲区的地址,此处是0x0000:7c00,这实际上
                                            # 就是bios初始加载主引导记录的地址,即本段代码被搬移到0x600区域之前
                                            # 所在的位置,这4个字节是由"pushw %es"和"pushw %bx"指令压入的。
                                            # 第8到15字节表示的是读入操作的起始扇区号(从0开始计算),此处是
                                            # 0x000000000000003f,本次读入的是第63扇区,这8个字节是由
                                            # "pushl $0x0"和"pushl 0x8(%si)"指令压入的,si指向的是之前在分区表中
                                            # 找到的活动分区条目,它的第8到11字节存放的就是该分区的起始扇区号:
                                            #    0x00000000000007be : 0x80 0x01 0x01 0x00 0xa5 0x0f 0xff 0xff
                                            #    0x00000000000007c6 : 0x3f 0x00 0x00 0x00 0xa1 0xff 0x7f 0x00
        movw %sp,%si         # Packer pointer       # 在栈上完成DAP的构造之后,将其起始地址赋给si
        movw $0x4200,%ax     # BIOS:    LBA Read from disk    # 写入功能号0x42
        jmp main.8           # Skip the CHS setup   # 跳转到"main.8:"调用0x13中断
main.6: popw %bx             # Restore %bx          # 若不支持EDD功能,则与此恢复之前压栈的bx和cx,压栈之前cx
                                                    # 存放的是柱面号和扇区号,bx存放的是用于存放读入的扇区数据的
                                                    # 缓冲区地址,都是0x13中断的0x02功能所需的入参。
        popw %cx             # Restore %cx
main.7: movw $0x201,%ax      # BIOS: Read from disk # 填写0x13中断的0x02功能所需的最后一点入参。al存放需要读入的
                                                    # 扇区数目,此处为1,只读入一个扇区。ah存放功能号0x02。
main.8: int $0x13            # Call the BIOS        # 不管是使用0x02功能读入,还是使用0x42功能读入,至此已完成各自
                                                    # 入参的设置,统一运行至此执行0x13中断。
        movw %di,%sp         # Restore stack        # 将sp恢复至扇区拷贝之前的位置,即在"main.5:"处保存在di中的内容
        jc err_rd            # If error             # CF为1表示读入过程出错
#
# Now that we've loaded the bootstrap, check for the 0xaa55 signature.  If it
# is present, execute the bootstrap we just loaded.
#
                                                    # 至此已完成扇区的读入,cpu主要寄存器的内容如下:
                                                    #    rax: 0x00000000:00000000 rcx: 0x00000000:00000007
                                                    #    rdx: 0x00000000:00000180 rbx: 0x00000000:00007c00
                                                    #    rsp: 0x00000000:00007c00 rbp: 0x00000000:00000000
                                                    #    rsi: 0x00000000:ffff7bee rdi: 0x00000000:00087c00
                                                    #    r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
                                                    #    r10: 0x00000000:00000000 r11: 0x00000000:00000000
                                                    #    r12: 0x00000000:00000000 r13: 0x00000000:00000000
                                                    #    r14: 0x00000000:00000000 r15: 0x00000000:00000000
                                                    #    rip: 0x00000000:00000692
                                                    #    eflags 0x00000002
        cmpw $MAGIC,0x1fe(%bx)      # Bootable?     # 前面已经通过0x13中断从磁盘读入了一个512字节的扇区到
                                                    # 0x7c00区域,此处判断该扇区的最后两个字节是否为0xaa55,
                                                    # 以确定该扇区是否可引导。
        jne err_os           # No                   # 读入扇区不可引导则报错
        jmp *%bx             # Invoke bootstrap     # 跳转至0x7c00执行刚读入的bootstrap代码
#
# Various error message entry points.
#
err_pt: movw $msg_pt,%si     # "Invalid partition
        jmp putstr           #  table"

err_rd: movw $msg_rd,%si     # "Error loading
        jmp putstr           #  operating system"

err_os: movw $msg_os,%si     # "Missing operating
        jmp putstr           #  system"
#
# Output an ASCIZ string to the console via the BIOS.
#
putstr.0: movw $0x7,%bx      # Page:attribute
        movb $0xe,%ah        # BIOS: Display
        int $0x10            #  character
putstr: lodsb                # Get character
        testb %al,%al        # End of string?
        jnz putstr.0         # No
putstr.1: jmp putstr.1       # Await reset

msg_pt: .asciz "Invalid partition table"
msg_rd: .asciz "Error loading operating system"
msg_os: .asciz "Missing operating system"

        .org PT_OFF-1,0x90
flags:  .byte FLAGS          # Flags

partbl: .fill 0x10,0x4,0x0   # Partition table
        .word MAGIC          # Magic number   



[ 本帖最后由 雨丝风片 于 2008-6-27 17:17 编辑 ]
作者: 大大狗    时间: 2008-06-27 18:09
嘿嘿 我刚装完FreeBSD7.0 就看到了这个文章 学习啦
作者: congli    时间: 2008-06-27 19:41

作者: bukaihua    时间: 2008-06-27 20:46
过几天考完试我发个netbsd/algor或loongson的
作者: gvim    时间: 2008-06-30 09:25
这个mbr.s只是一个开始
作者: 剑心通明    时间: 2008-06-30 11:38
原帖由 gvim 于 2008-6-30 09:25 发表
这个mbr.s只是一个开始

期待中
作者: 雨丝风片    时间: 2008-06-30 13:03
原帖由 gvim 于 2008-6-30 09:25 发表
这个mbr.s只是一个开始


从BSD源代码的角度来讲,这句话是正确的。
作者: gvim    时间: 2008-07-01 11:38
为什么是 0x0600?
这个地址是必须的(dedicated)还是约定上(convenient)的?
作者: abutter    时间: 2008-07-01 12:14
原帖由 gvim 于 2008-7-1 11:38 发表
为什么是 0x0600?
这个地址是必须的(dedicated)还是约定上(convenient)的?


不是必须的,算是约定的。

因为 mbr.S 不能直接加载内核,所以需要加载其他的引导程序,为了方便起见通常将新的引导程序加载到 0x7c00 处,所以要把自己拷贝到另外一个地方。

BIOS加载 mbr 之后只有分散的几片空间可以用,最初的人在这几片空间中选择了 0x600,后续也就没有再做修改。
作者: gvim    时间: 2008-07-01 13:51
原帖由 abutter 于 2008-7-1 12:14 发表


不是必须的,算是约定的。

因为 mbr.S 不能直接加载内核,所以需要加载其他的引导程序,为了方便起见通常将新的引导程序加载到 0x7c00 处,所以要把自己拷贝到另外一个地方。

BIOS加载 mbr 之后只有分 ...


大概看了看lilo和grub,对比BSD的mbr,发现mbr本身的移动也只是为了方便
lilo和bsd一样,都会搬移到0x600
而grub的处理就是另一种模样:


[url]http://blog.sina.com.cn/s/reader_4c1810e0010009je.html
[/url]


果然还是过了2月没更新了。。。囧rz。。。
这篇也是刚好学校论坛上有人在讨论grub然后写的。。。就放这里算备份一下吧。。。-ω-

声明:以下内容部分整理部分我扯。。。出了问题我不负责。。。=v=b CNLAS 07.08.18

以下所说的过程和代码以grub0.97版为例。。。grub2和1的启动过程有点区别。。。后面再说。。。给出的路径大部分是指源代码包的

1、BIOS启动sc80t说了。。。但是注意一点BIOS是参与内存编址的。。。所以不存在读入物理内存这个概念

2、BIOS将0头0道1扇区也就是MBR读入内存地址0x7c00处。。。然后检查0x7dfe是否等于0xaa55(WORD)确认是有效MBR后开始执行

3、Grub安装好后0头0道1扇区就是stage1(/stage1/stage1.s)。。。其实stage1的任务非常的单纯。。。去读取0头0道2扇区然后闪人。。。XD
0头0道2扇区的内容就是源代码里\stage2\start.s编译后也是一个512字节。。。stage1将start.s读入内存地址0x8000然后跳过去执行

4、start.s就是stage1.5 or stage2的入口。。。一般情况下stage2还是放在需要具体的文件系统下的。。。这个时候需要读取stage1.5。。。但是注意。。。这里的stage1.5不是/boot目录下的那些文件。。。你都能读取/boot目录了还要stage1.5干嘛。。。
所以这里的stage1.5是将/boot目录对应的分区格式的stage1.5文件复制到了0头0道3扇区开始向后的位置。。。stage1.5文件一般就10几k。。。够放了。。。


root (hdx,x)/xxx/xxxx

setup (hdx)

当你执行这些指令的时候grub就知道自己装在哪里了。。。然后把对应分区格式的stage1.5复制一份到0头0道3扇区开始的位置。。。而且grub的安装目录就是一个根分区下的/boot/grub/。。。这是由源代码决定的。。。 root指令对应的函数是builtins.c中的root_func (char *arg, int flags) setup指令对应的函数是同一个文件下的setup_func (char *arg, int flags) 这个文件里还有一个struct stage1_5_map stage1_5_map[]存放各种文件系统对应的stage1.5的文件名


5、这样就可以顺利的识别文件系统然后转入stage2的运行了。。。stage2的入口在asm.S
(我最早看grub的时候就是研究这个文件。。。实模式保护模式的转换。。。GDT初始化。。。C语言运行环境初始化。。。都是由这部分代码完成的。。。也就是说这部分代码完成了从汇编语言向C语言的进化。。。XD)

然后仔细看asm.s的代码会看到

VARIABLE(config_file)

#ifndef STAGE1_5

.string "/boot/grub/menu.lst"

#else /* STAGE1_5 */

.long 0xffffffff

.string "/boot/grub/stage2"

#endif /* STAGE1_5 */

这下知道怎么找到menu.lst了吧。。。

6、asm.s将运行环境都初始化好了之后就进入c语言部分。。。这时转向stage2.c文件的cmain函数然后grub_open()函数(disk_io.c)去打开config_file。。。然后由print_entries等等函数吧菜单显示出来。。。不管是选了菜单选项还是输入 grub命令。。。最后都交给comline.c文件里的find_command()函数去builtin.c文件里的struct builtin *builtin_table[]变量找指令。。。然后执行对应指令的xxx_func()函数(builtin.c)。。。

7、然后grub利用它自己在disk_io.c提供的一些函数去吧linux的内核加载进来。。。最后执行boot指令。。。移交控制权。。。然后拍拍屁股走人咯。。。剩下的是linuxkernel的事情了。。。


——————————————LOLI飞过————————————

上面说的是以grub0.97为例的grub1的启动流程和代码分析。。。
现在grub已经开始开发2了。。。最新版本是1.95
在grub2里引入了kernel。。。而且还有module。。。还真做成miniOS了。。。囧。。。

在grub2里就没有stage*了。。。
/boot/i386/pc/boot.S对应stage1.s完成stage1的功能。。。去读取0头0道2扇区的内容
而在0头0道2扇区开始存放的就是grub2里的kernel。。。编译之后的core.img文件才20几k对chs模式的0头0道64扇区(32k)来说都放的下。。。而这个20多k的小东西将完成stage1.5(10几k)+stage2(100多k)的功能
/kern/i386/pc/startup.s完成gurb1里asm.s的任务。。。然后转到c语言。。。
/kern/i386/pc/init.c完成kernel的初始化然后转交给/kern/main.c调用grub_load_modules()加载模块完成剩下的任务。。。

暑假实在是太短了。。。只有一个星期了。。。口胡~~~~

[ 本帖最后由 gvim 于 2008-7-1 13:52 编辑 ]
作者: 雨丝风片    时间: 2008-07-01 14:29
原帖由 gvim 于 2008-7-1 11:38 发表
为什么是 0x0600?
这个地址是必须的(dedicated)还是约定上(convenient)的?


按照dos的传统来的:

0x0 - 0x3ff       中断向量表
0x400 - 0x4ff   bios数据区
Location 0x500 is used by the BIOS Print Screen function, so it is not available to the OS. The bytes 0x501-0x5ff look like they might be used by different versions of MS-DOS, so it might be that the lowest usable location for the operating system is 0x501, rather than 0x600, although nearly everyone uses 0x600, which is the address the MBR is normally relocated to.

作者: abutter    时间: 2008-07-01 18:20
原帖由 gvim 于 2008-7-1 13:51 发表


大概看了看lilo和grub,对比BSD的mbr,发现mbr本身的移动也只是为了方便
lilo和bsd一样,都会搬移到0x600
而grub的处理就是另一种模样:



第一个人如此,并没有十足的原因,呵呵,我是这样觉得。

反正 MBR 就 512 字节,完成不了太多的功能,复杂的功能必须再加载其他的模块才行。
作者: andy52013    时间: 2012-07-16 15:53
提示: 作者被禁止或删除 内容自动屏蔽




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2