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










2008-12-25 21:52:40


 * 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 MACHINFO_PHYSIO        8
#define MACHINFO_PGOFFIO    12

 * 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
 * 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

    .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
 * 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
 * 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

    .globl    swapper_pg_dir
    .equ    swapper_pg_dir, DATAADDR - 0x4000

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

 * 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.
  #define __INIT        .section    ".init.text","ax"
  __attribute__是gcc C语言扩展的一个关键字,这里表示将stext这个函数放在.init.text
  #ifndef ENTRY
     #define ENTRY(name) \
     .globl name; \
     ALIGN; \

    .type    stext, %function
    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.
 add pc, r10, #PROCINFO_INITFUNC
    .type    __arm926_proc_info,#object
    .long    0x41069260            @ ARM926EJ-S (v5TEJ)
    .long    0xff0ffff0
    .long PMD_TYPE_SECT | \
        PMD_BIT4 | \
        PMD_SECT_AP_WRITE | \
    b    __arm926_setup    @这条指令跳转到__arm926_setup中对cpu进行设置
    .long    cpu_arch_name
    .long    cpu_elf_name
    .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这条指令,
    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

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

    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
    orr    r0, r0, #0x4000            @ .1.. .... .... ....
    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
    .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

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
 __machine_arch_type、cr_alignment、init_thread_union + THREAD_START_SP 地址到
       .globl cr_alignment
       .globl cr_no_alignment
       .space 4     @这里space是指为cr_alignment分配4字节内存。
       .space 4
所以stmia r6, {r0, r4}把r0存到了cr_alignment,r4存到了cr_no_alignment
    .type    __mmap_switched, %function
    adr    r3, __switch_data + 4    @将__data_loc的地址加载到r3中

    ldmia     {r4, r5, r6, r7}
    cmp    r4, r5                @ Copy data segment if needed
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.
    .type    __enable_mmu, %function
    orr    r0, r0, #CR_A
    bic    r0, r0, #CR_A
    bic    r0, r0, #CR_C
    bic    r0, r0, #CR_Z
    bic    r0, r0, #CR_I
    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
    .align    5
    .type    __turn_mmu_on, %function
    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
虚拟地址从0x08000000开始的1M,也就是做一个等价映射(identity map)(事实上,以上
是靠不住的,跳转和异常都会清空流水线,[ARM参考手册]的Chapter A2详细解释了这种情况,
    .type    __create_page_tables, %function
    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
    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
 section,然后通过orr r3, r7, r6, lsl #20形成了这个section对应的描述符,并写入
     * 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字节

     * 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

     * 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]
/* 可以看的出来,在本平台,该段代码如果编译了的话,也没啥用 */

    bic    r7, r7, #0x0c            @ turn off cacheable
                        @ and bufferable bits
     * 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 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
     * 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]
    mov    pc, lr
    .ltorg    @这个伪指令声明了一个文字池,把ldr伪指令要加载的数据保存在文字池内


 * 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
    mov    r8, r0                @ preserve r0
    adr    r0, err_str
    bl    printascii
    mov    r0, r8
    bl    printch
 * 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
1:    mov    r0, r0
    b    1b

    .type    err_str, %object
    .asciz    "\nError: "

 * 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 = 0。在链接脚本arch/arm/kernel/vmlinux.lds中有:
  __proc_info_begin = .;
  __proc_info_end = .;
  __arch_info_begin = .;
  __arch_info_end = .;
 SIZEOF_MACHINE_DESC 在arch/arm/kernel/asm-offsets.c中定义:
 DEFINE(SIZEOF_MACHINE_DESC,    sizeof(struct machine_desc));
    MAINTAINER("Texas Instruments, PSP Team")
    .timer = &davinci_timer,
 IO_PHYS, IO_VIRT在include/asm-arm/arch-dainvci/io.h中定义:
 #define IO_PHYS        0x01c00000
 #define IO_VIRT        0xe1000000
 #define DAVINCI_DDR_BASE 0x80000000
    .type    __lookup_processor_type, %function
    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.
    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
    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.
    stmfd     {r4 - r6, lr}
    mov    r1, r0
    bl    __lookup_machine_type
    mov    r0, r5
    ldmfd     {r4 - r6, pc}

设置,为打开mmu作准备。主要是清空了I/Dcache、tlb及write buffer。然后设置好TTB


阅读(4037) | 评论(1) | 转发(1) |

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

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