历史上的今天
今天是:2024年12月26日(星期四)
2019年12月26日 | arm-linux启动过程
2019-12-26 来源:eefocus
1. kernel运行的史前时期和内存布局
在 arm平台下,zImage.bin压缩镜像是由bootloader加载到物理内存,然后跳到zImage.bin里一段程序,它专门于将被压缩的 kernel解压缩到KERNEL_RAM_PADDR开始的一段内存中,接着跳进真正的kernel去执行。该kernel的执行起点是stext函数,定义于arch/arm/kernel/head.S。
1在分析stext函数前,先介绍此时内存的布局如下图所示

在开发板tqs3c2440中,SDRAM连接到内存控制器的Bank6中,它的开始内存地址是0x30000000,大小为64M,即 0x20000000。 arm Linux kernel将SDRAM的开始地址定义为PHYS_OFFSET。经bootloader加载kernel并由自解压部分代码运行后,最终kernel 被放置到KERNEL_RAM_PADDR(=PHYS_OFFSET + TEXT_OFFSET,即0x30008000)地址上的一段内存,经此放置后,kernel代码以后均不会被移动。
1在进入kernel代码前,即bootloader和自解压缩阶段,arm未开启MMU功能。因此kernel启动代码一个重要功能是设置好相应的页表,并开启MMU功能。为了支持MMU功能,kernel镜像中的所有符号,包括代码段和数据段的符号,在链接时都生成了它在开启MMU时,所在物理内存地址映射到的虚拟内存地址。
1以arm kernel第一个符号(函数)stext为例,在编译链接,它生成的虚拟地址是0xc0008000,而放置它的物理地址为0x30008000(还记得这是PHYS_OFFSET+TEXT_OFFSET吗?)。实际上这个变换可以利用简单的公式进行表示:va = pa – PHYS_OFFSET + PAGE_OFFSET。arm linux最终的kernel空间的页表,就是按照这个关系来建立。
之所以较早提及arm linux 的内存映射,原因是在进入kernel代码,里面所有符号地址值为清一色的0xCXXXXXXX地址,而此时arm未开启MMU功能,故在执行stext 函数第一条执行时,它的PC值就是stext所在的内存地址(即物理地址,0x30008000)。因此,下面有些代码,需要使用地址无关技术。
2.一览stext函数
stext函数定义在Arch/arm/kernel/head.S,它的功能是获取处理器类型和机器类型信息,并创建临时的页表,然后开启MMU功能,并跳进第一个C语言函数start_kernel。
stext函数的在前置条件是:MMU, D-cache, 关闭; r0 = 0, r1 = machine nr, r2 = atags prointer.
代码如下:
.section ".text.head", "ax"
(stext)
/* 设置CPU运行模式为SVC,并关中断 */
msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
@ and irqs disabled
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cupid
/* r10指向cpu对应的proc_info记录 */
movs r10, r5 @ invalid processor (r5=0)?
beq __error_p @ yes, error 'p'
bl __lookup_machine_type @ r5=machinfo
/* r8 指向开发板对应的arch_info记录 */
movs r8, r5 @ invalid machine (r5=0)?
beq __error_a @ yes, error 'a'
/* __vet_atags函数涉及bootloader造知kernel物理内存的情况,我们暂时不分析它。 */
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.
*/
/* 这里的逻辑关系相当复杂,先是从proc_info结构中的中跳进__arm920_setup函数,
* 然后执__enable_mmu 函数。最后在__enable_mmu函数通过mov pc, r13来执行__switch_data,
* __switch_data函数在最后一条语句,鱼跃龙门,跳进第一个C语言函数start_kernel。
*/
ldr r13, __switch_data @ address to jump to after
@ mmu has been enabled
adr lr, __enable_mmu @ return (PIC) address
add pc, r10, #PROCINFO_INITFUNC
OC(stext)
3 __lookup_processor_type 函数
__lookup_processor_type 函数是一个非常讲究技巧的函数,如果你将它领会,也将领会kernel了一些魔法。
Kernel 代码将所有CPU信息的定义都放到.proc.info.init段中,因此可以认为.proc.info.init段就是一个数组,每个元素都定义了一个或一种CPU的信息。目前__lookup_processor_type使用该元素的前两个字段cpuid和mask来匹配当前CPUID,如果满足 CPUID & mask == cpuid,则找到当前cpu的定义并返回。
下面是tqs3c2440开发板,CPU的定义信息,cpuid = 0x41009200,mask = 0xff00fff0。如果是码是运行在tqs3c2440开发板上,那么函数返回下面的定义:
.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
/* __arm920_setup函数在stext的未尾被调用,请往回看。*/
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
/*
* Read processor ID register (CP#15, CR0), and look up in the linker-built
* supported processor list. Note that we can't use the absolute addresses
* for the __proc_info lists since we aren't running with the MMU on
* (and therefore, we are not in the correct address space). We have to
* calculate the offset.
*
* r9 = cpuid
* Returns:
* r3, r4, r6 corrupted
* r5 = proc_info pointer in physical address space
* r9 = cpuid (preserved)
*/
__lookup_processor_type:
/* adr 是相对寻址,它的寻计算结果是将当前PC值加上3f符号与PC的偏移量,
* 而PC是物理地址,因此r3的结果也是3f符号的物理地址 */
adr r3, 3f
/* r5值为__proc_info_bein, r6值为__proc_ino_end,而r7值为.,
* 也即3f符号的链接地址。请注意,在链接期间,__proc_info_begin和
* __proc_info_end以及.均是链接地址,也即虚执地址。
*/
ldmda r3, {r5 - r7}
/* r3为3f的物理地址,而r7为3f的虚拟地址。结果是r3为虚拟地址与物理地址的差值,即PHYS_OFFSET - PAGE_OFFSET。*/
sub r3, r3, r7 @ get offset between virt&phys
/* r5为__proc_info_begin的物理地址, 即r5指针__proc_info数组的首地址 */
add r5, r5, r3 @ convert virt addresses to
/* r6为__proc_info_end的物理地址 */
add r6, r6, r3 @ physical address space
/* 读取r5指向的__proc_info数组元素的CPUID和mask值 */
1: ldmia r5, {r3, r4} @ value, mask
/* 将当前CPUID和mask相与,并与数组元素中的CPUID比较是否相同
* 若相同,则找到当前CPU的__proc_info定义,r5指向访元素并返回。
*/
and r4, r4, r9 @ mask wanted bits
上一篇:关于ARM CM3的启动文件分析
下一篇:ARM 之LCD和LCD控制器
史海拾趣
|
概述 直接数字频率合成技术(Direct Digital Frequency Synthesis,即DDFS,一般简称DDS),是从相 位概念出发直接合成所需要波形的一种新的频率合成技术。目前各大芯片制造厂商都相继推出采用先进 CMOS工艺生产的高性能、多功能的DDS芯 ...… 查看全部问答> |
|
请问各位:基于PID算法的有刷直流电机PWM调速系统中对有刷直流电机调速用到的算法是模拟 PID控制原理还是增量式PID控制或者别的PID控制原理?能否将PID算法式子告诉我?谢谢各位了!!QQ:286410824… 查看全部问答> |
|
如题!IAR汇编程序中多次使用RSEG伪指令是什么意思?IAR汇编程序中多次使用RSEG伪指令是什么意思?IAR汇编程序中多次使用RSEG伪指令是什么意思?IAR汇编程序中多次使用RSEG伪指令是什么意思?… 查看全部问答> |
|
PC系统为2000,所用工具是EVC3.0 PDA为操作系统为WINCE,PPC2002,所用连接软件是ActiveSync,编译一个没有错误的程序时,在PDA上显示 Application *.EXE has performed an illegal operation and will be shut down .If the problem persists,con ...… 查看全部问答> |
|
听说STM32F103VCT6的Load是采用串口升级的,那我们设备与外通讯仅仅有USB接口,如果我们用IAP方式进行升级,万一出现意外擦除了,那岂不是很麻烦! 有人碰到这个问题吗?怎么解决呢? 谢谢了!… 查看全部问答> |




