本文共 5828 字,大约阅读时间需要 19 分钟。
/arch/arm/kernel/head.S
/* * Look infor information about the __proc_info structure. */ .align 2 .type __lookup_processor_type_data, %object__lookup_processor_type_data: .long . @ “.”表示当前这行代码编译连接后的虚拟地址 .long __proc_info_begin @ proc_info_list结构的开始地址,这是链接地址,也是虚拟地址 .long __proc_info_end @ @proc_info_list结构的结束地址,这是链接地址,也是虚拟地址 .size __lookup_processor_type_data, . - __lookup_processor_type_data
/arch/arm/kernel/vmlinux.lds.S
#define PROC_INFO \ . = ALIGN(4); \ VMLINUX_SYMBOL(__proc_info_begin) = .; \ *(.proc.info.init) \ VMLINUX_SYMBOL(__proc_info_end) = .;
/arch/arm/mm/Makefile
obj-$(CONFIG_CPU_V7) += proc-v7.o
/arch/arm/mm/proc-v7.S
.section ".proc.info.init", #alloc, #execinstr /* * Standard v7 proc info content */.macro __v7_proc initfunc, mm_mmuflags = 0, io_mmuflags = 0, hwcaps = 0, proc_fns = v7_processor_functions ALT_SMP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \ PMD_SECT_AF | PMD_FLAGS_SMP | \mm_mmuflags) ALT_UP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \ PMD_SECT_AF | PMD_FLAGS_UP | \mm_mmuflags) .long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | \ PMD_SECT_AP_READ | PMD_SECT_AF | \io_mmuflags W(b) \initfunc .long cpu_arch_name .long cpu_elf_name .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_FAST_MULT | \ HWCAP_EDSP | HWCAP_TLS | \hwcaps .long cpu_v7_name .long \proc_fns .long v7wbi_tlb_fns .long v6_user_fns .long v7_cache_fns.endm
/* * ARM Ltd. Cortex A9 processor. */ .type __v7_ca9mp_proc_info, #object__v7_ca9mp_proc_info: .long 0x410fc090 .long 0xff0ffff0 __v7_proc __v7_ca9mp_setup .size __v7_ca9mp_proc_info, . - __v7_ca9mp_proc_info
/* * Match any ARMv7 processor core. */ .type __v7_proc_info, #object__v7_proc_info: .long 0x000f0000 @ Required ID value .long 0x000f0000 @ Mask for ID __v7_proc __v7_setup .size __v7_proc_info, . - __v7_proc_info
通过使用__v7_proc_info宏定义初始化了一些processer的proc info。
该proc_info structure的定义在/arch/arm/include/asm/procinfo.h:
/* * Note! struct processor is always defined if we're * using MULTI_CPU, otherwise this entry is unused, * but still exists. * * NOTE! The following structure is defined by assembly * language, NOT C code. For more information, check: * arch/arm/mm/proc-*.S and arch/arm/kernel/head.S */struct proc_info_list { unsigned int cpu_val; unsigned int cpu_mask; unsigned long __cpu_mm_mmu_flags; /* used by head.S */ unsigned long __cpu_io_mmu_flags; /* used by head.S */ unsigned long __cpu_flush; /* used by head.S */ const char *arch_name; const char *elf_name; unsigned int elf_hwcap; const char *cpu_name; struct processor *proc; struct cpu_tlb_fns *tlb; struct cpu_user_fns *user; struct cpu_cache_fns *cache;};
不同的proc_info_list结构被用来支持不同的CPU,它们都是定义在“.proc.info.init”段中,在连接内核时,这些结构体被组织在一起,开始地址为__proc_info_begin,结束地址为__proc_info_end。这可以从链接脚本文件“arch/arm/kernel/vmlinux.lds.S”中看出来
在使能MMU前使用的都是物理地址,而内核却是以虚拟地址连接的,所以在访问proc_init_list结构前,需要先将它的虚拟地址转换为物理地址。
NOTE:
/arch/arm/include/asm/assembler.h
#ifdef CONFIG_SMP#define ALT_SMP(instr...) \9998: instr/* * Note: if you get assembler errors from ALT_UP() when building with * CONFIG_THUMB2_KERNEL, you almost certainly need to use * ALT_SMP( W(instr) ... ) */#define ALT_UP(instr...) \ .pushsection ".alt.smp.init", "a" ;\ //放到".alt.smp.init section .long 9998b ;\9997: instr ;\ .if . - 9997b != 4 ;\ //4byte对齐 .error "ALT_UP() content must assemble to exactly 4 bytes";\ .endif ;\ .popsection#define ALT_UP_B(label) \ .equ up_b_offset, label - 9998b ;\ .pushsection ".alt.smp.init", "a" ;\ .long 9998b ;\ W(b) . + up_b_offset ;\ .popsection#else#define ALT_SMP(instr...)#define ALT_UP(instr...) instr#define ALT_UP_B(label) b label#endif
SMP=y,表示支持SMP;否则,只支持单核。CONFIG_SMP_ON_UP=y,在启动时,在线检测SMP或者单核,并自我修正不能在non-SMP运行的指令。因此,我们可以根据单核和SMP的差异,通过ALT_SMP和ALT_UP把两种版本的代码放在同一函数中。
ALT_SMP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \ PMD_SECT_AF | PMD_FLAGS_SMP | \mm_mmuflags) ALT_UP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \ PMD_SECT_AF | PMD_FLAGS_UP | \mm_mmuflags)
对于设置了SMP和SMP_ON_UP的情况,属于SMP的情况会被编译在原本执行函数的内容中,而属于单核的情况会被编译到section ".alt.smp.init中,并且在每4bytes地址后,记录对应4bytes单核版本指令集,以便修正时参考。如下例子:
0xc001858c <__smpalt_begin>:
…..
0xc0018634: c0029fd8
0xc0018638: ee080f37
0xc001863c: c0029fec
0xc0018640: ee07cfd5
0xc0018644: c002a00c
0xc0018648: ee080f37
…..
在最後的Link階段,會把Section .alt.smp.init放在Symbol __smpalt_begin與__smpalt_end之中,因此在程式碼執行階段,就可以透過這兩個Symbol取得 Section .alt.smp.init中所包含單核心程式碼的內容與記憶體範圍.
在Linux Kernel啟動後會呼叫函式__fixup_smp,如果判斷目前是在單核心平台上,就會把在__smpalt_begin到__smpalt_end記憶體範圍的單核心程式碼依據其對應的記憶體位址,進行修正動作.
運作概念如下圖所示
__fixup_smp_on_up: adr r0, 1f @ 标号1处的地址 ldmia r0, {r3 - r5} @ 虚拟地址和物理地址的转换 sub r3, r0, r3 add r4, r4, r3 add r5, r5, r3 b __do_fixup_smp_on_upENDPROC(__fixup_smp) .align1: .word . .word __smpalt_begin .word __smpalt_end .pushsection .data .globl smp_on_upsmp_on_up: ALT_SMP(.long 1) ALT_UP(.long 0) .popsection#endif .text__do_fixup_smp_on_up: cmp r4, r5 @ 如果已经end([r4] > [r5])就跳回 movhs pc, lr ldmia r4!, {r0, r6} @ r0:需要修正的指令地址,r6单核指令,[r4] = [r4] + 8 ARM( str r6, [r0, r3] ) @ 用单核指令覆盖原指令,[r0+r3] = [r6],r0+r3是虚拟和物理的转换 THUMB( add r0, r0, r3 )#ifdef __ARMEB__ THUMB( mov r6, r6, ror #16 ) @ Convert word order for big-endian.#endif THUMB( strh r6, [r0], #2 ) @ For Thumb-2, store as two halfwords THUMB( mov r6, r6, lsr #16 ) @ to be robust against misaligned r3. THUMB( strh r6, [r0] ) b __do_fixup_smp_on_upENDPROC(__do_fixup_smp_on_up)
转载地址:http://ldsqb.baihongyu.com/