首页
论坛
课程
招聘
[原创]grub for dos 引导部分 stage1分析
2009-9-2 17:34 7737

[原创]grub for dos 引导部分 stage1分析

2009-9-2 17:34
7737
本来是分析linux 2.6.11的bootsect.S 的,但是,在2.6.11里没有实质的引导部分,

只是简单的打印输出请使用引导器引导,大体上就是这样,故此,转手分析Grub,

看懂此文,希望你有些基础,至少也得懂什么是MBR吧,关于int 13 非扩展调用的部分

就不写了,那个bios调用随便找个引擎都能搜到;

grub for dos 的大体执行流程以及对应文件:
1: 开机后,完成硬件初始化后,BIOS 装载Stage1 模块于0X7c00处 ( int 19h)
2: Stage1 模块装载Start 模块(int 13h,int 13h扩展,中断调用参数初始化,引导磁盘类型判断,读MBR第2扇区到0x7000,并拷贝到0x8000执行)

3: Start 模块将整个GRUB 的内核载入内存 (保护模式切换,文件系统判断和支持)
4: GRUB 的一个Shell 的机制,作为一个小型的操作系统,来通过指令的方式装载不同的其他操作系统。

/*****************************************
    MBR结构偏移以宏的方式定义在stage1.h中
 *****************************************/
#include <stage1.h>
/*重定位宏*/
#define ABS(x) (x-_start+0x7c00) 

/*****************************************
    打印字符串宏定义
 *****************************************/
#define MSG(x)    movw $ABS(x), %si; call message 

#define    MOV_MEM_TO_AL(x)    .byte 0xa0;  .word x
/*****************************************
   .file op 指定和目标文件关联的源文件名
 *****************************************/ 

    .file    "stage1.S"
/*****************************************
    .text代码段声明; .code16使用实模式
 *****************************************/
    .text
    .code16 

/*****************************************
   代码段入口 .globl 全局符号定义
 *****************************************/
.globl _start; _start:
/*****************************************
    MBR开始跳转指令,跳转到真正的引导代码处
    后跟一个NOP指令,占MBR偏移的: 0x00-0x03
    更确切的说,这应该是DBR
 *****************************************/
    jmp    after_BPB
    nop    

/*****************************************
     .程序引用计数器,这里应该是对齐
 *****************************************/
    . = _start + 4 

/* 以下定义为int13扩展使用的的参数结构
 * 扇区号 :sectors
 * 磁头号: heads
 * 柱面:   cylinders
 * 在int 13H调用时使用
 */
mode:
    .byte    0
disk_address_packet:    
sectors:
    .long    0
heads:
    .long    0
cylinders:
    .word    0
sector_start:
    .byte    0
head_start:
    .byte    0
cylinder_start:
    .word    0
/*****************************************
   MBR偏移0x0B开始是BPB部分,占用50或者80字节
   这里占用50字节,故此
   #define STAGE1_BPBEND        0x3e
 *****************************************/ 

    . = _start + STAGE1_BPBEND 

/*    
 *stage1版本定义,版本号在stage1中以宏的方式定义
 * #define COMPAT_VERSION_MAJOR    3
 * #define COMPAT_VERSION_MINOR    2
 * #define COMPAT_VERSION        ((COMPAT_VERSION_MINOR << 8) \
 *                    | COMPAT_VERSION_MAJOR)
 *boot_drive 用于指出stage1需要装载的代码在何处
 *后面是stage2的载入地址定义
 ************************************************************************/ 

stage1_version:    
    .byte    COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR
boot_drive:    
    .byte    0xFF    
force_lba:                                  ;强制LBA模式
    .byte    0
stage2_address:                             ;stage2装入地址
    .word    0x8000
stage2_sector:                              ;stage2在磁盘上的扇区
    .long    1
stage2_segment:
    .word    0x800 

/*****************************************
   引导区代码
 *****************************************/ 

after_BPB: 

    cli                                ;关中断    
