标签:des style blog http io ar color 使用 sp
前段时间移植uboot仔细研究过uboot启动过程,最近耐不住寂寞,又想对kernel下手。只有知道kernel如何启动,我们才能真正的去理解kernel
作为一个嵌入式工作者,我想不能仅仅局限于某个module driver,而应深入到kernel的汪洋大海中去傲游!
学习启动过程,我本着打破沙锅问到底的原则,希望能研究的明明白白,但也鉴于水平有限,还是有很多纰漏之处
共享博文,希望大家多多交流指正,辛苦整理,如需转载,还请注明出处。
在arch/arm/kernel/head.S中,如下:
.arm
__HEAD
ENTRY(stext)
THUMB( adr r9, BSYM(1f) ) @ Kernel is always entered in ARM.
THUMB( bx r9 ) @ If this is a Thumb-2 kernel,
THUMB( .thumb ) @ switch to Thumb now.
THUMB(1: )
//处理器进入svc模式,关闭中断
setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
@ and irqs disabled
//获取处理器ID
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
//将proc_type_list pointer存在r10中,如果为NULL,则error_p
movs r10, r5 @ invalid processor (r5=0)?
THUMB( it eq ) @ force fixup-able long branch encoding
beq __error_p @ yes, error 'p'
//CONFIG_ARM_LPAE不太明白含义,我使用处理器配置文件没有选择该项,感兴趣朋友可以研究下
#ifdef CONFIG_ARM_LPAE
mrc p15, 0, r3, c0, c1, 4 @ read ID_MMFR0
and r3, r3, #0xf @ extract VMSA support
cmp r3, #5 @ long-descriptor translation table format?
THUMB( it lo ) @ force fixup-able long branch encoding
blo __error_p @ only classic page table format
#endif
#ifndef CONFIG_XIP_KERNEL
//获取物理地址与虚拟地址的offset,存在r8中
adr r3, 2f
ldmia r3, {r4, r8}
sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)
add r8, r8, r4 @ PHYS_OFFSET
#else
//定义CONFIG_XIP_KERNEL,offset为PHYS_OFFSET
ldr r8, =PHYS_OFFSET @ always constant in this case
#endif
/*
* r1 = machine no, r2 = atags or dtb,
* r8 = phys_offset, r9 = cpuid, r10 = procinfo
*/
//对bootloader传来的tags参数进行检查
bl __vet_atags
Kernel的入口函数是哪个,入口地址在哪,需要根据连接脚本来确定。OUTPUT_ARCH(arm)
ENTRY(stext)
#ifndef __ARMEB__
jiffies = jiffies_64;
#else
jiffies = jiffies_64 + 4;
#endif
SECTIONS
{
........
#ifdef CONFIG_XIP_KERNEL
. = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);
#else
. = PAGE_OFFSET + TEXT_OFFSET;
#endif
}入口函数是head.S中的stext,不采用XIP技术,入口地址是PAGE_OFFSET+TEXT_OFFSET。#define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET) Menuconfig中CONFIG_PAGE_OFFSET = 0xc0000000 ./arch/arm/Makefile中: textofs-y := 0x00008000 textofs-$(CONFIG_ARCH_CLPS711X) := 0x00028000 # We don't want the htc bootloader to corrupt kernel during resume textofs-$(CONFIG_PM_H1940) := 0x00108000 # SA1111 DMA bug: we don't want the kernel to live in precious DMA-able memory ifeq ($(CONFIG_ARCH_SA1100),y) textofs-$(CONFIG_SA1111) := 0x00208000 endif textofs-$(CONFIG_ARCH_MSM7X30) := 0x00208000 textofs-$(CONFIG_ARCH_MSM8X60) := 0x00208000 textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000 ...... # The byte offset of the kernel image in RAM from the start of RAM. TEXT_OFFSET := $(textofs-y)入口地址是0xc0008000.
__CPUINIT
__lookup_processor_type:
//3行汇编,计算出物理地址与虚拟地址之间的offset,存在r3中
adr r3, __lookup_processor_type_data
ldmia r3, {r4 - r6}
sub r3, r3, r4 @ get offset between virt&phys
//获取__proc_info_begin的物理地址
add r5, r5, r3 @ convert virt addresses to
//获取__proc_info_end的物理地址
add r6, r6, r3 @ physical address space
//mask cp15读出的cpuid,与proc_type_list中value对比
1: ldmia r5, {r3, r4} @ value, mask
and r4, r4, r9 @ mask wanted bits
teq r3, r4
//一致则返回,不一致则跳到下一个proc_type_list,继续对比
beq 2f
add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)
cmp r5, r6
blo 1b
//匹配成功,r5存该proc_type_list指针,匹配失败,r5置0
mov r5, #0 @ unknown processor
2: mov pc, lr
ENDPROC(__lookup_processor_type)
/*
* Look in <asm/procinfo.h> for information about the __proc_info structure.
*/
.align 2
.type __lookup_processor_type_data, %object
__lookup_processor_type_data:
.long .
.long __proc_info_begin
.long __proc_info_end
.size __lookup_processor_type_data, . - __lookup_processor_type_data</span>因为kernel要开启MMU,所以kernel编译链接地址是虚拟地址(物理地址经过MMU转换后CPU看到的地址),并不是物理地址, kernel现阶段给出的解决方法,就是lookup_processor_type前3行汇编:
adr r3, __lookup_processor_type_data 加载__lookup_processor_type_data地址(实际运行地址,这里就是物理地址)到r3
ldmia r3, {r4 - r6} 获取以r3 r3+4 r3+8为地址的变量到r4,r5,r6.
地址变量值是在链接时确定的,所以r4中存的是__lookup_processor_type_data的链接地址(虚拟地址)。
sub r3 ,r3 ,r4 r3中存储的是物理地址与虚拟地址的偏移。
这是多么genius的操作啊!
_proc_info_begin _proc_info_end在链接脚本中定义,是.proc.info.init段的首尾。
该段中是proc_info_list struct,表示处理器相关信息,定义如下:
struct proc_info_list {
unsigned int cpu_val;
unsigned int cpu_mask;
unsigned long __cpu_mm_mmu_flags; /* used by head.S */
unsigned long __cpu_io_mmu_flags; /* used by head.S */
unsigned long __cpu_flush; /* used by head.S */
const char *arch_name;
const char *elf_name;
unsigned int elf_hwcap;
const char *cpu_name;
struct processor *proc;
struct cpu_tlb_fns *tlb;
struct cpu_user_fns *user;
struct cpu_cache_fns *cache;
};该段是在arch/arm/mm/proc-xxx.S中填充,不需要软件人员修改,感兴趣朋友可以研究下。__vet_atags:
tst r2, #0x3 @ aligned?
bne 1f
ldr r5, [r2, #0]
//判断是否是dtb类型
#ifdef CONFIG_OF_FLATTREE
ldr r6, =OF_DT_MAGIC @ is it a DTB?
cmp r5, r6
beq 2f
#endif
cmp r5, #ATAG_CORE_SIZE @ is first tag ATAG_CORE?
cmpne r5, #ATAG_CORE_SIZE_EMPTY
bne 1f
ldr r5, [r2, #4]
ldr r6, =ATAG_CORE
cmp r5, r6
bne 1f
//正确tags,返回
2: mov pc, lr @ atag/dtb pointer is ok
//错误tags,清空r2,返回
1: mov r2, #0
mov pc, lr
ENDPROC(__vet_atags)检查tag头4 byte(tag_core的size)和第二个4 byte(tag_core的type)是否正确。联想之前分析的uboot relocation原理(博文链接:http://blog.csdn.net/skyflying2012/article/details/37660265),
uboot在relocation之后,kernel在开启MMU之前,都实现了链接地址和运行地址不一致,看看它们用的什么方法?
(1)uboot在relocation时修改rel.dyn段(存储所有变量地址),实现将所有变量地址重定位到新运行地址
今天先分析到这,start_kernel之前剩余部分汇编会再写2篇文章来分析学习。
arm-linux kernel启动过程分析(1)-start_kernel之前第一步
标签:des style blog http io ar color 使用 sp
原文地址:http://blog.csdn.net/skyflying2012/article/details/41344377