前言
记录一下自己动收实现操作系统的过程。只会给出关键地方的代码,一旦给出会尽可能详细地解释。
正文
开机时,硬件会自动把0号扇区(512b)的内容加载到内存0x7c00上。所以要在0号扇区放引导启动操作系统的代码(bootloader)。如果操作系统足够小的话,也可以直接把操作系统内核代码放在0号扇区。
以下为bootloader代码,用asm汇编语言写。
.code16#一开始处于实模式下,所以用16位
.global start
start:
#初始化段寄存器
movw %cs, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
#切换到保护模式
cli # clear interuption
inb $0x92, %al # Fast setup A20 Line with port 0x92, necessary or not?
orb $0x02, %al
outb %al, $0x92
data32 addr32 lgdt gdtDesc # loading gdtr, data32, addr32
movl %cr0, %eax
orb $0x01, %al
movl %eax, %cr0 # setting cr0
data32 ljmp $0x08, $start32 # reload code segment selector and ljmp to start32, data32
#保护模式是32位
#通用寄存器扩展为32位,段寄存器仍为16位
.code32
start32:
#保护模式下段寄存器存放的是段选择子
#段选择子低两位是特权级,这里为最高特权级0级
#段选择子第三位是0,表示全局段
#段选择子高十三位是全局描述符表中的编号,表示偏移
#一个段描述符占8b
movw $0x10, %ax # setting data segment selector
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %ss
movw $0x18, %ax # setting graphics data segment selector
movw %ax, %gs
movl $0x1fffff, %eax # setting esp
movl %eax, %esp
#bootMain:操作系统内核代码入口
jmp bootMain # jump to bootMain in boot.c
.p2align 2
gdt: # 8 bytes for each table entry, at least 1 entry
#按约定GDT第一项为空
.word 0,0 # empty entry
.byte 0,0,0,0
#代码段
.word 0xffff,0 # code segment entry
.byte 0,0x9a,0xcf,0
#数据段
.word 0xffff,0 # data segment entry
.byte 0,0x92,0xcf,0
#图像段
.word 0xffff,0x8000 # graphics segment entry
.byte 0x0b,0x92,0xcf,0
gdtDesc: # 6 bytes in total
.word (gdtDesc - gdt -1) # size of the table, 2 bytes, 65536-1 bytes, 8192 entries
.long gdt # offset, i.e. linear address of the table itself