1. main函数

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

arch/x86/boot/main.c

  1. 1: void main(void)

  1. 2: {

  1. 3: /* First, copy the boot header into the "zeropage" */

  1. 4: copy_boot_params();

  1. 5: 

  1. 6: /* Initialize the early-boot console */

  1. 7: console_init();

  1. 8: if (cmdline_find_option_bool("debug"))

  1. 9: puts("early console in setup code\n");

  1. 10: 

  1. 11: /* End of heap check */

  1. 12: init_heap();

  1. 13: 

  1. 14: /* Make sure we have all the proper CPU support */

  1. 15: if (validate_cpu()) {

  1. 16: puts("Unable to boot - please use a kernel appropriate "

  1. 17: "for your CPU.\n");

  1. 18: die();

  1. 19: }

  1. 20: 

  1. 21: /* Tell the BIOS what CPU mode we intend to run in. */

  1. 22: set_bios_mode();

  1. 23: 

  1. 24: /* Detect memory layout */

  1. 25: detect_memory();

  1. 26: 

  1. 27: /* Set keyboard repeat rate (why?) */

  1. 28: keyboard_set_repeat();

  1. 29: 

  1. 30: /* Query MCA information */

  1. 31: query_mca();

  1. 32: 

  1. 33: /* Query Intel SpeedStep (IST) information */

  1. 34: query_ist();

  1. 35: 

  1. 36: /* Query APM information */

  1. 37: #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)

  1. 38: query_apm_bios();

  1. 39: #endif

  1. 40: 

  1. 41: /* Query EDD information */

  1. 42: #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)

  1. 43: query_edd();

  1. 44: #endif

  1. 45: 

  1. 46: /* Set the video mode */

  1. 47: set_video();

  1. 48: 

  1. 49: /* Do the last things and invoke protected mode */

  1. 50: go_to_protected_mode();

  1. 51: }

2. 进入保护模式

  1. 1: /*

  1. 2: * Actual invocation sequence

  1. 3: */

  1. 4: void go_to_protected_mode(void)

  1. 5: {

  1. 6: /* Hook before leaving real mode, also disables interrupts */

  1. 7: realmode_switch_hook();

  1. 8: 

  1. 9: /* Enable the A20 gate */

  1. 10: if (enable_a20()) {

  1. 11: puts("A20 gate not responding, unable to boot...\n");

  1. 12: die();

  1. 13: }

  1. 14: 

  1. 15: /* Reset coprocessor (IGNNE#) */

  1. 16: reset_coprocessor();

  1. 17: 

  1. 18: /* Mask all interrupts in the PIC */

  1. 19: mask_all_interrupts();

  1. 20: 

  1. 21: /* Actual transition to protected mode... */

  1. 22: setup_idt();

  1. 23: setup_gdt();

  1. 24: protected_mode_jump(boot_params.hdr.code32_start,

  1. 25: (u32)&boot_params + (ds() << 4));

  1. 26: }

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

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

  1. 1: struct gdt_ptr {

  1. 2: u16 len;

  1. 3: u32 ptr;

  1. 4: } __attribute__((packed));

  1. 5: 

  1. 6: static void setup_gdt(void)

  1. 7: {

  1. 8: /* There are machines which are known to not boot with the GDT

  1. 9: being 8-byte unaligned. Intel recommends 16 byte alignment. */

  1. 10: static const u64 boot_gdt[] __attribute__((aligned(16))) = {

  1. 11: /* CS: code, read/execute, 4 GB, base 0 */

  1. 12: [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff),

  1. 13: /* DS: data, read/write, 4 GB, base 0 */

  1. 14: [GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff),

  1. 15: /* TSS: 32-bit tss, 104 bytes, base 4096 */

  1. 16: /* We only have a TSS here to keep Intel VT happy;

  1. 17: we don't actually use it for anything. */

  1. 18: [GDT_ENTRY_BOOT_TSS] = GDT_ENTRY(0x0089, 4096, 103),

  1. 19: };

  1. 20: /* Xen HVM incorrectly stores a pointer to the gdt_ptr, instead

  1. 21: of the gdt_ptr contents. Thus, make it static so it will

  1. 22: stay in memory, at least long enough that we switch to the

  1. 23: proper kernel GDT. */

  1. 24: static struct gdt_ptr gdt;

  1. 25: 

  1. 26: gdt.len = sizeof(boot_gdt)-1;

  1. 27: gdt.ptr = (u32)&boot_gdt + (ds() << 4);

  1. 28: 

  1. 29: asm volatile("lgdtl %0" : : "m" (gdt));

  1. 30: }

protected_mode_jump又跳转到了汇编语言。

