开启A20线
在查看或编写操作系统内核时一定会遇到A20线这个问题。本人对此一直都是似懂非懂的,查了些资料,决定弄明白于是有了这篇文章。其中前一部分是翻译一篇外国博文,但光有这篇文章依旧不能清楚地说明A20线的问题。所以将另一些资料也放在一起,这样看的人应该会明白A20线的问题了。
A20 gate
开启A20线(翻译)
原文地址:http://kernelx.weebly.com/a20-address-line.html
When IBM PC AT System was introduced ,the new Intel 286 processor was not compatible with the old x86 processor. The older x86 micro-processors(Intel 8086) had address bus of 20bits which would total and give access up to 1megabyte of memory. The Intel 386 and above had address bus up to 32 bits allowing 4 Gigabytes of memory. But the old 8086 processors did not have such a big address bus. To keep in compatible with the older processors and solve the problem the Intel introduced a logical OR gate at the 20 bit of the address bus which could be enabled a or disabled. So, to keep in compatible with the older processors and programs the A20 is disabled at the startup.
当ibm pc at系统被制造出来时,新的286处理器以及以后版本和旧有x86处理器并不兼容。旧的X86处理器(Intel 8086)有20位地址总线,这样可以访问最高1M内存。而386和以后版本有32地址总线,这样可以访问最高4G的内存。但是旧的8086处理器没有这么大的地址总线。为了保持兼容性Intel在地址线的第20位上制造了一个逻辑OR门,以便可以开启或关闭超过20位的地址总线。这样,为了兼容旧的处理器,在机器开启时A20被禁止的。
NOTE:BIOS
actually enables the A20 for counting and testing the available
memory and then disables it before booting again to keep in
compatible with older processors.
注意:BIOS在计算和测试可用内存时实际上是开启了A20线的,然后在启动系统之前又关闭了它以保持兼容旧的处理器。
The
A20 gate is an electronic OR gate which can be disabled and enabled
and placed at the 20th bit of the address bus. It is connected
through a P21 line of the keyboard controller which made it possible
for the keyboard controller to enable or disable the A20 Gate.
A20线是一个OR逻辑电路门,被放置在第20位的地址总线上,而且可以开启或关闭。这是通过键盘控制器的P21线来完成的,这样,通过键盘控制器可以开启开关闭A20线。
In
the current generations, there is a need to have memory lot more than
just 1MB. The applications, games,etc need a lot of memory. Even a
operating system kernel might eat up the whole 1MB. So it is
something like impossible to run modern programs in 1MB of memory.
Looks like the A20 is an important feature for good functional of the
operating system.
在当前情况下,多于1M的内存是必须的。各种应用,游戏或者其他程序等都需要更多的内存。甚至一个操作系统的内核都可能会用尽整整1M的内存。所以在1M内存以内运行一个当下的程序有点怪。看起来对于一个功能良好的操作系统A20线就象是一个重要的特性。
To
enable the A20 gate there are 3 methods or you can skip this step by
using the high memory managers such as HIMEM.sys or using bootloaders
such as GRUB(GRUB will set up you up with protected mode with A20
enabled)
开启A20线有三种方法。或者你可以通过使用象HIMEM.sys或者象GRUB这样的bootloaders来跳过这些步骤(GRUB会设置你的机器开启A20线并进入保护模式)。
The
3 methods for enabling the A20 Gate are
开启A20线的三种方法:
1.
Keyboard Controller
1、通过键盘控制器
2.
BIOS Function
2、调用BIOS功能
3.
System Port
使用系统端口
===============================================================================================
Keyboard
Controller:
键盘控制器:
This
is the most common method of enabling A20 Gate. The keyboard
micro-controller provides functions for disabling and enabling A20.
Before enabling A20 we need to disable interrupts to prevent our
kernel from getting messed up. The port 0x64 is used to send the
command byte.
这是开启A20线通常的作法。键盘的微控制器提供了一个关闭或开启A20线的功能。在开启A20线之前需要关闭中断以防止我们的内核陷入混乱。命令字节是通过端口0x64来发送的。
Command
Bytes and ports
命令字和端口
0xDD
Enable A20 Address Line
0xDD可以开启A20线
0xDF
Disable A20 Address Line
0xDF关闭A20线
0x64
Port of the 8042 micro-controller for sending commands
8042微控制器的0x64端口用来发送命令
Using
the keyboard to enable A20:
使用键盘来开启A20线:
EnableA20_KB: cli ;Disables interrupts #关中断 push ax ;Saves AX #保存AX寄存器 mov al, 0xdd ;Look at the command list #开启命令 out 0x64, al ;Command Register #将命令发送到0x64端口 pop ax ;Restore's AX #恢复AX寄存器 sti ;Enables interrupts #开中断 ret
Using the BIOS functions to enable the A20 Gate:
使用BIOS功能开启A20线:
The
INT 15 2400,2401,2402 are used to disable,enable,return status of the
A20 Gate respectively.
INT
15的2400,2401,2402命令被用来关闭,开启和返回A20线状态。
Return
status of the commands 2400 and 2401(Disabling,Enabling)
2400和2401(关闭、开启)命令返回状态:
CF
= clear if success #如果成功则清空
AH
= 0 #
CF
= set on error #如果被设置则表明发生了错误
AH
= status (01=keyboard controller is in secure mode, 0x86=function not
supported)
#AH中保存的是状态码,01=键盘控制器在安全模式,0x86=功能不被支持
Return
Status of the command 2402
2042命令返回状态
CF
= clear if success #如果成功则清空
AH
= status (01: keyboard controller is in secure mode; 0x86: function
not supported)
#AH中保存的是状态码,01=键盘控制器在安全模式,0x86=功能不被支持
AL
= current state (00: disabled, 01: enabled)
#AL表示A20线的当前状态,00=关闭,01=开启
CX
= set to 0xffff is keyboard controller is no ready in 0xc000 read
attempts
#CX值如果是0xffff则表明键盘控制器在0xc000次尝试中均没准备好。
CF
= set on error #发生错误进CF会置位
Disabling
the A20
关闭A20线
push ax mov ax, 0x2400 int 0x15 pop ax
Enabling the A20
开启A20线:
push ax mov ax, 0x2401 int 0x15 pop ax
Checking A20
检查A20线
push ax push cx mov ax, 0x2402 int 0x15 pop cx pop ax
=====================================================================================
Using
System Port 0x92
使用系统0x92端口
This
method is quite dangerous because it may cause conflicts with some
hardware devices forcing the system to halt.
这个方法是十分危险的,因为它可以导致和其他硬件冲突并强制关机。
Port
0x92 Bits
0x92端口位功能
Bit
0 -
Setting to 1 causes a fast reset #为1时快速启动
Bit
1 -
0: disable A20, 1: enable A20 #为0时关闭A20线,为1时开启A20线
Bit
2 -
Manufacturer defined #工厂定义
Bit
3 -
power on password bytes. 0: accessible, 1:
inaccessible #口令开机,0表示使用,1表示禁止
Bits
4-5 -
Manufacturer defined #工厂定义
Bits
6-7 -
00: HDD activity LED off, 01 or any value is
"on" #为00时表示HDD访问LED关闭,其他值为开启。
Code
to enable A20 through port 0x92
通过0x92端口开启A20线的代码
push ax mov al, out 0x92, al pop ax
注:通过以上可以看出,0x92端口由于可能存在的风险,所以一般不使用。而通过BIOS功能来开启或关闭A20线会影响多个标志位,所以实际系统中多使用键盘控制器的方式来开启或关闭A20线。而实际工作的代码要比上面的例子复杂些,因为还要考虑到键盘缓冲区中是否有内容,键盘控制器是否已准备好等等情况。
从理论上讲,打开A20
Gate的方法是通过设置8042芯片输出端口(64h)的2nd-bit,但事实上,当你向8042芯片输出端口进行写操作的时候,在键盘缓冲区中,或许还有别的数据尚未处理,因此你必须首先处理这些数据。
所以,激活A20地址线的流程为:
1.禁止中断;
2.等待,直到8042
Input buffer为空为止;
3.发送Write
8042 Output Port命令到8042
Input buffer;
4.等待,直到8042
Input buffer为空为止;
5.向P2写入数据,将OR2置1。
下面是JOS系统和LINUX0.11中使用的开启A20线代码:
JOS中开启A20线的代码
seta20.: inb $0x64,%al #等待直到键盘不忙 取0x64端口状态值 testb $0x2,%al #测试第2位是否为0 jnz seta20. #测试指令,与运算。判断第二位是否为0,如为0,则代表8042是空的 movb $0xd1,%al # 0xd1 -> port 0x64 将0xd1放在al中 outb %al,$0x64 #将0xd1发送到0x64端口,D1的意思是向8042端口的P2写数据,这样,下面的命令才能有效 seta20.: inb $0x64,%al # Wait for not busy 取0x64端口状态值 testb $0x2,%al # jnz seta20. movb $0xdf,%al # 0xdf -> port 0x60 将0xdf放在al中 outb %al,$0x60 #将0xdf发送到0x60端口,DF为11011111,A20位置1,开通A20线
LINUX0.11中开启A20线代码:
call empty_8042 mov al,#0xD1 ! command write out #0x64,al call empty_8042 mov al,#0xDF ! A20 on out #0x60,al call empty_8042 empty_8042: .word 0x00eb,0x00eb in al,#0x64 ! status port test al,# ! is input buffer full? jnz empty_8042 ! yes - loop ret