关于head.S的一点说明,希望对大家有点用处
里面还有好多疑惑,恳请高人赐教/*
*linux/boot/head.S
*
*Copyright (C) 1991, 1992, 1993Linus 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'代表解压的是个大内核.
jnz3f # '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:
学习中...
多谢了!!! je 1b # 奇怪的语法, 这个'b'难道是说'Before'.
不奇怪呀。。。哥们。b可能是backwards,表示如果条件成立就转移到向后的第一个1处。
je 1f #f是forward 表示如果条件成立就转移到向前的第一个1处。
注意:
这个向前,向后是以程序的执行方向做为标准的
:P:P ------------------------------
将BSS段内容清零, WHY?
------------------------------
内核跟其它程序一样有个bss段,存储全局变量和静态变量,使用前要清零的。
页:
[1]