产生的历史
16位实模式情况下,地址16位段基地址:16位偏移,这样最大地址表示值是FFFF:FFFF,也就是0x10ffef。
1)在8088时代,只有20条地址线,也就是寻址 0xFFFFF,这样能用线性地址表示出来的0x100000-0x10ffef的地址,在20根地址线表示不出来,通过求模,然后回环到,比如0x10ffef%0x100000=0xffef,也就是读写0x10ffef等于读写0xffef。
2)在80286时代,有24条地址线,硬件上可以支持0xffffff,实模式下支持地址最大范围0x10ffef,显然能表达出0x100000-0x10ffef。
关闭0x100000bit,则在0x100000-0x10ffef会回环。打开0x100000bit,在0x100000-0x10ffef不会回环。
通过打开/关闭0x100000bit,来判断0x100000-0x10ffef,是否回环。
A20与保护模式关系
1)不是打开A20,就进入保护模式系统。很多系统把A20是否打开,作为切换实模式和保护模式的一部分。
2)保护模式下,80286中A20关闭(始终为0),则用户的地址只能是:0 - (1MB-1), 2 - (3MB-1), 4 - (5MB-1)。
怎么判断A20在保护模式下是否打开
原理:如果A20Gate被打开了,则在实模式下,程序员可以直接访问100000H-10FFEFH之间的内存,如果A20 Gate被禁止,则在实模式下,若程序访问100000H-10FFEFH之间的内存,则会被硬件自动转换为0H-FFEFH之间的内存,所以我们可以利用这个差异来检测A20 Gate是否被打开。
#ifndef CONFIG_X86_VOYAGER
# This routine tests whether or not A20 is enabled. If so, it
# exits with zf = 0.
#
# The memory address used, 0x200, is the int $0x80 vector, which
# should be safe.
A20_TEST_ADDR = 4*0x80
a20_test:
pushw %cx
pushw %ax
xorw %cx, %cx
movw %cx, %fs # Low memory
decw %cx
movw %cx, %gs # High memory area
movw $A20_TEST_LOOPS, %cx
movw %fs:(A20_TEST_ADDR), %ax
pushw %ax
a20_test_wait:
incw %ax
movw %ax, %fs:(A20_TEST_ADDR)
call delay # Serialize and make delay constant
cmpw %gs:(A20_TEST_ADDR+0x10), %ax
loope a20_test_wait
popw %fs:(A20_TEST_ADDR)
popw %ax
popw %cx
ret
fs=0x gs=0xffff
fs:(A20_TEST_ADDR)=0x0:A20_TEST_ADDR=A20_TEST_ADDR
gs:(0x200+0x10)=0xffff0+0x10+0x200=0x100000+A20_TEST_ADDR
则(0x100000+A20_TEST_ADDR)%0x100000=A20_TEST_ADDR,所以地址0x100000+A20_TEST_ADDR回环后的地址是A20_TEST_ADDR.
则向 A20_TEST_ADDR写入一个值,则从0x100000+A20_TEST_ADDR读出也是这个值,则说明没有开启A20。
也就是cmpw %gs:(A20_TEST_ADDR+0x10), %ax,ZF=1说明没有开启A20;ZF=0说明开启了A20
控制A20的方式
1 通过键盘控制器
打开A20Gate的方法是通过设置8042芯片输出端口(64h)的2nd-bit.
Bit 0 is used to reset the CPU (go to real mode) - a reset happens when bit 0 is 0.
Bit 1 is used to control A20 - it is enabled when bit 1 is 1, disabled when bit 1 is 0.
2 通过系统控制器Port A(0x92)
Bit 0 (w): writing 1 to this bit causes a fast reset (used to switch back to real mode; for MCA this took 13.4 ms).
Bit 1 (rw): 0: disable A20, 1: enable A20.
使用这个口来打开A20的好处
1)键盘控制器的响应慢
2)嵌入式系统中没有键盘控制器
inb $0x92, %al #
orb $02, %al # "fast A20" version
outb %al, $0x92 # some chips have only this
这样会带来一个问题,端口0x92读出的值的Bit 0是1的话,然后写进端口0x92,会导致一个快速启动。
a20_fast:
inb $0x92, %al # Configuration Port A
orb $0x02, %al # "fast A20" version
andb $0xFE, %al # don't accidentally reset
outb %al, $0x92
多添加一句andb $0xFE, %al ,将bit 0位置位为0。
3 bois(int15 ax=240x)
INT 15 AX=2400 disable A20
INT 15 AX=2401 enable A20
INT 15 AX=2402 query status A20
INT 15 AX=2403 query A20 support (kbd or port 92)
bios例程不知道是用的哪个端口,还是其他技术 ?
setup.S(linux-2.6.11)中的A20逻辑
#ifndef CONFIG_X86_VOYAGER
a20_try_loop:
# First, see if we are on a system with no A20 gate.
a20_none:
call a20_test //0x90b45
jnz a20_done
# Next, try the BIOS (INT 0x15, AX=0x2401)
a20_bios:
movw $0x2401, %ax
pushfl # Be paranoid about flags
int $0x15
popfl
call a20_test
jnz a20_done
# Try enabling A20 through the keyboard controller
#endif /* CONFIG_X86_VOYAGER */
a20_kbc:
call empty_8042
#ifndef CONFIG_X86_VOYAGER
call a20_test # Just in case the BIOS worked
jnz a20_done # but had a delayed reaction.
#endif
movb $0xD1, %al # command write
outb %al, $0x64
call empty_8042
movb $0xDF, %al # A20 on
outb %al, $0x60
call empty_8042
#ifndef CONFIG_X86_VOYAGER
# Wait until a20 really *is* enabled; it can take a fair amount of
# time on certain systems; Toshiba Tecras are known to have this
# problem.
a20_kbc_wait:
xorw %cx, %cx
a20_kbc_wait_loop:
call a20_test
jnz a20_done
loop a20_kbc_wait_loop
# Final attempt: use "configuration port A"
a20_fast:
inb $0x92, %al # Configuration Port A
orb $0x02, %al # "fast A20" version
andb $0xFE, %al # don't accidentally reset
outb %al, $0x92
# Wait for configuration port A to take effect
a20_fast_wait:
xorw %cx, %cx
a20_fast_wait_loop:
call a20_test
jnz a20_done
loop a20_fast_wait_loop
# A20 is still not responding. Try frobbing it again.
#
decb (a20_tries)
jnz a20_try_loop
movw $a20_err_msg, %si
call prtstr
a20_die:
hlt
jmp a20_die
a20_tries:
.byte A20_ENABLE_LOOPS
a20_err_msg:
.ascii "linux: fatal error: A20 gate not responding!"
.byte 13, 10, 0
# If we get here, all is good
a20_done:
逻辑:
1.判断A20是否打开,如果打开直接跳转a20_done
2.尝试bois(int15 ax=240x)打开A20,是否成功,如果成功则跳转到a20_done
3.尝试键盘控制器打开A20,是否成功,如果成功则跳转到a20_done
4.尝试系统控制器Port A(0x92),是否成功,如果成功则跳转到a20_done