Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1949790
  • 博文数量: 185
  • 博客积分: 10707
  • 博客等级: 上将
  • 技术积分: 1777
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-19 17:31
文章分类

全部博文(185)

文章存档

2014年(1)

2012年(6)

2011年(27)

2010年(13)

2009年(75)

2008年(63)

分类: LINUX

2008-12-25 21:52:40

--------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:http://zhiqiang0071.cublog.cn
--------------------------------------------



/*
 * linux/arch/arm/kernel/head.S
 *
 * Copyright (C) 1994-2002 Russell King
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Kernel startup code for all 32-bit CPUs
 */
#include <linux/config.h>
#include <linux/linkage.h>
#include <linux/init.h>

#include <asm/assembler.h>
#include <asm/domain.h>
#include <asm/mach-types.h>
#include <asm/procinfo.h>
#include <asm/ptrace.h>
#include <asm/constants.h>
#include <asm/system.h>

#define PROCINFO_MMUFLAGS    8
#define PROCINFO_INITFUNC    12

#define MACHINFO_PHYSRAM    4
#define MACHINFO_PHYSIO        8
#define MACHINFO_PGOFFIO    12

#ifndef CONFIG_XIP_KERNEL
/*
 * We place the page tables 16K below TEXTADDR. Therefore, we must make sure
 * that TEXTADDR is correctly set. Currently, we expect the least significant
 * 16 bits to be 0x8000, but we could probably relax this restriction to
 * TEXTADDR >= PAGE_OFFSET + 0x4000
 *
 * Note that swapper_pg_dir is the virtual address of the page tables, and
 * pgtbl gives us a position-independent reference to these tables. We can
 * do this because stext == TEXTADDR
 */
#if (TEXTADDR & 0xffff) != 0x8000
#error TEXTADDR must start at 0xXXXX8000
#endif

    .globl    swapper_pg_dir
    .equ    swapper_pg_dir, TEXTADDR - 0x4000    @TEXTADDR = 0xc0004000

    .macro    pgtbl, rd, phys
    adr    \rd, stext                @ 因为是相对寻址,所以rd = 0x80008000
    sub    \rd, \rd, #0x4000
    .endm
#else
/*
 * XIP Kernel:
 *
 * We place the page tables 16K below DATAADDR. Therefore, we must make sure
 * that DATAADDR is correctly set. Currently, we expect the least significant
 * 16 bits to be 0x8000, but we could probably relax this restriction to
 * DATAADDR >= PAGE_OFFSET + 0x4000
 *
 * Note that pgtbl is meant to return the physical address of swapper_pg_dir.
 * We can not make it relative to the kernel position in this case since
 * the kernel can physically be anywhere.
 */
#if (DATAADDR & 0xffff) != 0x8000
#error DATAADDR must start at 0xXXXX8000
#endif

    .globl    swapper_pg_dir
    .equ    swapper_pg_dir, DATAADDR - 0x4000

    .macro    pgtbl, rd, phys
    ldr    \rd, =((DATAADDR - 0x4000) - VIRT_OFFSET)
    add    \rd, \rd, \phys
    .endm
#endif

/*
 * Kernel startup entry point.
 * ---------------------------
 *
 * This is normally called from the decompressor code. The requirements
 * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
 * r1 = machine nr.
 *
 * This code is mostly position independent, so if you link the kernel at
 * 0xc0008000, you call this at __pa(0xc0008000).
 *
 * See linux/arch/arm/tools/mach-types for the complete list of machine
 * numbers for r1.
 *
 * We are trying to keep crap to a minimum; DO NOT add any machine specific
 * crap here - that is what the boot loader (or in extreme, well justified
 * circumstances, zImage) is for.
 */
 
 /*
  代码链接地址是从TEXTADDR=0xc0008000开始的,但是运行的程序是寻址无关地,所以可以
  在0x80008000处开始运行。
 */
 
 /*
  __INIT是一个宏定义在include/linux/init.h中:
  #define __INIT        .section    ".init.text","ax"
  __attribute__是gcc C语言扩展的一个关键字,这里表示将stext这个函数放在.init.text
  这个段中,而不是.text段,也就是说这段初始化代码所占内存在系统初始化完后释放掉。
  "ax"表示该段的属性是可allocatable和可执行的。
  ENTRY(stext)也是一个宏,在include/linux/linkage.h中定义
  #ifndef ENTRY
     #define ENTRY(name) \
     .globl name; \
     ALIGN; \
     name:
  #endif

  这段代码首先设置cpu为svc模式,禁止FIQ、IRQ。然后查找处理器类型、查找机器
  类型,如果出现错误则进行相应的处理,如果没错,则创建页表。
 */
    __INIT
    .type    stext, %function
