Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1759745
  • 博文数量: 1493
  • 博客积分: 38
  • 博客等级: 民兵
  • 技术积分: 5834
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-19 17:28
文章分类

全部博文(1493)

文章存档

2016年(11)

2015年(38)

2014年(137)

2013年(253)

2012年(1054)

2011年(1)

分类: 嵌入式

2013-04-08 15:34:38

//在对整个 head.S 及其相关文件代码分析做个总结
//首先通过 arch/arm/kernel/head.S 中的 __lookup_processor_type 和 
//__lookup_machine_type 
//两个函数,找到处理器类型和机器类型然后创建页表,创建页表时因为考虑到打开 mmu 
//前后 cpu 发出的地址由物理地址变成了虚拟地址,所以将内核开始的 1M 
//空间先进行等价映射,再将这 1M 
//映射到它对就的虚拟地址空间。页表创建结束后,跳转到 arch/arm/mm/proc-arm920.S 
//中的 __arm920_setup 函数,对 I/Dcache 和 TLB 进行相关设置,为打开 mmu 
//作准备。主要是清空了 I/Dcache 、 tlb 及 write buffer 。然后打开 mmu 。打开 
//mmu 前,先设置好 TTB 
//,然后再打开。最后判断是否需要进行数据段搬移,如果数据段已经在 RAM 
//中就不要进行搬移。然后清空 bss 段,跳转到 start_kernel 
//,开始执行处理器无关代码。 
//可参考文件:linux源码分析之cpu初始化 kernel-head_s.mht
/*
 *  linux/arch/arm/kernel/head.S
 *
 *  Copyright (C) 1994-2002 Russell King
 *  Copyright (c) 2003 ARM Limited
 *  All Rights Reserved
 *
 * 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
#include


#include
#include
#include
#include
#include
#include
#include


#if (PHYS_OFFSET & 0x001fffff)
#error "PHYS_OFFSET must be at an even 2MiB boundary!"
#endif


#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
#define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET)




/*
 * swapper_pg_dir is the virtual address of the initial page table.
 * We place the page tables 16K below KERNEL_RAM_VADDR.  Therefore, we must
 * make sure that KERNEL_RAM_VADDR is correctly set.  Currently, we expect
 * the least significant 16 bits to be 0x8000, but we could probably
 * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.
 */
#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
#error KERNEL_RAM_VADDR must start at 0xXXXX8000
#endif


.globl swapper_pg_dir
.equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000


.macro pgtbl, rd
ldr \rd, =(KERNEL_RAM_PADDR - 0x4000)
.endm


#ifdef CONFIG_XIP_KERNEL
#define KERNEL_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
#define KERNEL_END _edata_loc
#else
#define KERNEL_START KERNEL_RAM_VADDR
#define KERNEL_END _end
#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, r2 = atags pointer.
 *
 * 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're trying to keep crap to a minimum; DO NOT add any machine specific
 * crap here - that's what the boot loader (or in extreme, well justified
 * circumstances, zImage) is for.
 */
__INIT  //__INIT 是一个宏定义在 include/linux/init.h 中,#define __INIT   .section   ".init.text","ax" 
//a 表示 Section contains allocated data 
//x 表示 Section contains executable instructions. 
.section ".text.head", "ax"
/* 内核入口点 */
//ENTRY(stext) 也是一个宏,在 include/linux/linkage.h 中定义 
//  #ifndef ENTRY 
//  #define ENTRY(name) \ 
//    .globl name; \ 
//    ALIGN; \ 
//    name: 
//  #endif 
//这段代码首先设置 cpu 工作模式为 svc 模式,禁止 FIQ 、 IRQ 
//然后查找处理器类型、查找机器类型,如果出现错误则进行相应的处理,如果没错,则创建页表。
ENTRY(stext)
                       /* (1) 又一次设置程序状态,禁止FIQ、IRQ,设定SVC模式 */
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
 /* (2) 判断CPU类型,查找运行的CPU ID值与Linux编译支持的ID值是否支持 */
bl __lookup_processor_type @ r5=procinfo r9=cpuid
movs r10, r5   @ invalid processor (r5=0)?// 处理器信息结构基地址保存到r10 
  /*  (3) 判断如果r10的值为0,invalid processor,跳转到出错处理,*/
