A20门

了解一下A20历史

    每台 PC 的基本问题是其硬件与所有以前的硬件兼容。这就是为什么每台现代 PC 都包含甚至获得专利的古老技术成就和奇思妙想。其中就有A20门,在Itanium、Opteron甚至HyperTransport I/O协议中也没有死。A20 门是 8086 处理器时代的产物。它对于今天与 DOS 程序的兼容性仍然是必不可少的。

   A20 门是提供与 intel 8088 地址空间兼容性的门。

  有了 PC2001 硬件规范,英特尔和微软公司决定结束 A20 门。然而,到目前为止,这尚未实施(截至 2011 年)。处理器制造商 Intel 和 AMD 在其处理器与主板的连接中仍然有 A20 门。在Intel Core i7的Nehalem架构中,依然支持A20切换。
    问题的根源是微软的 MS-DOS 操作系统。  在实模式下,这确保地址总线只有 20 条地址线 (a0-a19) 处于活动状态。因此,x86 系列的处理器只能寻址 1 MB 的主内存

   这里解释下,为什么总是提到20条总线
 

  要记住的关键是 CPU 对它所连接的东西一无所知。它通过引脚(pin)与外部世界对话, 但它不关心外部世界是什么。它可能是计算机中的主板,但也可能是烤面包机、网络路由器、大脑植入物或 CPU 测试台。CPU与外界通信主要有三种方式:内存地址空间、I/O地址空间和中断。我们现在只担心主板和内存。

   在主板中,CPU 通往世界的门户是将它连接到北桥的前端总线。现代cpu已把南桥芯片组纳入到里面,每当 CPU 需要读取或写入内存时,它都会通过北桥的前端总线进行。它使用一些引脚来传输它要写入或读取的物理内存地址(地址总线负责),而其他引脚则发送要写入的值或接收要读取的值(数据总线负责)。举例Intel Core 2 QX6600 有 33 个引脚用于传输物理内存地址(因此有 2^33^ 个内存位置选择)和 64 个引脚用于发送或接收数据(因此数据在 64 位数据路径中传输,或 8 -字节块)。这允许 CPU 物理寻址 64 GB 的内存(2^33^ 位置 * 8 字节),尽管大多数芯片组最多只能处理 8 GB 的 RAM。那么20条总线就是20个引脚来用于传输物理内存地址,也就是有2^20个内存地址可以选择(0/1组合)

    

   1984年PC取得突破,IBM推出采用Intel 80286处理器的AT时,所有已经在使用的DOS程序也应该能正常运行。因此,IBM 工程师以小门 (74F257 多路复用器,三态) 的形式发明了 A20 门,可在需要时将 A20 地址线设置为零。因为这只是一个短期的解决方案,即临时措施,所以没有使用新的端口进行切换。相反,在 8042 键盘控制器中选择了一个空闲端口。但是,这有一个繁琐的协议,经过漫长的等待。该地址线的附加门将地址的有效性延迟了几纳秒。真正不必要的速度损失。省略了BIOS中的软件接口和查询A20门状态的功能。直到很久以后,当IBM和BIOS制造商AMI认识到其不可或缺性时,随后才提交了相应的BIOS功能。
在保护模式下,80286 的处理器能够寻址超过 1 MByte 的内存区域。在 DOS 下,HIMEM.SYS 文件负责启用地址行 20 (A20)。该文件试图识别多达 17 种不同的计算机类型以切换线路,但并不总是成功。有时它只是起作用,有时却不起作用。Windows 用户也一次次遇到 A20 门。特别是当系统管理模式 (SMM) 或 ACPI 出现问题时。

    根据目前的技术状况,A20 gate 和当前处理器对它的支持是不必要的,除非你想用旧的 DOS 启动计算机。如果没有 A20 门支持,则会出现来自 himem.sys 文件的错误消息。但也有 DOS 变体,例如 FreeDOS,可以在没有 A20 门的情况下使用而没有任何问题
    IBM PC架构 A20门

   控制 A20 系列是 IBM PC 架构发展的一个阶段的一个重要特征,因为它在实模式下增加了对额外 65,520 字节(64 KB − 16 字节)内存的访问,而无需对软件进行重大更改

   在可以说是“hack”的情况下,A20 门最初是主板上键盘控制器的一部分,它可以根据需要的行为打开或关闭它。

   A20 门仍然存在于许多现代 PC 上,并且门在启动后立即关闭。现代保护模式操作系统通常会在引导过程的早期打开 A20 门,并且再也不会关闭它。此类操作系统没有关闭它的兼容性原因,它们可以通过打开它来访问所有可用的物理地址。

