Chinaunix首页 | 论坛 | 博客
  • 博客访问: 411352
  • 博文数量: 118
  • 博客积分: 294
  • 博客等级: 二等列兵
  • 技术积分: 667
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-16 20:31
文章分类

全部博文(118)

文章存档

2014年(3)

2012年(25)

2011年(90)

分类:

2011-12-30 22:31:06

在上一篇博文中我们已经知道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信息。
阅读(686) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~