QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 2988|回复: 3

关于head.S的一点说明,希望对大家有点用处

[复制链接]
发表于 2003-4-1 13:17:34 | 显示全部楼层 |阅读模式
里面还有好多疑惑,恳请高人赐教

[code:1]
/*
*  linux/boot/head.S
*
*  Copyright (C) 1991, 1992, 1993  Linus Torvalds
*/

/*
*  head.S contains the 32-bit startup code.
*
* NOTE!!! Startup happens at absolute address 0x00001000, which is also where
* the page directory will exist. The startup code will be overwritten by
* the page directory. [According to comments etc elsewhere on a compressed
* kernel it will end up at 0x1000 + 1Mb I hope so as I assume this. - AC]
*
* Page 0 is deliberately kept safe, since System Management Mode code in
* laptops may need to access the BIOS data stored there.  This is also
* useful for future device drivers that either access the BIOS via VM86
* mode.
*/

/*
* High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
*/
.text

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

    .globl startup_32
   
startup_32:
    cld
    cli
    movl $(__KERNEL_DS),%eax
    movl %eax,%ds
    movl %eax,%es
    movl %eax,%fs
    movl %eax,%gs            # DS/ES/FS/GS全都变为0x10, 即GDT中的第三项.

    lss SYMBOL_NAME(stack_start),%esp  # SYMBOL_NAME是一个宏, 目前没意义,
                             # stack_start定义在misc.c中, 现在SS:ESP就
                             # 指向misc.c中的user_stack了.
    xorl %eax,%eax
1:  incl %eax                # 检测地址线A20是否已打开, 方法与setup.S中相同
    movl %eax,0x000000       # 不行就死循环吧(当然不会啦).
    cmpl %eax,0x100000
    je 1b                    # 奇怪的语法, 这个'b'难道是说'Befor'.

/*
* 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和_end是使用连接器ld时通过
    movl $ SYMBOL_NAME(_end),%ecx   # 参数文件'text'指定的BSS段的开始
    subl %edi,%ecx           # 和结束地址. 但我找不到这个'text'??
    cld
    rep
    stosb                    # 将BSS段内容清零, WHY?
/*
* 解压内核, 完成后跳到新内核去执行..
*/
    subl $16,%esp            # 堆栈的高16字节留出来给一个structure
    movl %esp,%eax           # EAX指向那个structure的地址
    pushl %esi               # ESI在setup.S已设为了0x90000, 这里把它作为
                             # 函数的第二个参数
    pushl %eax               # 将EAX作为函数调用的第一个参数
    call SYMBOL_NAME(decompress_kernel)
                             # 调用位于misc.c中的decompress_kernel.
    orl  %eax,%eax           # EAX就是misc.c中的decompress_kernel返回的
                             # high_loaded, 不是'0'代表解压的是个大内核.
    jnz  3f                  # 'f'搞不好是指'Follow'.
    popl %esi                # 小内核就不需再调整了, 已放在0x100000
    popl %esi                # ESI又还原为0x90000
    xorl %ebx,%ebx
    ljmp $(__KERNEL_CS), $0x100000  # 真的可以去执行了
# 疑问, 解压小内核时不会把上面的代码覆盖掉吗, 要知道它们也在0x100000呀???

/*
* 如果是大内核将执行以下的程序段. 注意以下使用数据时段址都是'0x0'.
* 提示: 对于小内核, 解压程序会把它从0x100000解压到0x10000开始的地方, 然后
* 再把它从0x10000搬到0x100000; 对于大内核, 低端内存可就放不下, 解压程序会
* 先往0x10000放, 放不下的时候再往比0x100000更高的地址放, 所以此程序在调用
* 了解压程序后还必须把这两段组合到0x100000去. 所以呢, 实际进行组合的程序
* 段move_routine_start当然不能留在原地(即0x100000后一点的地方)执行了, 不
* 然就会在执行时被覆盖了, 解决方法就是把它搬到不受影响的地方(0x1000).
*/
3:
    movl $move_routine_start,%esi
    movl $0x1000,%edi
    movl $move_routine_end,%ecx
    subl %esi,%ecx
    addl $3,%ecx             # 加'3'之后缩小'2'倍, 这样就转换为32位的
    shrl $2,%ecx             # 大小了; 用movsl一次传4字节总该快一点吧.
    cld                      # 将move_routine_start至move_routine_end
    rep                      # 的代码拷到0x1000. 这一段代码是用来组合
    movsl                    # 大内核的两段到0x100000的.

    popl %esi                # 这是调用函数时的第一个参数, 没用了
    popl %ebx                # 第二个参数, 也就是0x90000
# 执行以下指令时ESP已经指向调用decompress_kernel时设置的那个struct了,
# 现在清楚这个struct的内容了吧.
    popl %esi                # 大内核解压后在低端的开始地址
    popl %ecx                # 大内核解压后在低端的长度(单位为字节)
    popl %edx                # 大内核解压后在高端的开始地址
    popl %eax                # 大内核解压后在高端的长度(单位为字节)
    movl $0x100000,%edi
    cli                      # 确保不受中断影响(已经做过N次了)
    ljmp $(__KERNEL_CS), $0x1000 # 跳到现在move_routine_start所处的地方
/*
* Routine (template) for moving the decompressed kernel in place,
* if we were high loaded. This _must_ PIC-code !
*/
move_routine_start:
    movl %ecx,%ebp
    shrl $2,%ecx             # 此时EDI为0x100000,
    rep                      # ESI为大内核在低端的开始地址,
    movsl                    # ECX大内核在低端的长度(单位为4字节)
    movl %ebp,%ecx
    andl $3,%ecx             # 如果大内核在低端的长度不是4的倍数, 这里就
    rep                      # 算出还有几个字节没移动(可能为1-3), 然后把
    movsb                    # 它们也移过去.
    movl %edx,%esi           # 注意: 当ECX为'0'时'rep movsb'指令是不会移动的
    movl %eax,%ecx
    addl $3,%ecx
    shrl $2,%ecx
    rep                      # 这一段就是移动大内核在高端的部分了
    movsl                    # 奇怪的是它不怕有1-3字节没移?
    movl %ebx,%esi           # 同小内核的处理一样, 最后ESI一定恢复为0x90000
    xorl %ebx,%ebx           # EBX一定设为'0'
    ljmp $(__KERNEL_CS), $0x100000  # 下面就进入main.c中的start_kernel.
move_routine_end:
[/code:1]
发表于 2003-4-1 20:21:05 | 显示全部楼层
学习中...
多谢了!!!
回复

使用道具 举报

发表于 2003-12-27 12:01:03 | 显示全部楼层
je 1b                    # 奇怪的语法, 这个'b'难道是说'Before'.
不奇怪呀。。。哥们。b可能是backwards,表示如果条件成立就转移到向后的第一个1处。
je 1f                    #f是forward 表示如果条件成立就转移到向前的第一个1处。
注意:
     这个向前,向后是以程序的执行方向做为标准的
  
回复

使用道具 举报

发表于 2003-12-27 20:26:12 | 显示全部楼层
------------------------------
将BSS段内容清零, WHY?
------------------------------
内核跟其它程序一样有个bss段,存储全局变量和静态变量,使用前要清零的。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2024-11-16 09:34 , Processed in 0.062396 second(s), 16 queries .

© 2021 Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表