||
Arm linux 启动分析(1)
1.概述:
在内核运行之前需要系统引导程序(Bootloader)完成加载内核和一些辅助性的工作,然后跳转到内核代码的起始地址并执行。本文先分析了Bootloader的初始化工作,接着从内核镜像的起始地址进行分析。整个arm linux内核的启动可分为三个阶段:第一阶段主要是进行cpu和体系结构的检查、cpu本身的初始化以及页表的建立等;第二阶段主要是对系统中的一些基础设施进行初始化;最后则是更高层次的初始化,如根设备和外部设备的初始化。
第一阶段的初始化是从内核入口(ENTRY(stext))开始到start_kernel前结束。这一阶段的代码在/arch/arm/head_armv.S中。
本处介绍主要来自内核源代码下的Documentation/arm/Booting文件,适合于arm linux 2.4.18-rmk6及以上版本。
Bootloader主要作用是初始化一些必要的设备,然后调用内核,同时传递参数给内核。主要完成如下工作:
1. 建立和初始化RAM。
2. 初始化一个串口。
3. 检测机器的系统结构。
4. 建立内核的tagged list。
5. 调用内核镜像。
1.建立和初始化RAM。
要求:必须
功能:探测所有的RAM位置和大小,并对RAM进行初始化。
2.初始化一个串口。
要求:可选,建议
功能:Bootloader应该初始化并启动一个串口。这可以让内核的串口驱动自动探测哪个串口作为内核的控制台。另外也可以通过给内核传递“console=”参数完成此工作。
3.检测机器的系统结构。
要求:必须
功能:Bootloader应该通过某种方法探测机器类型,最后传递给内核一个MACH_TYPE_xxx值,这些值参看linux/arch/arm/tools/mach-types。
4.建立内核的tagged list。
要求:必须
功能:Bootloader必须创建和初始化内核的tagged list。一个合法的tagged list开始于ATAG_CORE 并结束于ATAG_NONE。ATAG_CORE tag可以为空。一个空的ATAG_CORE tag的size字段设为“2”(0x00000002)。ATAG_NONE 的size字段必须设为“0”。tagged list可以有任意多的tag。Bootloader必须至少传递系统内存的大小和位置,以及根文件系统的位置,一个最小化的tagged list应该像如下:
+-----------+
base -> | ATAG_CORE | |
+-----------+ |
| ATAG_MEM | | increasing address
+-----------+ |
| ATAG_NONE | |
+-----------+ v
tagged list应该放在内核解压时和initrd的”bootp”程序都不会覆盖的内存区域。建议放在RAM的起始的16K大小的地方。
5.调用内核镜像。
要求:必须
功能:可以从flash调用内核,也可以从系统RAM中调用内核。对于后者需要注意,内核使用内核镜像以下的16K内存作为页表,建议把内核起始放在RAM的32K处。无论是哪种方法,如下条件必须满足:
- CPU register settings
r0 = 0,
r1 = machine type number discovered in (3) above.
r2 = physical address of tagged list in system RAM.
- CPU mode
All forms of interrupts must be disabled (IRQs and FIQs)
The CPU must be in SVC mode. (A special exception exists for Angel)
- Caches, MMUs
The MMU must be off.
Instruction cache may be on or off.
Data cache must be off.
- The boot loader is expected to call the kernel image by jumping
directly to the first instruction of the kernel image.
因为Skyeye暂时没有Bootloader,所以以上一些设置必须由Skyeye在初始化的时候自己来完成,如r1的设置等。
这个文件是arch/arm/kernel/head-armv.S,用汇编代码完成,是内核最先执行的一个文件。这一段汇编代码的主要作用,是检查cpu id,architecture number,初始化页表、cpu、bbs等操作,并跳到start_kernel函数。它在执行前,处理器的状态应满足:
l r0 - should be 0
l r1 - unique architecture number
l MMU - off
l I-cache - on or off
l D-cache – off
(略去一些条件编译的代码)
-------------------------------------------------------------------------------------------------------------------
/*
* We place the page tables 16K below TEXTADDR. Therefore, we must make sure
* that TEXTADDR is correctly set. Currently, we expect the least significant
* "short" to be 0x8000, but we could probably relax this restriction to
* TEXTADDR > PAGE_OFFSET + 0x4000
*
* Note that swapper_pg_dir is the virtual address of the page tables, and
* pgtbl gives us a position-independent reference to these tables. We can
* do this because stext == TEXTADDR
*
* swapper_pg_dir, pgtbl and krnladr are all closely related.
*/
#if (TEXTADDR & 0xffff) != 0x8000
#error TEXTADDR must start at 0xXXXX8000
#endif
.globl SYMBOL_NAME(swapper_pg_dir)
.equ SYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x4000
.macro pgtbl, reg, rambase
adr \reg, stext
sub \reg, \reg, #0x4000
.endm
/*
* Since the page table is closely related to the kernel start address, we
* can convert the page table base address to the base address of the section
* containing both.
*/
.macro krnladr, rd, pgtable, rambase
bic \rd, \pgtable, #0x000ff000
.endm
/*
* Kernel startup entry point.
*
* The rules are:
* r0 - should be 0
* r1 - unique architecture number
* MMU - off
* I-cache - on or off
* D-cache - off
*
* See linux/arch/arm/tools/mach-types for the complete list of numbers
* for r1.
*/
.section ".text.init",#alloc,#execinstr
.type stext, #function
ENTRY(stext) //内核入口点
mov r12, r0 //r0=0,r12=0
mov r0, #F_BIT | I_BIT | MODE_SVC@ make sure svc mode//程序状态,禁止FIQ、IRQ,设定Supervisor模式。0b11010011
msr cpsr_c, r0 @ and all irqs disabled//置当前程序状态寄存器
bl __lookup_processor_type//跳转到判断cpu类型,查找运行的cpu的id值,和
//此linux编译支持的id值,是否有相等
teq r10, #0 @ invalid processor?//没有则跳到__error
moveq r0, #'p' @ yes, error 'p'
beq __error
bl __lookup_architecture_type//跳转到判断体系类型,看r1寄存器的
//architecture number值是否支持。
teq r7, #0 @ invalid architecture? //不支持,跳到出错
moveq r0, #'a' @ yes, error 'a'
beq __error
bl __create_page_tables//创建核心页表
adr lr, __ret @ return address//lr=0xc0028054
add pc, r10, #12 @ initialise processor//r10:pointer to processor structure
//(__arm720_proc_info)
//r10+12:__arm720_setup;见
//__arm720_proc_info(proc-arm720.S)
『__arm720_setup: mov r0, #0
mcr p15, 0, r0, c7, c7, 0 @ invalidate caches
mcr p15, 0, r0, c8, c7, 0 @ flush TLB (v4)
mcr p15, 0, r4, c2, c0 @ load page table pointer