这次该进入main函数了,在arch/x86/boot/main.c中。

void main(void)
 {
     /* First, copy the boot header into the "zeropage" */
     copy_boot_params();             # 将hdr的参数拷贝到结构体boot_params.hdr中,在文件boot/bootparam.h里

     /* Initialize the early-boot console */
     console_init();
     if (cmdline_find_option_bool("debug"))
         puts("early console in setup code/n");

     /* End of heap check */
     init_heap();

     /* Make sure we have all the proper CPU support */
     if (validate_cpu()) {
         puts("Unable to boot - please use a kernel appropriate "
              "for your CPU./n");
         die();
     }

     /* Tell the BIOS what CPU mode we intend to run in. */
     set_bios_mode();

     /* Detect memory layout */
     detect_memory();

     /* Set keyboard repeat rate (why?) */
     keyboard_set_repeat();

     /* Query MCA information */
     query_mca();

     /* Query Intel SpeedStep (IST) information */
     query_ist();

     /* Query APM information */
 #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
     query_apm_bios();
 #endif

     /* Query EDD information */
 #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
     query_edd();
 #endif

     /* Set the video mode */
     set_video();

     /* Do the last things and invoke protected mode */
     go_to_protected_mode();
 }

 这个函数都可以看懂,至于各个函数具体实现有兴趣可以深入研究,这里主要还是要看这个函数go_to_protected_mode()。

 来自boot/pm.c:
 /*
  * Actual invocation sequence
  */
 void go_to_protected_mode(void)
 {
     /* Hook before leaving real mode, also disables interrupts */
     realmode_switch_hook();       # 这里就是调用了实模式下设置的函数地址,可以看hdr的realmode_swtch

     /* Enable the A20 gate */
     if (enable_a20()) {
         puts("A20 gate not responding, unable to boot.../n");
         die();
     }

     /* Reset coprocessor (IGNNE#) */
     reset_coprocessor();

     /* Mask all interrupts in the PIC */
     mask_all_interrupts();

     /* Actual transition to protected mode... */
     setup_idt();
     setup_gdt();           # 可以看看gdt的布局
     protected_mode_jump(boot_params.hdr.code32_start,
                 (u32)&boot_params + (ds() << 4));               # 注意进入保护模式后会采用段式管理,而cs,ds这些段寄存器选择子指向的首地址都
                                                                                      # 是0,因此要将传入的参数转换为线性地址
 }

 protected_mode_jump这个函数是用汇编写的。

 来自boot/pmjump.S:
 GLOBAL(protected_mode_jump)
     movl    %edx, %esi        # Pointer to boot_params table
  # 指向了传进来的第二参数,这里好像违背了C调用约定,关于调用约定这块一直还是个疑问,
 # 由于对分析代码没有什么障碍,于是可以先搁着,后面会找到相关资料说明的,
 # 这里呢,eax=第一个参数,edx=第二个参数

     xorl    %ebx, %ebx           # 清0
     movw    %cs, %bx            # 将实模式下的代码段放入bx中
     shll    $4, %ebx                /*left move*/       # 左移4为转换为线性地址
     addl    %ebx, 2f                /*[2f]=$in_pm32*/     
 # 注意这里的2f指的是下标指向的内存,可以看到下面它指向了in_pm32,也就是说,2f指向的long型内存保存了
 # 了in_pm32的地址,这里的addl指令就是将in_pm32实模式地址转换成线性地址,于是要加上原来的段地址
     jmp    1f            # Short jump to serialize on 38**86
 1:

     movw    $__BOOT_DS, %cx        # ds段选择子
     movw    $__BOOT_TSS, %di       # tss段选择子

     movl    %cr0, %edx
     orb    $X86_CR0_PE, %dl    # Protected mode  /*X86_CR0_PE=0x00000001*/
     movl    %edx, %cr0
 # 进入保护模式的标志就是将cr0寄存器的位0置位即可,然后进入保护模式

     # Transition to 32-bit mode
     .byte    0x66, 0xea        # ljmpl opcode
 2:    .long    in_pm32            # offset
     .word    __BOOT_CS        # segment
 # 整个就是一个长跳转指令,也就是跳到in_pm32下执行,注意现在已经在保护模式了
 ENDPROC(protected_mode_jump)

     .code32
     .section ".text32","ax"
 GLOBAL(in_pm32)
     # Set up data segments for flat 32-bit mode
     movl    %ecx, %ds
     movl    %ecx, %es
     movl    %ecx, %fs
     movl    %ecx, %gs
     movl    %ecx, %ss
     # The 32-bit code sets up its own stack, but this way we do have
     # a valid stack if some debugging hack wants to use it.
     addl    %ebx, %esp
 # 设置栈底,ebx保存是原来实模式下的cs段

     # Set up TR to make Intel VT happy
     ltr    %di          # 加载TR,实际只是个伪装

     # Clear registers to allow for future extensions to the
     # 32-bit boot protocol
     xorl    %ecx, %ecx
     xorl    %edx, %edx
     xorl    %ebx, %ebx
     xorl    %ebp, %ebp
     xorl    %edi, %edi

     # Set up LDTR to make Intel VT happy
     lldt    %cx          # 加载LDT,实际只是个伪装

     jmpl    *%eax            # Jump to the 32-bit entrypoint
 # eax保存着protected_mode_jump函数传进来的第一个参数,也就是boot_params.hdr.code32_start,这里存着vmlinux的首地址
 ENDPROC(in_pm32)



这里一个长跳转直接将我们带到了另外一个世界,因为这个世界很孤独,它虽然是内核的一部分,但是不是内核的身体,要想靠近内核的更核心,必须进入vmlinux。即将我们将到带自解压的内核中去旅行。这里,我还是得再次说明,启动部分的代码涉及很多内容,如果你连最基本的什么是GDT、LDT、TSS,那些最好去看看关于CPU和操作系统的原理性的书籍,里面有讲。你会问,这里为什么不直接说说呢,恐怕你还不晓得,说这些估计得说到明年都说不完,知识点很多,其实这些内容不会困难,主要是有些多。再次强调了,我们关注的重点,熟练掌握内核分析方法,寻找设备模型。

还要提醒一点,我们已经进入保护模式,但只是启动了分段模式,分页还没启动呢。