A20:历史的妥协
A20: 历史的妥协
今天看 MIT 6.828 中 JOS 的 Bootloader 部分,可以说是看几行代码查一小时资料了。
最近看英文文档比较多,中文社区这块相关的内容是在太过匮乏,还有不少错误,所以自己就来翻译一下。
在boot.S
中,Boot loader 开启了 A20 Line,并且注释说到
为了向下兼容旧 PC(的应用程序),物理地址的第 20 位被锁定为低位(也就是 0),所以高于 1MB 的内存会
被 wrap around 为 0 。而这段代码则正是为了将这一机制取消。
1 | # Enable A20: |
这个 20 位和 1MB 比较好理解。
寻址空间由地址总线数量 N 决定:
Size = 2^N Bytes
2021 年了,寻址空间已经大大的有了,而在上个世纪,Intel 8088 的地址总线只有 20 条,所以寻址空间为 2^20 Bytes,也就是 1 MB。至于这个 Wrap around是什么,还得搜搜才知道。
A20 是什么
A20,或者说 20 号地址总线,是 X86 地址总线的第 21 根,对应总线上的第 21 位。
注意,从第 0 位开始,而不是第 1 位。这就是为什么控制第 21 根总线却被叫做 A20
Gate-A20 是什么
Intel 8088 的时代遗风
最初,PC 中的 Intel 8088 只有 20 条地址总线,可寻址空间达到 1MB (2^20 Bytes) ,但是 8088 的寄存器都是 16 位的,这意味着其可表达地址最大为64KB
(2^16 Bytes)。这差距可太大了,Intel 想出了分段寻址的方式解决了这个问题,使用两个 16 位寄存器,最终地址可以采用如下公式表达:
Address = selector * 16 + offset
基于该算法,地址最大为FFFF:FFFF
,通过算法可以得到其对应的物理地址是0x10FFEF
,这显然已经超过了 1MB,于是 Intel 采用了一个截断方案:
当程序员给出超过 1MB(即位于
0x100000
-0x10FFEF
)的地址时,因为逻辑上正常,系统并不认为其访问越界而产生异常,而是自动从 0 开始计算,也就是说系统计算实际地址的时候是按照对 1MB求模的方式进行的。
所以,0x10FFEF
就变成了0x00FFEF
。
许多程序利用这一点,使不改变微处理器的段寄存器而去访问最开始的 64KB 内存成为一个通用的技巧
译注
与国内大部分的转载文不同,OSDev Wiki 的说法是:
For some reason a few short-sighted programmers decided to write programs that actually used this wraparound
Intel 80826 时代的妥协
随后 Intel 80286 横空出世,得益于其地址总线增加到 24 根,Intel 引入了 保护模式(Protected Mode),使得可访问寻址空间达到了16MB
。
为了向后兼容,Intel 还引入了实模式,旨在对 Intel 8088 的百分百兼容并仍然使用 20 根地址总线,这意味着实模式下寻址空间仍然被限制在 1MB 且沿用截断机制。然而,由于一个 BUG,这种地址截断并未生效:如果程序员访问0x100000-0x10FFEF
之间的内存,系统将实际访问这块内存,而不是预期中的那样像 8088 一样截断。
由于历史遗留,大量程序依赖于这种截断运作,为了实现完美的兼容性,IBM 在系统总线与处理器中间加了一个逻辑门,用于控制 A20 的工作,因此得名 Gate-A20。
Gate-A20 可以通过软件进行开/关从而达到允许/阻止系统总线接受 A20 的信号,为了使依赖于截断的旧程序正常运作,需要设置 Gate-A20 为关闭状态。
但是对于许多运行在保护模式下的 OS,开启 Gate-A20 是在控制权交给 Kernel 之前做的第一件事情,否则无法访问所有内存。因为在断掉来自 A20 的信号后,地址的第21位
永远是 0,这相当于内存有接近一半的合法地址无法被表示。
A20 原理
上段提到,当 Gate-A20 关闭后,地址总线的第 21 位永远为 0。
当表示 1MB 以下的空间时,表现不受影响。
当尝试表示 1MB 以上的空间时,将会出现断层:
[1MB,2MB) , [3MB,4MB)……这些区间的内存地址将无法表示,且占据[1MB,infinity]的一半
由于实模式下的程序最多访问到 1MB 以上再加一点点的空间,所以这对于实模式的兼容是完全足够的
如何控制 Gate-A20
键盘
起初,这个逻辑门被连接在 Intel 8042 键盘控制器上……因为这个芯片有一个空闲的接口
并通过 硬件 I/O 操作 发送信号
1 | void init_A20(void) |
译注
如果你可以找到比较那种比较老的主机,上面的 PS/2 接口所连接的可能就是这个芯片
这听起来有些离谱,因为你根本想不到会把开关装在键盘控制器上,但是那个时代确实没什么所谓“标准”。
当 PC 启动时,Gate-A20 总是默认关闭的,但某些 BIOS 和 模拟器(比如 QEMU) 会打开,然后做高位内存管理。
译注
高位内存管理听起来怪怪的,附原话
do some high-memory managers
这好像是荷兰人写的 Wiki来自 Kernel Developers 的参考提到,BIOS 会打开 Gate-A20 用于测试系统内存大小与可用性,这可能是访问高位内存的原因
有些 Bootload 也会打开 Gate-A20 ,比如 GRUB 会在进入保护模式时自动打开 Gate-A20
其他方法
还可以使用 INT 15H
等方法开启 Gate-A20,不再赘述,参考 OSDev Wiki - A20 Line
参考
A20 - a pain from the past (tue.nl)