/*
 * Drive Table
 * DL = 00h     1st floppy disk ( "drive A:" )
 * DL = 01h     2nd floppy disk ( "drive B:" )
 * DL = 80h     1st hard disk
 * DL = 81h     2nd hard disk    
 */ 

boot_drive_check:    
    jmp    1f
    testb    $0x80, %dl                  ;BIOS加载完启动代码会把%dl设置成启动盘号(boot drive number):
    jnz    1f
    movb    $0x80, %dl
1:    

    ljmp    $0, $ABS(real_start)        ;跳转到真正的引导代码处,远跳转 

real_start:    

    xorw    %ax, %ax                     
    movw    %ax, %ds
    movw    %ax, %ss                    ;段寄存器初始化 

    movw    $STAGE1_STACKSEG, %sp       ;堆栈地址初始化, 宏定义:#define STAGE1_STACKSEG        0x2000 

    sti                            ;关中断 

    MOV_MEM_TO_AL(ABS(boot_drive))    
    cmpb    $0xFF, %al                 ;检查是否设置了启动磁盘,如果不是,dl送al,将dx入栈后在屏幕上显示“GRUB”
    je    1f                         ;如果是,调整后一个1:处
    movb    %al, %dl
1:
    pushw    %dx 

    MSG(notification_string) 

    testb    $STAGE1_BIOS_HD_FLAG, %dl   ;测试启动磁盘是否是硬盘,DL内的值有BIOS设置
    jz    chs_mode                    ;不是硬盘则使用CHS模式
    movb    $0x41, %ah
    movw    $0x55aa, %bx
    int    $0x13                        ; int 13 0x41 扩展判断是否支持LBA模式 , 

    popw    %dx                          ;恢复dl值
    pushw    %dx                          ;保存dx值 

    jc    chs_mode                      
    cmpw    $0xaa55, %bx                  ;是否支持LBA模式,不支持则启用CHS模式
    jne    chs_mode 

    MOV_MEM_TO_AL(ABS(force_lba))          ;相当于 movb ABS(force_lba), %al         
    testb    %al, %al                      ;是否强制使用LBA模式
    jnz    lba_mode
    andw    $1, %cx
    jz    chs_mode

/****************************************************************************** 
struct DiskAddressPacket
    {
        BYTE PacketSize;     // 数据包尺寸:
                             //(固定值,恒等于16,即10H,指本结构所占用的存储空间)
        BYTE Reserved;       // ==0
        WORD BlockCount;     // 要传输的数据块个数(以扇区为单位)
        DWORD BufferAddr;    // 传输缓冲地址(segment:offset)
        QWORD BlockNum;      // 磁盘起始绝对块地址  8个字节
    }; 

    PacketSize 保存了 DAP 结构的尺寸, 以便将来对其进行扩充. 在
目前使用的扩展 Int13H 版本中 PacketSize 恒等于 16. 如果它小于
16, 扩展 Int13H 将返回错误码( AH=01, CF=1 ).
    BlockCount 对于输入来说是需要传输的数据块总数, 对于输出来说
是实际传输的数据块个数. BlockCount = 0 表示不传输任何数据块.
    BufferAddr 是传输数据缓冲区的 32 位地址 (段地址:偏移量). 数据
缓冲区必须位于常规内存以内(1M).
    BlockNum 表示的是从磁盘开始算起的绝对块地址(以扇区为单位),
与分区无关. 第一个块地址为 0. 一般来说, BlockNum 与 CHS 地址的关系
是:
    BlockNum =
(cylinder * NumberOfHeads + head) * SectorsPerTrack + sector - 1; 

    其中 cylinder, head, sector 是 CHS 地址, NumberOfHeads 是磁盘
的磁头数, SectorsPerTrack 是磁盘每磁道的扇区数.
    也就是说 BlockNum 是沿着 扇区->磁道->柱面 的顺序记数的. 这一顺
序是由磁盘控制器虚拟的, 磁盘表面数据块的实际排列顺序可能与此不同
(如为了提高磁盘速度而设置的间隔因子将会打乱扇区的排列顺序). 

*****************************************************************************/ 

