免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 11610 | 回复: 12
打印 上一主题 下一主题

FreeBSD7.0 /sys/boot/i386/mbr/mbr.s源代码分析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-06-27 16:35 |只看该作者 |倒序浏览
分析数据采集自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 编辑 ]

评分

参与人数 1可用积分 +5 收起 理由
gvim + 5 精品文章

查看全部评分

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
2 [报告]
发表于 2008-06-27 18:09 |只看该作者
嘿嘿 我刚装完FreeBSD7.0 就看到了这个文章 学习啦

论坛徽章:
1
寅虎
日期:2013-09-29 23:15:15
3 [报告]
发表于 2008-06-27 19:41 |只看该作者

论坛徽章:
0
4 [报告]
发表于 2008-06-27 20:46 |只看该作者
过几天考完试我发个netbsd/algor或loongson的

论坛徽章:
2
亥猪
日期:2014-03-19 16:36:35午马
日期:2014-11-23 23:48:46
5 [报告]
发表于 2008-06-30 09:25 |只看该作者
这个mbr.s只是一个开始

论坛徽章:
2
丑牛
日期:2013-09-29 09:47:222015七夕节徽章
日期:2015-08-21 11:06:17
6 [报告]
发表于 2008-06-30 11:38 |只看该作者
原帖由 gvim 于 2008-6-30 09:25 发表
这个mbr.s只是一个开始

期待中

论坛徽章:
0
7 [报告]
发表于 2008-06-30 13:03 |只看该作者
原帖由 gvim 于 2008-6-30 09:25 发表
这个mbr.s只是一个开始


从BSD源代码的角度来讲,这句话是正确的。

论坛徽章:
2
亥猪
日期:2014-03-19 16:36:35午马
日期:2014-11-23 23:48:46
8 [报告]
发表于 2008-07-01 11:38 |只看该作者
为什么是 0x0600?
这个地址是必须的(dedicated)还是约定上(convenient)的?

论坛徽章:
0
9 [报告]
发表于 2008-07-01 12:14 |只看该作者
原帖由 gvim 于 2008-7-1 11:38 发表
为什么是 0x0600?
这个地址是必须的(dedicated)还是约定上(convenient)的?


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

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

BIOS加载 mbr 之后只有分散的几片空间可以用,最初的人在这几片空间中选择了 0x600,后续也就没有再做修改。

论坛徽章:
2
亥猪
日期:2014-03-19 16:36:35午马
日期:2014-11-23 23:48:46
10 [报告]
发表于 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 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP