zoukankan      html  css  js  c++  java
  • Linux启动过程的C语言代码分析

    1. main函数

    参见上方http://www.cnblogs.com/long123king/p/3543872.html,代码跳转到main函数。

    arch/x86/boot/main.c

       1: void main(void)
       2: {
       3:     /* First, copy the boot header into the "zeropage" */
       4:     copy_boot_params();
       5:  
       6:     /* Initialize the early-boot console */
       7:     console_init();
       8:     if (cmdline_find_option_bool("debug"))
       9:         puts("early console in setup code
    ");
      10:  
      11:     /* End of heap check */
      12:     init_heap();
      13:  
      14:     /* Make sure we have all the proper CPU support */
      15:     if (validate_cpu()) {
      16:         puts("Unable to boot - please use a kernel appropriate "
      17:              "for your CPU.
    ");
      18:         die();
      19:     }
      20:  
      21:     /* Tell the BIOS what CPU mode we intend to run in. */
      22:     set_bios_mode();
      23:  
      24:     /* Detect memory layout */
      25:     detect_memory();
      26:  
      27:     /* Set keyboard repeat rate (why?) */
      28:     keyboard_set_repeat();
      29:  
      30:     /* Query MCA information */
      31:     query_mca();
      32:  
      33:     /* Query Intel SpeedStep (IST) information */
      34:     query_ist();
      35:  
      36:     /* Query APM information */
      37: #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
      38:     query_apm_bios();
      39: #endif
      40:  
      41:     /* Query EDD information */
      42: #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
      43:     query_edd();
      44: #endif
      45:  
      46:     /* Set the video mode */
      47:     set_video();
      48:  
      49:     /* Do the last things and invoke protected mode */
      50:     go_to_protected_mode();
      51: }

    2. 进入保护模式

       1: /*
       2:  * Actual invocation sequence
       3:  */
       4: void go_to_protected_mode(void)
       5: {
       6:     /* Hook before leaving real mode, also disables interrupts */
       7:     realmode_switch_hook();
       8:  
       9:     /* Enable the A20 gate */
      10:     if (enable_a20()) {
      11:         puts("A20 gate not responding, unable to boot...
    ");
      12:         die();
      13:     }
      14:  
      15:     /* Reset coprocessor (IGNNE#) */
      16:     reset_coprocessor();
      17:  
      18:     /* Mask all interrupts in the PIC */
      19:     mask_all_interrupts();
      20:  
      21:     /* Actual transition to protected mode... */
      22:     setup_idt();
      23:     setup_gdt();
      24:     protected_mode_jump(boot_params.hdr.code32_start,
      25:                 (u32)&boot_params + (ds() << 4));
      26: }

    enable_a20,打开20位以上的地址线,因为在实模式下,最高寻址1MB,20位以上的地址线没有用到,处于关闭状态。当我们把内核映射准备好,并且加载到了1MB物理内存时,要想跳转到内核代码中进行执行,必须开启20位以上的地址线。

    设置GDT,这个GDT是临时的,实际上只设置了CS/DS段,而且是简单的0~4GB范围。

       1: struct gdt_ptr {
       2:     u16 len;
       3:     u32 ptr;
       4: } __attribute__((packed));
       5:  
       6: static void setup_gdt(void)
       7: {
       8:     /* There are machines which are known to not boot with the GDT
       9:        being 8-byte unaligned.  Intel recommends 16 byte alignment. */
      10:     static const u64 boot_gdt[] __attribute__((aligned(16))) = {
      11:         /* CS: code, read/execute, 4 GB, base 0 */
      12:         [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff),
      13:         /* DS: data, read/write, 4 GB, base 0 */
      14:         [GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff),
      15:         /* TSS: 32-bit tss, 104 bytes, base 4096 */
      16:         /* We only have a TSS here to keep Intel VT happy;
      17:            we don't actually use it for anything. */
      18:         [GDT_ENTRY_BOOT_TSS] = GDT_ENTRY(0x0089, 4096, 103),
      19:     };
      20:     /* Xen HVM incorrectly stores a pointer to the gdt_ptr, instead
      21:        of the gdt_ptr contents.  Thus, make it static so it will
      22:        stay in memory, at least long enough that we switch to the
      23:        proper kernel GDT. */
      24:     static struct gdt_ptr gdt;
      25:  
      26:     gdt.len = sizeof(boot_gdt)-1;
      27:     gdt.ptr = (u32)&boot_gdt + (ds() << 4);
      28:  
      29:     asm volatile("lgdtl %0" : : "m" (gdt));
      30: }

    protected_mode_jump又跳转到了汇编语言。

    3. protected_mode_jump

       1:  
       2:     .text
       3:     .code16
       4:  
       5: /*
       6:  * void protected_mode_jump(u32 entrypoint, u32 bootparams);
       7:  */
       8: GLOBAL(protected_mode_jump)
       9:     movl    %edx, %esi        # Pointer to boot_params table
      10:  
      11:     xorl    %ebx, %ebx
      12:     movw    %cs, %bx
      13:     shll    $4, %ebx
      14:     addl    %ebx, 2f
      15:     jmp    1f            # Short jump to serialize on 386/486
      16: 1:
      17:  
      18:     movw    $__BOOT_DS, %cx
      19:     movw    $__BOOT_TSS, %di
      20:  
      21:     movl    %cr0, %edx
      22:     orb    $X86_CR0_PE, %dl    # Protected mode
      23:     movl    %edx, %cr0
      24:  
      25:     # Transition to 32-bit mode
      26:     .byte    0x66, 0xea        # ljmpl opcode
      27: 2:    .long    in_pm32            # offset
      28:     .word    __BOOT_CS        # segment
      29: ENDPROC(protected_mode_jump)

    该段开始的.code16指令,表示这段代码依然是16位的实模式代码。

    使能CR0寄存器中的PE(Protection Enable)位,进入保护模式。

    movl    %cr0, %edx
    orb    $X86_CR0_PE, %dl    # Protected mode
    movl    %edx, %cr0

        # Transition to 32-bit mode
        .byte    0x66, 0xea        # ljmpl opcode
    2:    .long    in_pm32            # offset
        .word    __BOOT_CS        # segment

    ljmpl跳转到GDT中设置好的BOOT_CS段的in_pm32地址处执行,这时就已经是32位的保护模式了。

    4. in_pm32

    in_pm32标号代表的意思就是“在32位保护模式下运行”

       1: .code32
       2: .section ".text32","ax"
       3: AL(in_pm32)
       4: # Set up data segments for flat 32-bit mode
       5: movl    %ecx, %ds
       6: movl    %ecx, %es
       7: movl    %ecx, %fs
       8: movl    %ecx, %gs
       9: movl    %ecx, %ss
      10: # The 32-bit code sets up its own stack, but this way we do have
      11: # a valid stack if some debugging hack wants to use it.
      12: addl    %ebx, %esp
      13:  
      14: # Set up TR to make Intel VT happy
      15: ltr    %di
      16:  
      17: # Clear registers to allow for future extensions to the
      18: # 32-bit boot protocol
      19: xorl    %ecx, %ecx
      20: xorl    %edx, %edx
      21: xorl    %ebx, %ebx
      22: xorl    %ebp, %ebp
      23: xorl    %edi, %edi
      24:  
      25: # Set up LDTR to make Intel VT happy
      26: lldt    %cx
      27:  
      28: jmpl    *%eax            # Jump to the 32-bit entrypoint
      29: ROC(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

    回想protected_mode_jump中对于ecx的设置:

    1:

        movw    $__BOOT_DS, %cx
        movw    $__BOOT_TSS, %di

    将各个数据段,包括栈段都设置成BOOT_DS段选择子。

    设置栈指针:

    # 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的设置:

    xorl    %ebx, %ebx 【清0】
    movw    %cs, %bx 【当前的CS代码段基地】
    shll    $4, %ebx 【左移4位,取到当前CS代码段的起始地址】
    addl    %ebx, 2f 【将标号2处设置成esp的位置】

    ……

        # Transition to 32-bit mode
        .byte    0x66, 0xea        # ljmpl opcode
    2:    .long    in_pm32            # offset
        .word    __BOOT_CS        # segment


     

    那么标号2在什么位置呢?

    回想Setup.ld链接脚本【该脚本用于指导链接器生成setup可执行程序】,以及protected_mode_jump开头的段定义

        .text
        .code16

    /*
    * void protected_mode_jump(u32 entrypoint, u32 bootparams);
    */
    GLOBAL(protected_mode_jump)

    . = 0;
    .bstext        : { *(.bstext) }
    .bsdata        : { *(.bsdata) }

    . = 497;
    .header        : { *(.header) }
    .entrytext    : { *(.entrytext) }
    .inittext    : { *(.inittext) }
    .initdata    : { *(.initdata) }
    __end_init = .;

    .text        : { *(.text) }
    .text32        : { *(.text32) }

    . = ALIGN(16);
    .rodata        : { *(.rodata*) }

    因此,32位的栈的栈指针被设置在了32位代码段的下部。

    然后是跳转到下一阶段的程序执行

        jmpl    *%eax            # Jump to the 32-bit entrypoint
    ENDPROC(in_pm32)

    那么eax寄存器的值是多少呢?

    回想调用protected_mode_jump时的参数

    /* Actual transition to protected mode... */
    setup_idt();
    setup_gdt();
    protected_mode_jump(boot_params.hdr.code32_start,
                (u32)&boot_params + (ds() << 4));

    因此跳转到了code32_start地址处的函数执行(因为%eax前面有*,代表解引用,因此是跳转到该指针指向的函数执行)。

    code32_start:                # here loaders can put a different
                        # start address for 32-bit code.
            .long    0x100000    # 0x100000 = default for big kernel

    也就是跳转到1MB物理内存处执行,这次是真的将控制权交给内核了。

  • 相关阅读:
    将博客搬至CSDN
    java面试题(一)
    大数据面试题(二)
    大数据面试题(一)
    现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?
    java序列化与反序列化总结
    Hashmap,Set,Map,List,ArrayList的区别
    Windows下安装Scrapy
    Eclipse java上手
    lucene文件检索(1)-linux java环境配置
  • 原文地址:https://www.cnblogs.com/long123king/p/3545688.html
Copyright © 2011-2022 走看看