200 /**********************************************************************
201 *
202 * This is where the bootblocks start us, set the ball rolling...
203 *
204 */
205 NON_GPROF_ENTRY(btext)
从/boot/kernel/kernel中可以读出btext的链接地址:
# readelf -a /boot/kernel/kernel | grep btext
6870: c0458a30 0 FUNC GLOBAL DEFAULT 5 btext
26381: c0458a30 0 FUNC GLOBAL DEFAULT 5 btext
因此,在物理地址0x458a30处设置断点,单步跟踪locore.s中的初始化代码。程序运行至此
的cpu主要寄存器的内容如下:
rax: 0x00000000:00458a30 rcx: 0x00000000:a0200000
rdx: 0x00000000:000488a0 rbx: 0x00000000:00458a30
rsp: 0x00000000:0009e844 rbp: 0x00000000:00094884
rsi: 0x00000000:000610e4 rdi: 0x00000000:0005b9cc
r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
r10: 0x00000000:00000000 r11: 0x00000000:00000000
r12: 0x00000000:00000000 r13: 0x00000000:00000000
r14: 0x00000000:00000000 r15: 0x00000000:00000000
rip: 0x00000000:00458a30
eflags 0x00000002
首先是向0x472写入0x1234,告知bios下次为热引导:
216 /* Tell the bios to warmboot next time */
217 movw $0x1234,0x472
构建一个新的栈帧:
220 /* Set up a real frame in case the double return in newboot is executed. */
221 pushl %ebp
222 movl %esp, %ebp
此时cpu主要寄存器的内容如下:
rax: 0x00000000:00458a30 rcx: 0x00000000:a0200000
rdx: 0x00000000:000488a0 rbx: 0x00000000:00458a30
rsp: 0x00000000:0009e840 rbp: 0x00000000:0009e840
rsi: 0x00000000:000610e4 rdi: 0x00000000:0005b9cc
r8 : 0x00000000:00000000 r9 : 0x00000000:00000000
r10: 0x00000000:00000000 r11: 0x00000000:00000000
r12: 0x00000000:00000000 r13: 0x00000000:00000000
r14: 0x00000000:00000000 r15: 0x00000000:00000000
rip: 0x00000000:00458a3c
eflags 0x00000002
将PSL_KRENEL赋给eflags:
224 /* Don't trust what the BIOS gives for eflags. */
225 pushl $PSL_KERNEL
226 popfl
PSL_KERNEL是在/sys/i386/include/psl.h中定义的:
60 /*
61 * The i486 manual says that we are not supposed to change reserved flags,
62 * but this is too much trouble since the reserved flags depend on the cpu
63 * and setting them to their historical values works in practice.
64 */
65 #define PSL_RESERVED_DEFAULT 0x00000002
66
67 /*
68 * Initial flags for kernel and user mode. The kernel later inherits
69 * PSL_I and some other flags from user mode.
70 */
71 #define PSL_KERNEL PSL_RESERVED_DEFAULT
72 #define PSL_USER (PSL_RESERVED_DEFAULT | PSL_I)
将ds的内容赋给fs和gs:
228 /*
229 * Don't trust what the BIOS gives for %fs and %gs. Trust the bootstrap
230 * to set %cs, %ds, %es and %ss.
231 */
232 mov %ds, %ax
233 mov %ax, %fs
234 mov %ax, %gs
236 /*
237 * Clear the bss. Not all boot programs do it, and it is our job anyway.
238 *
239 * XXX we don't check that there is memory for our bss and page tables
240 * before using it.
241 *
242 * Note: we must be careful to not overwrite an active gdt or idt. They
243 * inactive from now until we switch to new ones, since we don't load any
244 * more segment registers or permit interrupts until after the switch.
245 */
246 movl $R(end),%ecx
247 movl $R(edata),%edi
248 subl %edi,%ecx
249 xorl %eax,%eax
250 cld
251 rep
252 stosb
根据readelf -a kernel的结果,end的地址是0xc0c06020,由于KERNBASE是
0xc0000000,此处赋给ecx的就是0xc06020。edata的地址是0xc0bab9a0,这实际上就是
.bss段的起始地址,此处赋给edi的就是0xbab9a0。ecx减去edi之后的内容是0x5a680,
这是从.bss段起始地址到end地址之间的字节数。随后将eax清0,作为后续清0操作的
写入值。cld保证edi递增变化。stosb将al的内容写入edi指向的位置。这段代码从
0xc0bab9a0开始连续写入0x5a680个字节的0,从而实现将.bss段清0的目的。
253
254 call recover_bootinfo
255
调用recover_bootinfo获取由loader传入的引导信息。
487 movl 28(%ebp),%ebx /* &bootinfo.version */
488 movl BI_VERSION(%ebx),%eax
489 cmpl $1,%eax /* We only understand version 1 */
490 je 1f
491 movl $1,%eax /* Return status */
492 leave
493 /*
494 * XXX this returns to our caller's caller (as is required) since
495 * we didn't set up a frame and our caller did.
496 */
497 ret
locore.s入口btext的调用格式为(*btext)(howto, bootdev, 0, 0, 0, &bootinfo),在bootinfo
之后压栈的有5个参数,占20个字节,在加上返回地址和在调用recover_bootinfo之前压栈的ebp,
一共有28个字节,因此从当前ebp位置上溯28个字节就是bootinfo结构体的起始地址。上述代码
从bootinfo结构体中取出bi_version字段的内容,判断其是否为1,仅当版本为1时才继续处理。
500 /*
501 * If we have a kernelname copy it in
502 */
503 movl BI_KERNELNAME(%ebx),%esi
504 cmpl $0,%esi
505 je 2f /* No kernelname */
506 movl $MAXPATHLEN,%ecx /* Brute force!!! */
507 movl $R(kernelname),%edi
508 cmpb $'/',(%esi) /* Make sure it starts with a slash */
509 je 1f
510 movb $'/',(%edi)
511 incl %edi
512 decl %ecx
从bootinfo结构体中取出bi_kernelname字段,写入esi。MAXPATHLEN是最大路径长度,定义为1024,
这是kernel中的kernelname数组的尺寸。将该数组相对于KERNBASE偏移地址写入edi,并判断由
bootinfo传入的bi_kernelname是否以"/"开始,若是,则将剩余部分拷入kernelname数组。
519 /*
520 * Determine the size of the boot loader's copy of the bootinfo
521 * struct. This is impossible to do properly because old versions
522 * of the struct don't contain a size field and there are 2 old
523 * versions with the same version number.
524 */
525 movl $BI_ENDCOMMON,%ecx /* prepare for sizeless version */
526 testl $RB_BOOTINFO,8(%ebp) /* bi_size (and bootinfo) valid? */
527 je got_bi_size /* no, sizeless version */
528 movl BI_SIZE(%ebx),%ecx
将bootinfo中肯定会存在的字段的结束位置取出,写入ecx,实际上就是前三个字段,12个字节。
RB_BOOTINFO在/sys/sys/reboot.h中定义为0x80000000,是一个表示是否传入了完整的bootinfo
结构体信息的标志。若传入了完整的bootinfo结构体信息,则将bi_size字段的内容赋给ecx。
531 /*
532 * Copy the common part of the bootinfo struct
533 */
534 movl %ebx,%esi
535 movl $R(bootinfo),%edi
536 cmpl $BOOTINFO_SIZE,%ecx
537 jbe got_common_bi_size
538 movl $BOOTINFO_SIZE,%ecx
539 got_common_bi_size:
540 cld
541 rep
542 movsb
比较传入的bi_size字段的内容与bootinfo结构体的大小是否一致,若传入bi_size小于等于bootinfo
结构体的大小,则以bi_size为准,否则以bootinfo结构体的大小为准。将传入的bootinfo信息拷贝
到bootinfo结构体中。
562 /*
563 * The old style disk boot.
564 * (*btext)(howto, bootdev, cyloffset, esym);
565 * Note that the newer boot code just falls into here to pick
566 * up howto and bootdev, cyloffset and esym are no longer used
567 */
568 olddiskboot:
569 movl 8(%ebp),%eax
570 movl %eax,R(boothowto)
571 movl 12(%ebp),%eax
572 movl %eax,R(bootdev)
573
574 ret
分别从入参中取出相关信息存入boothowto和bootdev变量。返回btext。
256 /* Get onto a stack that we can trust. */
257 /*
258 * XXX this step is delayed in case recover_bootinfo needs to return via
259 * the old stack, but it need not be, since recover_bootinfo actually
260 * returns via the old frame.
261 */
262 movl $R(tmpstk),%esp
tmpstk是在本文件中定义的一块8192字节的连续空间,此处将esp指向这块空间。
实际上,这块空间就紧邻在bootinfo结构体的下面。
291 call identify_cpu
获取cpu识别信息。
292 call create_pagetables
创建第一个页面目录及其页表。
706 /**********************************************************************
707 *
708 * Create the first page directory and its page tables.
709 *
710 */
711
712 create_pagetables:
713
714 /* Find end of kernel image (rounded up to a page boundary). */
715 movl $R(_end),%esi
将kernel的结束地址赋给esi,示例中为0xc06020。
717 /* Include symbols, if any. */
718 movl R(bootinfo+BI_ESYMTAB),%edi
719 testl %edi,%edi
720 je over_symalloc
721 movl %edi,%esi
722 movl $KERNBASE,%edi
723 addl %edi,R(bootinfo+BI_SYMTAB)
724 addl %edi,R(bootinfo+BI_ESYMTAB)
先把bootinfo结构体中的bi_esymtab字段的内容赋给edi,并测试其是否为0,
如果为0,表示传入的bootinfo中没有这个字段。
若非0,则将此内容赋给esi,随后将KERNBASE赋给edi。bootinfo结构体的地址是
0xb00460,bi_symtab和bi_esymtab字段的地址分别是0xb004a0和0xb004a4,这部分结构体
的内容如下:
0xb00460 : 0x00000001 0x00065f24 0x00000000 0x00000000
0xb00470 : 0x0208fe3f 0x004f010f 0x004f010f 0x004f010f
0xb00480 : 0x004f010f 0x004f010f 0x004f010f 0x004f010f
0xb00490 : 0x00000054 0x00008001 0x0000027f 0x0003fbc0
0xb004a0 : 0x00c06020 0x00d06518
723行和724行就是把这两个地址中存放的物理地址加上KERNBASE,得到虚拟地址。
完成添加操作之后同一内存区域的内容如下:
0xb00460 : 0x00000001 0x00065f24 0x00000000 0x00000000
0xb00470 : 0x0208fe3f 0x004f010f 0x004f010f 0x004f010f
0xb00480 : 0x004f010f 0x004f010f 0x004f010f 0x004f010f
0xb00490 : 0x00000054 0x00008001 0x0000027f 0x0003fbc0
0xb004a0 : 0xc0c06020 0xc0d06518
727 /* If we are told where the end of the kernel space is, believe it. */
728 movl R(bootinfo+BI_KERNEND),%edi
729 testl %edi,%edi
730 je no_kernend
731 movl %edi,%esi
732 no_kernend:
733
734 addl $PDRMASK,%esi /* Play conservative for now, and */
735 andl $~PDRMASK,%esi /* ... wrap to next 4M. */
736 movl %esi,R(KERNend) /* save end of kernel */
737 movl %esi,R(physfree) /* next free page is at end of kernel */
将bootinfo结构体中的bi_kernend字段的内容赋给edi,并测试其是否为0,
如果为0,表示传入的bootinfo中没有这个字段。
