- 清除BBS
xorl %eax,%eax //eax=0
movl $_edata,%edi //edi=0x262a2d
movl $_end,%ecx //ecx=26eea4
subl %edi,%ecx
cld
rep
stosb
说明:
arch/i386/kernel/vmlinux.lds.S
_edata和_end的定义是:
*(.bss)
}
. = ALIGN(4);
__bss_stop = .;
_end = . ;
在System.map中:
c03fe0fc A __bss_stop
c03fe0fc A _end
问题1:
_end=0x26eea4和System.map中的_end的数值0xc03fe0fc不相等,那么这里的_end是在哪里定义的?
arch/i386/kernel/vmlinux.lds.S是针对于生成主目录下的vmlinux的连接脚本.
现在执行的是compress/vmlinux,生成的命令如下:
cmd_arch/i386/boot/compressed/vmlinux := ld -m elf_i386 -Ttext 0x100000 -e startup_32 arch/i386/boot/compressed/head.o arch/i386/boot/compressed/misc.o arch/i386/boot/compressed/piggy.o -o arch/i386/boot/compressed/vmlinux
可以看出是直接的ld命令,使用的是内部的连接脚本,所以会根据内部链接脚本的定义生成:ld --verbose
.data1 : { *(.data1) }
_edata = .; PROVIDE (edata = .);
__bss_start = .;
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we don't
pad the .data section. */
. = ALIGN(. != 0 ? 32 / 8 : 1);
}
. = ALIGN(32 / 8);
. = ALIGN(32 / 8);
_end = .; PROVIDE (end = .);
从这个脚本可以看出,_edata 和__bss_start的值相等。就是清空bbs的数值。
- 解压缩内核,就是从压缩的vmlinux,解压出vmlinux.bin。解压函数是
decompress_kernel。
/*
* 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
//esi=0x9000 传递给decompress_kernel的第2个参数
pushl %eax # address of structure as first arg
//eax=1 传递给decompress_kernel的第1个参数
call decompress_kernel
// eax=1 ecx=0x266a30 edx=0x2e2086
// esp=0x266a28
orl %eax,%eax //eax=1
jnz 3f //跳转到标签3
popl %esi # discard address
popl %esi # real mode pointer
xorl %ebx,%ebx
ljmp $(__BOOT_CS), $0x100000
调用decompress_kernel后,内核解压后的buffer,地址放在堆栈中。
因为是bzImage,会解压到两个地址,一个是0x2000-0x90000;另外一个地址是在0x100000的0x3000之后,且离0x100000不小于低端长度的最大长度。
为什么低端内存不联系放?
因为刚开始镜像在0x10 0000,在0x2000-0x90000放不下这么大的镜像,如果接着放,会冲掉放在0x10 0000地址的内容。
为什么不一起高端内存?
猜测
1)高端的内存没有初始化好。
2) 汇编中的跳转命令,跳转距离没有这么远。
- 把move_routine_start搬移到0x1000处。
3:
movl $move_routine_start,%esi //esi=0x100082
movl $0x1000,%edi // edi=0x1000
movl $move_routine_end,%ecx // ecx=0x1000a7
subl %esi,%ecx //ecx=0x25 搬移的长度
addl $3,%ecx //ecx=0x28
shrl $2,%ecx //ecx=0xa
cld
rep
movsl
popl %esi # discard the address
popl %ebx # real mode pointer
popl %esi # low_buffer_start //解压的vmlinux的开始地址 0x2000
popl %ecx # lcount //0x8e00
popl %edx # high_buffer_start //0x271ea4
popl %eax # hcount //0x254086
movl $0x100000,%edi //edi=0x10 0000
cli # make sure we don't get interrupted
ljmp $(__BOOT_CS), $0x1000 # and jump to the move routine
把move_routine_start例程搬运到0x1000处,然后把vmlinux解压到低端内存(0x2000)的启始地址放到esi寄存器中,长度(lcount)存放到ecx中;高端内存地址(0x271ea4)放到edx中,长度放到eax中。这四个值是由decompress_kernel返回得到的。
然后跳转到0x1000处,执行例程move_routine_start。
- 把解压到0x2000的低端内容和0x1000a7的高端内容搬运到0x10 0000处。
具体操作就是0x2000的内容搬运到0x10 0000处,后面紧接着存放0x1000a7的内容,这样完成了vmlinux.bin的合并,并且存放在0x10 0000(也就是一起在 1M处的内容)。
move_routine_start:
movl %ecx,%ebp //ebp=ecx=0x8e00
shrl $2,%ecx //ecx=0x23800
rep
movsl //把地址0x2000的地址搬移到0x100000地址
movl %ebp,%ecx //
andl $3,%ecx //ecx=0x254089
rep //
movsb //
movl %edx,%esi //esi=0x271ea4
movl %eax,%ecx //ecx= 0x254086 # NOTE: rep movsb won't move if %ecx == 0
addl $3,%ecx //ecx=0x254089
shrl $2,%ecx //ecx=0x95022
//SI=0x271ea4 DI=0x18e000
rep
movsl
movl %ebx,%esi # Restore setup pointer
xorl %ebx,%ebx
ljmp $(__BOOT_CS), $0x100000
move_routine_end:
- 最后执行0x10 0000内容,也就是arch/i386/kernel/head.S处执行。