lba_mode:                                      ;使用LBA方式读入下一个扇区
    movl    0x10(%si), %ecx 

    movw    $ABS(disk_address_packet), %si  ;esi保存 stDiskAddressPacket结构体 

    movb    $1, -1(%si)
    movl    ABS(stage2_sector), %ebx 

    movw    $0x0010, (%si)              ;PacketSize 保存了 DAP 结构的尺寸,恒等于0x10 

    movw    $1, 2(%si)                  ;输入的数据块个数  [esi +2]
    movl    %ebx, 8(%si)                ;磁盘起始绝对块地址 [esi +8] 起8个字节为绝对块地址 

    movw    $STAGE1_BUFFERSEG, 6(%si)   ;读数据的缓冲区 [esi +6 ] 

    xorl    %eax, %eax                   ;eax清零
    movw    %ax, 4(%si)                  ;[esi +4] == 0
    movl    %eax, 12(%si)                ; [esi +12] == 0
/*
 * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
 *    Call with    %ah = 0x42
 *            %dl = drive number
 *            %ds:%si = segment:offset of disk address packet
 *    Return:
 *            %al = 0x0 on success; err code on failure
 */ 

    movb    $0x42, %ah
    int    $0x13 

    jc    chs_mode                     ;Jump if ECX is Zero 

    movw    $STAGE1_BUFFERSEG, %bx
    jmp    copy_buffer
chs_mode:    
    movb    $8, %ah
    int    $0x13                       ;int 08h中断读磁盘参数 ,读磁盘参数
    jnc    final_init                  ;cx不为0跳走,调用int 13 2号功能读取数据 

    testb    $STAGE1_BIOS_HD_FLAG, %dl
    jz    floppy_probe                ;是否是软盘 

    jmp    hd_probe_error               ;硬盘可能出错 

final_init:
    movw    $ABS(sectors), %si 

    movb    $0, -1(%si)
    xorl    %eax, %eax
    movb    %dh, %al
    incw    %ax
    movl    %eax, 4(%si) 

    xorw    %dx, %dx
    movb    %cl, %dl
    shlw    $2, %dx
    movb    %ch, %al
    movb    %dh, %ah 

    incw    %ax
    movw    %ax, 8(%si) 

    xorw    %ax, %ax
    movb    %dl, %al
    shrb    $2, %al 

    movl    %eax, (%si) 

setup_sectors:
    movl    ABS(stage2_sector), %eax 

    xorl    %edx, %edx 

    divl    (%si) 

    movb    %dl, 10(%si) 

    xorl    %edx, %edx    
    divl    4(%si)        

    movb    %dl, 11(%si) 

    movw    %ax, 12(%si) 

    cmpw    8(%si), %ax
    jge    geometry_error 

    movb    13(%si), %dl 

    shlb    $6, %dl        
    movb    10(%si), %cl    

    incb    %cl        
    orb    %dl, %cl    
    movb    12(%si), %ch    

    popw    %dx
    movb    11(%si), %dh 

/*
 * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
 *    Call with    %ah = 0x2
 *            %al = number of sectors
 *            %ch = cylinder
 *            %cl = sector (bits 6-7 are high bits of "cylinder")
 *            %dh = head
 *            %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
 *            %es:%bx = segment:offset of buffer
 *    Return:
 *            %al = 0x0 on success; err code on failure
 */ 

    movw    $STAGE1_BUFFERSEG, %bx
    movw    %bx, %es    

    xorw    %bx, %bx    
    movw    $0x0201, %ax    
    int    $0x13 

    jc    read_error                  ;cx为0,则读取错误 

    movw    %es, %bx
copy_buffer:
    movw    ABS(stage2_segment), %es    ;stage寄存器入ES段 

    pusha                               ;环境保存
    pushw    %ds                         ;dl保存
    movw    $0x100, %cx                 ;因为使用的是movsw,所以字MBR字节数减半
    movw    %bx, %ds                    ;读出的数据地址初始化DS段;
    xorw    %si, %si
    xorw    %di, %di
    cld                                  ;设置方向标志,+
    rep
    movsw                               ;movsw es:[di},ds:[si} 

    popw    %ds
    popa 

    jmp    *(stage2_address)          ;跳转执行stage2代码; 