Intel 80486和Pentium

   添加了一个A20M#的特殊引脚,当它断言为低电平时,对于所有片上高速缓存或外部存储器访问,强制物理地址的第 20 位为零。这是必要的,因为 80486 引入了片上高速缓存,因此不再可能在外部逻辑中屏蔽该位。软件仍然需要操纵门并且仍然必须为此处理外部外围设备

 在Nehalem 微架构
       它
更改了对 A20 门的支持(一些消息来源错误地声称 A20 支持已被删除)。CPU 没有专门的 A20M# 引脚来接收是否屏蔽 A20 位的信号,而是对其进行了虚拟化,以便使用特殊的总线周期将信息从外围硬件发送到 CPU。从软件的角度来看,该机制与以前完全一样,操作系统仍必须对外部硬件(反过来将上述总线周期发送到 CPU)进行编程以禁用 A20 掩码。

   Intel 不再支持 A20 门,从Haswell开始。英特尔系统程序员手册第 271 页。2013 年 6 月的 3A 指出:“A20M# 的功能主要由较旧的操作系统使用,现代操作系统不使用。在较新的 Intel 64 处理器上,A20M# 可能不存在

一、A20 gate

    当 IBM PC AT(一款pc机,其它系列还有PC, XT等)  1984年推出时,新的英特尔 286 处理器与旧的 x86 处理器不兼容。
     

   IBM个人计算机 XT(通常简称为 IBM XTPC XT或简称XT)是 IBM 对原始 IBM PC的继承者。它于 1983 年 3 月 8 日作为 IBM 产品编号5160发布,并标配 硬盘驱动器它基于与原始 PC 基本相同的架构,只是进行了渐进式改进; AT中将采用新的 16 位总线架构 XT主要是作为商务用途的增强型机器,对应的 3270 PC具有 3270终端 仿真于 1983 年 10 月晚些时候发布。XT 代表X -tended T技术。

  标准XT原装128KB内存,360KB双面5.25" 全高 软驱,10MB希捷 ST-412 硬盘,Xebec 1210 MFM控制器,异步适配器( 8250 UART串口卡)和一个130W PSU。主板有八个 8 位 ISA扩展槽,以及一个 运行频率为 4.77 MHz 的 Intel 8088微处理器(带有一个用于 8087数学 协处理器的插槽);通常与它一起出售的操作系统是 PC-DOS2.0及以上。八个扩展槽比 IBM PC 中的五个增加了,尽管软盘驱动器适配器、硬盘驱动器适配器和异步卡占用了三个。基本规格很快升级为标配 256k 内存。XT 主板上的插槽 8 的接线与其他插槽略有不同,使其与某些卡不兼容。这是为允许 XT 连接到 IBM 大型机而设计的卡完成的。视频卡最初包括 MDA和 CGA, EGA和 PGC于 1984 年上市。

  XT 主板有两种配置。第一个可以在主板本身(四排 64kB 芯片)上支持高达 256k,通过使用扩展卡可以达到最大 640k。这是 XT 最初的配置。第二种配置 - 1986 年在库存单位推出 - 可以支持主板上的整个 640k(两组 256kb 芯片,两组 64kB),具有后来修订的 AT 兼容 BIOS具有更快的启动时间,以及对 101 键键盘和 3.5" 软盘驱动器的支持。经过一些小的修改后,早期的配置可以适应“后期”配置。

主板也有两个版本,但它们之间只有细微差别。最值得注意的是,第一次修订缺少 U90,并且某些部件位于主板上的另一个位置。

从 1985 年开始,XT 以没有硬盘的纯软盘型号提供。带有 256k-640k 主板的 XTs 标配半高软盘驱动器代替全高驱动器,以及 20MB 半高硬盘和“增强型”键盘的选项(基本上是没有 LED 的 M 型面板和一个 5 针 AT 型连接器)。XT 于 1987 年春季停产,取而代之的是 PS/2 Model 30。

1986 年推出了配备 6 MHz Intel 80286处理器的XT/286 (IBM 5162) 。该系统实际上比当时使用 8 MHz 286 处理器的 AT 更快,因为它具有零 等待状态 RAM,可以更快地移动数据。

与最初的 PC 一样,XT 的 ROM 中带有 BASIC。尽管 XT 上没有盒式端口,IBM 与 Microsoft 的许可协议迫使他们在所有机器上都包含 BASIC。