ENTRY(stext)
    msr    cpsr_c, #PSR_F_BIT | PSR_I_BIT | MODE_SVC @ ensure svc mode
                        @ and irqs disabled
    bl    __lookup_processor_type        @ r5=procinfo r9=cpuid
    movs    r10, r5                @ invalid processor (r5=0)? 处理器信息结构基地址
    moveq    r0, #'p'            @ yes, error 'p'
    beq    __error
    bl    __lookup_machine_type        @ r5=machinfo
    movs    r8, r5                @ invalid machine (r5=0)? 机器类型结构的基地址保存
    moveq    r0, #'a'            @ yes, error 'a'
    beq    __error
    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.
     */
/*    
 这里把__switch_data和__enablemmu函数的地址分别存储到r13、lr寄存器中,通过
 add pc, r10, #PROCINFO_INITFUNC
 这条指令,跳转到处理器相关的函数去执行,这里r10中存放着处理器信息结构的基地址,
 PROCINFO_INITFUNC是一个偏移量,arm926ejs的信息结构定义在arch/arm/mm/proc-arm926.S:
    .type    __arm926_proc_info,#object
__arm926_proc_info:
    .long    0x41069260            @ ARM926EJ-S (v5TEJ)
    .long    0xff0ffff0
    .long PMD_TYPE_SECT | \
        PMD_SECT_BUFFERABLE | \
        PMD_SECT_CACHEABLE | \
        PMD_BIT4 | \
        PMD_SECT_AP_WRITE | \
        PMD_SECT_AP_READ
    b    __arm926_setup    @这条指令跳转到__arm926_setup中对cpu进行设置
    .long    cpu_arch_name
    .long    cpu_elf_name
    .long    HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_JAVA
    .long    cpu_arm926_name
    .long    arm926_processor_functions
    .long    v4wbi_tlb_fns
    .long    v4wb_user_fns
    .long    arm926_cache_fns
    .size    __arm926_proc_info, . - __arm926_proc_info
通过 add pc, r10, #PROCINFO_INITFUNC 找到 b __arm926_setup这条指令,
然后跳到__arm926_setup这个函数中,这个函数的定义在arm/arm/mm/proc-arm926.S中:
__arm926_setup:
    mov    r0, #0
    mcr    p15, 0, r0, c7, c7        @ invalidate I,D caches on v4
    mcr    p15, 0, r0, c7, c10, 4        @ drain write buffer on v4
    mcr    p15, 0, r0, c8, c7        @ invalidate I,D TLBs on v4

#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
    mov    r0, #4                @ disable write-back on caches explicitly
    mcr    p15, 7, r0, c15, c0, 0
#endif

    mrc    p15, 0, r0, c1, c0        @ get control register v4
    ldr    r5, arm926_cr1_clear    @0x7f3f
    bic    r0, r0, r5
    ldr    r5, arm926_cr1_set        @0x3135
    orr    r0, r0, r5
#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN
    orr    r0, r0, #0x4000            @ .1.. .... .... ....
#endif
    mov    pc, lr
    .size    __arm926_setup, . - __arm926_setup
这段代码首先使I/D cache、write buffer无效和I/D TLB无效,然后将mmu,I/D cache等位置位。
由于lr = __enable_mmu,故跳转到__enable_mmu继续执行。
*/
    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

    .type    __switch_data, %object
__switch_data:
    .long    __mmap_switched
    .long    __data_loc            @ r4
    .long    __data_start            @ r5
    .long    __bss_start            @ r6
    .long    _end                @ r7
    .long    processor_id            @ r4
    .long    __machine_arch_type        @ r5
    .long    cr_alignment            @ r6
    .long    init_thread_union+8192        @ sp

/*
init_thread_union在arch/arm/kernel/init_task.c中定义:
union thread_union init_thread_union
       __attribute__((__section__(".init.task"))) =
              { INIT_THREAD_INFO(init_task) };
由此可知,init_thread_union被链接到.init.task section中。
*/

/*
 * The following fragment of code is executed with the MMU on, and uses
 * absolute addresses; this is not position independent.
 *
 * r0 = cp#15 control register
 * r1 = machine ID
 * r9 = processor ID
 */
 
