您的位置:首页 > 运维架构 > Linux

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

2010-12-29 12:48 246 查看
这次该进入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 38**86

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

还要提醒一点,我们已经进入保护模式,但只是启动了分段模式,分页还没启动呢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: