Redboot(i386)启动流程(一) - [源码分析]
现在将通过阅读代码,看看redboot是如何启动的,这是每个系统执行的第一步,也是不可缺少的一步。这部分会分几篇完成,这是第一部分,主要是一个概要介绍。
由于系统启动跟硬件的紧密关系,所以在不同的硬件平台下,这部分都会有相应不同的处理。
下面这幅图来自《EMBEDDED SOFTWARE DEVELOPMENT WITH ECOS》书中,介绍了一款PowerPC的设备启动流程。

而这里,仍将介绍PC下的启动流程,通过阅读代码可以了解其具体的执行步骤,也可以通过修改代码重新编译、运行前面介绍的实验,查看结果。在阅读代码的过程中,最好结合实验,这样能够证实系统是否象我们理解的那样运行。
在今后,会进一步分析eCos操作系统的其他部分代码结构,比如:调度,驱动等等。
也会对其他不同体系架构(如 mips arm)下的代码进行分析阅读。
在Redboot最先执行的文件通常命名为 vectors.S
因为我们是i386架构下,所以可以在下面目录下找到相应文件:
package/hal/i386/arch/v2_0/src/vectors.S
程序执行的入口是 _start。
从 _start 处开始执行,至 call cyg_start 完成启动部分。
i386 体系
|
#==============================================================================
// .file "vectors.S"
#==============================================================================
# Real startup code. We jump here from the various reset vectors to set up the
# world.
.text
.globl _start
_start:
hal_cpu_init
hal_smp_init
hal_diag_init
hal_mmu_init
hal_memc_init
hal_intc_init
hal_cache_init
hal_timer_init
# Loading the stack pointer seems appropriate now.
# In SMP systems, this may not be the interrupt stack we
# actually need to use for this CPU. We fix that up later.
movl $__interrupt_stack, %esp
#if defined(CYG_HAL_STARTUP_FLOPPY) \
|| defined(CYG_HAL_STARTUP_ROM) \
|| defined(CYG_HAL_STARTUP_GRUB)
# If we are here first, initialize the IDT. RAM startup
# configurations can assume that Redboot has already set
# the IDT up.
hal_idt_init
#endif
hal_mon_init
# Init FPU
# Do this after the monitor init so that we can plant our
# own FPU unavailable VSR.
hal_fpu_init # WARNING: may adjust stack pointer
# Zero the BSS. If the BSS is not a whole number of words
# long we will write up to 3 extra bytes at the end.
# (This should not be a problem usually).
movl $__bss_end,%ecx # ECX = end of BSS
movl $__bss_start,%edi # EDI = base of BSS
subl %edi,%ecx # ECX = size of BSS
addl $3,%ecx # ECX += sizeof(long)-1
shrl $2,%ecx # ECX >>= 2 = number of words to fill
xorl %eax,%eax # EAX = 0 = fill value
rep stosl # Fill it in
#ifdef CYG_HAL_STARTUP_ROM
# In a ROM booted system, we also need to copy the data section
# out to the RAM.
movl $__rom_data_start,%esi # ESI = base of ROM data area
movl $__ram_data_start,%edi # EDI = base of RAM data area
movl $__ram_data_end,%ecx # ECX = end of data
subl %edi,%ecx # ECX = size of data in bytes
shrl $2,%ecx # ECX >>= 2 = number of words to copy
rep movsl # Copy it over
#endif
.extern hal_variant_init
call hal_variant_init
.extern hal_platform_init
call hal_platform_init
#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
// This is here so we can debug the constructors.
.extern initialize_stub
call initialize_stub
#endif
.extern cyg_hal_invoke_constructors
call cyg_hal_invoke_constructors
#ifdef CYGPKG_HAL_SMP_SUPPORT
# Now move SP to actual interrupt stack we will use for this
# processor.
hal_init_istack %esp
#ifndef CYG_HAL_STARTUP_RAM
# Only start other CPUs when we are the original boot executable.
# RAM executables are loaded via RedBoot, so only FLOPPY, GRUB
# and ROM startups count here.
.extern cyg_hal_smp_cpu_start_all
call cyg_hal_smp_cpu_start_all
#endif
#endif
#ifdef CYGDBG_HAL_DEBUG_GDB_INITIAL_BREAK
.extern breakpoint
call breakpoint
#endif
.extern cyg_start
call cyg_start
# Hmm. Not expecting to return from 0.
1: hlt
jmp 1b |
在函数 cyg_start 中,将进入 Redboot的执行流程。
这并不是很长的一段代码,却完成了整个PC的启动和初始化的部分。其中相当部分代码为汇编,所以要阅读这些代码必须首先对i386体系结构有基本的了解。
待续
Redboot(i386)启动流程(二) - [源码分析]
接上篇
虽然 Redboot的启动过程已经相对简单,它没有使用分页机制,也没有区分内核态和用户态。
所有的代码都被放在一个段中统一编址。只使用了两个段(selector)一个为code,另一个为data。这个对于redboot的size(大概在100k左右吧)已经足够了。
如果对于一个操作系统来讲,这是一个相当程度简化的操作系统。即便如此,启动过程也不是一目了然的清晰。所以,这里仍然会忽略掉一些不太必要的内容,比如对SMP,FPU,GDB的支持等等。
整个启动过程中大概分为以下几个部分:
1. boot-loader: 这部分的工作就是将代码加载(load)到内存中,对于不同的设备,代码会装在不同的地方,通常PC会放在硬盘里面,某些嵌入式设备会把代码放在ROM(一般是flash)里面,而这里是放在软盘里面。boot-loader就是把软盘的代码load至内存,然后运行它们。
2. 进入保护模式,对于i386体系架构的cpu上电后缺省时实模式,内存访问空间也只有1M,这只是为了跟最初的8086兼容,所以要进入保护模式。
3. 初始化中断向量和异常。
4. 初始化基本的输入输出设备,如显示器,键盘,串行口等等。
5. 其他还有一些零散的初始化工作。
从下次开始将按运行次序对启动过程进行介绍。
待续
Redboot(i386)启动流程(三) - [源码分析]
接上篇继续
_start之后的第一行是一个宏 hal_cpu_init, 该宏定义在package/hal/i386/pcmb/v2_0/include/pcmb.inc 文件中。 由于从软盘引导,所以 hal_cpu_init分为两部分: 一部分是引导分区部分(以0xAA55结束),另一部分的功能是使CPU进入保护模式。
解释:PC上电后会进入BIOS, BIOS会检测磁盘(软盘和硬盘),如果软盘(或硬盘)的第一个分区是引导分区(以 0xAA55结束为标志)则将软盘的第一个分区内容加载到内存 0x7c00处,并运行它。
引导分区被运行之后,将整个redboot逐个分区地加载到0x3000为起始地址的内存空间。
注意:这个过程,引导分区内容会再次被加载到0x3000处,且马上会跳转到0x3000处执行引导分区的代码。而0x7c00处的内容会在后面被覆盖掉。
在执行加载工作前,会先执行两个小任务:
1. 设置栈,栈顶为0x3000
2. 通过BIOS提供的中断,得到扩展内存和标准内存大小,压入栈中
引导分区代码结束后,程序将进入保护模式。大概次序如下:
1. 关中断
2. 初始化GDT和IDT
3. 进入保护模式
4. 设置数据段
5. 重新设置栈段(因为保护模式和实模式,内存编址方式不同)
GDT主要用两个Selector分别是 0x08 (code) 和 0x10 (data)。
寻址空间都是从0x00000000 - 0xFFFFFFFF
特权级(DPL)为 0
IDT的地址为:0x1000
这部分比较复杂,这里不在详细说明,如果有时间,会在讨论i386架构的文章里,详细说明。而有关这部分的资料也相当丰富,可以在网上搜寻。
到这里hal_cpu_init已经执行结束了 ,不过进入保护模式还需要 激活A20地址线 。不激活A20地址线只能访问1M空间内的内存,激活A20才能访问所有内存。这个任务在另外一个宏 hal_memc_init 中实现,它位于文件 packages/hal/i386/arch/v2_0/include/arch.inc
这样,就完成了加载和进入保护模式的任务。
Redboot(i386)启动流程(四) - [源码分析]
接上篇继续
进入保护模式之后做以下工作:
设置中断栈空间,在之前栈定义在小于0x3000的内存空间中。通过
movl $__interrupt_stack, %esp
将栈重新设置在__interrupt_stack处,并分配了4096kB的栈空间。
栈大小也可以通过改变cdl文件进行调整。
hal_idt_init(packages/hal/i386/pc/v2_0/include/platform.inc)
设置idt向量, 回头会在介绍中断的文章中介绍这部分。
BSS段初始化(清零)
解释: BSS(Block Started by Symbol)用来存放程序中未初始化的全局变量的一块內存区域,属于静态內存分配。
hal_platform_init(packages/hal/i386/pc/v2_0/src/plf_misc.c)
这部分是内容比较多,也比较重要。主要为 初始化 中断,异常 以及虚拟向量表。
初始化虚拟向量表的工作通过调用函数 hal_if_init 来完成
在eCos中设置了3张向量表,idt, hal_vsr_table, hal_virtual_vector_table
内存地址如下:
idtStart = 0x00001000;
hal_vsr_table = 0x00001800;
hal_virtual_vector_table = 0x00001c00;
idt 是硬件实际设置的中断区域,当中断发生后进入调用idt的中断向量。而eCos将idt的中断向量有指向 hal_vsr_table
(猜测是为了隔离不同的cpu中断机制不同,专门设置了hal_vsr_table,使其中断处理方式一致)
在 hal_platform_init 函数中,初始化所有中断(和异常),设定为缺省的中断处理例程,以防止有中断意外发生,造成程序进入未知状态。
有关中断,异常,虚拟向量等相关处理机制内容比较多,会在以后的文章具体介绍。
cyg_hal_invoke_constructors
这个函数主要执行相关的构造函数,因为ecos一部分代码是用c++写的,所以有些类具有构造函数,需要在首先执行,这里面做这部分的处理。
cyg_start
最后则进入主函数 cyg_start, 这个函数中,首先执行外围设备的初始化,之后就开始轮询处理用户输入的命令。
以上就是Redboot启动的全过程。由于篇幅有限,对很多部分没有深入讨论。在之后计划对以下部分进行讨论:
1 eCos设备驱动结构和编写
2 PCI设备的初始化过程
3 中断和异常处理过程
4 虚拟向量(hal_virtual_vector_table)介绍
5 Redboot 命令介绍
等等……
| 卓越网上书城 eCos & linux 及计算机 相关图书 |
| |
|
|
|
|
|
|
|
|
|
|