Chinaunix首页 | 论坛 | 博客
  • 博客访问: 536171
  • 博文数量: 139
  • 博客积分: 6000
  • 博客等级: 准将
  • 技术积分: 1840
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-11 22:40
文章分类

全部博文(139)

文章存档

2011年(1)

2009年(3)

2008年(135)

我的朋友

分类: LINUX

2008-05-16 17:33:47

在网上参考很多高手的文章,又加入了自己的一点儿内容,整理了一下,里面还有很多不明白的地方,而且也会有理解错误

的地方,望高手指点,自己也会不断进行修改


当进入linux内核后,arch/arm/kernel/head-armv.S是内核最先执行的一个文件,包括从内核入口ENTRY(stext)到
start_kernel之间的初始化代码,下面以我所是用的平台intel pxa270为例,说明一下他的汇编代码:

1    .section ".text.init",#alloc,#execinstr
2    .type   stext, #function
/* 内核入口点 */
3 ENTRY(stext)
4    mov r12, r0
/* 程序状态,禁止FIQ、IRQ,设定SVC模式 */    
5     mov r0, #F_BIT | I_BIT | MODE_SVC   @ make sure svc mode
6    msr cpsr_c, r0          @ and all irqs disabled
/* 判断CPU类型,查找运行的CPU ID值与Linux编译支持的ID值是否支持 */
7    bl  __lookup_processor_type
/* 判断如果r10的值为0,则表示函数执行错误,跳转到出错处理,*/
/* 出错处理函数__error的实现代码定义在debug-armv.S中,这里就不再作过多介绍了 */
8    teq r10, #0             @ invalid processor?
9    moveq   r0, #'p'            @ yes, error 'p'
10   beq __error
/* 判断体系类型,查看R1寄存器的Architecture Type值是否支持 */
11   bl  __lookup_architecture_type
/* 判断如果r7的值为0,则表示函数执行错误,跳转到出错处理,*/
12   teq r7, #0              @ invalid architecture?
13   moveq   r0, #'a'            @ yes, error 'a'
14   beq __error
/* 创建核心页表 */
15   bl  __create_page_tables
16   adr lr, __ret           @ return address
17   add pc, r10, #12            @ initialise processor
                              @ (return control reg)
                             
第5行,准备进入SVC工作模式,同时关闭中断(I_BIT)和快速中断(F_BIT)
第7行,查看处理器类型,主要是为了得到处理器的ID以及页表的flags。
第11行,查看一些体系结构的信息。
第15行,建立页表。
第17行,跳转到处理器的初始化函数,其函数地址是从__lookup_processor_type中得到的,
需要注意的是第16行,当处理器初始化完成后,会直接跳转到__ret去执行,
这是由于初始化函数最后的语句是mov pc, lr。

前面一篇文章,简单介绍了内核启动的汇编主流程,这篇介绍其中调用的汇编子函数__lookup_processor_type

函数__lookup_processor_type介绍:

内核中使用了一个结构struct proc_info_list,用来记录处理器相关的信息,该结构定义在
kernel/include/asm-arm/procinfo.h头文件中。

/* 
 * Note!  struct processor is always defined if we're
 * using MULTI_CPU, otherwise this entry is unused,
 * but still exists.
 *
 * NOTE! The following structure is defined by assembly
 * language, NOT C code.  For more information, check:
 *  arch/arm/mm/proc-*.S and arch/arm/kernel/head-armv.S
 */    
struct proc_info_list {
    unsigned int        cpu_val;
    unsigned int        cpu_mask;
    unsigned long       __cpu_mmu_flags;    /* used by head-armv.S */
    unsigned long       __cpu_flush;        /* used by head-armv.S */
    const char      *arch_name;
    const char      *elf_name;
    unsigned int        elf_hwcap;
    struct proc_info_item   *info;
    struct processor    *proc;
}; 

在arch/arm/mm/proc-xscale.S文件中定义了所有和xscale有关的proc_info_list,我们使用的pxa270定义如下:

