ucore OS lab: bootloader启动ucore (3)

分析bootloader进入保护模式的过程

BIOS将通过读取硬盘主引导扇区到内存,并转跳到对应内存中的位置执行bootloader。

1.为何开启A20,以及如何开启A20

为了向下兼容早期8086 PC(只能访问1M内存空间), IBM决定在PC AT计算机系统上加个硬件逻辑,来模仿回绕特征(寻址超过1M,则又从0开始,相当于实际内存地址由取模操作得到),于是出现了A20 Gate。他们的方法就是把A20地址线控制和8042键盘控制器的一个输出进行AND操作,这样来控制A20地址线的打开(使能)和关闭(屏蔽\禁止)。当80386处于保护模式时,需要将A20开启,才能访问全部有效内存空间。

关于A20 Gate的更多描述以及如何操作8042键盘控制器,可见:
https://chyyuu.gitbooks.io/ucore_os_docs/content/lab1/lab1_appendix_a20.html

以下为bootloader中由实模式进入保护模式的相关代码,在此之前将各个数据段初始化好,然后开启A20 :

.code16                        # Assemble for 16-bit mode
    cli                        # Disable interrupts
    cld                        # String operations increment
    # Set up the important data segment registers (DS, ES, SS).
    xorw %ax, %ax              # Segment number zero
    movw %ax, %ds              # -> Data Segment
    movw %ax, %es              # -> Extra Segment
    movw %ax, %ss              # -> Stack Segment
seta20.1:
    inb $0x64, %al             # Wait for not busy(8042 input buffer empty).
    testb $0x2, %al
    jnz seta20.1

    movb $0xd1, %al            # 0xd1 -> port 0x64
    outb %al, $0x64            # 0xd1 means: write data to 8042's P2 port

seta20.2:
    inb $0x64, %al             # Wait for not busy(8042 input buffer empty).
    testb $0x2, %al
    jnz seta20.2

    movb $0xdf, %al            # 0xdf -> port 0x60
    outb %al, $0x60            # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 1

2.如何初始化GDT表

lgdt gdtdesc

lgdt命令将如下描述的gdt表的大小和地址载入到GDTR寄存器中。

# Bootstrap GDT
.p2align 2                                          # force 4 byte alignment
gdt:
    SEG_NULLASM                                     # null seg
    SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)           # code seg for bootloader and kernel
    SEG_ASM(STA_W, 0x0, 0xffffffff)                 # data seg for bootloader and kernel

gdtdesc:
    .word 0x17                                      # sizeof(gdt) - 1
    .long gdt                                       # address gdt

3.如何使能和进入保护模式

经过上面的准备,
将CR0中的保护模式允许位(PE)置1,就启动了保护模式.

    movl %cr0, %eax
    orl $CR0_PE_ON, %eax
    movl %eax, %cr0

接着跳转到保护模式32位下的代码段,

ljmp $PROT_MODE_CSEG, $protcseg

完成段选择子和堆栈的设置后,进入 bootmain 方法

.code32                                             # Assemble for 32-bit mode
protcseg:
    # Set up the protected-mode data segment registers
    movw $PROT_MODE_DSEG, %ax                       # Our data segment selector
    movw %ax, %ds                                   # -> DS: Data Segment
    movw %ax, %es                                   # -> ES: Extra Segment
    movw %ax, %fs                                   # -> FS
    movw %ax, %gs                                   # -> GS
    movw %ax, %ss                                   # -> SS: Stack Segment

    # Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00)
    movl $0x0, %ebp
    movl $start, %esp
    call bootmain

发表评论