__proc_info_begin->__proc_info_end

/arch/arm/kernel/head.S

/*
 * Look in <asm/procinfo.h> for 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宏定义初始化了一些processerproc 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中看出来

  __proc_info_begin = .;   //proc_info_list结构的开始地址

     *(.proc.info.init)    

   __proc_info_end = .;     //proc_info_list结构的结束地址

 在使能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
<p> </p>

      SMP=y,表示支持SMP;否则,只支持单核。CONFIG_SMP_ON_UP=y,在启动时,在线检测SMP或者单核,并自我修正不能在non-SMP运行的指令。因此,我们可以根据单核和SMP的差异,通过ALT_SMPALT_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)

      对于设置了SMPSMP_ON_UP的情况,属于SMP的情况会被编译在原本执行函数的内容中,而属于单核的情况会被编译到section ".alt.smp.init中,并且在每4bytes地址后,记录对应4bytes单核版本指令集,以便修正时参考。如下例子:

0xc001858c <__smpalt_begin>:

…..

0xc0018634: c0029fd8     .word 0xc0029fd8 //4 bytes 要取代的目標記憶體位址

0xc0018638: ee080f37     mcr    15, 0, r0, cr8, cr7, {1} //4bytes UniProcessor版本指令

0xc001863c: c0029fec     .word 0xc0029fec

0xc0018640: ee07cfd5     mcr    15, 0, ip, cr7, cr5, {6}

0xc0018644: c002a00c    .word 0xc002a00c

0xc0018648: ee080f37     mcr    15, 0, r0, cr8, cr7, {1}

…..

在最後的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_up
ENDPROC(__fixup_smp)

	.align
1:	.word	.
	.word	__smpalt_begin
	.word	__smpalt_end

	.pushsection .data
	.globl	smp_on_up
smp_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_up
ENDPROC(__do_fixup_smp_on_up)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值