1. setup结束后跳的目的地址
1.1 setup结束后会跳到 0x100000处
在0x100000处是compressed/head.o的地址
-
从linux-2.4.12/arch/i386/boot/compressed的编译打印可以看出:
-
ld -m elf_i386 -Ttext 0x100000 -e startup_32 -o bvmlinux head.o misc.o piggy.o
链接程序ld将arch/i386/boot/compressed下的head.o连接在了0x100000(1M)处
1.2所以在0x100000处是compressed/head.S的代码
arch/i386/boot/bootsect -->ljmp $SETUPSEG, $0
--> arch/i386/boot/setup -->jmpi 0x100000,__KERNEL_CS
--> arch/i386/boot/compressed/head.S
2. 解压的流程
2.1 compressed下的head.S源码分析
arch/i386/boot/compressed/head.S
-
.text
-
-
#include <linux/linkage.h>
-
#include <asm/segment.h>
-
-
.globl startup_32
-
-
startup_32: -->开始地址 0010:00100000
-
cld
-
cli
-
movl $(__KERNEL_DS),%eax -->mov eax, 0x00000018
-
movl %eax,%ds
-
movl %eax,%es
-
movl %eax,%fs
-
movl %eax,%gs -->ds=es=fs=gs=0x18
-
-
lss SYMBOL_NAME(stack_start),%esp -->执行过后ss=0x18,esp指向user_stack的结束处
-
//检查A20地址线是否己开启:向0x0地址写1,如果0x100000(1M)也变成了1,说明A20地址线没有开启
-
//前提条件-->0x100000(1M)处的数据是本代码的cld,机器码是0xfc不是1
-
xorl %eax,%eax
-
1: incl %eax # check that A20 really IS enabled
-
movl %eax,0x000000 # loop forever if it isn't
-
cmpl %eax,0x100000
-
je 1b
-
-
/*
-
* Initialize eflags. Some BIOS's leave bits like NT set. This would
-
* confuse the debugger if this code is traced.
-
* XXX - best to initialize before switching to protected mode.
-
*/
-
pushl $0
-
popfl
-
//清BSS区
-
xorl %eax,%eax
-
movl $ SYMBOL_NAME(_edata),%edi -->_edata是初始化数据区结束后的第1个地址;
-
movl $ SYMBOL_NAME(_end),%ecx -->_end是未初始化数据区(bss)结束后的第1个地址位置。
-
subl %edi,%ecx -->所以end-edata=BSS
-
cld
-
rep
-
stosb -->rep stosb byte ptr es:[edi], al清BSS
-
/*
-
* Do the decompression, and jump to the new kernel..
-
*/
-
subl $16,%esp # place for structure on the stack
-
movl %esp,%eax
-
pushl %esi # real mode pointer as second arg
-
pushl %eax # address of structure as first arg
-
call SYMBOL_NAME(decompress_kernel)
-
orl %eax,%eax -->解压结束后eax=1
-
jnz 3f -->不等于0跳到下面的3处
-
popl %esi # discard address
-
popl %esi # real mode pointer
-
xorl %ebx,%ebx
-
ljmp $(__KERNEL_CS), $0x100000
-
-
/*
-
* We come here, if we were loaded high.
-
* We need to move the move-in-place routine down to 0x1000
-
* and then start it with the buffer addresses in registers,
-
* which we got from the stack.
-
*/
-
//将move_routint_stat从0x100082处移动到0x1000处
-
3:
-
movl $move_routine_start,%esi -->执行后esi=0x00100082
-
movl $0x1000,%edi -->edi: 0x00001000 目的地址
-
movl $move_routine_end,%ecx -->执行后ecx=0x001000a7
-
subl %esi,%ecx -->执行后ecx=0x25也就是move_routeine的size
-
addl $3,%ecx -->4字节对齐,为什么呢?因为下面是一次移动4个字节
-
shrl $2,%ecx -->现在己经处于32位模式了
-
cld
-
rep -->rep movsd dword ptr es:[edi], dword ptr ds:[esi]
-
movsl
-
-
popl %esi # discard the address
-
popl %ebx # real mode pointer -->ebx=0x00090000 -->解压后内核第1部分的结束地址
-
popl %esi # low_buffer_start -->esi=0x00002000 -->解压后内核第1部分的开始地址
-
popl %ecx # lcount -->ecx=0x0008e000 -->解压后内核第1部分的长度
-
popl %edx # high_buffer_start -->edx=0x001d4fc0 -->解压后内核第2部分的开始地址
-
popl %eax # hcount -->eax=0x0012c160 -->解压后内核第2部分的长度
-
movl $0x100000,%edi -->edi=0x00100000 -->将这两部分合并到1M处
-
cli # make sure we don't get interrupted
-
ljmp $(__KERNEL_CS), $0x1000 -->jmpf 0x0010:00001000跳到刚才移动的move_routine_start处
-
-
/*
-
* Routine (template) for moving the decompressed kernel in place,
-
* if we were high loaded. This _must_ PIC-code !
-
*/
-
move_routine_start:
-
//将解压后内核第1部分的数据,由[0x2000-0x90000]复制到[0x100000-0x18e000]
-
movl %ecx,%ebp -->备份一下ecx
-
shrl $2,%ecx -->复制的dst与src都在上面设置好了
-
rep
-
movsl -->4字节4字节的复制
-
movl %ebp,%ecx -->还原ecx,防止有不足4字节的内容
-
andl $3,%ecx
-
rep -->剩下不足4字节的按字节复制
-
movsb
-
//将解压后内核第2部分的数据,由[0x001d4fc0-len]复制到[0x18e000-len]处
-
movl %edx,%esi -->将edx(解压后内核第2部分的开始地址)设为src
-
movl %eax,%ecx -->将eax(解压后内核第2部分的长度)赋给ecx
-
addl $3,%ecx -->将ecx4字节对齐,第二部分多复制几个字节没有关系
-
shrl $2,%ecx
-
rep -->rep movsd dword ptr es:[edi], dword ptr ds:[esi]
-
movsl
-
movl %ebx,%esi # Restore setup pointer
-
xorl %ebx,%ebx
-
//最后跳到真正内核的起始处运行
-
ljmp $(__KERNEL_CS), $0x100000 -->vmlinux终于组装完成了可以跳了
-
move_routine_end:
参考文章:
linux内核分析
http://blog.csdn.net/linux_xiaomugua/article/details/7098032
2.2 总结一下到目前为止的流程
a.开机启动,BIOS会把启动介质中的bootsect加载到0x7c00
b. bootsect 复制自己到0x90000,跳到0x90000运行
c. bootsect复制setup到0x90200;bootsect复制带自解压头的kernel到0x100000;跳到0x90200运行
d. 0x90200是setup,setup开启保护模式之后跳到0x100000处
e. 0x100000处是自解压的头,会解压内核为两部分k_low=[0x2000-0x90000]与k_high=[0x1xxxxx-0x1xxxxx]
f. 自解压头会把move_routine复制到0x1000处,并跳到0x1000处运行
g. move_route会把k_low与k_high合并成完整的解压后的kernel,并放在0x100000处,最后跳到0x100000运行
2.3 技巧生成map文件
cong@msi:/work/os/linux-2.4.12/arch/i386/boot/compressed$ nm bvmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > bvmlinux.map
2.4 为什么解压头的运行地址在0x100000处,后来的内核还是运行在0x100000处呢?
这样就使得看起来好像0x100000处不存在一个解压程序一样
2.4 为什么decompress_kernel不直接把内核解压到0x100000处?
因为自解压头本身就运行在0x100000处,把内核直接解压到0x100000处就把自己覆盖掉了,这样谁来解压内核呢?
2.5 关于k_high的起始地址
阅读(1453) | 评论(0) | 转发(0) |