beq __error_p @ yes, error 'p'
bl __lookup_machine_type @ r5=machinfo
// 机器类型结构的基地址保存到r8 
movs r8, r5 @ invalid machine (r5=0)?
/* (4) 判断如果r8的值为0,则表示invalid machine,跳转到出错处理,*/
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
 ARM( add pc, r10, #PROCINFO_INITFUNC )
 THUMB( add r12, r10, #PROCINFO_INITFUNC )
 THUMB( mov pc, r12 )
//这里把 __switch_data 和 __enablemmu 函数的地址分别存储到 r13 、 lr 
//寄存器中,最后通过 add pc, r10, #PROCINFO_INITFUNC 
//这条指令,跳转到处理器相关的函数去执行,这里 r10 
//中存放着处理器相关信息结构的基地址, PROCINFO_INITFUNC 是一个偏移量, arm920t 
//的信息结构在 arch/arm/mm/proc-arm920.S 的__arm920_proc_info:中初始化
ENDPROC(stext)


#if defined(CONFIG_SMP)
ENTRY(secondary_startup)
/*
* Common entry point for secondary CPUs.
*
* Ensure that we're in SVC mode, and IRQs are disabled.  Lookup
* the processor type - there is no need to check the machine type
* as it has already been validated by the primary processor.
*/
setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type
movs r10, r5 @ invalid processor?
moveq r0, #'p' @ yes, error 'p'
beq __error


/*
* Use the page tables supplied from  __cpu_up.
*/
adr r4, __secondary_data
ldmia r4, {r5, r7, r12} @ address to jump to after
sub r4, r4, r5 @ mmu has been enabled
ldr r4, [r7, r4] @ get secondary_data.pgdir
adr lr, BSYM(__enable_mmu) @ return address
mov r13, r12 @ __secondary_switched address
 ARM( add pc, r10, #PROCINFO_INITFUNC ) @ initialise processor
 @ (return control reg)
 THUMB( add r12, r10, #PROCINFO_INITFUNC )
 THUMB( mov pc, r12 )
ENDPROC(secondary_startup)


/*
* r6  = &secondary_data
*/
ENTRY(__secondary_switched)
ldr sp, [r7, #4] @ get secondary_data.stack
mov fp, #0
b secondary_start_kernel
ENDPROC(__secondary_switched)


.type __secondary_data, %object
__secondary_data:
.long .
.long secondary_data
.long __secondary_switched
#endif /* defined(CONFIG_SMP) */






/*
 * Setup common bits before finally enabling the MMU.  Essentially
 * this is just loading the page table pointer and domain access
 * registers.
 */
__enable_mmu:
// 在开头先根据配置,对控制寄存器中的位进行设置,然后设置域访问控制寄存器,把页表地址保存到 TTB 中,这个页表基址是在 __create_page_tables 这个函数在加载到 r4 
//寄存器中的。
 /* (6) 使能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
ENDPROC(__enable_mmu)


/*
 * 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
__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, r13
mov pc, r3
ENDPROC(__turn_mmu_on)
//这里先将前面对控制寄存器的配置写入控制寄存器,打开 mmu , I/Dcache 
//等,然后读处理器 ID 寄存器到 r3 中,最后把 r13 加载到 pc ,前面提到, 
//__switch_data 的地址被加载到 r13 中




/*
 * 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, r6, r7 corrupted
 *  r4 = physical page table address
 */
  /* (7) 创建页表*/
__create_page_tables:
pgtbl r4 @ page table address


/*
* Clear the 16K level 1 swapper page table
*/
mov r0, r4             /*  通过页表,对应地找出其物理地址  */
mov r3, #0
add r6, r0, #0x4000    /*  影射4M 的环境来跑C 代码 */
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_MM_MMUFLAGS] @ mm_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
mov r6, r6, lsr #20 @ start of kernel section
orr r3, r7, r6, lsl #20 @ flags + kernel base
str r3, [r4, r6, lsl #2] @ identity mapping


/*
* Now setup the pagetables for our kernel direct
* mapped region.
*/
add r0, r4,  #(KERNEL_START & 0xff000000) >> 18
str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
ldr r6, =(KERNEL_END - 1)
add r0, r0, #4
add r6, r4, r6, lsr #18
1: cmp r0, r6
add r3, r3, #1 << 20
strls r3, [r0], #4
bls 1b


#ifdef CONFIG_XIP_KERNEL
/*
* Map some ram to cover our .data and .bss areas.
*/
orr r3, r7, #(KERNEL_RAM_PADDR & 0xff000000)
.if (KERNEL_RAM_PADDR & 0x00f00000)
orr r3, r3, #(KERNEL_RAM_PADDR & 0x00f00000)
.endif
add r0, r4,  #(KERNEL_RAM_VADDR & 0xff000000) >> 18
str r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
ldr r6, =(_end - 1)
add r0, r0, #4
add r6, r4, r6, lsr #18
1: cmp r0, r6
add r3, r3, #1 << 20
strls r3, [r0], #4
bls 1b
#endif


/*
* Then map first 1MB of ram in case it contains our boot params.
*/
add r0, r4, #PAGE_OFFSET >> 18
orr r6, r7, #(PHYS_OFFSET & 0xff000000)
.if (PHYS_OFFSET & 0x00f00000)
orr r6, r6, #(PHYS_OFFSET & 0x00f00000)
.endif
str r6, [r0]


#ifdef CONFIG_DEBUG_LL
ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
/*
* 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're using the NetWinder or CATS, we also need to map
* in the 16550-type serial port for the debug messages
*/
add r0, r4, #0xff000000 >> 18
orr r3, r7, #0x7c000000
str r3, [r0]
#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, #0x02000000 >> 18
orr r3, r7, #0x02000000
str r3, [r0]
add r0, r4, #0xd8000000 >> 18
str r3, [r0]
#endif
#endif
mov pc, lr
ENDPROC(__create_page_tables)
.ltorg


#include "head-common.S"

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