Linux 非压缩内核的入口位于文件/arch/arm/kernel/head.s 中的 stext 段。该段的基地址就是压缩内核解压后的跳转地址。如果系统中加载的内核是非压缩的 Image,那么bootloader将内核从 Flash中拷贝到 RAM 后将直接跳到该地址处,从而启动 Linux 内核。不同体系结构的 Linux 系统的入口文件是不同的,而且因为该文件与具体体系结构有关,所以一般均用汇编语言编写。对基于 ARM 处理的 Linux 系统来说,该文件就是head.s。该程序通过查找处理器内核类型和处理器类型调用相应的初始化函数,再建立页表,最后跳转到 start_kernel()函数开始内核的初始化工作。
内核的入口是stext,这是在arch/arm/kernel/vmlinux.lds.S中定义的:
00011:
ENTRY(stext)
对于vmlinux.lds.S,这是ld script文件,此文件的格式和汇编及C程序都不同,本文不对ld script作过多的介绍,只对内核中用到的内容进行讲解,关于ld的详细内容可以参考ld.info
这里的ENTRY(stext) 表示程序的入口是在符号stext.
而符号stext是在arch/arm/kernel/head.S中定义的:
一 架构/开发板相关的引导过程概述
引导过程按以下几步进行
1.检查内核是否支持当前处理器架构--在arch/arm/kernel/head.s文件中的见调用了 __lookup_processor_type
2.检查是否支持当前的开发板machid--在arch/arm/kernel/head.s文件中的见调用了__lookup_machine_type
3.设置页表,使能MMU,为了连接内核时使用虚拟地址
4.调用C语言编写的start_kernel之前的常规性工作,包括复制数据段、清楚BSS段、调用start_kernel函数
在head.s文件中的程序代码如下
-
00072: ENTRY(stext)
-
00073: msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
-
00074: @ and irqs disabled
-
00075: mrc p15, 0, r9, c0, c0 @ get processor id
-
00076: bl __lookup_processor_type @ r5=procinfo r9=cpuid
-
00077: movs r10, r5 @ invalid processor (r5=0)?
-
00078: beq __error_p @ yes, error 'p'
-
00079: bl __lookup_machine_type @ r5=machinfo
-
00080: movs r8, r5 @ invalid machine (r5=0)?
-
00081: beq __error_a @ yes, error 'a'
-
00082: bl __create_page_tables
-
00083:
-
00084: /*
-
00085: * The following calls CPU specific code in a position independent
-
00086: * manner. See arch/arm/mm/proc-*.S for details. r10 = base of
-
00087: * xxx_proc_info structure selected by __lookup_machine_type
-
00088: * above. On return, the CPU will be ready for the MMU to be
-
00089: * turned on, and r0 will hold the CPU control register value.
-
00090: */
-
00091: ldr r13, __switch_data @ address to jump to after
-
00092: @ mmu has been enabled
-
00093: adr lr, __enable_mmu @ return (PIC) address
-
00094: add pc, r10, #PROCINFO_INITFUNC
1.检查内核是否支持当前处理器架构——__lookup_processor_type
1.1 head.s检查内核是否支持处理器架构代码
这一部分检查工作见以下代码,移植工作时需要注意,如果这部分检查不通过,那么内核是无法往下运行的
-
00075: mrc p15, 0, r9, c0, c0 @ get processor id
-
00076: bl __lookup_processor_type @ r5=procinfo r9=cpuid
-
00077: movs r10, r5 @ invalid processor (r5=0)?
-
00078: beq __error_p @ yes, error 'p'
75行: 通过cp15协处理器的c0寄存器来获得processor id的指令. 关于cp15的详细内容可参考相关的arm手册,也可直接参考s3c2410的data sheet。
CPUID是一个32位的值表示,目前ARM位定义如下
31:24 厂商编号,目前有以下值,示例 0x41=A,表示ARM公司
23:20 由厂商定义
19:16 ARM体系版本号,0x01-ARM版本号是4,0x02-ARM版本号是4T,0x03-ARM版本号是5
15:4 产品主编号
3:0 处理器版本号
76行: 跳转到__lookup_processor_type.在__lookup_processor_type中,会把找到匹配的processor type 对象存储在r5中。
77,78行: 判断r5中的processor type是否是0,如果是0,说明系统中没找到匹配当前processor type的对象, 则跳转到__error_p(出错)。系统中会预先定义本系统支持的processor type 对象集。
1.2 __lookup_processor_type函数分析
__lookup_processor_type 函数主要是之前获得的processor
id和Linux系统中预先定义的本系统能支持的proc_info集进行匹配,看系统能否支持当前的processor,
并将匹配到的proc_info的基地址存到r5中, 0表示没有找到对应的processor type.
下面我们分析__lookup_processor_type函数,arch/arm/kernel/head-common.S中:
-
00145: .type __lookup_processor_type, %function
-
00146: __lookup_processor_type:
-
00147: adr r3, 3f @r3是178对应的物理地址
-
00148: ldmda r3, {r5 - r7} @r5=__proc_info_begin??
-
00149: sub r3, r3, r7 @ get offset between virt&phys
-
00150: add r5, r5, r3 @ convert virt addresses to
-
00151: add r6, r6, r3 @ physical address space
-
00152:1: ldmia r5, {r3, r4} @ value, mask
-
00153: and r4, r4, r9 @ mask wanted bits
-
00154: teq r3, r4
-
00155: beq 2f
-
00156: add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)
-
00157: cmp r5, r6
-
00158: blo 1b
-
00159: mov r5, #0 @ unknown processor
-
00160:2: mov pc, lr
-
00161:
-
00162: /*
-
00163: * This provides a C-API version of the above function.
-
00164: */
-
00165:ENTRY(lookup_processor_type)
-
00166: stmfd {r4 - r7, r9, lr}
-
00167: mov r9, r0
-
00168: bl __lookup_processor_type
-
00169: mov r0, r5
-
00170: ldmfd {r4 - r7, r9, pc}
-
00171:
-
00172: /*
-
00173: * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for
-
00174: * more information about the __proc_info and __arch_info structures.
-
00175: */
-
00176: .long __proc_info_begin
-
00177: .long __proc_info_end
-
-
00178:3: .long .
-
00179: .long __arch_info_begin
-
00180: .long __arch_info_end
145, 146行是函数定义
147行: 取地址指令,这里的3f是向前symbol名称是3的位置,即第178行,将该地址存入r3.
这里需要注意的是,adr指令取址,获得的是基于pc的一个地址,要格外注意,这个地址是3f处的"运行时地址",由于此时MMU还没有打开,也可以理解成物理地址(实地址).(详细内容可参考arm指令手册)
148行: 因为r3中的地址是178行的位置的地址,因而执行完后:
-
r5存的是176行符号 __proc_info_begin的地址; ??为什么是这样的?
-
r6存的是177行符号 __proc_info_end的地址; ??为什么是这样的?
-
r7存的是3f处的地址.
这里需要注意链接地址和运行时地址的区别. r3存储的是运行时地址(物理地址),而r7中存储的是链接地址(虚拟地址).
上面的__lookup_processor_type程序最让人绕的虚拟地址和物理地址的转换。其规律是
虚拟地址:“.”在编译后的对应的地址,
物理地址:3f,4b这样的标号似的地址
__proc_info_begin和__proc_info_end是在arch/arm/kernel/vmlinux.lds.S中:
-
00031: __proc_info_begin = .;
-
00032: *(.proc.info.init)
-
00033: __proc_info_end = .
这里是声明了两个变量:__proc_info_begin 和 __proc_info_end,其中等号后面的"."是location counter(详细内容请参考ld.info)
这三行的意思是: __proc_info_begin 的位置上,放置所有文件中的 ".proc.info.init" 段的内容,然后紧接着是 __proc_info_end 的位置.
kernel 使用struct proc_info_list来描述processor type.在 include/asm-arm/procinfo.h 中:
-
struct proc_info_list {
-
00030: unsigned int cpu_val;
-
00031: unsigned int cpu_mask;
-
00032: unsigned long __cpu_mm_mmu_flags; /* used by head.S */
-
00033: unsigned long __cpu_io_mmu_flags; /* used by head.S */
-
……
-
}
那么对于我们使用的架构体系的ARM,proc_info_list其放在哪里呢?
其位置在在arch/arm/mm/proc-arm920.S 中:
-
.align
-
-
.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
对于上s3c2440或s3c2410,其CPUID都是0x41129200,而在__arm920_proc_info定义的cpuval为0x41009200,mask为0xff00fff0,cpuval&mask等于CPUID。
要包含这个文件,内核中编译中arch/arm/mm/Makefile中必须包含有proc-arm920.o;
在菜单配置中在SystemType->Surpport ARM920T processer 项必须选上。
问题:编译一个内核时是不是只可以选一种处理器型号??
阅读(919) | 评论(0) | 转发(0) |