linux启动之__lookup_processor_type

本文基于Linux版本:linux-4.19.125  arm-V7架构

在Linux启动过程中,执行完stext的第17行 :bl    __lookup_processor_type后,r5寄存器存放了procinfo,procinfo指的是processor information,即处理器信息。这篇文章我们将探索procinfo的存储及访问过程。

@arch\arm\kernel\head.S:

	__HEAD
ENTRY(stext)
 ARM_BE8(setend	be )			@ ensure we are in BE8 mode
 b .
 THUMB(	badr	r9, 1f		)	@ Kernel is always entered in ARM.
 THUMB(	bx	r9		)	@ If this is a Thumb-2 kernel,
 THUMB(	.thumb			)	@ switch to Thumb now.
 THUMB(1:			)

#ifdef CONFIG_ARM_VIRT_EXT
	bl	__hyp_stub_install
#endif
	@ ensure svc mode and all interrupts masked
	safe_svcmode_maskall r9

	mrc	p15, 0, r9, c0, c0		@ get processor id
	bl	__lookup_processor_type		@ r5=procinfo r9=cpuid
	movs	r10, r5				@ invalid processor (r5=0)?
......
ENDPROC(stext)

__lookup_processor_type的实现位于arch\arm\kernel\head-common.S文件:

/*
 * Read processor ID register (CP#15, CR0), and look up in the linker-built
 * supported processor list.  Note that we can't use the absolute addresses
 * for the __proc_info lists since we aren't running with the MMU on
 * (and therefore, we are not in the correct address space).  We have to
 * calculate the offset.
 *
 *	r9 = cpuid
 * Returns:
 *	r3, r4, r6 corrupted
 *	r5 = proc_info pointer in physical address space
 *	r9 = cpuid (preserved)
 */
__lookup_processor_type:
	adr	r3, __lookup_processor_type_data
               /* 取__lookup_processor_type_data地址存入r3, 这里得到的是物理地址
                */
	ldmia	r3, {r4 - r6}
               /* 从寄存器r3地址处读取数据,依次存储到寄存器 r4、r5、r6 中
                * r4: __lookup_processor_type_data 虚拟地址
                * r5: __lookup_processor_type_data.__proc_info_begin 虚拟地址
                * r6: __lookup_processor_type_data.__proc_info_end 虚拟地址
                */
	sub	r3, r3, r4			@ get offset between virt&phys
               /* 计算__lookup_processor_type_data物理地址和虚拟地址的的偏移量
                * 用于将虚拟地址转换为物理地址 
                */
	add	r5, r5, r3			@ convert virt addresses to
               /* r5: __lookup_processor_type_data.__proc_info_begin 物理地址
                */
	add	r6, r6, r3			@ physical address space
               /* r6: __lookup_processor_type_data.__proc_info_end 物理地址
                */
1:	ldmia	r5, {r3, r4}			@ value, mask
               /*从地址 r5 处读取数据,存储到寄存器 r3 和 r4 中
                * r5: __proc_info 物理地址
                * r3: value, 对应proc_info_list结构体的cpu_value
                * r4: mask, 对应proc_info_list结构体的cpu_mask
                */
	and	r4, r4, r9			@ mask wanted bits
               /* r9: cpuid
                * 执行 and	r4, r4, r9后
                * r4: mask之后的cpuid, 便于后面的lookup
                */
	teq	r3, r4 /*比较 r3 和 r4 的值是否相等。*/
	beq	2f     /*如果相等,说明已经找到匹配的CPU了,则跳转到标签 2,返回*/
	add	r5, r5, #PROC_INFO_SZ		@ sizeof(proc_info_list)
               /* 将 r5 加上 PROC_INFO_SZ,即 sizeof(proc_info_list) 的大小
                * @1: 关于proc_info_list结构体的定义,请参考后面说明
                * @2: 关于PROC_INFO_SZ的计算,请参考后面说明
               */
	cmp	r5, r6 /*比较 r5 和 r6 的值*/
	blo	1b     /*如果 r5 小于 r6,则跳回标签 1 继续循环。*/
	mov	r5, #0				@ unknown processor
               /*
                * 运行到这,说明当前的CPU是不支持的类型,r5赋值0
                */
2:	ret	lr
ENDPROC(__lookup_processor_type)

