在boot/目录下看到的三个汇编文件,构成我们linux0.11启动的过程。分别为setup.s, bootsect.s, head.s 。
接下来就来看看setup.s做了哪些工作。
setup.s获取基本硬件参数
从bootsect.s的 jmpi 0, SETUPSEG 进入setup.s(也是引导扇区之后的下一个扇区)。
entry start
start:
mov ax, #INITSEG
mov ds, ax
mov ah, #0x03
//clean bh
xor bh, bh
//取光标位置dx
int 0x10
//取出光标位置(包括其他硬件参数)
//到0x90000处,ds=#INITSEG
mov [0], dx
mov ah,#0x88
//获取扩展内存数(所谓扩展内存是指1M以后的内存空间20bits,32bits最高可达4G内存空间)
int 0x15
//内存数放入0x90002(间接寻址)
mov [2], ax
//关中断
cli
mov ax, #0x0000
cld
do_move:
mov es, ax
add ax, #0x1000
cmp ax, #0x9000
jz end_move
mov ds, ax
sub di, di
sub si, si
mov cx, #0x8000
//将system模块移到0地址
rep
movsw
jmp do_move
...
(0地址原有重要内容,譬如中断向量表)
(因为操作系统将进入保护模式,保护模式下int和jmpi CPU解释方式不再和实模式一样,分别创建 LDT(Local Descriptor Table) 和 GDT(Global Descriptor Table))
end_move:
mov ax, #SETUPSEG
mov ds, ax
//设置IDT和GDT,设置保护模式下的中断和寻址
lidt idt_48
lgdt gdt_48
idt_48:
//保护模式中断函数表
.word 0
.word 0, 0
gdt_48:
(伪操作符.word用于在当前位置定义一个双字节内存对象(变量),其后可以是一个数或者是一个表达式)
.word 0x800
.word 512+gdt, 0x9
gdt:
//ip = 0
.word 0, 0, 0, 0
//以下两个gdt表项,都是0x0000,一个只读(代码),一个读写(数据)
//ip = 8
.word 0x07FF, 0x0000, 0x9A00, 0x00C0
//ip = 16
.word 0x07FF, 0x0000, 0x9200, 0x00C0
...
//进入保护模式
call empty_8042
mov al, #0xD1
out #0x64, al
//8042是键盘控制器,其输出端口P2用来控制A20地址线(32位地址线)
call empty_8042
mov al, #0xDF
out #0x60, al
//选通A20地址线
call empty_8042
...
//初始化8259(中断控制)
...
mov ax, #0x0001
//cr0寄存器: PE=1,启动保护模式(0 bit); PG=1,启动分页(31 bit)
mov cr0, ax
//跳转到0x0地址,进入system模块的head.s
//cs=8用来查gdt,再加上ip的偏移量
jmpi 0, 8
empty_8042:
.word 0x00EB, 0x00EB
in al, #0x64
test al, #2
jnz empty_8042
ret