该部分程序是在内核代码的开头部分,用来
做一些初始化的操作,比如重新设置idt,gdt,
设置页表等,最后将控制转移到内核的main函数。
.text
.globl _idt,_gdt,_pg_dir,_tmp_floppy_area
这个标志是站位的,之后该程序会将页目录表存放在该处
将head.s的开头部分覆盖。也就是说把页目录表放在内存
最开始处。
_pg_dir:
程序开始
startup_32:
段选择符0x10表示,RPL为0,在全局描述符表
中,索引值为2,
movl $0x10,%eax
用段选择符加载各个段寄存器
在setup.s程序中,设置了GDT表,0x10表示
内核数据段,内存基地址为0
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
设置系统堆栈,_stack_start定义在kernel/sched.c中
/*
long user_stack [ PAGE_SIZE>>2 ] ;
struct {
long * a;
short b;
} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
*/
表示系统堆栈空间可以达到4 * 1024 byte.即4k,一个页面
lss _stack_start,%esp
重新设置中断描述符表
call setup_idt
重新设置全局描述符表
call setup_gdt
重新用0x10选择符加载段寄存器
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
重新加载堆栈段
lss _stack_start,%esp
eax寄存器清0
xorl %eax,%eax
检查A20地址线是否开启,如果没开启,
最高20位地址 + 1必将回环
1: incl %eax
movl %eax,0x000000
cmpl %eax,0x100000
je 1b
检查协处理器是否存在
movl %cr0,%eax
andl $0x80000011,%eax
设置协处理器存在标志位
orl $2,%eax
movl %eax,%cr0
调用协处理器检查子过程
call check_x87
安装页表
jmp after_page_tables
check_x87:
向协处理器发送初始化命令
fninit
取状态值
fstsw %ax
如果为0表示协处理器不存在
cmpb $0,%al
je 1f
取CR0寄存器进行重置协处理器标志位mp,em
movl %cr0,%eax
xorl $6,%eax
movl %eax,%cr0
ret
.align 2
fsetpm的机器码,看作空操作即可。
1: .byte 0xDB,0xE4
ret
安装中断描述符表
setup_idt:
将所有中断描述符项对应的中断处理子程序设置成
亚中断处理子程序的函数地址
lea ignore_int,%edx
0x00080000对应idt表描述符
0x0008表示指向内核代码段的选择符,
movl $0x00080000,%eax
将idt描述符的开始2个字节付值为哑中断处理子程序
的偏移地址
movw %dx,%ax
0x8E00表示中断描述符的高4个字节的
值。存在位置位,DPL位0级。
movw $0x8E00,%dx
设置256项中断描述符表
lea _idt,%edi
mov $256,%ecx
rp_sidt:
movl %eax,(%edi)
movl %edx,4(%edi)
addl $8,%edi
dec %ecx
jne rp_sidt
加载中断描述符表寄存器
lidt idt_descr
ret
setup_gdt:
加载全局描述附表寄存器
lgdt gdt_descr
ret
第一个也表的位置0x1000
.org 0x1000
pg0:
第二个也表的位置0x2000
.org 0x2000
pg1:
第三个页表的位置0x3000
.org 0x3000
pg2:
第四个页表的位置0x4000
.org 0x4000
pg3:
为软盘驱动预留缓冲,大小为1024byte.
.org 0x5000
_tmp_floppy_area:
.fill 1024,1,0
after_page_tables:
为init/main.c压入输入参数
参数char * enivorn
pushl $0
参数char ** argc
pushl $0
参数int argv
pushl $0
main函数返回地址
pushl $L6
压入函数地址
pushl $_main
安装页表
jmp setup_paging
main函数不会返回,否则死机
L6:
jmp L6
哑中断处理子程序打印的信息
int_msg:
.asciz "Unknown interrupt/n/r"
.align 2
哑中断处理子程序
ignore_int:
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
仅仅重新用内核数据段描述符加载各个段寄存器
在屏幕上打印"Unknown interrupt"
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
pushl $int_msg
call _printk
popl %eax
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
.align 2
安装页表
setup_paging:
一共5个页表,每个页表1024个表项
movl $1024*5,%ecx
初始化eax寄存器为0
edi寄存器为0
xorl %eax,%eax
xorl %edi,%edi
开始加载页目录表和页表
cld;rep;stosl
设置页目录表项
7表示页目录表项对应的页表具有读写权限,以及该页在内存中
movl $pg0+7,_pg_dir
movl $pg1+7,_pg_dir+4
movl $pg2+7,_pg_dir+8
movl $pg3+7,_pg_dir+12
在第四个页表的最后一个页表项开始初始化页表
movl $pg3+4092,%edi
初始化页表所用的值:0xfff007,物理页地址为:0xfff000,该页可读写、存在内存中
movl $0xfff007,%eax
std
循环初始化
1: stosl
subl $0x1000,%eax
jge 1b
页目录表的地址在内存中0x00000000
设置页目录表寄存器
xorl %eax,%eax
movl %eax,%cr3
movl %cr0,%eax
打开CR0控制寄存器的PG位,启用分页机制
orl $0x80000000,%eax
movl %eax,%cr0
ret
定义中断描述符表地址空间
.align 2
.word 0
idt_descr:
.word 256*8-1
.long _idt
定义全局描述符表地址空间
.align 2
.word 0
gdt_descr:
.word 256*8-1
.long _gdt
.align 3
_idt: .fill 256,8,0
定义全局描述符表_gdt: .quad 0x0000000000000000 .quad 0x00c09a0000000fff .quad 0x00c0920000000fff .quad 0x0000000000000000 .fill 252,8,0