linux 0.11 源码学习(五)
head.s
head.s 是系统模块的入口,其编译器已经是GNU汇编,但从功能上将仍然属于内核启动阶段,主要的功能是对386 CPU的初始化,如用户堆栈、IDT、GDT和页表。因此从文件夹的归属看,它仍然放在boot文件夹中,与bootsect和setup一块。head.s的核心功能(简单的寄存器初始化不做赘述)如下:
- 初始化堆栈寄存器:lss stack_start, %esp,其中stack_start定义在sched中,如下:
struct { long * a; short b; } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
- 初始化中断描述符表,256个中断描述符都被挂接一个叫ignore_int的函数,只打印消息不做处理,如下:
setup_idt: lea ignore_int,%edx movl $0x00080000,%eax movw %dx,%ax /* selector = 0x0008 = cs */ movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ lea idt,%edi //设置idt中的256个中断描述符 mov $256,%ecx rp_sidt: movl %eax,(%edi) movl %edx,4(%edi) addl $8,%edi dec %ecx jne rp_sidt //如果ecs(256) != 0,则跳转 lidt idt_descr //加载中断描述符表,这里的idt_desscr包含了idt变量(256个中断描述符) ret
- 加载全局描述符表寄存器
setup_gdt: lgdt gdt_descr ret
gdt_descr: .word 256*8-1 # so does gdt (not that that's any .long gdt # magic number, but it works for me :^) gdt: .quad 0x0000000000000000 /* NULL descriptor */ .quad 0x00c09a0000000fff /* 16Mb */ .quad 0x00c0920000000fff /* 16Mb */ .quad 0x0000000000000000 /* TEMPORARY - don't use */ .fill 252,8,0 /* space for LDT's and TSS's etc */
- 检查A20地址线是否真实开启
- 初始化软盘缓冲区:tmp_floppy_area:.fill 1024,1,0
- 初始化页表:
注:在386体系的CPU中支持段页式的内存管理方式,即逻辑地址(基地址+偏移)->通过分段管理->线性地址->通过分页管理->物理地址。针对分页管理386的机制是CR3寄存器指向页表目录,页表目录中的PDE (page Directory Entry)指向一个页表,页表中的PTE(page Table Entry)指向一个物理页。因此386的线性地址实际上是三个偏移量:页目录表偏移量(找到页表)、页表的偏移量(找到物理页)、物理页的偏移量,三者的综合完成具体物理页面的转换。
在linux的页表初始化代码中主要是三块工作:
- 初始化一个页目录表和四个页表:
setup_paging: movl $1024*5,%ecx /* 5 pages - pg_dir+4 page tables */ xorl %eax,%eax xorl %edi,%edi /* pg_dir is at 0x000 */ cld;rep;stosl movl $pg0+7,pg_dir /* set present bit/user r/w */ pg_dir的地址是0x000,没搞清楚为什么? movl $pg1+7,pg_dir+4 /* --------- " " --------- */ movl $pg2+7,pg_dir+8 /* --------- " " --------- */ movl $pg3+7,pg_dir+12 /* --------- " " --------- */
-
- 将edi指向最后一个页表的最后一项:movl $pg3+4092, %edi
- 设置页目录表的地址至CR3寄存器,设置CR0寄存器启动分页机制;
xorl %eax,%eax /* pg_dir is at 0x0000 */ movl %eax,%cr3 /* cr3 - page directory start */ //cr3被设置成0x0000 movl %cr0,%eax orl $0x80000000,%eax movl %eax,%cr0 /* set paging (PG) bit */ ret /* this also flushes prefetch-queue */