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

原创 2011年01月07日 14:13:00

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

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

相关文章推荐

linux内核学习(10)启动全过程概述之一

下面这段时间,我要好好分析一下内核启动过程的源代码,怎么来分析,而且更好的和网友们进行交流,我想,最好的方式莫过于采用赵炯博士编著的《linux 内核完全注释》一书的编写规范。将中文注释夹杂在代码中...

linux内核学习(12)启动全过程概述之三

进入了vmlinux内核映像部分,就进入了arch/x86/compressed/head_32.S。这个文件主要工作就是解压了,将真正的内核给释放出来,不过还得把这个文件分析完,内核才能出世。来自a...

linux内核学习(12)启动全过程概述之三

进入了vmlinux内核映像部分,就进入了arch/x86/compressed/head_32.S。这个文件主要工作就是解压了,将真正的内核给释放出来,不过还得把这个文件分析完,内核才能出世。来自a...

linux内核学习(9)启动全过程概述之内核映像结构

启动过程这块内容还蛮多的,因此得慢慢来分析,在此之前我也收集到了很多资料,这里将这些整理加上自己的一些理解。好不容易啊,总算开始内核源代码之旅了,我们要珍惜这来之不易的这个时刻。 要得到内核结构,我...

ubifs文件系统的制作过程&&遇到的问题及解决方案总结---之二“Linux内核中添加对UBIFS文件系统的支持”

一、移植环境 §主机:vmware-redhat5      §开发板:mini2440--256MB nandflash §kernel:2.6.29     编译器:arm-linux-gc...

11、Linux内核启动过程和Bootloader(总述)

1.Linux内核启动过程概述    一个嵌入式 Linux 系统从软件角度看可以分为四个部分:引导加载程序(Bootloader),Linux 内核,文件系统,应用程序。其中 Bootloader...
  • mianyy
  • mianyy
  • 2011年08月23日 14:55
  • 275

linux内核内存管理学习之二(物理内存管理--伙伴系统)

linux使用伙伴系统来管理物理内存页。 一、伙伴系统原理 1. 伙伴关系 定义:由一个母实体分成的两个各方面属性一致的两个子实体,这两个子实体就处于伙伴关系。在操作系统分配内存的过程中,一个内存块...

linux内核学习之二:编译内核

在linux内核学习系列的第一课中讲述了搭建学习环境的过程(http://www.cnblogs.com/xiongyuanxiong/p/3523306.html),环境搭好后,马上就进入到下一环节...

【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之二--Linux中断的组织形式

转载:http://blog.csdn.net/sailor_8318/archive/2008/07/09/2627136.aspx 深入剖析Linux中断机制之二 --Linux中断的组织形式...

linux内核启动过程学习总结

下面是学习linux内核启动过程的记录 平台是:powerpc mpc8548 + linux2.6.23 内核           通用寄存器的作用r0 :在函数开始时使用r1 :...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:linux内核学习(11)启动全过程概述之二
举报原因:
原因补充:

(最多只允许输入30个字)