在arch/arm/kernel/head-common.S文件中关于__lookup_processor_type_data的定义如下:

/*
 * 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
	.long	__proc_info_end
	.size	__lookup_processor_type_data, . - __lookup_processor_type_data

__lookup_processor_type_data的定义

__lookup_processor_type_data描述了__proc_info的信息:

1. __lookup_processor_type_data地址;

2. __proc_info_begin地址;

3. __proc_info_end地址;

4. __lookup_processor_type_data的大小;

__proc_info的填充

从__lookup_processor_type中的实现我们会发现,__proc_info_begin和__proc_info_end之间填充了若干个__proc_info,那么这些__proc_info是如何填充的呢?

在arch/arm/mm/proc-v7.S中有如下实现:

(备注:为什么是proc-v7.S文件?因为我的defconfig配置了CONFIG_CPU_V7)

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

	/*
	 * Standard v7 proc info content
	 */
.macro __v7_proc name, initfunc, mm_mmuflags = 0, io_mmuflags = 0, hwcaps = 0, proc_fns = v7_processor_functions, cache_fns = v7_cache_fns
	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
	initfn	\initfunc, \name
	.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	\cache_fns
.endm

#ifndef CONFIG_ARM_LPAE
	/*
	 * ARM Ltd. Cortex A5 processor.
	 */
	.type   __v7_ca5mp_proc_info, #object
__v7_ca5mp_proc_info:
	.long	0x410fc050
	.long	0xff0ffff0
	__v7_proc __v7_ca5mp_proc_info, __v7_ca5mp_setup
	.size	__v7_ca5mp_proc_info, . - __v7_ca5mp_proc_info

	/*
	 * ARM Ltd. Cortex A9 processor.
	 */
	.type   __v7_ca9mp_proc_info, #object
__v7_ca9mp_proc_info:
	.long	0x410fc090
	.long	0xff0ffff0
	__v7_proc __v7_ca9mp_proc_info, __v7_ca9mp_setup, proc_fns = ca9mp_processor_functions
	.size	__v7_ca9mp_proc_info, . - __v7_ca9mp_proc_info

	/*
	 * ARM Ltd. Cortex A8 processor.
	 */
	.type	__v7_ca8_proc_info, #object
__v7_ca8_proc_info:
	.long	0x410fc080
	.long	0xff0ffff0
	__v7_proc __v7_ca8_proc_info, __v7_setup, proc_fns = ca8_processor_functions
	.size	__v7_ca8_proc_info, . - __v7_ca8_proc_info

#endif	/* CONFIG_ARM_LPAE */

	/*
	 * Marvell PJ4B processor.
	 */
#ifdef CONFIG_CPU_PJ4B
	.type   __v7_pj4b_proc_info, #object
__v7_pj4b_proc_info:
	.long	0x560f5800
	.long	0xff0fff00
	__v7_proc __v7_pj4b_proc_info, __v7_pj4b_setup, proc_fns = pj4b_processor_functions
	.size	__v7_pj4b_proc_info, . - __v7_pj4b_proc_info
#endif

	/*
	 * ARM Ltd. Cortex R7 processor.
	 */
	.type	__v7_cr7mp_proc_info, #object
__v7_cr7mp_proc_info:
	.long	0x410fc170
	.long	0xff0ffff0
	__v7_proc __v7_cr7mp_proc_info, __v7_cr7mp_setup
	.size	__v7_cr7mp_proc_info, . - __v7_cr7mp_proc_info

	/*
	 * ARM Ltd. Cortex R8 processor.
	 */
	.type	__v7_cr8mp_proc_info, #object
__v7_cr8mp_proc_info:
	.long	0x410fc180
	.long	0xff0ffff0
	__v7_proc __v7_cr8mp_proc_info, __v7_cr8mp_setup
	.size	__v7_cr8mp_proc_info, . - __v7_cr8mp_proc_info

	/*
	 * ARM Ltd. Cortex A7 processor.
	 */
	.type	__v7_ca7mp_proc_info, #object
__v7_ca7mp_proc_info:
	.long	0x410fc070
	.long	0xff0ffff0
	__v7_proc __v7_ca7mp_proc_info, __v7_ca7mp_setup
	.size	__v7_ca7mp_proc_info, . - __v7_ca7mp_proc_info

	/*
	 * ARM Ltd. Cortex A12 processor.
	 */
	.type	__v7_ca12mp_proc_info, #object
