6)进入保护模式

打开 A20 地址线

在 8086 和 8088 处理器中,地址总线宽度为 20 位,这意味着这些处理器最多可以寻址 1MB 的内存(2^20 = 1,048,576 字节)。地址总线的最高位是 A19,因此地址空间的范围是 0x00000 到 0xFFFFF。

当 IBM PC 引入了 80286 处理器时,它支持 24 位地址总线,能够寻址 16MB 的内存。为了保持与早期软件的兼容性,尤其是那些使用 8086 的 20 位寻址模式的应用,IBM 设计了一种机制来模拟 8086 的地址环绕特性。这就是 A20 地址线的控制。

在 20 位地址总线系统中,当地址超出 1MB 时,会发生地址环绕。例如,在 8086 模式下,段地址 0xFFFF 加上偏移地址 0x0010 的结果是 0x10000,但由于只有 20 位地址线,它会变成 0x00000。这种行为称为地址环绕。

引入 80286 后,地址线增加到 24 位,这样上面的地址计算将直接得到 0x10000 而不是 0x00000。为了解决这种兼容性问题,IBM 添加了一条控制 A20 地址线的机制,可以在需要时屏蔽 A20 地址线的信号,使其始终为 0。这种机制称为 A20 Gate(A20 门控)

1
2
3
4
mov al,#0xD1        ; command write
out #0x64,al
mov al,#0xDF ; A20 on
out #0x60,al

这段代码通过向键盘控制器发送特定命令来启用 A20 地址线。具体来说:

  1. mov al,#0xD1out #0x64,al:告诉键盘控制器接下来将要写入一个命令。
  2. mov al,#0xDFout #0x60,al:将命令 0xDF 写入键盘控制器的数据端口,启用 A20 地址线。

可编程中断控制器 8259 芯片进行的编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
; well, that went ok, I hope. Now we have to reprogram the interrupts :-(
; we put them right after the intel-reserved hardware interrupts, at
; int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
; messed this up with the original PC, and they haven't been able to
; rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
; which is used for the internal hardware interrupts as well. We just
; have to reprogram the 8259's, and it isn't fun.

mov al,#0x11 ; initialization sequence
out #0x20,al ; send it to 8259A-1
.word 0x00eb,0x00eb ; jmp $+2, jmp $+2
out #0xA0,al ; and to 8259A-2
.word 0x00eb,0x00eb
mov al,#0x20 ; start of hardware int's (0x20)
out #0x21,al
.word 0x00eb,0x00eb
mov al,#0x28 ; start of hardware int's 2 (0x28)
out #0xA1,al
.word 0x00eb,0x00eb
mov al,#0x04 ; 8259-1 is master
out #0x21,al
.word 0x00eb,0x00eb
mov al,#0x02 ; 8259-2 is slave
out #0xA1,al
.word 0x00eb,0x00eb
mov al,#0x01 ; 8086 mode for both
out #0x21,al
.word 0x00eb,0x00eb
out #0xA1,al
.word 0x00eb,0x00eb
mov al,#0xFF ; mask off all interrupts for now
out #0x21,al
.word 0x00eb,0x00eb
out #0xA1,al

由于 IBM 在最初的 PC 设计中将硬件中断放置在 0x08 到 0x0F,这可能会与其他系统功能发生冲突,因此有必要重新编程中断控制器以避免这些冲突。新编程之后,8259 这个芯片的引脚与中断号的对应关系,变成了后面这样:

切换模式

1
2
3
mov ax,#0x0001  ; protected mode (PE) bit
lmsw ax ; This is it;
jmpi 0,8 ; jmp offset 0 of segment 8 (cs)
  • mov ax,#0x0001
    • 这条指令将立即数 0x0001 装载到 AX 寄存器中。
    • 0x0001 的二进制表示是 0000 0000 0000 0001,其中最低位是 PE(Protection Enable)位。
  • lmsw ax
    • 这条指令将 AX 寄存器的值加载到机器状态字(MSW,Machine Status Word)。
    • MSW 的最低位是 PE 位。将 PE 位设置为 1 后,处理器就会进入保护模式。

jmpi 0,8

  • 指令将跳转到段选择子为 8 的段的偏移 0 处。

8 用二进制表示就是:00000,0000,0000,1000对照上面段选择子的结构,可以知道描述符索引值是 1,也就是 CPU 要去全局描述符表(gdt)中找索引 1 的描述符。

第一项被表示为代码段描述符,是个可读可执行的段,第二项为数据段描述符,是个可读可写段,不过他们的段基址都是 0。

所以,这里取的就是这个代码段描述符,段基址是 0,偏移也是 0,那加一块就还是 0 。那么最终这个跳转指令,就是跳转到内存地址的 0 地址处,开始执行。

0地址处就是system,往后就开始执行真正的系统了,这是由 head.s 和 main.c 以及其余各模块的操作系统代码合并来的。

1
2
3
4
5
6
7
8
tools/system: boot/head.o init/main.o \
$(ARCHIVES) $(DRIVERS) $(MATH) $(LIBS)
$(LD) $(LDFLAGS) boot/head.o init/main.o \
$(ARCHIVES) \
$(DRIVERS) \
$(MATH) \
$(LIBS) \
-o tools/system > System.map

6)进入保护模式
https://carl-5535.github.io/2024/07/29/Linux0.11/6)进入保护模式/
作者
Carl Chen
发布于
2024年7月29日
许可协议