linux内核学习(11)启动全过程概述之二

这次该进入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 386/486
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和操作系统的原理性的书籍,里面有讲。你会问,这里为什么不直接说说呢,恐怕你还不晓得,说这些估计得说到明年都说不 完,知识点很多,其实这些内容不会困难,主要是有些多。再次强调了,我们关注的重点,熟练掌握内核分析方法,寻找设备模型。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值