/*
 这段代码首先检查数据段的起始地址__data_start是否放到了指定位置__data_loc中,
 如果不是,则要进行数据搬移。之后,对bss段清零。然加载processor_id、
 __machine_arch_type、cr_alignment、init_thread_union + THREAD_START_SP 地址到
 r4、r5、r6、sp。接下来保存处理器ID和机器类型。把r0、r4保存到cr_alignment和
 cr_no_alignment变量中,最后跳到start_kernel处。
 cr_alignment在arch/arm/kernel/entry-armv.S中定义:
       .globl cr_alignment
       .globl cr_no_alignment
cr_alignment:
       .space 4     @这里space是指为cr_alignment分配4字节内存。
cr_no_alignment:
       .space 4
所以stmia r6, {r0, r4}把r0存到了cr_alignment,r4存到了cr_no_alignment
*/
    .type    __mmap_switched, %function
__mmap_switched:
    adr    r3, __switch_data + 4    @将__data_loc的地址加载到r3中

    @加载__data_loc、__data_start、__bss_start及_end的地址
    ldmia     {r4, r5, r6, r7}
    cmp    r4, r5                @ Copy data segment if needed
                            @比较__data_start与__data_loc是否相等
1:    cmpne    r5, r6            @如果不相等,进行数据搬运
    ldrne    fp, [r4], #4
    strne    fp, [r5], #4
    bne    1b

    mov    fp, #0                @ Clear BSS (and zero fp)
1:    cmp    r6, r7
    strcc    fp, [r6],#4
    bcc    1b

    ldmia    r3, {r4, r5, r6, sp}
    str    r9, [r4]            @ Save processor ID
    str    r1, [r5]            @ Save machine type
    bic    r4, r0, #CR_A            @ Clear 'A' bit
    stmia    r6, {r0, r4}            @ Save control register values
    b    start_kernel

/*
 * Setup common bits before finally enabling the MMU. Essentially
 * this is just loading the page table pointer and domain access
 * registers.
 */
 
/*
 在开头先根据配置,对控制寄存器中的位进行设置,然后设置域访问控制寄存器,把页表
 基址保存到TTB中,这个页表基址是在__create_page_tables这个函数在加载到r4寄存器中的。
 接着跳转到函数__turn_mmu_on。
*/
    .type    __enable_mmu, %function
__enable_mmu:
#ifdef CONFIG_ALIGNMENT_TRAP
    orr    r0, r0, #CR_A
#else
    bic    r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
    bic    r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
    bic    r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
    bic    r0, r0, #CR_I
#endif
    mov    r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
         domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
         domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
         domain_val(DOMAIN_IO, DOMAIN_CLIENT))
    mcr    p15, 0, r5, c3, c0, 0        @ load domain access register
    mcr    p15, 0, r4, c2, c0, 0        @ load page table pointer
    b    __turn_mmu_on

/*
 * Enable the MMU. This completely changes the structure of the visible
 * memory space. You will not be able to trace execution through this.
 * If you have an enquiry about this, *please* check the linux-arm-kernel
 * mailing list archives BEFORE sending another post to the list.
 *
 * r0 = cp#15 control register
 * r13 = *virtual* address to jump to upon completion
 *
 * other registers depend on the function called upon completion
 */
 
/*
 这里先将前面对控制寄存器的配置写入控制寄存器,打开mmu,I/Dcache等,然后读处理器
 ID寄存器到r3中,最后把r13加载到pc,前面提到,__switch_data的地址被加载到r13中,
 而在__switch_data中第一个地址是函数__mmap_switched,故跳转到函数__mmap_switched
 去执行
*/
    .align    5
    .type    __turn_mmu_on, %function
__turn_mmu_on:
    mov    r0, r0
    mcr    p15, 0, r0, c1, c0, 0        @ write control reg
    mrc    p15, 0, r3, c0, c0, 0        @ read id reg
    mov    r3, r3
    mov    r3, r3
    mov    pc, r13    @质的飞越,真正跳入内核虚空间,pc=0xc0008000+__mmap_switched的偏移
    
/*
 * Setup the initial page tables. We only setup the barest
 * amount which are required to get the kernel running, which
 * generally means mapping in the kernel code.
 *
 * r8 = machinfo
 * r9 = cpuid
 * r10 = procinfo
 *
 * Returns:
 * r0, r3, r5, r6, r7 corrupted
 * r4 = physical page table address
 */
 
