setup.S之A20

产生的历史

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

猜你喜欢

转载自blog.csdn.net/chengbeng1745/article/details/82964590