转自尘的日志 linux/arch/arm/kernel/head.S是linux内核映像解压后执行的第一个文件。 //PAGE_OFFSET //PHYS_OFFSET #define #define .section ENTRY(stext) msr
cpsr_c, mrc
p15, bl
__lookup_processor_type
@ movs
r10, // beq
__error_p
@ //查询machine bl
__lookup_machine_type
@ movs
r8, beq
__error_a
@ bl
__vet_atags bl
__create_page_tables ldr
r13, adr
lr, //此命令将导致程序段__arm920_setup的执行,后面会将到。 //r10中存放的基地址是从__lookup_processor_type中得到的,如上面movs
r10, ENDPROC(stext)
接下来将对上面遇到的几个程序段展开分析。 __lookup_processor_type 在讲解该程序段之前先来看一些相关知识。 内核做支持的每一种CPU类型都由结构体proc_info_list 该结构体在文件arch/arm/include/asm/procinfo.h中定义: struct unsigned unsigned unsigned unsigned unsigned const const unsigned const struct struct struct struct }; 对于arm920来说,其对应结构体在文件 初始化。 .section .type __arm920_proc_info,#object __arm920_proc_info: .long 0x41009200 .long 0xff00fff0 .long PMD_SECT_BUFFERABLE PMD_SECT_CACHEABLE PMD_BIT4 PMD_SECT_AP_WRITE PMD_SECT_AP_READ .long PMD_BIT4 PMD_SECT_AP_WRITE PMD_SECT_AP_READ b __arm920_setup ……………………………… .section 在链接文件arch/arm/kernel/vmlinux.lds中: SECTIONS { #ifdef . #else . #endif .text.head _stext _sinittext *(.text.head) } .init INIT_TEXT _einittext __proc_info_begin *(.proc.info.init) __proc_info_end __arch_info_begin *(.arch.info.init) __arch_info_end __tagtable_begin *(.taglist.init) __tagtable_end ……………………………… } 所有CPU类型对应的被初始化的proc_info_list结构体都放在__proc_info_begin 和__proc_info_end之间。 __lookup_processor_type: //r3存储的是标号3的物理地址(由于没有启用mmu,所以当前肯定是物理地址) adr r3, //R5=__proc_info_begin,r6=__proc_info_end,r7=标号3处的虚拟地址。 ldmda r3, sub r3, add r5, add r6, 1: ldmia r5, and r4, ////查看代码和CPU硬件是否匹配(比如想在arm920t上运行为cortex-a8编译的内核?不让!) teq r3, beq 2f //PROC_INFO_SZ add r5, //判断是否已经到了结构体proc_info_list存放区域的末尾__proc_info_end, cmp r5, blo 1b //如果没有匹配成功就将r5清零,如果匹配成功r5中放的是该CPU类型对应的结构体//proc_info_list的基地址。 mov r5, 2: mov pc, ENDPROC(__lookup_processor_type) 3: .long .
__lookup_machine_type 每一个CPU平台都可能有其不一样的结构体,描述这个平台的结构体是machine_desc。 这个结构体在文件arch/arm/include/asm/mach/arch.h中定义: struct unsigned unsigned ……………………………… }; 对于平台smdk2410来说其对应machine_desc结构在文件 linux/arch/arm/mach-s3c2410/mach-smdk2410.c中初始化: MACHINE_START(SMDK2410, .phys_io
= .io_pg_offst
= .boot_params
= .map_io = .init_irq
= .init_machine
= .timer = MACHINE_END 对于宏MACHINE_START在文件arch/arm/include/asm/mach/arch.h中定义: #define static .nr = .name = #define }; __attribute__((__section__(".arch.info.init")))表明该结构体在并以后存放的位置。 在链接文件链接脚本文件arch/arm/kernel/vmlinux.lds中 SECTIONS { #ifdef . #else . #endif .text.head _stext _sinittext *(.text.head) } .init INIT_TEXT _einittext __proc_info_begin *(.proc.info.init) __proc_info_end __arch_info_begin *(.arch.info.init) __arch_info_end ……………………………… } 在__arch_info_begin和
3: .long .
__lookup_machine_type: adr r3, ////R4=标号3处的虚拟地址,r5=__arch_info_begin,r6=__arch_info_end。 ldmia r3, sub r3, add r5, add r6, //读取machine_desc结构的nr参数,对于smdk2410来说该值是MACH_TYPE_SMDK2410。 //这个值在文件linux/arch/arm/tools/mach-types中: 1: ldr r3, teq r3, beq 2f //如果匹配成功就返回 add r5, cmp r5, blo 1b mov r5, 2: mov pc, ENDPROC(__lookup_machine_type)
__vet_atags 关于参数链表: 内核参数链表的格式和说明可以从内核源代码目录树中的 找到,参数链表必须以ATAG_CORE ATAG_NONE是各个参数的标记,本身是一个32位值,例如:ATAG_CORE=0x54410001。 其它的参数标记还包括: ATAG_COMDLINE 数链表。参数结构体的定义如下:
参数结构体包括两个部分,一个是 tag_header结构体的定义如下: }; 其中 tag_header的大小加上 size=(sizeof(tag->tag_header)+sizeof(tag->u.core))>>2,一般通过函数 来获得每个参数结构体的
等。 __vet_atags: tst
r2, bne 1f ldr
r5,
//#define subs
r5, bne 1f ldr
r5, ldr
r6, cmp
r5, bne
1f mov
pc, 1:
mov
r2, mov
pc, ENDPROC(__vet_atags)
__create_page_tables .macro pgtbl, ldr \rd, .endm
__create_page_tables: //r4 //这个值必须是16K对齐的。 pgtbl r4 //为内核代码存储区域创建页表,首先将内核起始地址-0x4000到内核起始地址之间的16K mov r0, mov r3, add r6, 1: str r3, str r3, str r3, str r3, teq r0, bne 1b //从proc_info_list结构中获取字段__cpu_mm_mmu_flags,该字段包含了存储空间访问权限 //等。此处指令执行之后r7=0x00000c1e ldr r7, mov r6, orr r3, str r3, add r0, str r3, ldr r6, add r0, add r6, 1: cmp r0, add r3, strls r3, bls 1b #ifdef //如果是XIP就进行以下映射,这只是将内核代码存储的空间重新映射, orr r3, .if (KERNEL_RAM_PADDR orr r3, .endif add r0, str r3, ldr r6, add r0, add r6, 1: cmp r0, add r3, strls r3, bls 1b #endif //r0 add r0, orr r6, .if (PHYS_OFFSET orr r6, .endif str r6, #ifdef ldr r7, ldr r3, add r0, rsb r3, cmp r3, movhi r3, add r6, ldr r3, orr r3, 1: str r3, add r3, teq r0, bne 1b #if add r0, orr r3, str r3, #endif #ifdef add r0, orr r3, str r3, add r0, str r3, #endif #endif mov pc, ENDPROC(__create_page_tables)
__arm920_setup 在上面程序段.section ldr r13, @ adr lr, add pc, R10中存放的是在函数__lookup_processor_type中成功匹配的结构体proc_info_list。 对于arm920来说在文件linux/arch/arm/mm/proc-arm920.S中有: .section .type __arm920_proc_info,#object __arm920_proc_info: .long 0x41009200 .long 0xff00fff0 .long PMD_SECT_BUFFERABLE PMD_SECT_CACHEABLE PMD_BIT4 PMD_SECT_AP_WRITE PMD_SECT_AP_READ .long PMD_BIT4 PMD_SECT_AP_WRITE PMD_SECT_AP_READ b __arm920_setup ……………………………… add pc, .type __arm920_setup, __arm920_setup: mov r0, mcr p15, mcr p15, #ifdef mcr p15, #endif adr r5, ldmia r5, mrc p15, //通过查看arm920_crval的值可知该行是清除r0中相关位,为以后对这些位的赋值做准备 bic r0, orr r0, mov pc,
.size __arm920_setup, .type arm920_crval, arm920_crval: crval clear=0x00003f3f,
文件linux/arch/arm/kernel/head.S中 __enable_mmu: #ifdef orr r0, #else bic r0, #endif #ifdef bic r0, #endif #ifdef bic r0, #endif #ifdef bic r0, #endif mov r5, mcr p15, mcr p15, b __turn_mmu_on ENDPROC(__enable_mmu)
文件linux/arch/arm/kernel/head.S中 __turn_mmu_on: mov r0, mcr p15, mrc p15, mov r3, mov r3, mov pc, ENDPROC(__turn_mmu_on)
在前面有过这样的指令操作ldr
r13, mov pc, 在文件linux/arch/arm/kernel/head-common.S中: .type __switch_data, __switch_data: .long __mmap_switched .long __data_loc
@ .long _data @ .long __bss_start
@ .long _end @ .long processor_id
@ .long __machine_arch_type
@ .long __atags_pointer
@ .long cr_alignment
@ .long init_thread_union
…………………………
.bss
__bss_start *(.bss) *(COMMON)
_end } ……………………………… }
init_thread_union
00033:
00034:
00035:
*/ __mmap_switched: adr r3, ldmia r3!, cmp r4, 1: cmpne r5, ldrne fp, strne fp, bne 1b mov fp, 1: cmp r6, strcc fp, bcc 1b ldmia r3, str r9, str r1, str r2, bic r4, stmia r7, b start_kernel ENDPROC(__mmap_switched) |
|