PC 和 XT 键盘与更现代的 PC(IBM AT 或更高版本)上的键盘不兼容,即使使用 DIN到 PS/2 mini-DIN插头适配器也是如此,因为 PC/XT 键盘与 PC/AT 键盘具有不同的传输协议并且还使用不同的键盘 扫描码一些键盘可在两个接口之间切换,以与任何一台计算机兼容。此外,'parkbd' 驱动程序可以在 Linux 下使用,以便通过一个简单的适配器通过并行端口支持任何一种键盘。
来自: http://jupiter.plymouth.edu/~tom/forms/IBM_XT.htm

  AT 与IBM PC 兼容,最显着的区别是从先前型号的 8088 处理器转移到 80286 处理器。与 IBM PC 一样,AT 支持可选的数学协处理器芯片Intel 80287,以更快地执行浮点运算

此外,它还引入了AT 总线,后来称为 ISA 总线,这是一种 16 位总线,可向后兼容 8 位 PC 兼容扩展卡。该总线还提供了 15 个IRQ和 7 个DMA通道,从 PC 的 8 个 IRQ 和 4 个 DMA 通道扩展而来,通过添加另一个8259A IRQ 控制器和另一个8237A DMA 控制器实现。[5] [6]一些 IRQ 和 DMA 通道被主板使用,并没有暴露在扩展总线上。双 IRQ 和 DMA 芯片组都是级联的,它们共享主对。除了这些芯片组,Intel 82284 Clock Driver and Ready Interface 和 Intel 82288 Bus Controller 也支持微处理器。

286 的 24 位地址总线将 RAM 容量扩展到 16  MB

https://en.wikipedia.org/wiki/IBM_Personal_Computer_AT


    1、旧的 x86 微处理器(英特尔 8086和8088)的地址总线为 20 位,总共可以访问 1 M字节的内存,最大地址 FFFF:FFFF 地址为 0x10ffef,这将自动换行到 0x0ffef。
     2、286(有24条地址线)推出时,它有一个实模式,目的是与8088 100%兼容。
    3、Intel 386 及更高版本的地址总线高达 32 位,允许 4 GB 的内存。
     但是旧的 8086/8088 处理器没有这么大的地址总线。为了与旧处理器兼容并解决问题,Intel 引入了逻辑或门在可以启用或禁用的地址总线的 20 位。因此,为了与旧处理器和程序兼容,A20 在启动时被禁用。 

注意:BIOS 实际上启用 A20 来计算和测试可用内存,然后在再次启动之前禁用它以保持与旧处理器的兼容。

   E:\linux内核\0.0.1\boot\boot.s
;that was painless, now we enable 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
 
  A20 门是一个电子或门,可以被禁用和启用,位于地址总线的第 20 bits。它通过键盘控制器的 P21 线连接,使键盘控制器可以启用或禁用 A20门。

   为了比较感性的认识P21,看下面一张图

 

        74257 IC四路2输入多路复用器,带3状态输出2对1多路复用器.

        

   在现代,需要的内存不仅仅是 1MB。应用程序、游戏等需要大量内存。即使是操作系统内核也可能吃掉整个 1MB。所以它几乎不可能运行1MB 内存中的现代程序。看起来 A20 是操作系统良好功能的重要特征。

二、要启用 A20 门,有 3 种方法
      或者您可以使用高级内存管理器(例如 HIMEM.sys)或使用引导加载程序(例如 GRUB)跳过此步骤(GRUB 将在启用 A20 的情况下为您设置保护模式)这 3 种方法


    用于启用A20 Gate的有
   1.键盘控制器(Keyboard Controller)
   2.BIOS功能( BIOS Function)
   3.系统端口(System Port)

1)键盘控制器(Keyboard Controller)
   这是启用 A20 门的最常见方法。键盘微控制器提供禁用和启用 A20 的功能。在启用 A20 之前,我们需要禁用中断以防止我们的内核被搞砸了。

  命令字节和端口
   0xDD Enable A20 Address Line
   0xDF Disable A20 Address Line 

 8042微控制器的0x64端口 用于发送命令

