在arch\i386\kernel\head.S文件中,自line 100开始有这么几行:
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 /* ..and set paging (PG) bit */
jmp 1f /* flush the prefetch-queue */
1:
movl $1f,%eax
jmp *%eax /* make sure eip is relocated */
1:
/* Set up the stack pointer */
lss stack_start,%esp
我看了很久都不明白第一次跳转到底是为了什么,情景分析那本书上说这是为了刷新指令预取队列,我把Intel手册翻了个遍也没找到关于预取队列的详细信息,维基百科上介绍的也不够详细。
在我终于弄明白之后,写一写我的分析过程,这份博客写写改改,用时一下午加一晚上才完工,一边写一边发现了很多自己得过且过的问题,写博客的过程也是查资料的过程,还是比较累的,如果写的有错误,欢迎指出。下面分析的过程也是思考的过程。
首先从setup.S看起,在arch\i386\boot\setup.S中line 113处有这么几行代码:
code32_start: # here loaders can put a different
# start address for 32-bit code.
#ifndef __BIG_KERNEL__
.long 0x1000 # 0x1000 = default for zImage
#else
.long 0x100000 # 0x100000 = default for big kernel
#endif
因为我们编译的是bzImage,所以code32_start标号处的数值为0x100000,占用四字节。
再看line 532处的几行代码:
# we get the code32 start address and modify the below 'jmpi'
# (loader may have changed it)
movl %cs:code32_start, %eax
movl %eax, %cs:code32
在执行这些代码时CPU还处于实模式,所以CS里面是段基址,不是selector!第一句是把code32_start处的一个双字(四字节)装入eax,这个双字的值就是0x100000;然后第二句把eax即0x100000赋值到code32标号所指的内存位置里。那么这个位置在哪呢?请继续看下面line 719的代码:
# NOTE: For high loaded big kernels we need a
# jmpi 0x100000,__KERNEL_CS
#
# but we yet haven't reloaded the CS register, so the default size
# of the target offset still is 16 bit.
# However, using an operant prefix (0x66), the CPU will properly
# take our 48 bit far pointer. (INTeL 80386 Programmer's Reference
# Manual, Mixing 16-bit and 32-bit code, page 16-6)
.byte 0x66, 0xea # prefix + jmpi-opcode
code32: .long 0x1000 # will be set to 0x100000
# for big kernels
.word __KERNEL_CS #这个数字是0x10
0x100000这个数字最终被写到了code32