ARM Linux启动分析----head-armv.S(下)
armlinux吧
全部回复
仅看楼主
level 1
cymwin18 楼主
接着代码通过访问P15协处理器,得到当前的CPU的处理器ID,然后与以__proc_info_begin开始的处理器信息结构中的处理器ID相比较,相等则返回,不等则跳转到下一个处理器信息结构继续比较。从__proc_info_begin开始的保存处理器信息的结构的类型为struct proc_info_list,在include/asm-arm/procinfo.h中有具体的定义。实际的各处理器信息结构的赋值在arch/arm/mm/proc-xscale.S文件的.section ".proc.info",
#alloc, #
execinstr语句下面。为什么是在.proc.info段的下面呢?这是由vmlinux-armv.lds.in文件中的代码__proc_info_begin = .; *(.proc.info)__proc_info_end = .;所决定的。该段子程序完成后,各寄存器情况如下:R8 = 页表目录项的标志位R9 = 处理器IDR10 = 指向当前处理器信息结构的指针__lookup_architecture_type: adr r4, 2b ldmia r4, {r2, r3, r5, r6, r7} @ throw away r2, r3 sub r5, r4, r5 @ convert addresses add r4, r6, r5 @ to our address space add r7, r7, r51: ldr r5, [r4] @ get machine type teq r5, r1 beq 2f add r4, r4, #SIZEOF_MACHINE_DESC cmp r4, r7 blt 1b mov r7, #0 @ unknown architecture mov pc, lr2: ldmib r4, {r5, r6, r7} @ found, get results mov pc, lr这里开始查找机器的体系结构信息。前面已经提到过,在开始执行head-armv.S文件之前,R1中已经包含了当前的体系结构的类型号。现在所要做的,就是在__arch_info_begin开始的地址中,查找与R1中的值相匹配的机器类型信息。从__arch_info_begin开始的保存机器体系结构信息的类型为struct machine_desc,该结构在include/asm-arm/mach/arch.h中有具体的定义。对该结构的赋值使用MACHINE_START宏,该宏的定义同样在arch.h文件中。具体的机器体系结构的信息在arch/arm/kernel/arch.c中,当然也可以在arch/arm/mach-($machine-type)目录下的文件中添加与你自己的机器体系相对应的代码。这一点在进行linux移植的工作中很重要。该段子程序完成后,各寄存器情况如下:R5 = 内存(SDRAM)的起始物理地址R6 = IO的起始物理地址R7 = IO虚拟地址在页表中的索引项?这里有一个疑问。将R7赋值是为了调试的需要,使得串口可以打印调试信息,因此需要操作IO。现在,我的IO起始地址为0x40000000,在这里被映射到了0xFC000000。获得UART的虚拟地址的宏addruart在/arch/arm/kernel/debug-armv.S中被定义,这个宏通过io_p2v宏得到IO的虚拟地址,该宏在include/asm-arm/arch-pxa/hardware.h中被定义,但是它将0x40000000的物理地址映射到了0xF8000000,而不是上面提到的0xFC000000,这是为什么呢?希望能有人解释一下这个问题。__create_page_tables: pgtbl r4, r5 @ page table address /* Clear the 16K level 1 swapper page table */ mov r0, r4 mov r3,
#0 add r2, r0, #
0x40001: str r3, [r0],
#4 str r3, [r0], #
4 str r3, [r0],
#4 str r3, [r0], #
4 teq r0, r2 bne 1b /* * Create identity mapping for first MB of kernel to * cater for the MMU enable. This identity mapping * will be removed by paging_init() */ krnladr r2, r4, r5 @ start of kernel add r3, r8, r2 @ flags + kernel base
2008年03月15日 06点03分 1
level 1
cymwin18 楼主
str r3, [r4, r2, lsr #18] @ identity mapping /* * Now setup the pagetables for our kernel direct * mapped region. We round TEXTADDR down to the * nearest megabyte boundary. */ add r0, r4, #(TEXTADDR & 0xff000000) >> 18 @ start of kernel bic r2, r3, #0x00f00000 str r2, [r0] @ PAGE_OFFSET + 0MB add r0, r0, #(TEXTADDR & 0x00f00000) >> 18 str r3, [r0], #4 @ KERNEL + 0MB add r3, r3, #1 << 20 str r3, [r0], #4 @ KERNEL + 1MB add r3, r3, #1 << 20 str r3, [r0], #4 @ KERNEL + 2MB add r3, r3, #1 << 20 str r3, [r0], #4 @ KERNEL + 3MB /* * Ensure that the first section of RAM is present. * we assume that: * 1. the RAM is aligned to a 32MB boundary * 2. the kernel is executing in the same 32MB chunk * as the start of RAM. */ bic r0, r0, #0x01f00000 >> 18 @ round down and r2, r5, #0xfe000000 @ round down add r3, r8, r2 @ flags + rambase str r3, [r0] bic r8, r8, #0x0c @ turn off cacheable @ and bufferable bits代码创建页表目录。首先清空从0xA0004000开始的16K页表项。然后,为了可以访问从0xA0000000开始的内核的1M空间,将该地址对应的页表项赋值。接着映射从TEXTADDR开始的4M的虚拟地址空间,这需要4个页表项。最后,由于SDRAM开始的第一MB的空间存放有启动时的一些参数,所以也需要映射。在这里,该映射和前面的虚拟地址的映射在地址上是相等的。在创建页表目录完成后,代码通过前面主程序的最后一句add pc, r10, #12跳转到实际的CPU的设置子程序__xscale_setup。__xscale_setup: mov r0, #F_BIT|I_BIT|SVC_MODE msr cpsr_c, r0 mcr p15, 0, ip, c7, c7, 0 @ invalidate I, D caches & BTB mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer mcr p15, 0, ip, c8, c7, 0 @ invalidate I, D TLBs mcr p15, 0, r4, c2, c0, 0 @ load page table pointer mov r0, #0x1f @ Domains 0, 1 = client mcr p15, 0, r0, c3, c0, 0 @ load domain access register mov r0, #1 @ Allow user space to access mcr p15, 0, r0, c15, c1, 0 @ ... CP 0 only.#if CACHE_WRITE_THROUGH mov r0, #0x20
#else Mov r0, #
0x00#endif mcr p15, 0, r0, c1, c1, 0 @ set auxiliary control reg mrc p15, 0, r0, c1, c0, 0 @ get control register bic r0, r0, #0x0200 @ ......R......... bic r0, r0, #0x0082 @ ........B.....A. orr r0, r0, #0x0005 @ .............C.M orr r0, r0,
#0x3900 @ ..VIZ..S........#
ifdef CONFIG_XSCALE_CACHE_ERRATA bic r0, r0, #0x0004 @ see cpu_xscale_proc_init#endif mov pc, lr主要是操作协处理器,设置页表目录项基地址,对CACHE和BUFFER的控制位进行一些操作。具体大家可以看看介绍ARM编程的书。.type __switch_data, %object__switch_data: .long __mmap_switched .long SYMBOL_NAME(__bss_start) .long SYMBOL_NAME(_end) .long SYMBOL_NAME(processor_id) .long SYMBOL_NAME(__machine_arch_type) .long SYMBOL_NAME(cr_alignment) .long SYMBOL_NAME(init_task_union)+8192 .type __ret, %function__ret: ldr lr, __switch_data mcr p15, 0, r0, c1, c0 mov r0, r0 mov r0, r0 mov r0, r0 mov pc, lr .align 5__mmap_switched: adr r3, __switch_data + 4 ldmia r3, {r4, r5, r6, r7, r8, sp} @ r2 = compat @ sp = stack pointer mov fp, #0 @ Clear BSS (and zero fp)1: cmp r4, r5 strcc fp, [r4],#4 bcc 1b str r9, [r6] @ Save processor ID str r1, [r7] @ Save machine type#ifdef CONFIG_ALIGNMENT_TRAP orr r0, r0, #2 @ ...........A.
#endif bic r2, r0, #
2 @ Clear 'A' bit stmia r8, {r0, r2} @ Save control register values b SYMBOL_NAME(start_kernel)最后这段代码的作用主要是在进入C函数前先做一些变量的初始化和保存工作。首先清空BSS区域,然后保存处理器ID和机器类型到各自变量地址,接着保存cr_alignment,最后跳转到init/main.c中的start_kernel函数运行。以上介绍的是head-armv.S文件的主要内容和功能,它是linux运行的第一个文件,具有非常重要的意义。很好的阅读该文件,对于我们理解ARM处理器的工作方式有很大的帮助。同时,在许多linux系统的移植工作中,往往需要对该文件透彻的理解。
2008年03月15日 06点03分 2
1