linux内核启动过程分析(二)

linux内核启动过程分析(二),第1张

linux内核启动过程分析(二)

接上篇:linux内核启动过程分析(一)

三、内核启动过程
1.汇编阶段
同样可以从内核的编译链接文件(arch/arm/kernel/head.S)中得到内核代码的入口地址"stext"。

OUTPUT_ARCH(arm)
ENTRY(stext)			
jiffies = jiffies_64; 
SECTIONS
{
 
 /DISCARD/ : {
  *(.ARM.exidx.exit.text)
  *(.ARM.extab.exit.text)
 
 
 
 
  *(.exitcall.exit)
  *(.discard)
  *(.discard.*)
 }

内核入口stext(archarmkernelhead.S)

	.arm

	__HEAD
ENTRY(stext)			
 ARM_BE8(setend	be )			@ ensure we are in BE8 mode

 THUMB(	adr	r9, BSYM(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)?
 THUMB( it	eq )		@ force fixup-able long branch encoding
	beq	__error_p			@ yes, error 'p'  

#ifdef CONFIG_ARM_LPAE
	mrc	p15, 0, r3, c0, c1, 4		@ read ID_MMFR0
	and	r3, r3, #0xf			@ extract VMSA support
	cmp	r3, #5				@ long-descriptor translation table format?
 THUMB( it	lo )				@ force fixup-able long branch encoding
	blo	__error_lpae			@ only classic page table format
#endif

#ifndef CONFIG_XIP_KERNEL
	adr	r3, 2f
	ldmia	r3, {r4, r8}
	sub	r4, r3, r4			@ (PHYS_OFFSET - PAGE_OFFSET)
	add	r8, r8, r4			@ PHYS_OFFSET
#else
	ldr	r8, =PLAT_PHYS_OFFSET		@ always constant in this case
#endif

	
	bl	__vet_atags
#ifdef CONFIG_SMP_ON_UP
	bl	__fixup_smp
#endif
#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
	bl	__fixup_pv_table
#endif
	bl	__create_page_tables     

	
	 
	ldr	r13, =__mmap_switched		@ address to jump to after
						@ mmu has been enabled
	adr	lr, BSYM(1f)			@ return (PIC) address
	mov	r8, r4				@ set TTBR1 to swapper_pg_dir
	ldr	r12, [r10, #PROCINFO_INITFUNC]
	add	r12, r12, r10
	ret	r12

1:	b	__enable_mmu
ENDPROC(stext)   

__lookup_processor_type分析:
为了更好的理解__lookup_processor_type,在这之前需要先了解一下__lookup_processor_type_data。代码中可以看到__lookup_processor_type的第一条指令中就用到了__lookup_processor_type_data这个地址。__lookup_processor_type_data被定义在archarmkernelhead-common.S中。代码如下:

	.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的结构,然后这个结构中的前三个元素被赋值成,当前的地址(用于虚拟地址和物理地址的转化)、__proc_info_begin的地址、__proc_info_end的地址。其中__proc_info_begin和__proc_info_end在archarmkernelvmlinux.lds链接文件中作为(.proc.info.init)段的开始和结束地址被定义。(.proc.info.init)段中存放了所有支持的cpu。对于ARM架构的CPU,表示支持的CPU的源码定义在在arch/arm/mm/目录下。
内存结构入下图:

archarmincludeasmprocinfo.h中proc_info_list结构定义

struct proc_info_list {
	unsigned int		cpu_val;
	unsigned int		cpu_mask;
	unsigned long		__cpu_mm_mmu_flags;	
	unsigned long		__cpu_io_mmu_flags;	
	unsigned long		__cpu_flush;		
	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;
};


如下代码是ARMv7架构CPU的proc_info_list结构,被定义在archarmmmproc-v7.S中

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

	
.macro __v7_proc name, 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
	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	v7_cache_fns
.endm

__lookup_processor_type函数
archarmkernelhead-common.S

__lookup_processor_type:
	
	adr	r3, __lookup_processor_type_data
	ldmia	r3, {r4 - r6}	
	sub	r3, r3, r4			@ get offset between virt&phys
	add	r5, r5, r3			@ convert virt addresses to
	add	r6, r6, r3			@ physical address space
	
1:	ldmia	r5, {r3, r4}			@ value, mask
	and	r4, r4, r9			@ mask wanted bits
	teq	r3, r4
	beq	2f
	add	r5, r5, #PROC_INFO_SZ		@ sizeof(proc_info_list)
	cmp	r5, r6
	blo	1b
	mov	r5, #0				@ unknown processor
2:	ret	lr
ENDPROC(__lookup_processor_type)


	.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

__enable_mmu函数分析
archarmkernelhead.S

__enable_mmu:
#if defined(CONFIG_ALIGNMENT_TRAP) && __LINUX_ARM_ARCH__ < 6
	orr	r0, r0, #CR_A
#else
	bic	r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
	bic	r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
	bic	r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
	bic	r0, r0, #CR_I
#endif
#ifndef CONFIG_ARM_LPAE
	mov	r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | 
		      domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | 
		      domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | 
		      domain_val(DOMAIN_IO, DOMAIN_CLIENT))
	mcr	p15, 0, r5, c3, c0, 0		@ load domain access register
	mcr	p15, 0, r4, c2, c0, 0		@ load page table pointer
#endif
	b	__turn_mmu_on       
ENDPROC(__enable_mmu)


	.align	5
	.pushsection	.idmap.text, "ax"
ENTRY(__turn_mmu_on)
	mov	r0, r0
	instr_sync
	mcr	p15, 0, r0, c1, c0, 0		@ write control reg
	mrc	p15, 0, r3, c0, c0, 0		@ read id reg
	instr_sync
	mov	r3, r3
	mov	r3, r13	 
	ret	r3
__turn_mmu_on_end:
ENDPROC(__turn_mmu_on)

__mmap_switched分析
archarmkernelhead-common.S

	__INIT
__mmap_switched:
	adr	r3, __mmap_switched_data

	ldmia	r3!, {r4, r5, r6, r7}
	cmp	r4, r5				@ Copy data segment if needed   
1:	cmpne	r5, r6
	ldrne	fp, [r4], #4
	strne	fp, [r5], #4
	bne	1b

	mov	fp, #0				@ Clear BSS (and zero fp)   
1:	cmp	r6, r7
	strcc	fp, [r6],#4
	bcc	1b

 ARM(	ldmia	r3, {r4, r5, r6, r7, sp})
 THUMB(	ldmia	r3, {r4, r5, r6, r7}	)
 THUMB(	ldr	sp, [r3, #16]		)
	str	r9, [r4]			@ Save processor ID
	str	r1, [r5]			@ Save machine type
	str	r2, [r6]			@ Save atags pointer
	cmp	r7, #0
	strne	r0, [r7]			@ Save control register values
	b	start_kernel				
ENDPROC(__mmap_switched)

到这内核启动汇编阶段结束,下面将进入C代码阶段运行。

接下一篇:linux内核启动过程分析(三)

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/zaji/5720472.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存