Last updated
Last updated
为何开启A20,以及如何开启A20
如何初始化GDT表
如何使能和进入保护模式
A20是什么?
A20为处理器的第21根地址线。A即Address。A0是第一根地址线,以此类推,A19是第20根地址线,A20是第21根地址线。
开启A20是什么?
开启A20指的是开启A20 Gate。
A20 Gate是80286处理器为了兼容8086处理器的程序而设置的,一个控制第21根地址线(A20 Line)的与门。
为什么要有A20 Gate?
8086处理器有20根地址线(A0 ~ A19),可以寻址1MB内存。当物理地址达到0xFFFFF
(11111111111111111111B
)时,再加1,由于只能维持20位地址,进位会丢失导致地址变为0x00000
(00000000000000000000B
)。
而80286处理器有24根地址线(A0 ~ A23),可以寻址16M内存。当物理地址达到0x0FFFFF
(000011111111111111111111B
),再加1,得到的地址为0x100000
(000100000000000000000000
)。若运行8086处理器的程序,会导致寻址出现错误。
为了让80286处理器兼容8086处理器的程序,IBM在主板上添加了A20 Gate,当A20 Gate关闭(值为0)时,在与门的操作下,A20 Line的值始终为0,所以当0x(0)FFFFF
[(0000)11111111111111111111B
]增加1时80286处理器可以得到跟8086处理器一样的结果0x(0)00000
[(0000)00000000000000000000
]。
如何开启A20 Gate?
A20 Gate的控制阀门在8042键盘控制器(8042芯片)内,为输出端口P2的第二个bit(P21引脚)。
为开启A20 Gate,只需向P2端口写入第二个bit为1的一个字节即可。
为何开启A20?
为了使CPU可以正常访问高于16位的地址,为进入可寻址32位的保护模式做准备。
如何开启A20?
以下为开启A20的代码:
8042芯片的I/O端口为0x60~0x6f,实际上IBM PC/AT使用的只有0x60和0x64两个端口。
8042芯片有四个8 bit长的寄存器:Input Buffer, Output Buffer, Status Register, Control Register。
为向输出端口P2写入数据,需要先将要写入的数据传入8042芯片的Output Buffer,然后让8042芯片会将Output Buffer的内容写入输出端口P2。
向Output Buffer写入数据的流程为:向0x64端口发送0xd1,表示写入,然后再将要写入的数据传入0x60端口。
在向Output Buffer写入数据前,需要先读取Status Register的数据已获取芯片状态,得到的结果第二个bit表示0x60和0x64端口是否有数据,若无数据才可正常写入。
以下为具体步骤:
首先,从0x64端口读入数据存入al,将al的值和0x02(00000010B)进行与操作,若得到结果不为0,即0x60和0x64端口内有数据,则跳转回标记seta20.1
并重复这一操作,直到端口内无数据,则进行下一步。
然后将0xd1
经al写入0x64端口,表示将要向0x60端口传入要写进输出端口P2的数据。
这里与seta20.1
部分类似,从0x64端口获取状态,直到0x60和0x64端口内没有数据,即可执行下一部分。
然后经al向0x60端口写入数据以传入Output Buffer,然后8042芯片自动将Output Buffer中的数据写入输出端口P2。
在这段代码中写入0x60端口的数据为0xdf
(11011111B
),其第二个bit为1,即表示开启A20 Gate。另外7个bit表示其它参数,这里不再做了解。
《x86汇编语言:从实模式到保护模式》,电子工业出版社;
什么是GDT表
Global Descriptor Table(全局描述符表)是IA32架构CPU的一种数据结构,它保存多个段描述符,其起始地址保存在全局描述符表寄存器(GDTR)中。GDTR长48位,其中高32位为基地址,低16位为段界限。
其中,全局描述符表中第一个段描述符设定为空段描述符。GDTR中的段界限以字节为单位。对于含有N个描述符的描述符表的段界限通常可设为8*N-1。
GDT表由汇编指令lgdt
加载,该指令将GDT表地址加载到GDTR中。
GDT表的作用
在保护模式下,大多数内存管理和中断服务例程都是通过GDT表控制的。每个描述符(Descriptor)都存储着CPU可能需要的对象信息。
GDT表存储的内容
空描述符(不会被加载)
一个代码段描述符(在内核中,其类型应设为0x9A)
一个数据段描述符(其类型应设为0x92)
一个TSS(Task State Segment 任务段描述符)(uCore中没有使用)
...
如何初始化GDT表
gdtdesc
即GDT表的地址(见下文),使用lgdt
指令加载即可。
uCore中GDT表的格式
gdt
为存储的两个段描述符(以及之前的一个空的段描述符),gdtdesc
为GDT表的段界限及段描述符(gdt
)的起始地址。两个段描述符分别为:代码段描述符,数据段描述符。
以下为代码中创建段描述符的宏以及其参数。
由以上代码可知,代码描述符的类型(type)值为(0x90 | (STA_X|STA_R))
即(0x90 | 0x0A) = 0x9A
,数据段描述符的类型(type)值为(0x90 | STA_W)
即(0x90 | 0x02) = 0x92
,与文档描述一致。
什么是保护模式
保护模式与实模式相对应,是在80286处理器之后现代Intel处理器主要的操作模式。在80386处理器或更高版本上,32位保护模式允许使用多个虚拟空间,每个虚拟空间最多具有4GB的可寻址内存,并能通过Rings限制可用的指令集使系统能够执行严格的内存和硬件I/O保护。
如何进入保护模式
在进入保护模式前,需要进行一系列操作:
禁用所有中断(执行cli
命令)
启用A20 Line
使用适用于代码、数据和堆栈的段描述符加载GDT表
做完准备工作后,就可以切换到保护模式了。
控制实模式与保护模式切换的开关位于CR0寄存器。CR0是处理器内部的控制寄存器(Control Register, CR),它是32位的寄存器,包含了一系列用于控制处理器操作模式和运行状态的标志位,它的第1位是保护模式允许位,把该位置设为1后处理器就会进入保护模式。
由于CR0是32位寄存器,所以我们需要先将其值存入EAX寄存器。EAX寄存器是一个32位寄存器,常用的AX寄存器是EAX的低16位。
在存入数据后,对EAX的值与常量CR0_PE_ON
进行位或操作,常量CR0_PE_ON
的值为0x1
,进行位或操作后可使EAX内数值的第1位变为1,即 将CR0状态标志位的保护模式允许设为打开。
然后将EAX寄存器内的值传入CR0,这时CR0内第1位的值变成了1,其它位的值也没有发生变化,这就标志着保护模式的正常启动。
《x86汇编语言:从实模式到保护模式》,电子工业出版社;
;
;