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\n");

  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.\n");

  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...\n");

  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物理内存处执行,这次是真的将控制权交给内核了。

Linux启动过程的C语言代码分析的更多相关文章

  1. 深入理解Linux启动过程

    深入理解Linux启动过程       本文详细分析了Linux桌面操作系统的启动过程,涉及到BIOS系统.LILO 和GRUB引导装载程序,以及bootsect.setup.vmlinux等映像文件 ...

  2. 从Linux启动过程到android启动过程

    Linux启动过程: 1.首先开机给系统供电,此时硬件电路会产生一个确定的复位时序,保证cpu是最后一个被复位的器件.为什么cpu要最后被复位呢?因为 如果cpu第一个被复位,则当cpu复位后开始运行 ...

  3. Linux 启动过程详解

    目录 1. Linux启动过程 2. 启动过程概述 3. 引导加载阶段 4. 内核阶段 4.1 内核加载阶段 4.2 内核启动阶段 5. 早期的用户空间 6. 初始化过程 6.1 SysV init ...

  4. 转-Linux启动过程详解(inittab、rc.sysinit、rcX.d、rc.local)

    http://blog.chinaunix.net/space.php?uid=10167808&do=blog&id=26042   1)BIOS自检2)启动Grub/Lilo3)加 ...

  5. Linux启动过程详解(inittab、rc.sysinit、rcX.d、rc.local)

    启动第一步--加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它.这是因为BIOS中包含了CPU的相关信息.设备启动顺序信息.硬 ...

  6. Linux启动过程详解

    Linux启动过程详解 附上两张图,加深记忆 图1: 图2: 第一张图比较简洁明了,下面对第一张图的步骤进行详解: 加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的 ...

  7. 嵌入式Linux启动过程中的问题积累

    嵌入式Linux启动过程中的问题积累 Dongas 07-12-19 1.Bad Magic Number ## Booting image at 33000000 ... Bad Magic Num ...

  8. [linux 整理] linux启动过程3

    本文介绍linux启动过程的第三步 busybox--------------------> rc init busybox位置即内容 busybox/init/init.c 1.各种设置信号 ...

  9. Linux启动过程简述

    Linux启动过程: 图片来自:https://www.cnblogs.com/codecc/p/boot.html 简单来讲: 加载BIOS–>读取MBR–>Boot Loader–&g ...

随机推荐

  1. leetcode上的一些单链表

    147- 思路: 148- 思路: 24- 思路: 25- 思路: 21- 思路: 109- 思路: 237- 思路:

  2. PHP-文件和目录操作

    目录操作 创建目录:mkdir(目录地址, 权限, 是否递归创建 = false); 删除目录:rmdir(目录地址);(仅仅可以删除空目录,不支持递归删除) 移动(改名):rename(旧地址, 新 ...

  3. Java关于Math类的三个取整方法

    0x01 在java的Math类中有三个关于浮点数取整数的方法,分别是ceil (向上取整) floor(向下取整) round(四舍五入) 三个方法 0x02 ceil 向上取整,取整后总是比原来的 ...

  4. 用processing生成屏保程序

    想法 利用随机数控制圆圈的大小.位置以及颜色,可以产生随机的美感. 让小球动起来,并且在屏幕边界处产生反弹效果. 代码 1: float circle_x = (float) 0.0; 2: floa ...

  5. 一张图搞清楚Java异常机制

    下面是Java异常类的组织结构,红色区域的异常类表示是程序需要显示捕捉或者抛出的. Throwable Throwable是Java异常的顶级类,所有的异常都继承于这个类. Error,Excepti ...

  6. [轉]udp_sendmsg空指针漏洞分析 by wzt

    udp_sendmsg空指针漏洞分析    by wzt 漏洞描述: 由于Linux ipv4协议栈中udp_sendmsg()函数设计上存在缺陷, 导致struct rtable *rt以空指针形式 ...

  7. go中的string操作

    strings 判断字符串s是否以prefix开头 strings.HasPrefix(s string,preffix string) bool: 判断字符串s是否以suffix结尾 stirngs ...

  8. 自定义DbUtils通用类

    本实例使用C3P0连接池做连接,详见https://www.cnblogs.com/qf123/p/10097662.html开源连接池C3P0的使用 DBUtils.java package com ...

  9. 用户界面样式(cursor,resize,vertical-align,outline,文字超出显示省略号)

    1. 鼠标样式 cursor default: 小白(箭头)默认 pointer:小手 move:移动 text:文本 not-allowed:禁止 2. 轮廓线(表单外发光)outline 给表单添 ...

  10. http(python)

    1.client 1) httpie http -f POST example.org hello=World http POST http://192.168.200.251:55101/Api/C ...