A20地址线
来源:互联网 发布:java定义长整型 编辑:程序博客网 时间:2024/04/30 01:56
void go_to_protected_mode(void){/* Hook before leaving real mode, also disables interrupts */realmode_switch_hook();/* Move the kernel/setup to their final resting places */move_kernel_around();/* 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));}上面是内核从实模式进入保护模式的代码,其中调用enable_a20()函数以打开A20地址线,A20地址线是什么呢?这是本文的重点。先了解一些历史。
8086/8088 PC中,地址总线只有20根,最大的寻址能力只有1M,在实模式下,内存寻址采用16位段和偏移量的组合来寻址(注意这里的最大寻址空间:FFFF0+FFFF=10FFEF),因为地址线只有20,所以如果寻址地址在100000以上时,就采用取模的方式,返回地址。到了80286的时候,地址总线变为24根,这样最大的寻址能力就有16M了,如果此时向下兼容,就会有一个问题:寻址在100000以上时,实际的物理内存是存在的,因为内存寻址最大都可以到16M,但在8086时,从0开始(取模)寻址。
如下图
为了解决这个问题,用第21根线(就是A20)来控制是否允许对100000以上的实际内存就行寻址。称为A20 Gate
- 如果A20 Gate开启,那么对0x100000-0x10FFEF之间的地址进行寻址时,系统访问实际的地址对应的物理内存。
- 如果A20 Gate被禁用,则对0x100000-0x10FFEF之间的地址进行寻址时,还是取模,从0开始访问内存。
int enable_a20(void){int loops = A20_ENABLE_LOOPS;#if defined(CONFIG_X86_ELAN)/* Elan croaks if we try to touch the KBC */enable_a20_fast();while (!a20_test_long());return 0;#elif defined(CONFIG_X86_VOYAGER)/* On Voyager, a20_test() is unsafe? */enable_a20_kbc();return 0;#elsewhile (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 */empty_8042();if (a20_test_short())return 0; /* BIOS worked, but with delayed reaction */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;#endif}这个函数具体的就不分析了,引用一个高人分析的结果:
如果A20 Gate被打开了,则在实模式下,程序员可以直接访问100000H~10FFEFH之间的内存,如果A20 Gate被禁止,则在实模式下,若程序员访问100000H~10FFEFH之间的内存,则会被硬件自动转换为0H~0FFEFH之间的内存,所以我a20_test函数就是利用这个差异来检测A20 Gate是否被打开。
首先,61、62行,把fs和gs的值分别设置为0x0000和0xffff。然后64行调用rdfs32函数得到0000: 4*0x80的内容并存放到32位的临时变量saved和ctr中。4*0x80=0x200,所以saved和ctr中存放的是内存地址0x200处的值。对应内核映像解压缩后的内存布局,我们知道这个地址属于BIOS的一些数据存放的区域,虽然我不知道这个地址到底是存的什么数据,但是可以肯定的是,这个数据可以随意修改,来做我们的A20测试的。
67行进入循环,使ctr加1,具体等于多少不知道,把这个32位的值写入0000: 4*0x80对应的内存单元中。69行,“^”是按位异或运算符,即如果ffff: 4*0x80+0x10内存单元存放的值与ctr相等,则说明有可能刚刚写入的ctr其实是写入的0000: 4*0x80内存单元。注意,ffff: 4*0x80+0x10换算实模式地址就是ffff0+4*0x80+0x10=0x100200,不过也不排除偶然的情况,这两个内存单元相等。所以继续循环,修改一下ctr的值,多试几次。
74行,跳出循环后,你刚才给人家0x200的地址对应的内存修改了数据,得给人家改回去啊,最后返回ok。如果执行了loops次这个ok都是0,就说明A20肯定没有打开。
回到enable_a20函数,如果bootloader没有已经关闭了A20的话(grub是肯定关闭了的),也就是第一个a20_test_short()没有成功,试试142行enable_a20_bios():
91static void enable_a20_bios(void)
92{
93 struct biosregs ireg;
94
95 initregs(&ireg);
96 ireg.ax = 0x2401;
97 intcall(0x15, &ireg, NULL);
98}
嗯,没问题,调用BIOS的15号服务程序,打开A20。如果没成功,执行147行代码:
18#define MAX_8042_LOOPS 100000
19#define MAX_8042_FF 32
20
21static int empty_8042(void)
22{
23 u8 status;
24 int loops = MAX_8042_LOOPS;
25 int ffs = MAX_8042_FF;
26
27 while (loops--) {
28 io_delay();
29
30 status = inb(0x64);
31 if (status == 0xff) {
32 /* FF is a plausible, but very unlikely status */
33 if (!--ffs)
34 return -1; /* Assume no KBC present */
35 }
36 if (status & 1) {
37 /* Read and discard input data */
38 io_delay();
39 (void)inb(0x60);
40 } else if (!(status & 2)) {
41 /* Buffers empty, finished! */
42 return 0;
43 }
44 }
45
46 return -1;
47}
0x64号端口号对应的是键盘控制器(keyboard controller, KBC)的状态寄存器,首先30行获得该控制器的状态值,保存到内部变量status中。status不能为0xff,否则出错返回。其次,status的最低位D0如果被设置,则说明键盘缓存中还有数据,则通过inb(0x60)从键盘缓存对应的端口读出来,并且置空。
介绍一下键盘控制器:主板的一个芯片。通过LPC总线和南桥相连。一般作用:键盘控制、LCD明暗度的调节、低级电源的管理、风扇、蓝牙等一些小功能。
注意这个逻辑关系,如果键盘缓存中没有内容,并且status的次低位D1不为1,则说明键盘缓存是空的,这时候empty_8042返回0,enable_a20再一次检测一下BIOS然后来到了153行,调用enable_a20_kbc()函数,利用键盘控制器来尝试打开A20:
100static void enable_a20_kbc(void)
101{
102 empty_8042();
103
104 outb(0xd1, 0x64); /* Command write */
105 empty_8042();
106
107 outb(0xdf, 0x60); /* A20 on */
108 empty_8042();
109
110 outb(0xff, 0x64); /* Null command, but UHCI wants it */
111 empty_8042();
112}
如果还打不开A20,没辙了,就来到enable_a20()的159行进行最后的努力,调用enable_a20_fast()函数。
114static void enable_a20_fast(void)
115{
116 u8 port_a;
117
118 port_a = inb(0x92); /* Configuration port A */
119 port_a |= 0x02; /* Enable A20 */
120 port_a &= ~0x01; /* Do not reset machine */
121 outb(port_a, 0x92);
122}
注意这个enable_a20_fast,基于x86的主板都提供0x92端口来作为一个主板控制寄存器,所以,如果这个函数执行后都还是打不开A20的话,那也就没辙了,只好放弃。
- 关于A20地址线
- A20地址线问题
- 关于A20地址线
- 打开A20地址线
- 关于A20地址线
- A20地址线
- A20地址线问题
- A20地址线
- A20地址线
- intel A20地址线
- A20地址线问题
- A20地址线问题
- A20地址线
- A20地址线问题
- 地址回绕及A20地址线
- 地址环绕和A20地址线问题
- 地址环绕和A20地址线问题
- 地址回绕及A20地址线
- 转载自风宇冲Unity3D教程学院 ----shader分类
- Oracle count(1) count(*) count(col)
- CSS浏览器兼容问题集
- jsp文件上传简单实例
- Linux 内核剖析史和体系结构分析
- A20地址线
- 最新的智能移动终端ios,android等市场占有率情况
- hdu1722 cake
- 部分常用的正则表达式
- qt for android
- ORACLE RAC的crs命令
- Eclipse快捷键
- Android应用程序快速启动的秘诀
- mongodb 记录