__v7_ca12mp_proc_info:
	.long	0x410fc0d0
	.long	0xff0ffff0
	__v7_proc __v7_ca12mp_proc_info, __v7_ca12mp_setup, proc_fns = HARDENED_BPIALL_PROCESSOR_FUNCTIONS
	.size	__v7_ca12mp_proc_info, . - __v7_ca12mp_proc_info

	/*
	 * ARM Ltd. Cortex A15 processor.
	 */
	.type	__v7_ca15mp_proc_info, #object
__v7_ca15mp_proc_info:
	.long	0x410fc0f0
	.long	0xff0ffff0
	__v7_proc __v7_ca15mp_proc_info, __v7_ca15mp_setup, proc_fns = ca15_processor_functions
	.size	__v7_ca15mp_proc_info, . - __v7_ca15mp_proc_info

	/*
	 * Broadcom Corporation Brahma-B15 processor.
	 */
	.type	__v7_b15mp_proc_info, #object
__v7_b15mp_proc_info:
	.long	0x420f00f0
	.long	0xff0ffff0
	__v7_proc __v7_b15mp_proc_info, __v7_b15mp_setup, proc_fns = ca15_processor_functions, cache_fns = b15_cache_fns
	.size	__v7_b15mp_proc_info, . - __v7_b15mp_proc_info

	/*
	 * ARM Ltd. Cortex A17 processor.
	 */
	.type	__v7_ca17mp_proc_info, #object
__v7_ca17mp_proc_info:
	.long	0x410fc0e0
	.long	0xff0ffff0
	__v7_proc __v7_ca17mp_proc_info, __v7_ca17mp_setup, proc_fns = HARDENED_BPIALL_PROCESSOR_FUNCTIONS
	.size	__v7_ca17mp_proc_info, . - __v7_ca17mp_proc_info

	/* ARM Ltd. Cortex A73 processor */
	.type	__v7_ca73_proc_info, #object
__v7_ca73_proc_info:
	.long	0x410fd090
	.long	0xff0ffff0
	__v7_proc __v7_ca73_proc_info, __v7_setup, proc_fns = HARDENED_BPIALL_PROCESSOR_FUNCTIONS
	.size	__v7_ca73_proc_info, . - __v7_ca73_proc_info

	/* ARM Ltd. Cortex A75 processor */
	.type	__v7_ca75_proc_info, #object
__v7_ca75_proc_info:
	.long	0x410fd0a0
	.long	0xff0ffff0
	__v7_proc __v7_ca75_proc_info, __v7_setup, proc_fns = HARDENED_BPIALL_PROCESSOR_FUNCTIONS
	.size	__v7_ca75_proc_info, . - __v7_ca75_proc_info

	/*
	 * Qualcomm Inc. Krait processors.
	 */
	.type	__krait_proc_info, #object
__krait_proc_info:
	.long	0x510f0400		@ Required ID value
	.long	0xff0ffc00		@ Mask for ID
	/*
	 * Some Krait processors don't indicate support for SDIV and UDIV
	 * instructions in the ARM instruction set, even though they actually
	 * do support them. They also don't indicate support for fused multiply
	 * instructions even though they actually do support them.
	 */
	__v7_proc __krait_proc_info, __v7_setup, hwcaps = HWCAP_IDIV | HWCAP_VFPv4
	.size	__krait_proc_info, . - __krait_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_proc_info, __v7_setup
	.size	__v7_proc_info, . - __v7_proc_info

 我们先看第一行代码:

	.section ".proc.info.init", #alloc
  • .section:这是一个汇编伪指令,用于定义一个新的段(section)
  • ".proc.info.init":这是段的名称,就是我们关注的proc info段
  • #alloc:这是段的属性,告诉编译器这个段需要被分配内存

接下来是一个__v7_proc宏:

	 * Standard v7 proc info content
	 */
.macro __v7_proc name, initfunc, mm_mmuflags = 0, io_mmuflags = 0, hwcaps = 0, proc_fns = v7_processor_functions, cache_fns = v7_cache_fns
	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
	initfn	\initfunc, \name
	.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	\cache_fns
.endm

