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\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语言代码分析的更多相关文章
- 深入理解Linux启动过程
深入理解Linux启动过程 本文详细分析了Linux桌面操作系统的启动过程,涉及到BIOS系统.LILO 和GRUB引导装载程序,以及bootsect.setup.vmlinux等映像文件 ...
- 从Linux启动过程到android启动过程
Linux启动过程: 1.首先开机给系统供电,此时硬件电路会产生一个确定的复位时序,保证cpu是最后一个被复位的器件.为什么cpu要最后被复位呢?因为 如果cpu第一个被复位,则当cpu复位后开始运行 ...
- Linux 启动过程详解
目录 1. Linux启动过程 2. 启动过程概述 3. 引导加载阶段 4. 内核阶段 4.1 内核加载阶段 4.2 内核启动阶段 5. 早期的用户空间 6. 初始化过程 6.1 SysV init ...
- 转-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)加 ...
- Linux启动过程详解(inittab、rc.sysinit、rcX.d、rc.local)
启动第一步--加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它.这是因为BIOS中包含了CPU的相关信息.设备启动顺序信息.硬 ...
- Linux启动过程详解
Linux启动过程详解 附上两张图,加深记忆 图1: 图2: 第一张图比较简洁明了,下面对第一张图的步骤进行详解: 加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的 ...
- 嵌入式Linux启动过程中的问题积累
嵌入式Linux启动过程中的问题积累 Dongas 07-12-19 1.Bad Magic Number ## Booting image at 33000000 ... Bad Magic Num ...
- [linux 整理] linux启动过程3
本文介绍linux启动过程的第三步 busybox--------------------> rc init busybox位置即内容 busybox/init/init.c 1.各种设置信号 ...
- Linux启动过程简述
Linux启动过程: 图片来自:https://www.cnblogs.com/codecc/p/boot.html 简单来讲: 加载BIOS–>读取MBR–>Boot Loader–&g ...
随机推荐
- 64、saleforce 删除操作举例
String deletePrivelegeRoleSql = 'SELECT ROLEID__c, NAME__c, Id, PRIVELEGEID__r.ID, ROLEID__r.ID FROM ...
- Sublime 代码段设置
# Sublime 代码段 > 依次找到:Tools -> Developer -> New Snippet,默认代码段配置文件如下: <snippet><cont ...
- VS2008中编译运行MFC应用程序时,出现无法启动程序,因为计算机中丢失mfc90ud.dll的解决方案
解决方法:"工具"->"选项"->"项目和解决方案"->"VC++目录",在可执行文件栏中加上如 ...
- 转 jmeter 实现loadrunner init end 功能
一.JMeter 介绍 Apache JMeter是100%纯JAVA桌面应用程序,被设计为用于测试客户端/服务端结构的软件(例如web应用程序).它可以用来测试静态和动态资源的性能,例如:静态文件, ...
- 转 jmeter 关联
jmeter(十二)关联之正则表达式提取器 如果有这样的情况:一个完整的操作流程,需要先完成某个操作,获得某个值或数据信息,然后才能进行下一步的操作(也就是常说的关联/将上一个请求的响应结果作为下 ...
- 微信小程序 使用wxParse解析html
微信小程序 加载 HTML 标签:https://blog.csdn.net/zclengendary/article/details/54312030 微信小程序 使用wxParse解析html:h ...
- bootstrap学习(四)表格
基础样式: 自适应沾满浏览器 <table class="table"> <tr> <th>序号</th> <th>姓名 ...
- ios兼容问题
滑动卡顿: -webkit-overflow-scrolling:touch;
- 三、hibernate中持久化类的使用
hibernate的持久化类 持久化:将内存中的一个对象持久化到数据库中的过程,hibernate就是一个用来进行持久化的框架 持久化类:一个Java对象与数据库中表建立了关系映射,那么这个类在hib ...
- RocketMQ事务性消息
mq事务介绍 mq事务消息流程 生产者发送消息到mq,消息状态为:SEND_OK.此消息是消费者不可见(消费者无法消费此条消息) 执行本地任务:成功则返回COMMIT_MESSAGE,此时消费者可消费 ...