.section ".proc.info", #alloc, #execinstr

.type   __bva0_proc_info,#object
__bva0_proc_info:
    .long   0x69054110          @ Bulverde A0: 0x69054110, A1 : 0x69054111.
    .long   0xfffffff0          @ and this is the CPU id mask.
#if CACHE_WRITE_THROUGH
    .long   0x00000c0a
#else
    .long   0x00000c0e
#endif
  b   __xscale_setup
  .long   cpu_arch_name
    .long   cpu_elf_name
    .long   HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_XSCALE
    .long   cpu_bva0_info
    .long   xscale_processor_functions
    .size   __bva0_proc_info, . - __bva0_proc_info
   
由于.section指示符,上面定义的__bva0_proc_info信息在编译的时候被放到了.proc.info段中,这是由linux的
链接脚本文件vmlinux.lds指定的,参考如下:
       SECTIONS
       {
           . = 0xC0008000;
           .init : {           /* Init code and data       */
              _stext = .;
              __init_begin = .;
                  *(.text.init)
              __proc_info_begin = .;
                  *(.proc.info)
              __proc_info_end = .;
             
这里的符号__proc_info_begin指向.proc.info的起始地址,而符号__proc_info_end指向.proc.info的结束地址。
后面就会引用这两个符号,来指向.proc.info这个段。
    

下面来来看看函数的源代码,为了分析方便将函数按行进行编号,其中17-18行就是前面提到的对.proc.info的引用,
第2行将17行的地址放到寄存器r5中,adr是小范围的地址读取伪指令。第3行将r5所指向的数据区的数据读出到r7,r9
r10,执行结果是r7=__proc_info_end,r9=__proc_info_begin,r10=第19行的地址,第4-6行的结果应该是r10指向
__proc_info_begin的地址,第7行读取cpu的id,这是一个协处理器指令,将processor ID存储在r9中,第8行将r10指向
的__bva0_proc_info开始的数据读出放到寄存器r5,r6,r8,结果r5=0x69054110(cpu_val),r6=0xfffffff0(cpu_mask),
r8=0x00000c0e(__cpu_mmu_flags),第9-10行将读出的id和结构中的id进行比较,如果id相同则返回,返回时r9存储
processor ID,如果id不匹配,则将指针r10增加36(proc_info_list结构的长度),如果r10小于r7指定的地址,也就是
__proc_info_end,则继续循环比较下一个proc_info_list中的id,如第11-14行的代码,如果查找到__proc_info_end
仍未找到一个匹配的id,则将r10清零并返回,如15-16行,也就是说如果函数执行成功则r10指向匹配的proc_info_list
结构地址,如果函数返回错误则r10为0。

/*     
 * 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.
 *     
 * Returns:
 *  r5, r6, r7 corrupted           
 *  r8  = page table flags
 *  r9  = processor ID
 *  r10 = pointer to processor structure
 */    
1 __lookup_processor_type:   
2    adr r5, 2f
3    ldmia   r5, {r7, r9, r10}
4    sub r5, r5, r10         @ convert addresses
5    add r7, r7, r5          @ to our address space
6    add r10, r9, r5
7    mrc p15, 0, r9, c0, c0      @ get processor id
8 1:   ldmia   r10, {r5, r6, r8}       @ value, mask, mmuflags
9    and r6, r6, r9          @ mask wanted bits
10   teq r5, r6
11   moveq   pc, lr
12   add r10, r10, #36           @ sizeof(proc_info_list)
13   cmp r10, r7
14   blt 1b
15   mov r10, #0             @ unknown processor
16   mov pc, lr
   
/*     
 * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for
 * more information about the __proc_info and __arch_info structures.
 */    
17 2:     .long   __proc_info_end
18        .long   __proc_info_begin
19        .long   2b
20        .long   __arch_info_begin
21        .long   __arch_info_end

阅读(1273) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~