Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2122701
  • 博文数量: 438
  • 博客积分: 3871
  • 博客等级: 中校
  • 技术积分: 6075
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-10 00:11
个人简介

邮箱: wangcong02345@163.com

文章分类

全部博文(438)

文章存档

2017年(15)

2016年(119)

2015年(91)

2014年(62)

2013年(56)

2012年(79)

2011年(16)

分类: LINUX

2016-10-30 21:00:45

1. setup结束后跳的目的地址
1.1 setup结束后会跳到 0x100000处
在0x100000处是compressed/head.o的地址
  1. 从linux-2.4.12/arch/i386/boot/compressed的编译打印可以看出:
  2. 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
  1. .text

  2. #include <linux/linkage.h>
  3. #include <asm/segment.h>

  4.     .globl startup_32
  5.     
  6. startup_32:                  -->开始地址 0010:00100000
  7.     cld
  8.     cli
  9.     movl $(__KERNEL_DS),%eax  -->mov eax, 0x00000018
  10.     movl %eax,%ds
  11.     movl %eax,%es
  12.     movl %eax,%fs
  13.     movl %eax,%gs             -->ds=es=fs=gs=0x18

  14.     lss SYMBOL_NAME(stack_start),%esp  -->执行过后ss=0x18,esp指向user_stack的结束处
  15. //检查A20地址线是否己开启:向0x0地址写1,如果0x100000(1M)也变成了1,说明A20地址线没有开启
  16. //前提条件-->0x100000(1M)处的数据是本代码的cld,机器码是0xfc不是1
  17.     xorl %eax,%eax
  18. 1:    incl %eax        # check that A20 really IS enabled
  19.     movl %eax,0x000000    # loop forever if it isn't
  20.     cmpl %eax,0x100000
  21.     je 1b

  22. /*
  23.  * Initialize eflags. Some BIOS's leave bits like NT set. This would
  24.  * confuse the debugger if this code is traced.
  25.  * XXX - best to initialize before switching to protected mode.
  26.  */
  27.     pushl $0
  28.     popfl
  29. //清BSS区
  30.     xorl %eax,%eax
  31.     movl $ SYMBOL_NAME(_edata),%edi    -->_edata是初始化数据区结束后的第1个地址;
  32.     movl $ SYMBOL_NAME(_end),%ecx      -->_end是未初始化数据区(bss)结束后的第1个地址位置。
  33.     subl %edi,%ecx                     -->所以end-edata=BSS
  34.     cld
  35.     rep
  36.     stosb                              -->rep stosb byte ptr es:[edi], al清BSS
  37. /*
  38.  * Do the decompression, and jump to the new kernel..
  39.  */
  40.     subl $16,%esp    # place for structure on the stack
  41.     movl %esp,%eax
  42.     pushl %esi    # real mode pointer as second arg
  43.     pushl %eax    # address of structure as first arg
  44.     call SYMBOL_NAME(decompress_kernel)
  45.     orl %eax,%eax                    -->解压结束后eax=1
  46.     jnz 3f                           -->不等于0跳到下面的3处
  47.     popl %esi    # discard address
  48.     popl %esi    # real mode pointer
  49.     xorl %ebx,%ebx
  50.     ljmp $(__KERNEL_CS), $0x100000

  51. /*
  52.  * We come here, if we were loaded high.
  53.  * We need to move the move-in-place routine down to 0x1000
  54.  * and then start it with the buffer addresses in registers,
  55.  * which we got from the stack.
  56.  */
  57. //将move_routint_stat从0x100082处移动到0x1000处
  58. 3:
  59.     movl $move_routine_start,%esi    -->执行后esi=0x00100082
  60.     movl $0x1000,%edi                -->edi: 0x00001000 目的地址
  61.     movl $move_routine_end,%ecx      -->执行后ecx=0x001000a7
  62.     subl %esi,%ecx                   -->执行后ecx=0x25也就是move_routeine的size 
  63.     addl $3,%ecx                     -->4字节对齐,为什么呢?因为下面是一次移动4个字节
  64.     shrl $2,%ecx                     -->现在己经处于32位模式了 
  65.     cld
  66.     rep                              -->rep movsd dword ptr es:[edi], dword ptr ds:[esi]
  67.     movsl

  68.     popl %esi    # discard the address   
  69.     popl %ebx    # real mode pointer     -->ebx=0x00090000 -->解压后内核第1部分的结束地址 
  70.     popl %esi    # low_buffer_start      -->esi=0x00002000 -->解压后内核第1部分的开始地址
  71.     popl %ecx    # lcount                -->ecx=0x0008e000 -->解压后内核第1部分的长度
  72.     popl %edx    # high_buffer_start     -->edx=0x001d4fc0 -->解压后内核第2部分的开始地址
  73.     popl %eax    # hcount                -->eax=0x0012c160 -->解压后内核第2部分的长度  
  74.     movl $0x100000,%edi                  -->edi=0x00100000 -->将这两部分合并到1M处
  75.     cli        # make sure we don't get interrupted
  76.     ljmp $(__KERNEL_CS), $0x1000         -->jmpf 0x0010:00001000跳到刚才移动的move_routine_start处

  77. /*
  78.  * Routine (template) for moving the decompressed kernel in place,
  79.  * if we were high loaded. This _must_ PIC-code !
  80.  */
  81. move_routine_start:
  82. //将解压后内核第1部分的数据,由[0x2000-0x90000]复制到[0x100000-0x18e000]
  83.     movl %ecx,%ebp       -->备份一下ecx
  84.     shrl $2,%ecx         -->复制的dst与src都在上面设置好了
  85.     rep
  86.     movsl                -->4字节4字节的复制
  87.     movl %ebp,%ecx       -->还原ecx,防止有不足4字节的内容
  88.     andl $3,%ecx
  89.     rep                  -->剩下不足4字节的按字节复制
  90.     movsb
  91. //将解压后内核第2部分的数据,由[0x001d4fc0-len]复制到[0x18e000-len]处
  92.     movl %edx,%esi       -->将edx(解压后内核第2部分的开始地址)设为src
  93.     movl %eax,%ecx       -->将eax(解压后内核第2部分的长度)赋给ecx
  94.     addl $3,%ecx         -->将ecx4字节对齐,第二部分多复制几个字节没有关系   
  95.     shrl $2,%ecx
  96.     rep                  -->rep movsd dword ptr es:[edi], dword ptr ds:[esi]
  97.     movsl
  98.     movl %ebx,%esi    # Restore setup pointer
  99.     xorl %ebx,%ebx
  100. //最后跳到真正内核的起始处运行
  101.     ljmp $(__KERNEL_CS), $0x100000 -->vmlinux终于组装完成了可以跳了
  102. 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的起始地址
阅读(1421) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~