linux引导程序剖析(三)

该部分程序是在内核代码的开头部分,用来
做一些初始化的操作,比如重新设置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 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值