/*如果设置好页表之,最终有一条指令是启用MMU的,假设该指令的PA是0x0800810c,根据
我们要做的映射关系,它的VA应该是0xc000810c,没有启用MMU之前CPU核发出的都是物理
地址,从0x0800810c地址取这条指令来执行,然而该指令执行之后,CPU核发出的地址都要
被MMU拦截,CPU核就必须用虚拟地址来取指令了,因此下一条指令应该从0xc0008110处取得,
然而这时pc寄存器(也就是r15寄存器)的值并没有变,CPU核取下一条指令仍然要从
0x08008110处取得,此时0x08008110已经成了非法地址了为了解决这个问题,要求启用MMU的
那条指令及其附近的指令虚拟地址跟物理地址相同,这样在启用MMU前后,附近指令的地址
不会发生变化,从而实现平稳过渡。因此需要将物理地址从0x08000000开始的1M再映射到
虚拟地址从0x08000000开始的1M,也就是做一个等价映射(identity map)(事实上,以上
解释并不完全正确,这里还有一个更复杂的细节,启用MMU的指令在执行时,后面两条指令
已经预取到CPU流水线里了,如果利用那两条指令跳转到0xc0008110不就行了?但是流水线
是靠不住的,跳转和异常都会清空流水线,[ARM参考手册]的Chapter A2详细解释了这种情况,
按该手册的建议应该采用等价映射的方法解决这个问题。)
本文件中采用的就是等价映射的方法。
*/
    .type    __create_page_tables, %function