/************************************************
   错误处理
 *************************************************/
geometry_error:
    MSG(geometry_error_string)
    jmp    general_error 

hd_probe_error:
    MSG(hd_probe_error_string)
    jmp    general_error 

read_error:
    MSG(read_error_string) 

general_error:
    MSG(general_error_string) 

stop:    jmp    stop                             ;死循环 

notification_string:    .string "GRUB "
geometry_error_string:    .string "Geom"
hd_probe_error_string:    .string "Hard Disk"
read_error_string:    .string "Read"
general_error_string:    .string " Error" 

/* BIOS int 10号中断 0号功能以打印机模式显示字符
 * AH = 功能号 0EH
 * AL = 打印的字符
 * BH = 页号
 * BL = 前景颜色
 *16位色彩编码表 (D7 D6 D5 D4为背景色,D3 D2 D1 D0为前景色)
 */ 

1:
    movw    $0x0001, %bx
    movb    $0xe, %ah
    int    $0x10        
message:
    lodsb
    cmpb    $0, %al
    jne    1b    
    ret 

    . = _start + STAGE1_WINDOWS_NT_MAGIC
nt_magic:    
    .long 0
    .word 0 

part_start:    
    . = _start + STAGE1_PARTSTART 

probe_values:
    .byte    36, 18, 15, 9, 0 

floppy_probe: 

    movw    $ABS(probe_values-1), %si 

probe_loop:
    xorw    %ax, %ax
    int    $0x13 

    incw    %si
    movb    (%si), %cl 

    cmpb    $0, %cl
    jne    1f 

    MSG(fd_probe_error_string)
    jmp    general_error 

fd_probe_error_string:    .string "Floppy" 

1:    
    movw    $STAGE1_BUFFERSEG, %bx
    movw    $0x201, %ax
    movb    $0, %ch
    movb    $0, %dh
    int    $0x13
    jc    probe_loop    
    movb    $1, %dh
    movb    $79, %ch 

    jmp    final_init 

    . = _start + STAGE1_PARTEND
    .word    STAGE1_SIGNATURE


看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (11)
雪    币: 291
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
leking 活跃值 2009-9-2 18:41
2
0
潜水有牛人真多
雪    币: 228
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
lijingli 活跃值 2009-9-3 09:11
3
0
哎......
雪    币: 308
活跃值: 活跃值 (76)
能力值: ( LV12,RANK:470 )
在线值:
发帖
回帖
粉丝
zhuwg 活跃值 11 2009-9-3 10:01
4
0
原来偶有在第一页膜拜的机会的哇
雪    币: 83
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
子令 活跃值 2009-9-3 10:45
5
0
學習
好東西!
雪    币: 21
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
stary 活跃值 2009-9-11 17:19
6
0
确实是好 如果能扩展下让他跳过PE就更好了
雪    币: 27
活跃值: 活跃值 (1032)
能力值: ( LV17,RANK:1820 )
在线值:
发帖
回帖
粉丝
riusksk 活跃值 41 2009-9-12 11:00
7
0
Support!成功留在第一页。
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Champion 活跃值 2009-9-13 23:20
8
0
mark

学习一下
雪    币: 100
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ー絆綪秂 活跃值 2009-9-13 23:22
9
0
jmp    after_BPB
    nop   

WHy not
雪    币: 108
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
八千里路 活跃值 2009-9-16 12:37
10
0
学习。。。。。。。。
雪    币: 220
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
asusmaster 活跃值 2009-9-27 19:50
11
0
好教程啊,楼主真牛人也,汇编搞的这么熟悉。
雪    币: 277
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
qq2003 活跃值 2009-9-28 18:11
12
0
不错 !~感谢楼主分享!~学习中……
游客
登录 | 注册 方可回帖
返回