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 ...
随机推荐
- strcpy函数学习
strcpy的功能如下: 原型声明:char *strcpy(char* dest, const char *src); 头文件:#include <string.h> 和 #includ ...
- SQL之in和like的连用实现范围内的模糊查询
我们知道in可以实现一个范围内的查询,like可以实现模糊查询, 如 select *where col like 123%但是我们如果有一列attri,如123,132,165... 我们想实现12 ...
- (转)GitHub上想下载单个文件方法
找到该文件,单机raw,如下图: 然后会在网页打开该文件,复制URL,下载即可(如果是不可预览文件,会自动下载). 转自: GitHub上想下载单个文件方法 - Smallcaff的博客 - CSDN ...
- PAT甲级——A1141 PATRankingofInstitution【25】
A clique is a subset of vertices of an undirected graph such that every two distinct vertices in the ...
- 快速上手的Glide4.x教程
安卓基础开发库,让开发简单点. DevRing & Demo地址:https://github.com/LJYcoder/DevRing 学习/参考地址: https://blog.csdn. ...
- junit单元测试报错Failed to load ApplicationContext,但是项目发布到tomcat浏览器访问没问题
junit单元测试报错Failed to load ApplicationContext,但是项目发布到tomcat浏览器访问没问题,说明代码是没问题的,配置也没问题.开始时怀疑是我使用junit版本 ...
- Nginx反向代理与负载均衡应用实践(一)
Nginx反向代理与负载均衡应用实践(一) 链接:https://pan.baidu.com/s/1xB20bnuanh0Avs4kwRpSXQ 提取码:migq 复制这段内容后打开百度网盘手机App ...
- NFS(网络文件系统)
NFS(网络文件系统) 1.关于NFS介绍 1.1NFS在企业中的应用场景 在企业集群架构的工作场景中,NFS网络文件系统一般被用来存储共享视频,图片,附件等静态资源文件,通常网站用户上传的文件都会放 ...
- Largest Submatrix 3
Largest Submatrix 3 给出一个\(n\times m\)的网格图,第i行第j列上的格子有数字\(a[i][j]\),显然,你可以从中找到一个子矩阵,保证子矩阵中的数字互不相同,求子矩 ...
- windows10 注销 锁定
锁定,暂时离开电脑时使用. 跑程序,下载内容(注意有时要修改一些软件的设置)继续进行. 锁定电脑,这时就不要关机. 注销快于重启. 一个账号 后台跑程序 https://zhidao.baidu.co ...