在上一篇博文中我们已经知道zImage内核是需要经过解压缩的,然后跳到arch/arm/kernel/head.S
这里的。所以对于Arm Linux内核在调用start_kernle()之前的启动顺序是:
对于经过压缩的内核(zImage),先运行解压缩decompress_kernel,然后还需要重定位,然后调用内核,
就象跳到未压缩的内核中的开始处,内核的startup在arch/arm/kernel/head.S中,进行页表初始化和
处理器缓存初始化等工作,然后跳到C代码init/main.c中的start_kernel,接下来的事情就是大众化工作了,
在这里,我们继续分析解压缩后的工作,即是arch/arm/kernel/head.S的工作内容。
首先先说下内核的启动条件:
1. CPU必须处于SVC(supervisor)模式,并且IRQ和FIQ中断都是禁止的;
2. MMU(内存管理单元)必须是关闭的, 此时虚拟地址对物理地址;
3. 数据cache(Data cache)必须是关闭的
4. 指令cache(Instruction cache)可以是打开的,也可以是关闭的,这个没有强制要求;
5. CPU 通用寄存器0 (r0)必须是 0;
6. CPU 通用寄存器1 (r1)必须是 ARM Linux machine type
7. CPU 通用寄存器2 (r2) 必须是 kernel parameter list 的物理地址
在arch/arm/kernel/vmlinux.lds中知道入口地址为stext
OUTPUT_ARCH(arm)
ENTRY(stext)
jiffies = jiffies_64;
SECTIONS
{
. = 0xC0000000 + 0x00008000;
.init : { /* Init code and data */
_stext = .;
......
}
在arch/arm/kernel/head.S中:
__HEAD
ENTRY(stext)
setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
@ and irqs disabled
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
movs r10, r5 @ invalid processor (r5=0)?
beq __error_p @ yes, error 'p'
bl __lookup_machine_type @ r5=machinfo
movs r8, r5 @ invalid machine (r5=0)?
beq __error_a @ yes, error 'a'
bl __vet_atags
bl __create_page_tables
/*The following calls CPU specific code in a position independent
* manner. See arch/arm/mm/proc-*.S for details. r10 = base of
* xxx_proc_info structure selected by __lookup_machine_type
* above. On return, the CPU will be ready for the MMU to be
* turned on, and r0 will hold the CPU control register value.*/
ldr r13, __switch_data @ address to jump to after
@ mmu has been enabled
adr lr, BSYM(__enable_mmu) @ return (PIC) address
add pc, r10, #PROCINFO_INITFUNC
看到这里是不是开始迷糊了?别担心,下面开始分析每条语句的定义和作用。
==> setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
setmode是一个宏,其定义为:
.macro setmode, mode, reg
msr cpsr_c, #\mode
.endm
==> mrc p15, 0, r9, c0, c0 @ get processor id
通过cp15协处理器的c0寄存器来获得processor id的指令
==> bl __lookup_processor_type @ r5=procinfo r9=cpuid
跳转到__lookup_processor_type.在__lookup_processor_type中,会把
processor type 存储在r5中.
__lookup_processor_type 函数主要是根据从cpu中获得的processor id
和系统中的proc_info进行匹配,将匹配到的proc_info_list的基地址存到r5中,
0表示没有找到对应的processor type.
下面我们分析__lookup_processor_type函数
arch/arm/kernel/head-common.S中:
* r9 = cpuid
* Returns:
* r3, r4, r6 corrupted
* r5 = proc_info pointer in physical address space
* r9 = cpuid (preserved)
__lookup_processor_type:
adr r3, 3f @取地址指令,这里的3f是向前symbol名称是3的位置,将该地址存入r3.
ldmia r3, {r5 - r7}
add r3, r3, #8
sub r3, r3, r7 @ get offset between virt&phys
add r5, r5, r3 @ 将r5存储的虚拟地址(__proc_info_begin)转换成物理地址
add r6, r6, r3 @将r6存储的虚拟地址(__proc_info_end)转换成物理地址
1:
ldmia r5, {r3, r4} @ value, mask
@对照struct proc_info_list,可以得知,
@这句是将当前proc_info的cpu_val和cpu_mask分别存r3, r4中
and r4, r4, r9 @ r9与r4的cpu_mask进行逻辑与操作,得到我们需要的值
teq r3, r4
beq 2f @如果相等,说明我们找到了对应的processor type,返回
add r5, r5, #PROC_INFO_SZ @ (如果不相等) , 将r5指向下一个proc_info,
cmp r5, r6 @和r6比较,检查是否到了__proc_info_end.
blo 1b @如果没有到__proc_info_end,表明还有proc_info配置
mov r5, #0 @ 没有找到匹配的unknown processor
2:
mov pc, lr
.align 2
3: .long __proc_info_begin
.long __proc_info_end
r5存的是符号 __proc_info_begin的地址;
r6存的是符号 __proc_info_end的地址;
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
在arch/arm/mm/proc-arm920.S中
.section ".proc.info.init", #alloc, #execinstr
.type __arm920_proc_info,#object
__arm920_proc_info:
.long 0x41009200
.long 0xff00fff0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __arm920_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB
.long cpu_arm920_name
.long arm920_processor_functions
.long v4wbi_tlb_fns
.long v4wb_user_fns
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
.long arm920_cache_fns
#else
.long v4wt_cache_fns
#endif
.size __arm920_proc_info, . - __arm920_proc_info
从上面的分析我们可以知道r3中存储的是3f处的物理地址,而r7存储的是3f处的虚拟地址,
这一行是计算当前程序运行的物理地址和虚拟地址的差值,将其保存到r3中.
==> bl __lookup_machine_type
* r1 = machine architecture number
* Returns:
* r3, r4, r6 corrupted
* r5 = mach_info pointer in physical address space
__lookup_machine_type:
adr r3, 4b
ldmia r3, {r4, r5, r6}
sub r3, r3, r4 @ get offset between virt&phys
add r5, r5, r3 @ convert virt addresses to
add r6, r6, r3 @ physical address space
1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type
teq r3, r1 @ matches loader number?
beq 2f @ found
add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
cmp r5, r6
blo 1b
mov r5, #0 @ unknown machine
2: mov pc, lr
4: .long .
.long __arch_info_begin
.long __arch_info_end
r5存的是符号 __arch_info_begin的地址;
r6存的是符号 __arch_info_end的地址;
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
在arch/arm/include/asm/mach/arch.h文件中我们看到:
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
在arch/arm/mach-s3c2440/mach-smdk2440.c中:
MACHINE_START(S3C2440, "SMDK2440")
/* Maintainer: Ben Dooks */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
OK, __lookup_machine_type这个例程的我们也搞明白了。
==> bl __vet_atags
这个例程同样同样也是在arch/arm/kernel/head-common.S文件中定义:
__vet_atags:
tst r2, #0x3 @ aligned?
bne 1f
ldr r5, [r2, #0] @ is first tag ATAG_CORE?
cmp r5, #ATAG_CORE_SIZE
cmpne r5, #ATAG_CORE_SIZE_EMPTY
bne 1f
ldr r5, [r2, #4]
ldr r6, =ATAG_CORE
cmp r5, r6
bne 1f
mov pc, lr @ atag pointer is ok
1:
mov r2, #0
mov pc, lr
这个例程接收机器信息(R8寄存器)为参数,并检测r2中传入的ATAGS 指针的合法性。
内核使用tag来作为bootloader传递内核参数的方式。系统要求r2中传进来的ATAGS
指针式4字节对齐的,同时要求ATAGS列表的第一个tag是一个ATAG_CORE类型的。
此时R10寄存器中保存有指向CPU信息结构体的指针,R8寄存器中保存有指向机器结构体的指针,
R2寄存器中保存有指向tag表的指针,R9中还保存有CPU ID信息。
阅读(681) | 评论(0) | 转发(0) |