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