逐段分析

line 12:清理环境

关中断,重要的段寄存器清零

# start address should be 0:7c00, in real mode, the beginning address of the running bootloader
.globl start
start:
.code16                                             # Assemble for 16-bit mode
    cli                                             // 关中断
    cld                                             // 清除方向标志

    # Set up the important data segment registers (DS, ES, SS).
    xorw %ax, %ax                                   // ax 清 0
    movw %ax, %ds                                   // ds 清 0
    movw %ax, %es                                   // es 清 0
    movw %ax, %ss                                   // ss 清 0

line 25:开启A20

通过将键盘控制器上的A20线置于高电位,全部32条地址线可用,可以访问4G的内存空间。

为了与最早的PC兼容,物理地址线绑定在了低20位,因此高于1MB的地址默认情况下会回零。 此代码撤消了此操作

    # Enable A20:
    #  For backwards compatibility with the earliest PCs, physical
    #  address line 20 is tied low, so that addresses higher than
    #  1MB wrap around to zero by default. This code undoes this.
seta20.1:											// 等待 8042 键盘控制器不忙
    inb $0x64, %al                                  // 从 0x64 端口中读入一个字节到 al 中
    testb $0x2, %al									// 测试 al 的第 2 位
    jnz seta20.1									// al 的第 2 位为 0,则跳出循环

    movb $0xd1, %al                                 // 将 0xd1 写入到 al 中
    outb %al, $0x64                                 // 将 0xd1 写入到 0x64 端口中
seta20.2:
    inb $0x64, %al                                  // 从 0x64 端口中读入一个字节到 al 中
    testb $0x2, %al									// 测试 al 的第 2 位
    jnz seta20.2									// al 的第 2 位为 0,则跳出循环

    movb $0xdf, %al                                 // 将 0xdf 入到 al 中
	outb %al, $0x60                                 // 将 0xdf 入到 0x64 端口中,打开 A20

line 45:初始化GDT

GDT(全局描述表,Global Descriptor Table),一个简单的GDT表和其描述符已经静态储存在引导区中,载入即可

# Switch from real to protected mode, using a bootstrap GDT
# and segment translation that makes virtual addresses
# identical to physical addresses, so that the
# effective memory map does not change during the switch.
lgdt gdtdesc	// 载入 GDT 表

line 50:进入保护模式

通过将cr0寄存器PE位(第0位)置1便开启了保护模式

// Line 50:
	movl %cr0, %eax									// 加载 cr0 到 eax
    orl $CR0_PE_ON, %eax							// 将 eax 的第 0 位置为 0
    movl %eax, %cr0									// 将 cr0 的第 0 位置为 1

通过长跳转更新cs的基地址

// Line 54:
    # Jump to next instruction, but in 32-bit code segment.
    # Switches processor into 32-bit mode.
    ljmp $PROT_MODE_CSEG, $protcseg

.code32                                             # Assemble for 32-bit mode
protcseg:

设置段寄存器,并建立堆栈

BIOS数据区:0x0000-0x7c00

// Line 68:
	# 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								// 设置栈指针
    

转到保护模式完成,进入 bootmain 方法

// Line 71:
call bootmain									// 调用 bbotmain 函数

完整代码:

#include <asm.h>

# Start the CPU: switch to 32-bit protected mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with %cs=0 %ip=7c00.

.set PROT_MODE_CSEG,        0x8                     # kernel code segment selector
.set PROT_MODE_DSEG,        0x10                    # kernel data segment selector
.set CR0_PE_ON,             0x1                     # protected mode enable flag

# start address should be 0:7c00, in real mode, the beginning address of the running bootloader
.globl start
start:
.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

    # Enable A20:
    #  For backwards compatibility with the earliest PCs, physical
    #  address line 20 is tied low, so that addresses higher than
    #  1MB wrap around to zero by default. This code undoes this.
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

    # Switch from real to protected mode, using a bootstrap GDT
    # and segment translation that makes virtual addresses
    # identical to physical addresses, so that the
    # effective memory map does not change during the switch.
    lgdt gdtdesc
    movl %cr0, %eax
    orl $CR0_PE_ON, %eax
    movl %eax, %cr0

    # Jump to next instruction, but in 32-bit code segment.
    # Switches processor into 32-bit mode.
    ljmp $PROT_MODE_CSEG, $protcseg

.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

    # If bootmain returns (it shouldn't), loop.
spin:
    jmp spin

# 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