逐段分析
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
评论
ValineWaline