linux2.4 启动代码head.S分析
来源:互联网 发布:mac 预览 目录 编辑:程序博客网 时间:2024/04/28 14:11
32位启动代码,暂时不考虑SMP的情况。关键代码分析
页目录表的起始地址在0x101000,由于目前仍然处于实模式,地址都是
物理地址
开始启动内核
startup_32:
清方向标志位
cld
用内核数据段的地址来初始化ds,es,fs,gs寄存器
宏__KERNEL_DS在segment.h中有定义,对于i386体系结构来说__KERNEL_DS=0x18
movl $(__KERNEL_DS),%eax
movl %eax,%ds
movl %eax,%es
movl %eax,%fs
movl %eax,%gs
初始化页表,由于程序中实用的符号的地址都是虚拟地址,所以$pg0 - __PAGE_OFFSET就是pg0的物理地址
movl $pg0-__PAGE_OFFSET,%edi
页表项的值:该页在内存中,用户可写
movl $007,%eax
进行初始化页表
2: stosl
索引值加1
add $0x1000,%eax
初始化pg0和pg1两张表
cmp $empty_zero_page-__PAGE_OFFSET,%edi
jne 2b
3:
页目录表的物理地址:$swapper_pg_dir-__PAGE_OFFSET
起始于0x101000
movl $swapper_pg_dir-__PAGE_OFFSET,%eax
页目录表的物理地址存入cr3寄存器中
movl %eax,%cr3
开启分页机制,重置cr0控制寄存器
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0
这样做只是为了刷新指令流水线,486之后采用了2条流水线,保证操作数是虚拟地址
这样才能平稳过渡到保护模式
jmp 1f
1:
movl $1f,%eax
jmp *%eax
1:
初始化堆栈指针寄存器,内核堆栈结构:
task_union+内核数据段。task_union占用8k
lss stack_start,%esp
初始化eax寄存器为0
xorl %eax,%eax
将为初始化数据段的其实地址存入edi寄存器中
movl $ SYMBOL_NAME(__bss_start),%edi
将内核映像的结束地址存入ecx中。
movl $ SYMBOL_NAME(_end),%ecx
将offset存入ecx中。
subl %edi,%ecx
对这段内存区域进行初始化操作,初始化为0
rep
stosb
设置中断描述符表
call setup_idt
利用push/pop指令初始化eflags寄存器
pushl $0
popfl
将第三张页表的首地址存入edi寄存器中
movl $ SYMBOL_NAME(empty_zero_page),%edi
一共需要初始化4k的内存。
前2k内存存放引导参数,后2kb内存存放命令行参数
movl $512,%ecx
cld
rep
movsl
将后2kb初始化为0
xorl %eax,%eax
movl $512,%ecx
rep
stosl
设置中断描述符表子程序
setup_idt:
将默认中断处理函数的有效地址放入edx寄存器中
lea ignore_int,%edx
初始化中断门描述符
中断处理程序入口地址放在0-15位
movl $(__KERNEL_CS << 16),%eax
内核代码段选择符存放在16-31位
movw %dx,%ax
movw $0x8E00,%dx
将中断描述符表的地址存入edi中
lea SYMBOL_NAME(idt_table),%edi
设置256项中断描述符表项
mov $256,%ecx
rp_sidt:
设置表项,一个表项占8字节
movl %eax,(%edi)
movl %edx,4(%edi)
addl $8,%edi
dec %ecx
jne rp_sidt
ret
定义内核栈
ENTRY(stack_start)
.long SYMBOL_NAME(init_task_union)+8192
.long __KERNEL_DS
int_msg:
.asciz "Unknown interrupt, stack: %p %p %p %p/n"
ALIGN
定义缺省中断处理过程,仅仅打印"Unknown interrupt, stack: %p %p %p %p/n"
ignore_int:
cld
movl $(__KERNEL_DS),%eax
movl %eax,%ds
movl %eax,%es
pushl 12(%esp)
pushl 12(%esp)
pushl 12(%esp)
pushl 12(%esp)
pushl $int_msg
call SYMBOL_NAME(printk)
1: hlt
jmp 1b
定义中断描述符表表项数量
#define IDT_ENTRIES 256
定义页目录表,首先定一个了2张页表,用来映射内核内存空间
分别用于内核和用户区使用,并且映射到相同的物理地址空间(0-8M),
但是不能通过用户地址空间的虚拟地址来访问内核空间,这样做的原因是保证实模式到保护模式的平稳过渡。
.org 0x1000
ENTRY(swapper_pg_dir)
.long 0x00102007
.long 0x00103007
.fill BOOT_USER_PGD_PTRS-2,4,0
.long 0x00102007
.long 0x00103007
.fill BOOT_KERNEL_PGD_PTRS-2,4,0
第一张页表
ljmpl $__KERNEL_CS, $0x00100000
.org 0x2000
ENTRY(pg0)
第二张页表
.org 0x3000
ENTRY(pg1)
两张页表映射8M空间
.org 0x4000
ENTRY(empty_zero_page)
定义全局表述符表,因为linux采用的是分页机制,所以在全局描述符表中设置4个表项,
简化分段到分页的地址转换,这时虚拟地址空间和线性地址空间是一样的,都能表示4G
空间。在虚拟空间中,内核起始地址和用户地址空间起始位置相同。
ENTRY(gdt_table)
.quad 0x0000000000000000 空描述表项,一般不用
.quad 0x0000000000000000 同上
.quad 0x00cf9a000000ffff 内核代码段
.quad 0x00cf92000000ffff 内核数据段
.quad 0x00cffa000000ffff 用户代码段
.quad 0x00cff2000000ffff 用户数据段
- linux2.4 启动代码head.S分析
- linux2.4 启动代码head.S分析
- linux2.6.11启动代码之bootsect.s
- linux2.6.11启动代码之bootsect.s
- Linux启动过程分析(head.s)
- android内核启动分析之head.S
- arm linux 从入口到start_kernel 代码分析——head.S分析——4
- startup_LPC11xx.s启动代码分析
- startup_stm32f10x_hd.s启动代码分析
- linux-1.2.13之启动代码head.S
- ARM Linux启动分析----head-armv.S内幕
- ARM Linux启动分析----head-armv.S内幕
- ARM Linux启动分析----head-armv.S内幕
- ARM Linux启动分析----head-armv.S内幕
- 嵌入式 arm平台kernel启动第一阶段汇编head.s分析
- Linux启动分析(2)— bootsect.S、setup.S、head.S分析
- Linux启动分析(2)— bootsect.S、setup.S、head.S分析
- Linux启动分析(2)— bootsect.S、setup.S、head.S分析
- Head First装饰者模式
- 考过了网工,证件不知怎么发下了
- 【转帖】爆笑一则!
- word中多级项目符号的使用
- 必须掌握的八个【cmd 命令行】
- linux2.4 启动代码head.S分析
- C#生成Word文档
- phpclass实现动态属性
- 窗口内置的滚动条如何绘制的更好?
- 过去的,就过去了
- Tomcat连接池配置
- 你竭尽全力了吗?
- 豁出去了
- 售前工程师、售前经理必备知识和技巧