在PC机中BIOS设定的中断向量表中int 0x41的中断向量位置(4*0x41 =
0x0000:0x0104)存放的并不是中断程序的地址,而是第一个硬盘的基本参数表
对于100%兼容的BIOS来说,这里存放着硬盘参数表阵列的首地址0xF000:0E401
第二个硬盘的基本参数表入口地址存于int 0x46中断向量位置处.每个硬盘参
数表有16个字节大小.
2>什么是A20门
在8086/8088中,只有20根地址总线,所以可以访问的地址是2^20=1M,但由
于8086/8088是16位地址模式,能够表示的地址范围是0-64K,所以为了在8086/
8088下能够访问1M内存,Intel采取了分段的模式:16位段基地址:16位偏移.
其绝对地址计算方法为:16位基地址左移4位+16位偏移=20位地址.
但这种方式引起了新的问题,通过上述分段模式,能够表示的最大内存为:
FFFFh:FFFFh=FFFF0h+FFFFh=10FFEFh=1M+64K-16Bytes(1M多余出来的部分被
称做高端内存区HMA).但8086/8088只有20位地址线,如果访问100000h-
10FFEFh之间的内存,则必须有第21根地址线.所以当程序员给出超过1M
(100000H-10FFEFH)的地址时,系统并不认为其访问越界而产生异常,而是自动
从重新0开始计算,也就是说系统计算实际地址的时候是按照对1M求模的方式
进行的,这种技术被称为wrap-around.
到了80286,系统的地址总线发展为24根,这样能够访问的内存可以达到
2^24=16M.Intel在设计80286时提出的目标是,在实模式下系统所表现的行为
应该和8086/8088所表现的完全一样,也就是说,在实模式下80286以及后续系
列,应该和8086/8088完全兼容.但最终,80286芯片却存在一个BUG:如果程序员
访问100000H-10FFEFH之间的内存,系统将实际访问这块内存,而不是象过去一
样重新从0开始.
为了解决上述问题,IBM使用键盘控制器上剩余的一些输出线来管理第21
根地址线(从0开始数是第20根),被称为A20 Gate.如果A20 Gate被打开,则当
程序员给出100000H-10FFEFH之间的地址的时候,系统将真正访问这块内存区
域;如果A20 Gate被禁止,则当程序员给出100000H-10FFEFH之间的地址的时候,
系统仍然使用8086/8088的方式.绝大多数IBM PC兼容机默认的A20 Gate是被
禁止的.由于在当时没有更好的方法来解决这个问题,所以IBM使用了键盘控制
器来操作A20 Gate.在80286以及更高系列的PC中,即使A20 Gate被打开,在实
模式下所能够访问的内存最大也只能为10FFEFH,尽管它们的地址总线所能够
访问的能力都大大超过这个限制.为了能够访问10FFEFH以上的内存,则必须进
入保护模式.
A20,其实它就是对于20-bit(从0开始数)的特殊处理(也就是对第21根地
址线的处理).如果A20 Gate被禁止,对于80286来说其地址为24bit,其地址表
示为EFFFFF;对于80386极其随后的32-bit芯片来说,其地址表示为FFEFFFFF.
这种表示的意思是如果A20 Gate被禁止,则其第20-bit在CPU做地址访问的时
候是无效的,永远只能被作为0;如果A20 Gate被打开,则其第20-bit是有效的,
其值既可以是0,又可以是1.
所以,在保护模式下,如果A20 Gate被禁止,则可以访问的内存只能是奇数
1M段,即1M,3M,5M…,也就是00000-FFFFF,200000-2FFFFF,300000-3FFFFF….
如果A20 Gate被打开,则可以访问的内存则是连续的.
3>如何开启A20地址线
多数PC都使用键盘控制器(8042芯片)来处理A20 Gate.从理论上讲,打开
A20 Gate的方法是通过设置8042芯片输出端口(64h)的2nd-bit,但事实上,当
你向8042芯片输出端口进行写操作的时候,在键盘缓冲区中,或许还有别的数
据尚未处理,因此你必须首先处理这些数据.
处理过程如下:
1. 禁止中断;
2. 等待,直到8042 Input buffer为空为止;
3. 发送禁止键盘操作命令到8042 Input buffer;
4. 等待,直到8042 Input buffer为空为止;
5. 发送读取8042 Output Port命令;
6. 等待,直到8042 Output buffer有数据为止;
7. 读取8042 Output buffer,并保存得到的字节;
8. 等待,直到8042 Input buffer为空为止;
9. 发送Write 8042 Output Port命令到8042 Input buffer;
10. 等待,直到8042 Input buffer为空为止;
11. 将从8042 Output Port得到的字节的第2位置1(OR 2),然后写入8042
Input buffer;
12. 等待,直到8042 Input buffer为空为止;
13. 发送允许键盘操作命令到8042 Input buffer;
14. 打开中断。
4>如何检测A20地址线是否开启
我们在之前已经提到,如果A20 Gate被打开了,则在实模式下,程序员可以
直接访问100000H~10FFEFH之间的内存,如果A20 Gate被禁止,则在实模式下,
若程序员访问100000H~10FFEFH之间的内存,则会被硬件自动转换为0H~0FFEFH
之间的内存,所以我们可以利用这个差异来检测A20 Gate是否被打开.
5>CR0寄存器
CR0寄存器是处理器4个控制寄存器之一,结构如下
+----+--------------------------+----+----+----+----+----+
| PG | Reserved | ET | TS | EM | MP | PE |
+----+--------------------------+----+----+----+----+----+
31 30 5 4 3 2 1 0
BIT 0:PE 如果该位被置位,则运行保护模式,否则运行实模式
BIT 1:MP 控制wait指令
BIT 2:EM 表示协处理器功能是否可以被仿真
BIT 3:TS 用于任务转换
BIT 4:ET 表示当前协处理器的类型(80287或80387)
BIT 31:PG表示处理器是否使用分页机制
6>描述符表
在保护模式下内存的寻址方式跟实模式下不同,它并不是简单的将段寄存
器的16位地址左移4位,然后加上偏移量来构成20位地址.在保护模式下,是可
以寻址4GB空间的,因为有32跟地址线,所以2^32 = 4GB.在该模式下是通过段
寄存器的索引值在描述符表(全局描述符表GDT或者局部描述符表LDT)内寻找
到相应的表项,然后通过获取表项里保存的段基地址,段基地址与偏移量相加
得到线性地址.如果系统没采用分页处理模式,那么线性地址就对应着实际的
物理地址了.分页处理模式放到内存管理文档里详细说明.
段寄存器格式:
+--------------------------------+------+-----+
| Index | TI | RPL |
+--------------------------------+------+-----+
15 32 1 0
RPL: 优先级别 0 - 4 LINUX只用了0核心态 3用户态
TI: 0使用全局描述符表(GDT),1使用局部描述符表(LDT)
描述符表是保存在gdtr或者ldtr寄存器里的,该寄存器共占用6个字节大
小.0-15位是表限,16-47位是表的基地址.表限是表示该表的大小,基地址是表
示描述符表所在的线性地址中的位置.
全局描述符号表和局部描述符号表的结构是一样的,如下:
+---------------------------------+----------+
| Base address | Limit |
+---------------------------------+----------+
47 1615 0
描述符表项是存储在描述符表里的内容.Intel公司将表项的第一个项设
为空,从第2个表项开始使用.描述符表项的结构定义如下:
63 5655 5251 4847 4039 3231 1615 87 0
+---------+----+-------+-----+---------+-------------+-----------------+
|B31 - B24| |L19-L16| |B23 - B16|基地址 B15-B0|段上限L15 - L0|
+---------+----+-------+-----+---------+-------------+-----------------+
+------+------+------+------+
| G | D/B | O | AV |
+------+------+------+------+
55 52
G: =1 段长以4K字节为单位 =0 段长以字节为单位
D/B: =1 表示对该段访问为32位指令 =0 表示为16位指令
O: 永远为0
AV: 可由软件使用,CPU忽略该位
+-----+----------+-----+-----+-------+-------+------+
| P | DPL | S | E | ED/C | R/W | A |
+-----+----------+-----+-----+-------+-------+------+
47 44 43 40
A: =1 已经被访问过 =0 还未被访问过
R/W: ---+
ED/C: ---|----+---> +---- E = 0 数据段
E: ---+ | |
| +---- ED = 0 向上伸(数据段) ED = 1 向下伸(堆栈段)
| |
| +---- W = 0不可写 W = 1 可写
|
+---> +---- E = 1代码段
|
+---- C = 0 忽略特权级 C = 不忽略特权级
|
+---- R = 0 不能读 R = 1可读
S = 0表示用于系统管理的系统段 = 1表示一般的代码段或者数据段
DPL: 特权级
P = 1该段在内存中 = 0 不在内存中
1楼 硬盘基本参数表 |
bios执行完后把硬盘的参数放在某个地方,其入口地址由中断向量号0x41指定
中断向量0x41的地址是4*0x41(每个向量四个字节)
用bochs查看[xp/11x 0x104]
0x00000104 <bogus+ 0>: 0x9fc0003d 0xf000ff53 0xc0002544
0xf000ff53
可见入口地址为:0x9fc0003d
又入口地址的前个字节为偏移量(0x003d),后两个字节为段地址(0x9fc0),所以物理地址为9fc00+003d=0x9fc3d
查看硬盘参数表[xp/11x 0x9fc3d]
0x0009fc3d <bogus+ 0>: 0x00040132 0x00ffff00 0x000000c0
0x00110132
第0,1字节为柱面数,即0x0132(不是0x3201),第2个字节为磁头数0x04,倒数第二个字节为没磁道扇区数,即0x11
硬盘可以看成一个圆柱,三个参数为:
磁头,哪个面
柱面,哪个圈
扇区,圈上哪个弧