内核启动过程中对CPU型号的确认

Posted 代二毛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内核启动过程中对CPU型号的确认相关的知识,希望对你有一定的参考价值。

1、内核为什么要确认CPU型号

内核和CPU都是不断发展的,内核会不断的更新版本,CPU会不断的出新型号。每当厂商推出一款新的CPU都需要移植内核,使内核能在新款CPU上运行。如果我们将没有针对该款CPU移植过的内核放到该款CPU上运行,结果就是运行不起来。所以内核在内部维护了一张支持CPU型号的表,在启动时内核会确认当前CPU型号是否在表中,如果不在,说明内核不支持该款CPU,终止启动。

2、内核维护支持的CPU型号表

2.1、 * (.proc.info.init)段

/*摘抄自内核的链接文件*/
 __proc_info_begin = .;
   *(.proc.info.init)
  __proc_info_end = .;

内核在链接时会将proc.info.init段属性的代码链接在一起,标号__proc_info_begin代表段的起始地址,标号__proc_info_end代表段的结束地址。proc.info.init段都是支持的CPU型号的信息,在启动时就根据段的起始地址和结束地址,在表格中遍历看是否能找到当前的CPU型号。

2.2、描述CPU信息的"proc_info_list"结构体

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信息的,

2.3、把CPU对应的proc_info_list结构体放入 * (.proc.info.init)段

	.section ".proc.info.init", #alloc, #execinstr

	/*
	 * Match any ARMv7 processor core.
	 */
	.type	__v7_proc_info, #object
__v7_proc_info:
	.long	0x000f0000		@ Required ID value
	.long	0x000f0000		@ Mask for ID
	.long   PMD_TYPE_SECT | \\
		PMD_SECT_AP_WRITE | \\
		PMD_SECT_AP_READ | \\
		PMD_FLAGS
	.long   PMD_TYPE_SECT | \\
		PMD_SECT_XN | \\
		PMD_SECT_AP_WRITE | \\
		PMD_SECT_AP_READ
	b	__v7_setup
	.long	cpu_arch_name
	.long	cpu_elf_name
	.long	HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
	.long	cpu_v7_name
	.long	v7_processor_functions
	.long	v7wbi_tlb_fns
	.long	v6_user_fns
	.long	v7_cache_fns
	.size	__v7_proc_info, . - __v7_proc_info

对于ARM架构的CPU,表示支持的CPU的源码定义在在arch/arm/mm/目录下。上面的proc_info_list结构体的信息摘抄自arch/arm/mm/proc-v7.S文件,任何ARMv7架构的CPU都是适用此结构体里的信息。
(1).section “.proc.info.init”:表示此处开始的内容属于".proc.info.init"段,也就是后面的proc_info_list结构体,在链接的程序的时候就会把proc_info_list结构体链接在一起,再根据标号的开始、结束地址去遍历proc_info_list结构体。

3.内核中启动时确认CPU型号

	@摘抄自内核启动的汇编代码
	mrc	p15, 0, r9, c0, c0		@ get processor id, CPU的ID号
	bl	__lookup_processor_type		@ r5=procinfo r9=cpuid
	movs	r10, r5				@ invalid processor (r5=0)?
	beq	__error_p			@ yes, error 'p'

(1)从CP15协处理器的c0寄存器读出CPU的ID号到r9寄存器;
(2)调用__lookup_processor_type函数查询内核支持的CPU型号中是否有当前CPU;
(3)如果没有匹配到CPU型号,则r5寄存器的值是0;如果查询到则r5寄存器的值是CPU对应proc_info_list结构体的地址;
(5)没有匹配到CPU型号则报错,终止启动;

3.1、__lookup_processor_type汇编函数

@此时r9保存的是从协处理器读取到的processor id
__lookup_processor_type:
	adr	r3, 3f	@将往后的标号3的地址加载到r3寄存器中,也就是__proc_info_begin
	ldmia	r3, r5 - r7  
	add	r3, r3, #8			@r3=r3+8,此时r3寄存器的值的等于标号4的地址
	sub	r3, r3, r7			@ 得到虚拟地址和物理地址的差值,r3=r3-r7
	add	r5, r5, r3			@ 将r5中保存的虚拟地址转换成物理地址
	add	r6, r6, r3			@ 将r6中保存的虚拟地址转换成物理地址
1:	ldmia	r5, r3, r4		@ r3存的cpu_val, r4存的cpu_mask
	and	r4, r4, r9			@ 将r9存的processor id 和cpu_mask相与:r4=r4 & r9
	teq	r3, r4				@判断r3和r4是否相等
	beq	2f						@如果r3=r4,则跳转到标号2处
	add	r5, r5, #PROC_INFO_SZ		@ sizeof(proc_info_list)	@r5=r5+ #PROC_INFO_SZ
	cmp	r5, r6				@判断r5是否等于r6,如果相等说明已经遍历完全部的proc_info_list结构体
	blo	1b					@r5!=r6则跳转到标号1处
	mov	r5, #0				@ r5=0,说明没有匹配到CPU型号
2:	mov	pc, lr					@函数返回
ENDPROC(__lookup_processor_type)

3:	.long	__proc_info_begin	@链接脚本里proc_info_list结构体的起始地址
	.long	__proc_info_end	@链接脚本里proc_info_list结构体的结束地址
4:	.long	.
	.long	__arch_info_begin
	.long	__arch_info_end

汇编语言解析:
(1)adr r3, 3f:将标号3的地址加载到r3中,adr是加载的是运行时地址,也就是物理地址;
(2)ldmia r3, r5 - r7 :将r3寄存器的值已经往后的值一次存在r5 - r7寄存器中。该语句的作用就是把long __proc_info_begin存在r5,__proc_info_end存到r6,标号4的地址存到r7。
(3)sub r3, r3, r7:此时r3里是标号4的物理地址,r4存的是标号4的链接地址,也就是虚拟地址;
(4)ldmia r5, r3, r4:r5存的是proc_info_list结构体的地址,而结构体的前两个成员就是cpu_val和cpu_mask;
(5)add r5, r5, #PROC_INFO_SZ:指向下一个proc_info_list结构体,理解成C语言就是**(proc_info_list *)r5++**;

以上是关于内核启动过程中对CPU型号的确认的主要内容,如果未能解决你的问题,请参考以下文章

20135239 益西拉姆 linux内核分析 跟踪分析Linux内核的启动过程

python学习之计算机基础详解

使用windows服务修改CPU型号(重启依然有效)

CPU 具体的有哪些型号怎么识别 谢谢各位老大了

Linux脚本练习之script026-显示当前主机系统信息,包括主机名,IPv4 地址,操作系统版本,内核版本,CPU 型号,内存大小,硬盘大小。

线程休眠时的线程与内核