下面是bochs中关于设置启用a20门的代码
E:\bochs\1.1\bios\rombios.c 
 Boolean set_enable_a20(val) Boolean val;
{
  Boolean oldval;
  Bit8u  temp8;

  // Use keyboard conroller to set A20 enable

  // get current Output Port settings first
  if ( (inb(0x64) & 0x02) != 0 )
    panic("set_a20(1): ctrl busy\n");
  outb(0x64, 0xd0); // send Read Output Port Command
  if ( (inb(0x64) & 0x01) != 1 )
    panic("set_a20(2): ctrl busy\n");
  temp8 = inb(0x60);
  // store old value for return
  oldval = (temp8 >> 1) & 0x01;
  // change A20 status in Output Port settings
  if (val)
    temp8 |= 0x02;
  else
    temp8 &= 0xfd;
  // write new Output Port back
  if ( (inb(0x64) & 0x02) != 0 )
    panic("set_a20(3): ctrl busy\n");
  outb(0x64, 0xd1); // send Write Output Port Command
  if ( (inb(0x64) & 0x02) != 0 )
    panic("set_a20(4): ctrl busy\n");
  outb(0x60, temp8);
  return(oldval);
}
使用键盘控制器开启 A20汇编代码:
cli                ;Disables interrupts
push    ax         ;Saves AX
mov al, 0xdd  ;Look at the command list 
out 0x64, al   ;Command Register 
pop ax          ;Restore's AX
sti                ;Enables interrupts
ret 

 

 

2)使用BIOS函数使能A20 Gate:
 很多当前的bios版本实现为 int15 ax= 2400/2401/2402 分别用于关闭、启用和查询 A20 Gate的状态。
INT 15 AX=2400 //关闭A20
INT 15 AX=2401 //启用 A20
INT 15 AX=2402 //查询 status A20
INT 15 AX=2403 //查询 A20 支持情况 (kbd 或 端口 92)
命令 2400 和 2401(禁用、启用)的返回状态
Return  命令 2400 and 2401(关闭,启用)的状态
Return:
  If successful: CF clear, AH = 00h
  On error: CF set, AH = status
  Status: 01h keyboard controller is in secure mode
          86h function not supported
  For AX=2402 the status (0: disabled, 1: enabled) is returned in AL
  For AX=2403 the status (bit 0: kbd, bit 1: port 92) is returned in BX
CF = clear if success
AH = 0
CF = set on error
AH = status (01=keyboard controller is in secure mode, 0x86=function not supported)
 命令的返回状态 2402
CF = clear if success
AH = status 
01: keyboard controller 在安装模式;
0x86: 功能不支持 AL = current state
00: 表示关闭
01: 表示启用 CX = set to 0xffff is keyboard controller is no ready in 0xc000 read attempts CF = set on error

禁用 A20
push ax
mov ax, 0x2400 
int 0x15 
pop ax
启用 A20 
push ax
mov ax, 0x2401 
int 0x15 
pop ax
检查 A20
push ax
push cx
mov ax, 0x2402 
int 0x15 
pop cx
pop ax
 

3) 使用系统端口 0x92

    这种方法非常危险,因为它可能会导致与一些硬件设备迫使系统停止。
    Port  0x92 Bits (8个bit,每个bit都有固定的意义)
  • Bit 0 - Setting to 1 causes a fast reset 
  • Bit 1 - 0: disable A20, 1: enable A20
  • Bit 2 - Manufacturer defined
  • Bit 3 - power on password bytes. 0: accessible, 1: inaccessible
  • Bits 4-5 - Manufacturer defined
  • Bits 6-7 - 00: HDD activity LED off, 01 or any value is "on"
通过 端口 0x92 启用 A20 的汇编代码
push ax
mov al, 2 
out 0x92, al 
pop ax