3. protected_mode_jump

  1. 1: 

  1. 2: .text

  1. 3: .code16

  1. 4: 

  1. 5: /*

  1. 6: * void protected_mode_jump(u32 entrypoint, u32 bootparams);

  1. 7: */

  1. 8: GLOBAL(protected_mode_jump)

  1. 9: movl %edx, %esi # Pointer to boot_params table

  1. 10: 

  1. 11: xorl %ebx, %ebx

  1. 12: movw %cs, %bx

  1. 13: shll $4, %ebx

  1. 14: addl %ebx, 2f

  1. 15: jmp 1f # Short jump to serialize on 386/486

  1. 16: 1:

  1. 17: 

  1. 18: movw $__BOOT_DS, %cx

  1. 19: movw $__BOOT_TSS, %di

  1. 20: 

  1. 21: movl %cr0, %edx

  1. 22: orb $X86_CR0_PE, %dl # Protected mode

  1. 23: movl %edx, %cr0

  1. 24: 

  1. 25: # Transition to 32-bit mode

  1. 26: .byte 0x66, 0xea # ljmpl opcode

  1. 27: 2: .long in_pm32 # offset

  1. 28: .word __BOOT_CS # segment

  1. 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. 1: .code32

  1. 2: .section ".text32","ax"

  1. 3: AL(in_pm32)

  1. 4: # Set up data segments for flat 32-bit mode

  1. 5: movl %ecx, %ds

  1. 6: movl %ecx, %es

  1. 7: movl %ecx, %fs

  1. 8: movl %ecx, %gs

  1. 9: movl %ecx, %ss

  1. 10: # The 32-bit code sets up its own stack, but this way we do have

  1. 11: # a valid stack if some debugging hack wants to use it.

  1. 12: addl %ebx, %esp

  1. 13: 

  1. 14: # Set up TR to make Intel VT happy

  1. 15: ltr %di

  1. 16: 

  1. 17: # Clear registers to allow for future extensions to the

  1. 18: # 32-bit boot protocol

  1. 19: xorl %ecx, %ecx

  1. 20: xorl %edx, %edx

  1. 21: xorl %ebx, %ebx

  1. 22: xorl %ebp, %ebp

  1. 23: xorl %edi, %edi

  1. 24: 

  1. 25: # Set up LDTR to make Intel VT happy

  1. 26: lldt %cx

  1. 27: 

  1. 28: jmpl *%eax # Jump to the 32-bit entrypoint

  1. 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. 64、saleforce 删除操作举例

    String deletePrivelegeRoleSql = 'SELECT ROLEID__c, NAME__c, Id, PRIVELEGEID__r.ID, ROLEID__r.ID FROM ...

  2. Sublime 代码段设置

    # Sublime 代码段 > 依次找到:Tools -> Developer -> New Snippet,默认代码段配置文件如下: <snippet><cont ...

  3. VS2008中编译运行MFC应用程序时,出现无法启动程序,因为计算机中丢失mfc90ud.dll的解决方案

     解决方法:"工具"->"选项"->"项目和解决方案"->"VC++目录",在可执行文件栏中加上如 ...

  4. 转 jmeter 实现loadrunner init end 功能

    一.JMeter 介绍 Apache JMeter是100%纯JAVA桌面应用程序,被设计为用于测试客户端/服务端结构的软件(例如web应用程序).它可以用来测试静态和动态资源的性能,例如:静态文件, ...

  5. 转 jmeter 关联

    jmeter(十二)关联之正则表达式提取器   如果有这样的情况:一个完整的操作流程,需要先完成某个操作,获得某个值或数据信息,然后才能进行下一步的操作(也就是常说的关联/将上一个请求的响应结果作为下 ...

  6. 微信小程序 使用wxParse解析html

    微信小程序 加载 HTML 标签:https://blog.csdn.net/zclengendary/article/details/54312030 微信小程序 使用wxParse解析html:h ...

  7. bootstrap学习(四)表格

    基础样式: 自适应沾满浏览器 <table class="table"> <tr> <th>序号</th> <th>姓名 ...

  8. ios兼容问题

    滑动卡顿: -webkit-overflow-scrolling:touch;

  9. 三、hibernate中持久化类的使用

    hibernate的持久化类 持久化:将内存中的一个对象持久化到数据库中的过程,hibernate就是一个用来进行持久化的框架 持久化类:一个Java对象与数据库中表建立了关系映射,那么这个类在hib ...

  10. RocketMQ事务性消息

    mq事务介绍 mq事务消息流程 生产者发送消息到mq,消息状态为:SEND_OK.此消息是消费者不可见(消费者无法消费此条消息) 执行本地任务:成功则返回COMMIT_MESSAGE,此时消费者可消费 ...