__create_page_tables:
    ldr    r5, [r8, #MACHINFO_PHYSRAM]    @ physram    r5 = 0x80000000
    pgtbl    r4, r5     @ page table address,页表基址在 r4 = 0x80004000

    /*
     * Clear the 16K level 1 swapper page table
     */
    mov    r0, r4
    mov    r3, #0
    add    r6, r0, #0x4000
1:    str    r3, [r0], #4
    str    r3, [r0], #4
    str    r3, [r0], #4
    str    r3, [r0], #4
    teq    r0, r6
    bne    1b
    @将原来的1:1映射的16K页表清空,在解压内核前创建的
    
    @r10是procinfo的基地址,这里加载了proc_infor_list结构中__cpu_mm_mmu_flags到r7
    ldr    r7, [r10, #PROCINFO_MMUFLAGS]    @ mmuflags

    /*
     * Create identity mapping for first MB of kernel to
     * cater for the MMU enable. This identity mapping
     * will be removed by paging_init(). We use our current program
     * counter to determine corresponding section base address.
     */
    mov    r6, pc, lsr #20            @ start of kernel section r6 = 0x800
    orr    r3, r7, r6, lsl #20        @ flags + kernel base    r3 = 0x80000c1e
    str    r3, [r4, r6, lsl #2]        @ identity mapping 0x80000c1e-->0x80006000
/*
 首先将当前运行的内核指令所在的物理地址除以1M(右移20位),看这条指令在第几个
 section,然后通过orr r3, r7, r6, lsl #20形成了这个section对应的描述符,并写入
 对应的页表入口,因为每个section描述符占4个字节,这里把起始section数乘以4,加上
 r4中的页表起始地址,找到对应的页表入口。然后写入描述符。目的是使得MMU开启后,pc
 在未转换到虚地址0xc0008000的空间中之前,还能够继续映射原空间,即在0x80006000中
 填入0x80000c1e,把0x80000000的虚拟空间映射到0x80000000的物理空间之中
*/
    /*
     * Now setup the pagetables for our kernel direct
     * mapped region. We round TEXTADDR down to the
     * nearest megabyte boundary. It is assumed that
     * the kernel fits within 4 contigous 1MB sections.
     */
    add    r0, r4, #(TEXTADDR & 0xff000000) >> 18    @ start of kernel
    str    r3, [r0, #(TEXTADDR & 0x00f00000) >> 18]!
                            @0x80000c1e-->0x80007000, 0xc0000000==>0x80000000
    add    r3, r3, #1 << 20
    str    r3, [r0, #4]!            @ KERNEL + 1MB    @0xc0100000==>0x80100000
    add    r3, r3, #1 << 20
    str    r3, [r0, #4]!            @ KERNEL + 2MB    @0xc0200000==>0x80200000
    add    r3, r3, #1 << 20
    str    r3, [r0, #4]            @ KERNEL + 3MB    @0xc0300000==>0x80300000
/*
 TEXTADDR是内核起始虚拟地址(c0008000),(TEXTADDR & 0xff000000) >> 18和
 (TEXTADDR & 0x00f00000) >> 18获得了虚拟地址的高14位,这14位中最低两位为0,4字节
 对齐,和ttb中页表基址一起索引到页表中的一个位置,然后将页描述符写入页表,这个
 页描述符和上一个是一样的,这样在这第一个1MB空间内不管cpu发出的是虚拟地址还是
 物理地址,取的都是同一个存储单元的数据。这就解决了mmu打开是pc中存的还是没打开前的
 物理地址的问题。
*/

    /*
     * Then map first 1MB of ram in case it contains our boot params.
     */
    add    r0, r4, #VIRT_OFFSET >> 18    @获得内核空间起始虚拟地址对应描述符在表中的位置
                                    @VIRT_OFFSET = 0xc00000000 r0 = 0x80007000
    orr    r6, r5, r7        @ r6 = 0x80000c1e
    str    r6, [r0]    @ 0x80000c1e-->0x80007000    0xc0000000==>0x80000000
/*
 可以看出这段代码在dm644x平台其实可以不要了,它所做的事跟前面第一个1M的映射是一样的,
 我已经测试过,把这段代码注释掉,系统能正常启动。
*/

#ifdef CONFIG_XIP_KERNEL
    /*
     * Map some ram to cover our .data and .bss areas.
     * Mapping 3MB should be plenty.
     */
    sub    r3, r4, r5    @r3 = 0x80004000 - 0x80000000 = 0x4000
    mov    r3, r3, lsr #20    @//这两行获得页表起始地址和ram物理起始地址间的section数 0
    add    r0, r0, r3, lsl #2    @r0 = 0x80007000 + 0 = 0x80007000
    add    r6, r6, r3, lsl #20    @ r6 = 0x80000c1e
    str    r6, [r0], #4    @0x80000c1e-->0x80007000, 0xc0000000==>0x80000000
    add    r6, r6, #(1 << 20)    @0x80100c1e-->0x80007000, @0xc0100000==>0x80100000
    str    r6, [r0], #4
    add    r6, r6, #(1 << 20)    @0x80200c1e-->0x80007000, @0xc0200000==>0x80200000
    str    r6, [r0]
#endif
/* 可以看的出来,在本平台,该段代码如果编译了的话,也没啥用 */

    bic    r7, r7, #0x0c            @ turn off cacheable
                        @ and bufferable bits
#ifdef CONFIG_DEBUG_LL
    /*
     * Map in IO space for serial debugging.
     * This allows debug messages to be output
     * via a serial console before paging_init.
     */
    ldr    r3, [r8, #MACHINFO_PGOFFIO]
    add    r0, r4, r3
    rsb    r3, r3, #0x4000            @ PTRS_PER_PGD*sizeof(long)
    cmp    r3, #0x0800            @ limit to 512MB
    movhi    r3, #0x0800
    add    r6, r0, r3
    ldr    r3, [r8, #MACHINFO_PHYSIO]
    orr    r3, r3, r7
1:    str    r3, [r0], #4
    add    r3, r3, #1 << 20
    teq    r0, r6
    bne    1b
#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
    /*
     * If we are using the NetWinder, we need to map in
     * the 16550-type serial port for the debug messages
     */
    teq    r1, #MACH_TYPE_NETWINDER
    teqne    r1, #MACH_TYPE_CATS
    bne    1f
    add    r0, r4, #0x3fc0            @ ff000000
    mov    r3, #0x7c000000
    orr    r3, r3, r7
    str    r3, [r0], #4
    add    r3, r3, #1 << 20
    str    r3, [r0], #4
1:
#endif
#endif
#ifdef CONFIG_ARCH_RPC
    /*
     * Map in screen at 0x02000000 & SCREEN2_BASE
     * Similar reasons here - for debug. This is
     * only for Acorn RiscPC architectures.
     */
    add    r0, r4, #0x80            @ 02000000
    mov    r3, #0x02000000
    orr    r3, r3, r7
    str    r3, [r0]
    add    r0, r4, #0x3600            @ d8000000
    str    r3, [r0]
#endif
    mov    pc, lr
    .ltorg    @这个伪指令声明了一个文字池,把ldr伪指令要加载的数据保存在文字池内

/*
 对创建页表作个总结,开始时通过一个宏获得页表基地址,然后清空页表,接着在
 proc_infor_list结构中获得__cpu_mm_mmu_flags,也就是一级描述符的低20位的值。然后
 对当前运行的内核指令地址所在的section进行等价映射,使其物理地址和虚拟地址一样,
 接着再把这1M物理地址映射到链接时设置的内核起始虚拟地址TEXTADDR对应的section描述符,
 这样在打开mmu时cpu发出的地址就不会被映射到错误的物理地址上去。紧接着再顺序映射3M,
 以把内核所占的物理地址映射。最后还要把第一个1M的ram空间映射相应的页表入口,因为
 这1M的空间可能存放着内核启动参数。如果定义了CONFIG_XIP_KERNEL还要再映射3M内存,
 在这没啥用。
*/

/*
 * Exception handling. Something went wrong and we can not proceed. We
 * ought to tell the user, but since we don not have any guarantee that
 * we are even running on the right architecture, we do virtually nothing.
 *
 * r0 = ascii error character:
 *    a = invalid architecture
 *    p = invalid processor
 *    i = invalid calling convention
 *
 * Generally, only serious errors cause this.
 */
    .type    __error, %function
__error:
#ifdef CONFIG_DEBUG_LL
    mov    r8, r0                @ preserve r0
    adr    r0, err_str
    bl    printascii
    mov    r0, r8
    bl    printch
#endif
#ifdef CONFIG_ARCH_RPC
/*
 * Turn the screen red on a error - RiscPC only.
 */
    mov    r0, #0x02000000
    mov    r3, #0x11
    orr    r3, r3, r3, lsl #8
    orr    r3, r3, r3, lsl #16
    str    r3, [r0], #4
    str    r3, [r0], #4
    str    r3, [r0], #4
    str    r3, [r0], #4
#endif
1:    mov    r0, r0
    b    1b

#ifdef CONFIG_DEBUG_LL
    .type    err_str, %object
err_str:
    .asciz    "\nError: "
    .align
#endif

/*
 * Read processor ID register (CP#15, CR0), and look up in the linker-built
 * supported processor list. Note that we can not use the absolute addresses
 * for the __proc_info lists since we are not running with the MMU on
 * (and therefore, we are not in the correct address space). We have to
 * calculate the offset.
 *
 * Returns:
 *    r3, r4, r6 corrupted
 *    r5 = proc_info pointer in physical address space
 *    r9 = cpuid
 */
 
/*
 这里能过查表的方式查找对应处理器的信息结构,如果找到,则把它的基地址放入r5寄存器,
 没有找到则r5 = 0。在链接脚本arch/arm/kernel/vmlinux.lds中有:
  __proc_info_begin = .;
   *(.proc.info.init)
  __proc_info_end = .;
这三行把所有处理器信息结构组合在一块,就像一个结构数组。这样查找时只要找到
__proc_infor_end的地址,很快就能找到处理器信息结构数组。对于机器信息也是一样:
  __arch_info_begin = .;
   *(.arch.info.init)
  __arch_info_end = .;
把这些信息组合在一起。
 SIZEOF_MACHINE_DESC 在arch/arm/kernel/asm-offsets.c中定义:
 DEFINE(SIZEOF_MACHINE_DESC,    sizeof(struct machine_desc));
 而DM644x平台的machine_desc结构体在arch/arm/mach-davinci/board_evm.c中定义:
 
 MACHINE_START(DAVINCI_EVM, "DaVinci EVM")
    MAINTAINER("Texas Instruments, PSP Team")
    BOOT_MEM(DAVINCI_DDR_BASE, IO_PHYS, IO_VIRT)
    BOOT_PARAMS(0x80000100)
    MAPIO(davinci_map_io)
    INITIRQ(davinci_irq_init)
    .timer = &davinci_timer,
    INIT_MACHINE(evm_init)
 MACHINE_END
 
 IO_PHYS, IO_VIRT在include/asm-arm/arch-dainvci/io.h中定义:
 
 #define IO_PHYS        0x01c00000
 #define IO_VIRT        0xe1000000
 
 DAVINCI_DDR_BASE在include/asm-arm/arch-dainvci/memory.h中定义:
 
 #define DAVINCI_DDR_BASE 0x80000000
*/
    .type    __lookup_processor_type, %function
__lookup_processor_type:
    adr    r3, 3f
    ldmda    r3, {r5, r6, r9}
    sub    r3, r3, r9            @ get offset between virt&phys
    add    r5, r5, r3            @ convert virt addresses to
    add    r6, r6, r3            @ physical address space
    mrc    p15, 0, r9, c0, c0        @ get processor id
1:    ldmia    r5, {r3, r4}            @ value, mask
    and    r4, r4, r9            @ mask wanted bits
    teq    r3, r4
    beq    2f
    add    r5, r5, #PROC_INFO_SZ        @ sizeof(proc_info_list)
    cmp    r5, r6
    blt    1b
    mov    r5, #0                @ unknown processor
2:    mov    pc, lr

/* 这是函数__lookup_machine_type的定义。查找方法和__lookup_processor_type是一样的 */
/*
 * This provides a C-API version of the above function.
*/
ENTRY(lookup_processor_type)
    stmfd     {r4 - r6, r9, lr}
    bl    __lookup_processor_type
    mov    r0, r5
    ldmfd     {r4 - r6, r9, pc}

/*
 * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for
 * more information about the __proc_info and __arch_info structures.
 */
    .long    __proc_info_begin
    .long    __proc_info_end
3:    .long    .
    .long    __arch_info_begin
    .long    __arch_info_end

/*
 * Lookup machine architecture in the linker-build list of architectures.
 * Note that we can not use the absolute addresses for the __arch_info
 * lists since we are not running with the MMU on (and therefore, we are
 * not in the correct address space). We have to calculate the offset.
 *
 * r1 = machine architecture number
 * Returns:
 * r3, r4, r6 corrupted
 * r5 = mach_info pointer in physical address space
 */
    .type    __lookup_machine_type, %function
__lookup_machine_type:
    adr    r3, 3b
    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
    rsb    r3, r5, r6            @ number of machine types 1 个
    teq    r3, #SIZEOF_MACHINE_DESC    @ only one?
    ldreq    r1, [r5]            @ if so do not bother with r1
    beq    2f                @ ...and be happy.
1:    ldr    r3, [r5]            @ get machine type
    teq    r3, r1                @ matches loader number?
    beq    2f                @ found
    add    r5, r5, #SIZEOF_MACHINE_DESC    @ next machine_desc
    cmp    r5, r6
    blt    1b
    mov    r5, #0                @ unknown machine
2:    mov    pc, lr

/*
 * This provides a C-API version of the above function.
 */
ENTRY(lookup_machine_type)
    stmfd     {r4 - r6, lr}
    mov    r1, r0
    bl    __lookup_machine_type
    mov    r0, r5
    ldmfd     {r4 - r6, pc}

/*    
现在对整个head.S及其相关文件代码分析做个总结。
首先通过__lookup_processor_type和__lookup_machine_type两个函数找到处理器类型
和机器类型,然后创建页表,创建页表时因为考虑到打开mmu前后cpu发出的地址由物理地址
变成了虚拟地址,所以将内核开始的1M空间先进行等价映射,再将这1M映射到它对应的
虚拟地址空间。页表创建结束后,跳转到__arm926_setup函数,对I/Dcache和TLB进行相关
设置,为打开mmu作准备。主要是清空了I/Dcache、tlb及write buffer。然后设置好TTB
并打开mmu。最后判断是否需要进行数据段搬移,如果数据段已经在RAM中就不要进行搬移。
然后清空bss段,跳转到start_kernel,开始执行处理器无关代码。
*/

/*
参考资料:
    1.《arm-linux源码分析之cpu初始化》,大部分分析解释来自该文章,本人补充了
    DM644x平台的一些知识和分析。非常感谢BoySKung。网址是:
     http://blog.csdn.net/BoySKung/archive/2008/12/09/3486026.aspx
    2.《ARM-Linux启动过程的空间映射之一》,网址是:
     http://cuijinbird.bokee.com/4812261.html
    
*/

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

chinaunix网友2009-03-21 23:15:23

无意间闯入了您的博客,写得太好了!!小弟刚开始学习davinci,导师要我现在先摸索一下,等下学期再做东西!!现在刚开始入门,守着一块DM6446的板子,而不知道怎么做,学长也没有做这个的,所以非常想听听前辈您的意见!!如果可以的话,能不能交个朋友?? 我的联系方式:wth.2008@163.com QQ:327796932