为了更深刻理解A20门的作用,我们看下linux内核当中处理A20 gate的做法,
E:\linux内核\linux-2.6.38.5\arch\x86\boot\a20.c
...
static void enable_a20_fast(void)
{
    u8 port_a;
    port_a = inb(0x92); /* Configuration port A */
    port_a |=  0x02;    /* Enable A20 */
    port_a &= ~0x01;    /* Do not reset machine */
    outb(port_a, 0x92);  
}
int enable_a20(void)
{
       int loops = A20_ENABLE_LOOPS;
       int kbc_err;
       while (loops--) {
           /* First, check to see if A20 is already enabled
          (legacy free, etc.) */
           if (a20_test_short())
               return 0;
           /* Next, try the BIOS (INT 0x15, AX=0x2401) */
           enable_a20_bios();
           if (a20_test_short())
               return 0;
           /* Try enabling A20 through the keyboard controller */
           kbc_err = empty_8042();

           if (a20_test_short())
               return 0; /* BIOS worked, but with delayed reaction */
    
           if (!kbc_err) {
               enable_a20_kbc();
               if (a20_test_long())
                   return 0;
           }
           
           /* Finally, try enabling the "fast A20 gate" */
           enable_a20_fast();
           if (a20_test_long())
               return 0;
       }
       
       return -1;
}
/* * Actual invocation sequence 进入保护模式 */ void go_to_protected_mode(void) { /* Hook before leaving real mode, also disables interrupts,需要关闭NMI(不可屏蔽中断) */ realmode_switch_hook(); //进入实模式 /* Enable the A20 gate */ if (enable_a20()) { puts("A20 gate not responding, unable to boot...\n"); die(); } /* Reset coprocessor (IGNNE#) */ reset_coprocessor(); /* Mask all interrupts in the PIC */ mask_all_interrupts(); /* Actual transition to protected mode... */ setup_idt(); setup_gdt();
//进入保护模式 protected_mode_jump(boot_params.hdr.code32_start, (u32)&boot_params + (ds() << 4)); }
/* * Set up the IDT 安装IDT(进程用到的本地描述表) */ static void setup_idt(void) { static const struct gdt_ptr null_idt = {0, 0}; asm volatile("lidtl %0" : : "m" (null_idt)); }
//安装gdt(全局描述表,全局描述表中的条目即段描述符,说的就是某一内存地址范围,多个地址范围构成gdt) static void setup_gdt(void) { /* There are machines which are known to not boot with the GDT being 8-byte unaligned. Intel recommends 16 byte alignment. */ static const u64 boot_gdt[] __attribute__((aligned(16))) = { /* CS: code, read/execute, 4 GB, base 0 */ [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff), /* DS: data, read/write, 4 GB, base 0 */ [GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff), /* TSS: 32-bit tss, 104 bytes, base 4096 */ /* We only have a TSS here to keep Intel VT happy; we don't actually use it for anything. */ [GDT_ENTRY_BOOT_TSS] = GDT_ENTRY(0x0089, 4096, 103), }; /* Xen HVM incorrectly stores a pointer to the gdt_ptr, instead of the gdt_ptr contents. Thus, make it static so it will stay in memory, at least long enough that we switch to the proper kernel GDT. */ static struct gdt_ptr gdt; gdt.len = sizeof(boot_gdt)-1; gdt.ptr = (u32)&boot_gdt + (ds() << 4); asm volatile("lgdtl %0" : : "m" (gdt)); }

static void realmode_switch_hook(void) { if (boot_params.hdr.realmode_swtch) { asm volatile("lcallw *%0" : : "m" (boot_params.hdr.realmode_swtch)
: "eax", "ebx", "ecx", "edx"); } else { //readlmode_swtichhook 提供指向禁用不可屏蔽中断的 16 位实模式 far 子例程的指针。 //检查钩子(它对我来说不存在)后realmode_switch,会禁用不可屏蔽中断(NMI): asm volatile("cli"); //c语言内联汇编指令,其中包含cli清除中断标志(IF)的指令。此后,外部中断被禁用。
//禁用 NMI(不可屏蔽中断)。 outb(0x80, 0x70); /* Disable NMI, 0x70即cmos端口,将字节0x80(禁用位)写入0x70(CMOS 地址寄存器)*/ io_delay(); } } ...

小结:
      上面这段代码大体就是linux启动阶段即grub2阶段(启动内核前),要进入实模式,开启A20门,进行gdt、idt等的安装,然后转入保护模式,后续会转入长模式。
    如下图所示

 

 


https://en.wikipedia.org/wiki/A20_line
http://kernelx.weebly.com/a20-address-line.html
https://www.minuszerodegrees.net/5170/motherboard/5170_motherboard_diagrams.htm
https://www.elektronik-kompendium.de/sites/com/0811181.htm
https://www.win.tue.nl/~aeb/linux/kbd/A20.html
http://jupiter.plymouth.edu/~tom/forms/IBM_XT.htm
https://en.wikipedia.org/wiki/IBM_Personal_Computer_AT
https://www.elektronik-kompendium.de/sites/com/0811181.htm
https://sourceforge.net/p/bochs/code/HEAD/tree/branches/REL_1_1/bochs/bios/rombios.c
https://bochs.sourceforge.io/
https://mrhopehub.github.io/2014/12/26/enabling-the-A20-Gate.html
https://www.cnblogs.com/aozhejin/p/17170337.html 
https://www.dmtf.org/sites/default/files/standards/documents/DSP0134v2.5Final.pdf  bios规范
https://www.infineon.com/dgdl/Infineon-OTG-Host_BIOS_User_Manual-UserManual-v01_00-EN.pdf?fileId=8ac78c8c7d0d8da4017d0eee68b57d44  bios手册
posted @ 2023-04-09 18:51  jinzi  阅读(46)  评论(0编辑  收藏  举报