首先我们来描述一下引导流程:
1)BIOS将控制权交给硬盘的bootloader(stage1).
2)bootloader(stage1)将stage1_5加载到内存.
3)bootloader通过stage1.5,识别文件系统,将stage2加载到内存.
4)stage2此时就可以在文件系统中将menu.lst配置文件加载,进入启动内核的引导过程.
一)BIOS引导过程
BIOS的作用:
BIOS在计算机启动时负责和所有硬件沟通,并将计算机呈现在用户面前.
BIOS与南/北桥:
北桥主要控制内存和CPU,而南桥主要负责PCI,PCI-E,USB,VGA等所有外围设备.
在南桥里面有一块特殊的区域,负责存储CMOS的信息,CMOS是用户存储BIOS设备的地方.
BIOS的引导过程:
1)开启电源.
2)CPU先被激活并去寻找BIOS.
3)BIOS会初始化各种主板芯片组.
4)BIOS初始化键盘控制器8042.
5)初始化中断向量,中断服务例程.
6)初始化VGA BIOS控制器.
7)显示BIOS的版本和公司名称
8)扫描软驱和各种介质容量
9)读取CMOS的启动顺序配置,并检测启动装置是否正常
10)调用INT 19H的2号功能来读出硬盘MBR扇区的内容,将其读入到内存0000:7C00h,并跳转至此处执行.
二)MBR和它的分区表
MBR位于硬盘的0柱面,0磁头,1扇区,被称为主引导程序.
它由三个部分组成:主引导程序,硬盘分区表DPT(Disk Partition table)和硬盘有效标志(55AA).
在总共512字节的主引导扇区里主引导程序(boot loader)占446个字节,第二部分是Partition table区(分区表),即DPT,占64个字节,硬盘中分区有多少以及每一分区的大小都记在其中.第三部分是magic number,占2个字节,固定为55AA.
我们可以用dd命令来将/dev/sda磁盘的MBR读出并写入到/tmp/d20文件,如下:
dd if=/dev/sda of=/tmp/d20 bs=512 count=1
用hexdump打开/tmp/d20文件,如下:
注:-C的作用是用十六进制及ASCII的方式查看文件,并采用低/高位的方式显示
hexdump -C /tmp/d20
00000000 eb 48 90 10 8e d0 bc 00 b0 b8 00 00 8e d8 8e c0 |.H..............|
00000010 fb be 00 7c bf 00 06 b9 00 02 f3 a4 ea 21 06 00 |...|.........!..|
00000020 00 be be 07 38 04 75 0b 83 c6 10 81 fe fe 07 75 |....8.u........u|
00000030 f3 eb 16 b4 02 b0 01 bb 00 7c b2 80 8a 74 03 02 |.........|...t..|
00000040 ff 00 00 20 01 00 00 00 00 02 fa 90 90 f6 c2 80 |... ............|
00000050 75 02 b2 80 ea 59 7c 00 00 31 c0 8e d8 8e d0 bc |u....Y|..1......|
00000060 00 20 fb a0 40 7c 3c ff 74 02 88 c2 52 be 7f 7d |. ..@|<.t...R..}|
00000070 e8 34 01 f6 c2 80 74 54 b4 41 bb aa 55 cd 13 5a |.4....tT.A..U..Z|
00000080 52 72 49 81 fb 55 aa 75 43 a0 41 7c 84 c0 75 05 |RrI..U.uC.A|..u.|
00000090 83 e1 01 74 37 66 8b 4c 10 be 05 7c c6 44 ff 01 |...t7f.L...|.D..|
000000a0 66 8b 1e 44 7c c7 04 10 00 c7 44 02 01 00 66 89 |f..D|.....D...f.|
000000b0 5c 08 c7 44 06 00 70 66 31 c0 89 44 04 66 89 44 |\..D..pf1..D.f.D|
000000c0 0c b4 42 cd 13 72 05 bb 00 70 eb 7d b4 08 cd 13 |..B..r...p.}....|
000000d0 73 0a f6 c2 80 0f 84 ea 00 e9 8d 00 be 05 7c c6 |s.............|.|
000000e0 44 ff 00 66 31 c0 88 f0 40 66 89 44 04 31 d2 88 |D..f1...@f.D.1..|
000000f0 ca c1 e2 02 88 e8 88 f4 40 89 44 08 31 c0 88 d0 |........@.D.1...|
00000100 c0 e8 02 66 89 04 66 a1 44 7c 66 31 d2 66 f7 34 |...f..f.D|f1.f.4|
00000110 88 54 0a 66 31 d2 66 f7 74 04 88 54 0b 89 44 0c |.T.f1.f.t..T..D.|
00000120 3b 44 08 7d 3c 8a 54 0d c0 e2 06 8a 4c 0a fe c1 |;D.}<.T.....L...|
00000130 08 d1 8a 6c 0c 5a 8a 74 0b bb 00 70 8e c3 31 db |...l.Z.t...p..1.|
00000140 b8 01 02 cd 13 72 2a 8c c3 8e 06 48 7c 60 1e b9 |.....r*....H|`..|
00000150 00 01 8e db 31 f6 31 ff fc f3 a5 1f 61 ff 26 42 |....1.1.....a.&B|
00000160 7c be 85 7d e8 40 00 eb 0e be 8a 7d e8 38 00 eb ||..}.@.....}.8..|
00000170 06 be 94 7d e8 30 00 be 99 7d e8 2a 00 eb fe 47 |...}.0...}.*...G|
00000180 52 55 42 20 00 47 65 6f 6d 00 48 61 72 64 20 44 |RUB .Geom.Hard D|
00000190 69 73 6b 00 52 65 61 64 00 20 45 72 72 6f 72 00 |isk.Read. Error.|
000001a0 bb 01 00 b4 0e cd 10 ac 3c 00 75 f4 c3 00 00 00 |........<.u.....|
000001b0 00 00 00 00 00 00 00 00 c2 b3 09 00 00 00 80 01 |................|
000001c0 01 00 83 fe ff ff 3f 00 00 00 41 29 54 02 00 fe |......?...A)T...|
000001d0 ff ff 05 fe ff ff 80 29 54 02 6e b1 6b 74 00 00 |.......)T.n.kt..|
000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
00000200
我们先来看下分区表,它的位置起始于MBR的446(0x1BE)个字节的位置,终止于510(0x1fd)个字节的位置.
每个分区是16个字节,最多有4个分区,也就是说分区表中的第一个分区表项从第一行的0x1bf(80 01)开始,到第二行的54 02结束,在/dev/sda磁盘中我们一共有2个分区,所以这里有两条记录.
如下:
地址 0 1 2 3 4 5 6 7 8 9 A B C D E F
000001b0 00 00 00 00 00 00 00 00 c2 b3 09 00 00 00 80 01 |................|
000001c0 01 00 83 fe ff ff 3f 00 00 00 41 29 54 02 00 fe |......?...A)T...|
000001d0 ff ff 05 fe ff ff 80 29 54 02 6e b1 6b 74 00 00 |.......)T.n.kt..|
000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
分析:
分区表的具体含义如下:
字节 含义 值
00 分区是否可引导。 0,不可引导。80,可引导
01-03 分区开始的CHS值(Head,Sector,Cylinder,Sector的高 位是Cylinder的第9,10位)
04 分区类型 7,NTFS,C, FAT32,5,F 扩展分区等等
05-07 分区结束的CHS值(Head,Sector,Cylinder,Sector的高 位是Cylinder的第9,10位)
08-11 LBA方式下,相对的起始扇区号
在MBR分区里边,相对0扇区,
扩展分区表示相对于扩展分区的起
始扇区(little endion 格式)
12-15 该分区拥有的扇区数量 (little endion)
我们先来看一下扇区数量,也就是每条分区记录的最后4个字节,我们看上例中的第一个分区,这里是41 29 54 02,把高位和低位转换后,也就是0x2542941个扇区,转换成十进制是39070017个扇区,再转换成字节就是39070017*512=20003848704.
20003848704也就是20GB.所以我们第一个分区是20GB
下面来看一下该分区在线性寻址方式下(LBA)的绝对起始扇区号(相对于0扇区).
LBA即(Logic Block Address)又称为线性寻址模式,在这种模式下,磁盘不再有柱面,磁头,扇区的三维定义,而是将磁盘上所有的扇区依次从0开始编号.所以CHS已经不是磁盘分区的唯一依据,且更没有必要用CHS来划分磁盘,因为CHS本身来讲也是一个逻辑三维.
我们看到第一个分区的这四个字节为3f 00 00 00,把高低和低位转换后,也就是0x3f个扇区,转换成十进制就是63个扇区,再转换成字节就是63*512=32256.
32256就是32kb,所以我们第一个分区是从磁盘第63个扇区开始存放数据.
那么第二个分区的这四个字节应该是第一个分区的扇区数(39070017)加上第一个分区的起始扇区(63),结果就是39070080与0x2542980相符.
下面是起始CHS和结束CHS
CHS是过去的一种寻址方式,也就是用柱面(Cylinder)/磁头(Head)/扇区(Sector)三个参数来定位一个唯一的扇区.
而CHS是一种逻辑上的三维定义,并不是和硬盘物理结构一一对应,比如我们看到的第一个分区表项从第1(0x01)磁头开始,从fe个磁头处结束,事实上我们不可能有这么多磁头的硬盘.
我们看到第一个分区的起始CHS地址为01 01 00,其中第一个字节0x01表示开始磁头号,第二个字节的低6位00 0001(0x01=0000 0001),第二个字节的高2位作为起始柱面号的高2位,第三个字节的8个bit作为分区起始柱面号的低8位.
我们现在已经不用CHS做为磁盘的寻址方式了,因为采用CHS寻址的分区最大也不会超过8GB,也就是柱面数最多(1024)*扇区最大数(64)*磁头最大数(256)*每扇区字节数(512)/1024/1024=8192MB,也就是8G.
为了使用8GB以上的分区,BIOS中开始支持LBA,而CHS这种方式已经被放弃,成为历史,在1998年以后的BIOS都会支持LBA方式.
LBA的寻址方式可以让我们支持2TB,这是因为分区相对起始扇区号(分区项08-11个字节)和分区最大扇区数(分区项12-15个字节)的位数都是32bit.也就是0xFFFFFFFF*512/1024/1024/1024=2048GB,
三)stage1,stage1.5,stage2
stage1即是mbr中的bootloader.
我们可以在系统中看到stage1文件,比如:
ls -l /boot/grub/stage1
-rw-r--r-- 1 root root 512 2009-08-23 07:11 /boot/grub/stage1
stage1的大小是512个字节,正是MBR的大小,但stage1的最后66个字节与mbr不同,mbr的最后66个字节分别是64个字节的分区表,和MBR标记55AA.
我们可以比对一下stage1与MBR的前446,它们的内容是完全一样的.我们可以理解stage1文件是MBR中bootloader的一个备份.
stage1.5
stage1功能有限,不能完成grub的很多功能,例如多重引导,选择内核文件等.
stage1将stage1.5读入到内存,来过渡整个引导过程,即通过stage1.5来识别文件系统,从而能加载stage2,完成上述的功能.
为了识别多种文件系统,所以有多个stage1_5文件
ls /boot/grub/*1_5
/boot/grub/e2fs_stage1_5 /boot/grub/jfs_stage1_5 /boot/grub/reiserfs_stage1_5
/boot/grub/fat_stage1_5 /boot/grub/minix_stage1_5 /boot/grub/xfs_stage1_5
那么stage1无法识别文件系统,又是怎么能找到stage1.5呢
因为在bootloader知道stage1.5放在哪里,stage1.5一般紧跟在mbr后面,即512个字节后面就放着stage1.5,如上面我们分析mbr那样,第一个分区是从线性寻址的63个扇区开始的,也就是线性地址32kb的地方,
所以用dd命令读取sda设备的最前面32kb到临时文件,如下:
dd if=/dev/sda of=/tmp/stage1.5 bs=32k count=1
00000000 eb 48 90 10 8e d0 bc 00 b0 b8 00 00 8e d8 8e c0 |.H..............|
00000010 fb be 00 7c bf 00 06 b9 00 02 f3 a4 ea 21 06 00 |...|.........!..|
00000020 00 be be 07 38 04 75 0b 83 c6 10 81 fe fe 07 75 |....8.u........u|
00000030 f3 eb 16 b4 02 b0 01 bb 00 7c b2 80 8a 74 03 02 |.........|...t..|
00000040 ff 00 00 20 01 00 00 00 00 02 fa 90 90 f6 c2 80 |... ............|
00000050 75 02 b2 80 ea 59 7c 00 00 31 c0 8e d8 8e d0 bc |u....Y|..1......|
00000060 00 20 fb a0 40 7c 3c ff 74 02 88 c2 52 be 7f 7d |. ..@|<.t...R..}|
00000070 e8 34 01 f6 c2 80 74 54 b4 41 bb aa 55 cd 13 5a |.4....tT.A..U..Z|
00000080 52 72 49 81 fb 55 aa 75 43 a0 41 7c 84 c0 75 05 |RrI..U.uC.A|..u.|
00000090 83 e1 01 74 37 66 8b 4c 10 be 05 7c c6 44 ff 01 |...t7f.L...|.D..|
000000a0 66 8b 1e 44 7c c7 04 10 00 c7 44 02 01 00 66 89 |f..D|.....D...f.|
000000b0 5c 08 c7 44 06 00 70 66 31 c0 89 44 04 66 89 44 |\..D..pf1..D.f.D|
000000c0 0c b4 42 cd 13 72 05 bb 00 70 eb 7d b4 08 cd 13 |..B..r...p.}....|
000000d0 73 0a f6 c2 80 0f 84 ea 00 e9 8d 00 be 05 7c c6 |s.............|.|
000000e0 44 ff 00 66 31 c0 88 f0 40 66 89 44 04 31 d2 88 |D..f1...@f.D.1..|
000000f0 ca c1 e2 02 88 e8 88 f4 40 89 44 08 31 c0 88 d0 |........@.D.1...|
00000100 c0 e8 02 66 89 04 66 a1 44 7c 66 31 d2 66 f7 34 |...f..f.D|f1.f.4|
00000110 88 54 0a 66 31 d2 66 f7 74 04 88 54 0b 89 44 0c |.T.f1.f.t..T..D.|
00000120 3b 44 08 7d 3c 8a 54 0d c0 e2 06 8a 4c 0a fe c1 |;D.}<.T.....L...|
00000130 08 d1 8a 6c 0c 5a 8a 74 0b bb 00 70 8e c3 31 db |...l.Z.t...p..1.|
00000140 b8 01 02 cd 13 72 2a 8c c3 8e 06 48 7c 60 1e b9 |.....r*....H|`..|
00000150 00 01 8e db 31 f6 31 ff fc f3 a5 1f 61 ff 26 42 |....1.1.....a.&B|
00000160 7c be 85 7d e8 40 00 eb 0e be 8a 7d e8 38 00 eb ||..}.@.....}.8..|
00000170 06 be 94 7d e8 30 00 be 99 7d e8 2a 00 eb fe 47 |...}.0...}.*...G|
00000180 52 55 42 20 00 47 65 6f 6d 00 48 61 72 64 20 44 |RUB .Geom.Hard D|
00000190 69 73 6b 00 52 65 61 64 00 20 45 72 72 6f 72 00 |isk.Read. Error.|
000001a0 bb 01 00 b4 0e cd 10 ac 3c 00 75 f4 c3 00 00 00 |........<.u.....|
000001b0 00 00 00 00 00 00 00 00 c2 b3 09 00 00 00 80 01 |................|
000001c0 01 00 83 fe ff ff 3f 00 00 00 41 29 54 02 00 fe |......?...A)T...|
000001d0 ff ff 05 fe ff ff 80 29 54 02 6e b1 6b 74 00 00 |.......)T.n.kt..|
000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
注意下面就是stage1.5,这和/boot/grub/e2fs_stage1_5的内容是一样的
00000200 52 56 be 03 21 e8 2a 01 5e bf f8 21 66 8b 2d 83 |RV..!.*.^..!f.-.|
00000210 7d 04 00 0f 84 ca 00 80 7c ff 00 74 3e 66 8b 1d |}.......|..t>f..|
00000220 66 31 c0 b0 7f 39 45 04 7f 03 8b 45 04 29 45 04 |f1...9E....E.)E.|
00000230 66 01 05 c7 04 10 00 89 44 02 66 89 5c 08 c7 44 |f.......D.f.\..D|
00000240 06 00 70 50 66 31 c0 89 44 04 66 89 44 0c b4 42 |..pPf1..D.f.D..B|
00000250 cd 13 0f 82 9f 00 bb 00 70 eb 56 66 8b 05 66 31 |........p.Vf..f1|
00000260 d2 66 f7 34 88 54 0a 66 31 d2 66 f7 74 04 88 54 |.f.4.T.f1.f.t..T|
00000270 0b 89 44 0c 3b 44 08 7d 74 8b 04 2a 44 0a 39 45 |..D.;D.}t..*D.9E|
00000280 04 7f 03 8b 45 04 29 45 04 66 01 05 8a 54 0d c0 |....E.)E.f...T..|
00000290 e2 06 8a 4c 0a fe c1 08 d1 8a 6c 0c 5a 52 8a 74 |...L......l.ZR.t|
000002a0 0b 50 bb 00 70 8e c3 31 db b4 02 cd 13 72 46 8c |.P..p..1.....rF.|
000002b0 c3 8e 45 06 58 c1 e0 05 01 45 06 60 1e c1 e0 04 |..E.X....E.`....|
000002c0 89 c1 31 ff 31 f6 8e db fc f3 a4 1f be 14 21 e8 |..1.1.........!.|
000002d0 60 00 61 83 7d 04 00 0f 85 3c ff 83 ef 08 e9 2e |`.a.}....<......|
000002e0 ff be 16 21 e8 4b 00 5a ea 00 22 00 00 be 19 21 |...!.K.Z.."....!|
000002f0 e8 3f 00 eb 06 be 1e 21 e8 37 00 be 23 21 e8 31 |.?.....!.7..#!.1|
00000300 00 eb fe 4c 6f 61 64 69 6e 67 20 73 74 61 67 65 |...Loading stage|
00000310 31 2e 35 00 2e 00 0d 0a 00 47 65 6f 6d 00 52 65 |1.5......Geom.Re|
00000320 61 64 00 20 45 72 72 6f 72 00 bb 01 00 b4 0e cd |ad. Error.......|
00000330 10 46 8a 04 3c 00 75 f2 c3 00 00 00 00 00 00 00 |.F..<.u.........|
00000340 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000003f0 00 00 00 00 00 00 00 00 02 00 00 00 0e 00 20 02 |.............. .|
00000400 ea 70 22 00 00 00 03 02 ff ff ff 00 00 00 00 00 |.p".............|
00000410 02 00 30 2e 39 37 00 ff ff 00 ff 2f 62 6f 6f 74 |..0.97...../boot|
00000420 2f 67 72 75 62 2f 73 74 61 67 65 32 20 2f 62 6f |/grub/stage2 /bo|
00000430 6f 74 2f 67 72 75 62 2f 6d 65 6e 75 2e 6c 73 74 |ot/grub/menu.lst|
00000440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
后面略
下面我们来尝试覆盖mbr中的引导程序,并将它恢复.
1)首先覆盖mbr中的stage1:
dd if=/dev/zero of=/dev/sda bs=446 count=1
2)导出/dev/sda磁盘的mbr,如下:
dd if=/dev/sda of=/tmp/cmbr bs=512 count=1
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.0023115 s, 222 kB/s
3)我们查看导出的mbr文件,如下:
hexdump -C /tmp/cmbr
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 01 |................|
000001c0 01 00 83 fe ff ff 3f 00 00 00 41 29 54 02 00 fe |......?...A)T...|
000001d0 ff ff 05 fe ff ff 80 29 54 02 6e b1 6b 74 00 00 |.......)T.n.kt..|
000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
00000200
4)安装stage1,如下:
grub> root (hd0,0)
Filesystem type is ext2fs, partition type 0x83
grub> setup (hd0)
Checking if "/boot/grub/stage1" exists... yes
Checking if "/boot/grub/stage2" exists... yes
Checking if "/boot/grub/e2fs_stage1_5" exists... yes
Running "embed /boot/grub/e2fs_stage1_5 (hd0)"... 15 sectors are embedded.
succeeded
Running "install /boot/grub/stage1 (hd0) (hd0)1+15 p (hd0,0)/boot/grub/stage2 /boot/grub/menu.lst"... succeeded
Done.
5)我们再次导出mbr,发现已经成功的恢复了.
dd if=/dev/sda of=/tmp/nmbr bs=512 count=1
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.00329825 s, 155 kB/s
hexdump -C /tmp/nmbr
00000000 eb 48 90 00 00 00 00 00 00 00 00 00 00 00 00 00 |.H..............|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 02 |................|
00000040 ff 00 00 20 01 00 00 00 00 02 fa 90 90 f6 c2 80 |... ............|
00000050 75 02 b2 80 ea 59 7c 00 00 31 c0 8e d8 8e d0 bc |u....Y|..1......|
00000060 00 20 fb a0 40 7c 3c ff 74 02 88 c2 52 be 7f 7d |. ..@|<.t...R..}|
00000070 e8 34 01 f6 c2 80 74 54 b4 41 bb aa 55 cd 13 5a |.4....tT.A..U..Z|
00000080 52 72 49 81 fb 55 aa 75 43 a0 41 7c 84 c0 75 05 |RrI..U.uC.A|..u.|
00000090 83 e1 01 74 37 66 8b 4c 10 be 05 7c c6 44 ff 01 |...t7f.L...|.D..|
000000a0 66 8b 1e 44 7c c7 04 10 00 c7 44 02 01 00 66 89 |f..D|.....D...f.|
000000b0 5c 08 c7 44 06 00 70 66 31 c0 89 44 04 66 89 44 |\..D..pf1..D.f.D|
000000c0 0c b4 42 cd 13 72 05 bb 00 70 eb 7d b4 08 cd 13 |..B..r...p.}....|
000000d0 73 0a f6 c2 80 0f 84 ea 00 e9 8d 00 be 05 7c c6 |s.............|.|
000000e0 44 ff 00 66 31 c0 88 f0 40 66 89 44 04 31 d2 88 |D..f1...@f.D.1..|
000000f0 ca c1 e2 02 88 e8 88 f4 40 89 44 08 31 c0 88 d0 |........@.D.1...|
00000100 c0 e8 02 66 89 04 66 a1 44 7c 66 31 d2 66 f7 34 |...f..f.D|f1.f.4|
00000110 88 54 0a 66 31 d2 66 f7 74 04 88 54 0b 89 44 0c |.T.f1.f.t..T..D.|
00000120 3b 44 08 7d 3c 8a 54 0d c0 e2 06 8a 4c 0a fe c1 |;D.}<.T.....L...|
00000130 08 d1 8a 6c 0c 5a 8a 74 0b bb 00 70 8e c3 31 db |...l.Z.t...p..1.|
00000140 b8 01 02 cd 13 72 2a 8c c3 8e 06 48 7c 60 1e b9 |.....r*....H|`..|
00000150 00 01 8e db 31 f6 31 ff fc f3 a5 1f 61 ff 26 42 |....1.1.....a.&B|
00000160 7c be 85 7d e8 40 00 eb 0e be 8a 7d e8 38 00 eb ||..}.@.....}.8..|
00000170 06 be 94 7d e8 30 00 be 99 7d e8 2a 00 eb fe 47 |...}.0...}.*...G|
00000180 52 55 42 20 00 47 65 6f 6d 00 48 61 72 64 20 44 |RUB .Geom.Hard D|
00000190 69 73 6b 00 52 65 61 64 00 20 45 72 72 6f 72 00 |isk.Read. Error.|
000001a0 bb 01 00 b4 0e cd 10 ac 3c 00 75 f4 c3 00 00 00 |........<.u.....|
000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 01 |................|
000001c0 01 00 83 fe ff ff 3f 00 00 00 41 29 54 02 00 fe |......?...A)T...|
000001d0 ff ff 05 fe ff ff 80 29 54 02 6e b1 6b 74 00 00 |.......)T.n.kt..|
000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
00000200
下面我们来尝试覆盖stage1.5,并将它恢复,如下:
1)导出sda设备节点的stage1.5
dd if=/dev/sda of=/tmp/mbr bs=512 count=64
64+0 records in
64+0 records out
32768 bytes (33 kB) copied, 3.1594e-05 s, 1.0 GB/s
2)覆盖sda设备节点的stage1.5
这里我们将MBR之后的63个扇区至0.
dd if=/dev/zero of=/dev/sda bs=512 count=63 seek=1
63+0 records in
63+0 records out
32256 bytes (32 kB) copied, 0.000339256 s, 95.1 MB/s
3)输出这63个扇区的数据,如下:
dd if=/dev/sda of=/tmp/stage1_5_1 bs=512 count=63 skip=1
63+0 records in
63+0 records out
4)我们查看sda磁盘stage1_5数据已经被清0
hexdump -C /tmp/stage1_5_1
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00008000
5)我们尝试用grub程序来重新安装stage1.5,如下:
grub
Probing devices to guess BIOS drives. This may take a long time.
GNU GRUB version 0.97 (640K lower / 3072K upper memory)
[ Minimal BASH-like line editing is supported. For
the first word, TAB lists possible command
completions. Anywhere else TAB lists the possible
completions of a device/filename. ]
grub> root (hd0,0)
Filesystem type is ext2fs, partition type 0x83
grub> setup (hd0)
Checking if "/boot/grub/stage1" exists... yes
Checking if "/boot/grub/stage2" exists... yes
Checking if "/boot/grub/e2fs_stage1_5" exists... yes
Running "embed /boot/grub/e2fs_stage1_5 (hd0)"... 15 sectors are embedded.
succeeded
Running "install /boot/grub/stage1 (hd0) (hd0)1+15 p (hd0,0)/boot/grub/stage2 /boot/grub/menu.lst"... succeeded
Done.
grub> quit
6)再次输出stage1_5到文件,并查看,如下:
dd if=/dev/sda of=/tmp/stage1_5_1 bs=512 count=63 skip=1
63+0 records in
63+0 records out
32256 bytes (32 kB) copied, 0.00589645 s, 5.5 MB/s
test2:~# hexdump -C /tmp/stage1_5_1
00000000 52 56 be 03 21 e8 2a 01 5e bf f8 21 66 8b 2d 83 |RV..!.*.^..!f.-.|
00000010 7d 04 00 0f 84 ca 00 80 7c ff 00 74 3e 66 8b 1d |}.......|..t>f..|
00000020 66 31 c0 b0 7f 39 45 04 7f 03 8b 45 04 29 45 04 |f1...9E....E.)E.|
00000030 66 01 05 c7 04 10 00 89 44 02 66 89 5c 08 c7 44 |f.......D.f.\..D|
00000040 06 00 70 50 66 31 c0 89 44 04 66 89 44 0c b4 42 |..pPf1..D.f.D..B|
00000050 cd 13 0f 82 9f 00 bb 00 70 eb 56 66 8b 05 66 31 |........p.Vf..f1|
00000060 d2 66 f7 34 88 54 0a 66 31 d2 66 f7 74 04 88 54 |.f.4.T.f1.f.t..T|
00000070 0b 89 44 0c 3b 44 08 7d 74 8b 04 2a 44 0a 39 45 |..D.;D.}t..*D.9E|
00000080 04 7f 03 8b 45 04 29 45 04 66 01 05 8a 54 0d c0 |....E.)E.f...T..|
00000090 e2 06 8a 4c 0a fe c1 08 d1 8a 6c 0c 5a 52 8a 74 |...L......l.ZR.t|
000000a0 0b 50 bb 00 70 8e c3 31 db b4 02 cd 13 72 46 8c |.P..p..1.....rF.|
000000b0 c3 8e 45 06 58 c1 e0 05 01 45 06 60 1e c1 e0 04 |..E.X....E.`....|
000000c0 89 c1 31 ff 31 f6 8e db fc f3 a4 1f be 14 21 e8 |..1.1.........!.|
000000d0 60 00 61 83 7d 04 00 0f 85 3c ff 83 ef 08 e9 2e |`.a.}....<......|
000000e0 ff be 16 21 e8 4b 00 5a ea 00 22 00 00 be 19 21 |...!.K.Z.."....!|
000000f0 e8 3f 00 eb 06 be 1e 21 e8 37 00 be 23 21 e8 31 |.?.....!.7..#!.1|
00000100 00 eb fe 4c 6f 61 64 69 6e 67 20 73 74 61 67 65 |...Loading stage|
00000110 31 2e 35 00 2e 00 0d 0a 00 47 65 6f 6d 00 52 65 |1.5......Geom.Re|
00000120 61 64 00 20 45 72 72 6f 72 00 bb 01 00 b4 0e cd |ad. Error.......|
00000130 10 46 8a 04 3c 00 75 f2 c3 00 00 00 00 00 00 00 |.F..<.u.........|
00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
后面略
stage2
stage2是grub的核心程序,包括选项操作系统加载,新增参数,修改选项.
stage1将/boot/grub/stage2读入到内存,并通过stage2进行引导.
grub> root (hd0,0)
Filesystem type is ext2fs, partition type 0x83
grub> setup (hd0)
Checking if "/boot/grub/stage1" exists... yes
Checking if "/boot/grub/stage2" exists... yes
Checking if "/boot/grub/e2fs_stage1_5" exists... yes
Running "embed /boot/grub/e2fs_stage1_5 (hd0)"... 15 sectors are embedded.
succeeded
Running "install /boot/grub/stage1 (hd0) (hd0)1+15 p (hd0,0)/boot/grub/stage2 /boot/grub/menu.lst"... succeeded
Done.
我们可以看到grub到底安装了哪些东西:
/boot/grub/stage1 (hd0) ---->它安装stage1到磁盘的MBR
(hd0)1+15 p ---->将stage1_5写入到hd0磁盘MBR后面的15个扇区中.
(hd0,0)/boot/grub/stage2 ----->告知bootloader,当需要加载stage2时,其位置在第一块硬盘的第一个分区的/boot/grub/目录下.
/boot/grub/menu.lst---->将menu.lst的位置告诉stage2
四)stage1的代码分析
/*****************************************************/
#include <stage1.h>
/*
* defines for the code go here
*/
/* Absolute addresses
This makes the assembler generate the address without support
from the linker. (ELF can't relocate 16-bit addresses!) */
/*取当前指令的绝对地址,即当前指令的地址减去程序的启始地址再加上0x7c00,这样的作法是为了得到物理地址,不依赖于编译器.*/
#define ABS(x) (x-_start+0x7c00)
/* Print message string */
#define MSG(x) movw $ABS(x), %si; call message
/*是将内存数据赋给al寄存器,这里使用机器码是为了实现兼容性*/
/* XXX: binutils-2.9.1.0.x doesn't produce a short opcode for this. */
#define MOV_MEM_TO_AL(x) .byte 0xa0; .word x
.file "stage1.S"
.text
/* Tell GAS to generate 16-bit instructions so that this code works
in real mode. */
.code16
/*BIOS调用INT 19H中断将本程序移到0:0x7c00内存地址处开始执行,这是本程序的入口*/
.globl _start; _start:
/*
* _start is loaded at 0x7c00 and is jumped to with CS:IP 0:0x7c00
*/
/*
* Beginning of the sector is compatible with the FAT/HPFS BIOS
* parameter block.
*/
/*跳转到after_BPB标记处,也就是跳过BPB,nop是为了指令对齐
*一般用来内存地址偶数对齐,比如有一条指令,占3字节,这时候使用nop指令,cpu 就可以从第四个字节处读取指令了*/
jmp after_BPB
nop /* do I care about this ??? */
/*
* This space is for the BIOS parameter block!!!! Don't change
* the first jump, nor start the code anywhere but right after
* this area.
*/
/*下面这个.是一个特殊的标号,代表当前的地址,从开始处的_start处填充空间至_start+4处,相当于4个字节的空间.
但是,从_start开始后的jmp after_BPB和nop已经占用了3个字节的空间,相当于在它们的后面再用0填充1个字节的空间即可.*/
. = _start + 4
/*下面定义了BPB,BPB(BIOS Parameter Block)表,描述逻辑盘结构组成*/
/* scratch space */
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
/* more space... */
/*和上面的_start+4一样,会把上条指令处填充0,直到STAGE1_BPBEND,因为STAGE1_BPBEND为0x3e,所以我们会把上条语句.word 0后面的空间填充为0.
. = _start + STAGE1_BPBEND
/*
* End of BIOS parameter block.
*/
stage1_version:
.byte COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR
boot_drive:
.byte GRUB_INVALID_DRIVE /* the disk to load stage2 from */
force_lba:
.byte 0
stage2_address:
.word 0x8000
stage2_sector:
.long 1
stage2_segment:
.word 0x800
/*跳转到这里了,程序继续运行*/
after_BPB:
/* general setup */
/*cli是禁用中断,当你修改中断向量或修改堆栈指针时一般要禁用它,因为在进行这些时如果有中断产生,那么会破坏当前的环境,造成程序崩溃.当干完这些时就可以sti恢复中断*/
cli /* we're not safe here! */
/*
* This is a workaround for buggy BIOSes which don't pass boot
* drive correctly. If GRUB is installed into a HDD, check if
* DL is masked correctly. If not, assume that the BIOS passed
* a bogus value and set DL to 0x80, since this is the only
* possible boot drive. If GRUB is installed into a floppy,
* this does nothing (only jump).
*/
/*下面的设定主要为了解决bios的bug,而不能正确引导的问题,如果安装在HDD(硬盘驱动器)上,将会覆盖掉jmp lf这条指令,在开机初始,BIOS加载完启动代码会把%dl寄存器设置成启动盘号,
* 通过testb判断dl寄存器是否是0x80,如果不是0x80,则将0x80赋值给dl寄存器.通过这种方式我们保证grub安装到硬盘,则DL必须是0x80,原因是0x80表示第一块硬盘,也就是MBR的所在.
* dl寄存器的值在bios通过int 19H时,它的值应该是80H,如果不是bootloader也会巧妙的解决这个问题.
BIOS引导时会把引导介质的代码赋值给DL寄存器
DL = 00h 1st floppy disk ( "drive A:" )
DL = 01h 2nd floppy disk ( "drive B:" )
DL = 80h 1st hard disk
DL = 81h 2nd hard disk
jmp 1f表示如果我们没有安装在硬盘驱动器,比如通过软盘引导,将会通过jmp跳转到标识符为1的地方.
*/
boot_drive_check:
jmp 1f
testb $0x80, %dl
jnz 1f
movb $0x80, %dl
1:
/*
* ljmp to the next instruction because some bogus BIOSes
* jump to 07C0:0000 instead of 0000:7C00.
*/
/*这里通过ljmp(长跳转)到cs:ip = 0x0000:$ABS(real_start)这个地方执行指令.相当于real_start-_start+0x7c00.
在正常情况下BIOS会将MBR被加载到cs:ip = 0x0000:0x7c00处,而对映的物理地址就是(Segment value * 16) + Offset value,
而有些糟糕的BIOS会将其加载到07c0:0000上,其实这两个代表的物理地址是完全一样的,
即:0x0000*16+0x7c00=0x0000+0x7c00=0x7c00
0x07c0*16+0x0000=0x7c00+0x0000=0x7c00
有些人从来就不考虑这种事实,那就是大多数人常常把segment值设为0,这样引导代码就可以假定任何段寄存器都是0从而只对付ip里的偏移量,所以,在grub里,加上这么一个长转移,就防止了这类糟糕的BIOS带来的麻烦. */
ljmp $0, $ABS(real_start)
/*进入real_start了,ax清零,ds赋值0,ss赋值0,将STAGE1_STACKSEG(0x2000)赋值给sp,这样就设置了实模式下的堆栈段地址(栈顶位置)ss:sp = 0x0000:0x2000*/
real_start:
/* set up %ds and %ss as offset from 0 */
xorw %ax, %ax
movw %ax, %ds
movw %ax, %ss
/* set up the REAL stack */
movw $STAGE1_STACKSEG, %sp
/*置中断允许位*/
sti /* we're safe again */
/*先用MOV_MEM_TO_AL宏将boot_drive量存到al中,然后与0xff进行比较,用的是cmpb $0xff,%al ;je 1f.
cmpb指令是将两个操作数进行相减,对标志位的影响同sub指令,但是不保存结果.其中,此处用到的是zf标志位(因为是je指令),这样,当操作数相等(即相减为零时)zf被置1.
所以,cmpb和je一起使用时,就是指当操作数相等时,跳转至je制定的标号.
所以,在这里,若boot_drive等于0xff,则使用BIOS传递过来的默认的驱动器进行启动.
如果不是则运行movb %al,%dl指令,将boot_drive的值保存至dl中,表示由boot_drive的值确定启动设备.
在这里宏GRUB_INVALID_DRIVE的值为0xff,而boot_drive的指向的值也是0xff,所以执行je 1f,跳转到下面的标号1.*/
MOV_MEM_TO_AL(ABS(boot_drive)) /* movb ABS(boot_drive), %al */
cmpb $GRUB_INVALID_DRIVE, %al
je 1f
movb %al, %dl
1:
/*驱动器号信息压栈,这里的%dl是0x80*/
pushw %dx
/*这里的MSG(notification_string)会输出GRUB字串到屏幕上,
MSG宏在程序上方定义,即#define MSG(x) movw $ABS(x), %si; call message
通过movw将标号notification_string的地址赋值给寄存器%si,注:notification_string标号后面定义了GRUB字符串.
然后通过call调用子程序message,message子程序通过lodsb将si寄存器的内容逐一读出到%al寄存器,然后检查al是否为零.
如果不为零,表明字符还未传输完,此时会跳转到int 10h中断前,用int 10h的0xe子功能在屏幕上以telemode模式写字符,其中ah是子功能,这里是0xe,al是字符,bh是页,bl是前背景色.
如果为零,表示字符已经传输完成了(.string伪指令会在指定的字符串后加入一个字节的0).此时调用ret返回.*/
MSG(notification_string)
/*首先判断是硬盘还是软盘或者根本就没有盘(如果不是硬盘,判断LBA或者CHS模式就没有意义了),所以,在判断硬盘是否支持LBA时,先判断是不是硬盘.
这里用testb $STAGE1_BIOS_HD_FLAG,%dl来判断,dl寄存器里装载的是磁盘号,有三大类情况:硬盘(0x80,0x81),软盘(0x00,0x01),无效的盘(0xff).
而前面的宏就是0x80,所以通过testb和jz指令判断,如果dl中不是80或81(也就是不是硬盘),就跳转到chs_mode函数下面.
在我们这里%dl为0x80,而$STAGE1_BIOS_HD_FLAG也是0x80,所以testb指令执行后,标志位z位为0,testb指令是按位与运算,如果寄存器%dl的值是0,那么z位将是1,即跳转到chs_mode*/
testb $STAGE1_BIOS_HD_FLAG, %dl
jz chs_mode
/*如果判断出是硬盘的话,再接着判断是否支持LBA,使用的工具就是BIOS的int 13h中断.通过BIOS调用 INT 0x13 来确定是否支持LBA.
ah = 41h表示检查LBA是否存在.
bx = 0x55aa表示MBR的标记为0x55AA
dl = 0x80表示第一台HDD(范围是0x80 ~ 0xff)
返回结果:
如果支持LBA,CF=0,下面的寄存器会受到影响,如下:
ah:扩展功能的主版本号( major version of extensions )
al:内部使用( internal use )
bx :AA55h ( magic number )
cx:Bits Description
0 extended disk access functions
1 removable drive controller functions supported
2 enhanced disk drive (EDD) functions (AH=48h,AH=4Eh) supported.
Extended drive parameter table is valid
3~15 reserved (0)
如果不支持LBA,CF=1,下面的寄存器值为:
ah = 0x01 ( invalid function )
*/
movb $0x41, %ah
movw $0x55aa, %bx
int $0x13
/*有的bios的int 13h中断会影响到dl,所以此处用pop和push指令将其保护起来*/
popw %dx
pushw %dx
/*如果不支持LBA,即CF位为1,那么将会通过jc chs_mode跳转到chs_mode标志处,再判断bx是不是aa55h,使用cmpw $0xaa55,%bx;jne chs_mode再判断一次,
如果bx里存的不是预期的返回值,同样不支持lba,也要进入chs_mode函数*/
jc chs_mode
cmpw $0xaa55, %bx
jne chs_mode
/*使用MOV_MEM_TO_AL宏将force_lba变量值传递到al,判断是否为0,不为0强行进入lba_mode.我们这里force_lba为0,所以不跳转到lba_mode.
然后判断cx,如果cx为0表明不支持扩展第一子集,这时也进入chs_mode函数,andw是对两个操作数进行与运算,并将与操作的结果赋值给寄存器,这里是%cx,如果结果为0,则将标志位z置为1.
最后总结进入chs_mode的条件,如下:
一)磁盘号非80h或81h,进入chs_mode
二)int13h,41h子功能,返回cf为0,进入进入chs_mode
三)int13h,41h子功能,返回bx不为aa55,进入chs_mode
四)如果没有设置强制LBA,而且也不支持扩展第一子集,进入chs_mode
五)其它情况,进入lba模式
*/
MOV_MEM_TO_AL(ABS(force_lba)) /* movb ABS(force_lba), %al */
testb %al, %al
jnz lba_mode
andw $1, %cx
jz chs_mode
/*进入了LBA模式.下面的工作是把数据填加到上面的BPB中.*/
lba_mode:
/*这个代码就是个废话,ecx寄存器被置入了一个无意义的值*/
movl 0x10(%si), %ecx
/*将标号disk_address_packet处的地址赋给si*/
movw $ABS(disk_address_packet), %si
/*将[si-1]内存处置1,我们看到disk_adress_packet标号上面就是mode标号,此时值是0,我们在这里将它置为1,表示LBA扩展读,如果是0,就是CHS寻址读)*/
movb $1, -1(%si)
/*将stage2所在的扇区赋予ebx,这里stage2_sector是1*/
movl ABS(stage2_sector), %ebx
/*在[si]和[si+1]处存放10和00(movw $0x0010,(si))
movw $0x0010, (%si)
/*在[si+2]和[si+3]处存放01和00*/
movw $1, 2(%si)
/*在[si+8][si+9][si+A][si+B]处存放0x01/0x00/0x00/0x00*/
movl %ebx, 8(%si)
/*[si+6]和[si+7]处存放0x00和0x70 (#define STAGE1_BUFFERSEG 0x7000)*/
movw $STAGE1_BUFFERSEG, 6(%si)
/*在[si+4]和[si+5]处存放00和00,在[si+c][si+d][si+e][si+f]处存放0x00/0x00/0x00/0x00*/
xorl %eax, %eax
movw %ax, 4(%si)
movl %eax, 12(%si)
/*功能号AH: 41H 检测是否支持扩展INT 13H调用 BX=55AAH AH=0支持
42H 读磁盘扇区
DL=磁盘号
DS:SI=调用结构的地址
调用结构:
disk_addr_pkt struc
packet_size db 16 ; disk_addr_pkt结构长度通常为16,大小为1字节
reserved db 0 ; 保留,必须为0,大小为1字节
block_count dw ? ; 读取扇区数目,本例为1个扇区,大小为2字节
buffer_addr dd ? ; 缓冲区地址,这里是0000:7000,大小为4字节
block_num dq ? ; 读取的绝对扇区起始号,这里是1,也就是相对1扇区的第二个扇区,大小为8字节.
disk_addr_pkt ends
成功:AH=0
失败:AH=错误代码,设置标志位c*/
movb $0x42, %ah
int $0x13
/*如果出错,就跳转到chs_mode处*/
jc chs_mode
/*将7000赋值给bx寄存器*/
movw $STAGE1_BUFFERSEG, %bx
jmp copy_buffer
/*进入chs模式时,进行一些检测,来确定具体的情况.
首先就是int13h的08功能号的使用,使用08功能可以检测chs模式中硬盘的参数,保存在各寄存器里:
DL:驱动器类型,第一块软盘是0x00,第一块硬盘是0x80.
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 指向软盘参数表
如果成功返回参数,则进入final_init函数;但是如果调用失败,进位标志CF=1,AH存放错误信息码.表明不支持硬盘的chs模式(前面也判断了不支持lba),那就要考虑是不是软盘了.
再使用testb和jz指令,若dl是00或01,则认为是软盘,就跳转到floppy_probe函数执行(后成讨论此过程).
但是若连软盘也不是,只好准备报错了,跳转到hd_probe_error函数,这个函数调用MSG函数连同general_error函数一道输出"hard disk error"的字符。*/
chs_mode:
movb $8, %ah
int $0x13
jnc final_init
testb $STAGE1_BIOS_HD_FLAG, %dl
jz floppy_probe
jmp hd_probe_error
final_init:
/*将扇区数地址保存到si寄存器*/
movw $ABS(sectors), %si
/*将mode设置为0,即sectors-1的地址*/
movb $0, -1(%si)
/*将dh寄存器中的磁头号保存到al寄存器,通过incw %ax将磁头号加1(因为磁头数是以0开始的),movl %eax,$(%si)将磁头数保存至[si+4][si+5][si+6][si+7]内存地址上*/
xorl %eax, %eax
movb %dh, %al
incw %ax
movl %eax, 4(%si)
/*cl中的0~5位存放的是扇区数,cl的6~7位存放的是柱面数高2位,先把cl寄存器的值保存dl寄存器中,所以dx逻辑左移2位后在dh中出现的两位就是柱面数的高2位,并且把这2位移到ah中,
ch存放的柱面数低8为移至al中,这样ax里就是柱面数了.*/
xorw %dx, %dx
movb %cl, %dl
shlw $2, %dx
movb %ch,%al
movb %dh,%ah
/*同样的道理要进行incw %ax操作,并且把真正的柱面数放到地址为[si+8][si+9]的内存上*/
incw %ax
movw %ax, 8(%si)
/*由于我们之前把dl寄存器左移了2位,所以这里把dl寄存器的值保存在al寄存器中,再把al寄存器右移2位,最后把真正的扇区数保存在地址为[si][si+1][si+2][si+3]的内存上.
到这里我们把扇区数保存在[si][si+1][si+2][si+3]
把磁头数保存在[si+4][si+5][si+6][si+7]
把柱面数保存在[si+8][si+9]*/
xorw %ax, %ax
movb %dl, %al
shrb $2, %al
movl %eax, (%si)
setup_sectors:
/*然后在使用int 13h*0x02)功能前要进行必备的参数设置:
eax存放stage2的扇区编号(stage2_sector,这里为1)
清空edx寄存器,然后通过(stage2扇区数)/(扇区数)获得引导扇区数.注意对于div指令来说,eax恒定存放被除数,div后面的寄存器存放的是除数.
余数在edx中存放,第一个余数(扇区数)放到地址为[si+10]的内存上,将edx清零,再用(上一步除法的商)/(磁头数)得到的余数为磁头数,存放在[si+11]内存地址上.
商为柱面数并存放在eax中并同时保存至[si+12][si+13]内存地址上.然后将之前中断获得的柱面数与此处stage2所占柱面数相比较,如果stage2柱面数大,那么明显错误,程序将跳至geometry_error处
这里我们将stage2所在的扇区放在内存地址[si+10]上.
将stage2所有的磁头数放在内存地址[si+11].
将stage2所有的柱面数放在内存地址[si+12][si+13]
*/
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
/*
为完成int 13h中断(将stage2所在的扇区读入到7000:0000这段内存地址)
我们要做如果的设置:
%al = number of sectors(需要读的扇区数)
%ah= 0x02(功能号)
%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 */
/*将[si+13]的内容赋值给dl(柱面数的高2位)并且左移6位*/
movb 13(%si), %dl
shlb $6, %dl /* shift left by 6 bits */
/*将[si+10]的内容赋值给cl(扇区数),将扇区数放到cl中再增1*/
movb 10(%si), %cl /* get sector */
incb %cl /* normalize sector (sectors go
from 1-N, not 0-(N-1) ) */
/*现在dl寄存器存放着柱面数高2位,cl寄存器的底6位存放着扇区数,通过orb将dl和cl两个寄存器组合在一起存放在cl寄存器中*/
orb %dl, %cl /* composite together */
/*将[si+12]的内容赋值给ch(柱面数的低8位)*/
movb 12(%si), %ch /* sector+hcyl in cl, cylinder in ch */
/*然后恢复驱动器号(popw %dx),然后将磁头数放置到dh中*/
popw %dx
movb 11(%si), %dh
/*将0x7000赋值给es并将bx清零,所以这里就是%es:%bx=0x7000:0x0000*/
movw $STAGE1_BUFFERSEG, %bx
movw %bx, %es /* load %es segment with disk buffer */
xorw %bx, %bx /* %bx = 0, put it at 0 in the segment */
/*将功能号02赋值给ah寄存器,将al赋值给al寄存器,开始调用int 13h中断*/
movw $0x0201, %ax /* function 2 */
int $0x13
jc read_error
/*将0x7000赋值给bx寄存器,这里同lba的方式是一样的*/
movw %es, %bx
/*程序进入copy_buffer,在chs和lba两种模式下都要来到这里,此时bx寄存器为0x7000*/
copy_buffer:
然后。然后popw %ds; popa还原寄存器。 */
movw ABS(stage2_segment), %es
/*pusha:将所有的16位通用寄存器压入堆栈*/
pusha
/*将ds寄存器压力堆栈*/
pushw %ds
/*给cx赋值0x100*/
movw $0x100, %cx
/*将bx寄存器内容赋值给ds寄存器,因为bx寄存器为0x7000,所ds寄存器为0x7000*/
movw %bx, %ds
/*将si和di寄存器清零*/
xorw %si, %si
xorw %di, %di
/*CLD是清方向标志,把标识寄存器的D位置设置为零*/
cld
/*使用rep和movsw指令将ds:si处连续的512字节内容传输到es:di指定的内存地址(0x8000:0x0000).其中,rep指令的含义就是重复执行后一句指令,每执行一次.cx减1,直至cx为0.
这也是前面cx赋值0x100(256)的原因.movsw每次传输一个字,256次就是512字节.*/
rep
movsw
/*还原ds寄存器,popa还原所有16位通用寄存器*/
popw %ds
popa
/* 跳转到0x8000:0x0000)继续运行stage2,代码中*(stage2_address)的星号是at&t汇编的规范:绝对跳转/调用(相对于与程序计数器有关的跳转/调用)操作数前面要加星号"*".
到这里stage1就算彻底执行完了.*/
jmp *(stage2_address)
/* END OF MAIN LOOP */
/*
* BIOS Geometry translation error (past the end of the disk geometry!).
*/
geometry_error:
MSG(geometry_error_string)
jmp general_error
/*
* Disk probe failure.
*/
hd_probe_error:
MSG(hd_probe_error_string)
jmp general_error
/*
* Read error on the disk.
*/
read_error:
MSG(read_error_string)
general_error:
MSG(general_error_string)
/* go here when you need to stop the machine hard after an error condition */
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"
/*
* message: write the string pointed to by %si
*
* WARNING: trashes %si, %ax, and %bx
*/
/*
* Use BIOS "int 10H Function 0Eh" to write character in teletype mode
* %ah = 0xe %al = character
* %bh = page %bl = foreground color (graphics modes)
*/
1:
movw $0x0001, %bx
movb $0xe, %ah
int $0x10 /* display a byte */
message:
lodsb
cmpb $0, %al
jne 1b /* if not end of string, jmp to display */
ret
/*
* Windows NT breaks compatibility by embedding a magic
* number here.
*/
. = _start + STAGE1_WINDOWS_NT_MAGIC
nt_magic:
.long 0
.word 0
/*
* This is where an MBR would go if on a hard disk. The code
* here isn't even referenced unless we're on a floppy. Kinda
* sneaky, huh?
*/
part_start:
. = _start + STAGE1_PARTSTART
probe_values:
.byte 36, 18, 15, 9, 0
/*开始对软盘进行检查*/
floppy_probe:
/*此时si寄存器的值为part_start标识符地址*/
movw $ABS(probe_values-1), %si
/*此时要使用int 13h(0x00功能号)来进行软驱的复位.成功的话cf=0*/
probe_loop:
/* reset floppy controller INT 13h AH=0 */
xorw %ax, %ax
int $0x13
/*软驱复位后,将[si]处的值赋给cl(cl是起始扇区数),由于循环,我们给了cl 4次机会,因为循环中有incw %si指令,所以si中的值是递增的,第一次运行incw后,si由part_start标识符地址转移到了probe_values标识符地址,依次给cl赋予0x24,0x12,0x0f,0x09,0x00五个值.*/
incw %si
movb (%si), %cl
/*当cl的值是0时,说明4次循环都没有检测成功,这里会打印软盘检测失败*/
cmpb $0, %cl
jne 1f
MSG(fd_probe_error_string)
jmp general_error
fd_probe_error_string: .string "Floppy"
1:
/*准备调用int 13h(功能号是0x02),这和chs中的int 13h,ah=0x02是一样的.
将0x7000赋值给bx寄存器
功能号02赋值给ah寄存器
因为读取1个扇区,这里将1赋值给al寄存器
因为是软盘,起始柱面和磁头都是0
之前我们已经把起始扇区(0x24或0x12等四个值)存入cl寄存器
最后调用int 13h,将该扇区写入到内存地址0x7000处*/
movw $STAGE1_BUFFERSEG, %bx
movw $0x201, %ax
movb $0, %ch
movb $0, %dh
int $0x13
/* if error, jump to "probe_loop" */
jc probe_loop
/*成功后,dh赋值1,ch赋值0x4f,dh 设置为 79,
表示柱面最大值为 79(80柱:0~79),dh 设置为 1 , 表示磁头数最大值为 1(2头:0~1),然后跳转至 final_init,
在上文中关于final_init的分析 , 我们知道保存时会把柱面和磁头分别加 1 , 扇区不变,因此 , 在软盘加载时 , 将设置 Cylinder : Head : Sector = 80 : 2 : start_sector.
最终就跳转至final_init函数处执行了.
/* %cl is already the correct value! */
movb $1, %dh
movb $79, %ch
jmp final_init
. = _start + STAGE1_PARTEND
/* the last 2 bytes in the sector 0 contain the signature */
.word STAGE1_SIGNATURE
/***********************************************************************/
参考:
http://zhumeng8337797.blog.163.com/blog/static/1007689142011828012299/
http://blog.chinaunix.net/uid-24774106-id-3500759.html
参考:
http://zhumeng8337797.blog.163.com/blog/static/1007689142011828012299/
http://blog.chinaunix.net/uid-24774106-id-3500759.html