__v7_proc宏参数说明如下:

  • name:  cpu name
  • initfunc: 初始化函数名称
  • mm_mmuflags: mmu flags,默认为0
  • io_mmuflags: io mmu flags,默认为0
  • hwcaps: 硬件功能标志位,默认为0
  • proc_fns: cpu处理函数,默认为 v7_processor_functions
  • cache_fns:  cache处理函数,默认为 v7_cache_fns

 __v7_proc宏的作用实际就是填充proc_info_list结构体。

__v7_proc宏下面的一系列如__v7_ca5mp_proc_info,__v7_ca9mp_proc_info等,都是arm-V7支持的一系列CPU。

所以proc info信息的填充正是在这里完成的。

proc_info_list结构体定义

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_SZ的计算

在arch/arm/kernel/asm-offset.c中,PROC_INFO_SZ是这样计算的:

  DEFINE(PROC_INFO_SZ,		sizeof(struct proc_info_list));

计算的结果,存放在在编译内核后生成的include/generated/asm-offset.h文件:

#define PROC_INFO_SZ 52 /* sizeof(struct proc_info_list) */

__proc_info_begin和__proc_info_end是如何计算得到的?

前面,我们已经知道了proc info是如何填充、如何获取的,但其实我们还有一个问题忽略了,就是填充和获取proc info的关键起始地址和结束地址,即__proc_info_begin和__proc_info_end是如何计算出来的呢?

在proc_info填充一节中,我们知道了proc info都放在了.init.proc.info段中,而.init.proc.info段在code中的位置,实际是在arch/arm/kernel/vmlinux.lds.S中确定的:

	.init.proc.info : {
		ARM_CPU_DISCARD(PROC_INFO)
	}

PROC_INFO定义在在arch/arm/kernel/vmlinux.lds.h中:

#define PROC_INFO							\
		. = ALIGN(4);						\
		__proc_info_begin = .;					\
		*(.proc.info.init)					\
		__proc_info_end = .;

由此我们知道,__proc_info_begin就是.init.proc.info段的起始地址,然后就是存放一系列的proc info数据,最后就是__proc_info_end地址。

所以__proc_info_begin和__proc_info_end的地址在编译时就已经确定了。

验证一把:

  (struct proc_info_list*)0x402CC20C = 0x402CC20C -> (
    cpu_val = 0x000F0000,
    cpu_mask = 0x000F0000,
    __cpu_mm_mmu_flags = 0x00011C0E,
    __cpu_io_mmu_flags = 0x0C02,
    __cpu_flush = 0xFFD496D7,
    arch_name = 0xC02CD5F4 -> "armv7",
    elf_name = 0xC02CD5FA -> "v7",
    elf_hwcap = 0x8097,
    cpu_name = 0xC00157CC -> "ARMv7 Processor",
    proc = 0xC045A060 -> (
      _data_abort = 0xC0014F21,
      _prefetch_abort = 0xC0014F41,
      _proc_init = 0xC0015765,
      check_bugs = 0xC0015AF9,
      _proc_fin = 0xC0015769,
      reset = 0xC0008275,
      _do_idle = 0xC0015781,
      dcache_clean_area = 0xC0015789,
      switch_mm = 0xC00156E1,
      set_pte_ext = 0xC00156FD,
      suspend_size = 36,
      do_suspend = 0xC00157DD,
      do_resume = 0xC001580D),
    tlb = 0xC043ED08 -> (
      flush_user_range = 0xC001566D,
      flush_kern_range = 0xC001569D,
      tlb_flags = 0x90F00000),
    user = 0xC045A058 -> (
      cpu_clear_user_highpage = 0xC00152A5,
      cpu_copy_user_highpage = 0xC00151D9),
    cache = 0xC045A000 -> (
      flush_icache_all = 0xC0014FA5,
      flush_kern_all = 0xC0015059,
      flush_kern_louis = 0xC0015071,
      flush_user_all = 0xC0015089,
      flush_user_range = 0xC0015089,
      coherent_kern_range = 0xC001508D,
      coherent_user_range = 0xC001508D,
      flush_kern_dcache_area = 0xC00150F1,
      dma_map_area = 0xC00151C1,
      dma_unmap_area = 0xC00151CD,
      dma_flush_range = 0xC0015195))

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值