Linux内核head.S分析4 - __enable_mmu

一. 前言

        head.S在__create_page_tables中创建了页表,接下来的工作就是开启D-CACHE,开启MMU了。

二. __enable_mmu铺垫代码部分分析

__enable_mmu铺垫相关代码如下:

/*
 * The following calls CPU specific code in a position independent
 * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of
 * xxx_proc_info structure selected by __lookup_machine_type
 * above.  On return, the CPU will be ready for the MMU to be
 * turned on, and r0 will hold the CPU control register value.
 */
ldr	r13, __switch_data		@ address to jump to after
						@ mmu has been enabled
adr	lr, __enable_mmu		@ return (PIC) address
add	pc, r10, #PROCINFO_INITFUNC

ldr    r13, __switch_data

        __switch_data定义在head_common.S中,如下:

	.type	__switch_data, %object
__switch_data:
	.long	__mmap_switched
	.long	__data_loc			@ r4
	.long	__data_start			@ r5
	.long	__bss_start			@ r6
	.long	_end				@ r7
	.long	processor_id			@ r4
	.long	__machine_arch_type		@ r5
	.long	cr_alignment			@ r6
	.long	init_thread_union + THREAD_START_SP @ sp

__mmap_switched是函数__mmap_switched的地址,如下。

	.type	__mmap_switched, %function
__mmap_switched:
	adr	r3, __switch_data + 4

	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

	ldmia	r3, {r4, r5, r6, sp}
	str	r9, [r4]			@ Save processor ID
	str	r1, [r5]			@ Save machine type
	bic	r4, r0, #CR_A			@ Clear 'A' bit
	stmia	r6, {r0, r4}			@ Save control register values
	b	start_kernel

        __data_loc定义在vmlinux.lds中,表示存放数据开始的地址。__data_start定义在vmlinux.lds中,作用后续补充。__bss_start表示bss段的开始地址,_end表示bss段的结束,也表示内核镜像数据的结束地址。

        ldr    r13, __switch_data是将_switch_data的首地址赋值到r13。

adr    lr, __enable_mmu

        将__enable_mmu函数的地址赋值到lr寄存器。

add    pc, r10, #PROCINFO_INITFUNC

        PROCINFO_INITFUNC 定义在./include/asm/asm-offsets.h中,如下:

#define PROCINFO_INITFUNC 16 /* offsetof(struct proc_info_list, __cpu_flush)    @ */

        所以这行代码表示将r10指向的struct proc_info_list结构体的__cpu_flush成员的地址赋值到pc,表示接下来将要执行__cpu_flush成员的指令。

        我们从前面的文章中已经知道了r10保存的是__arm920_proc_info函数的地址,所以__cpu_flush成员的表示的指令是b    __arm920_setup,所以,接下来要执行__arm920_setup函数,__arm920_setup函数如下。

	.type	__arm920_setup, #function
__arm920_setup:
	mov	r0, #0
	mcr	p15, 0, r0, c7, c7		@ invalidate I,D caches on v4
	mcr	p15, 0, r0, c7, c10, 4		@ drain write buffer on v4
#ifdef CONFIG_MMU
	mcr	p15, 0, r0, c8, c7		@ invalidate I,D TLBs on v4
#endif
	adr	r5, arm920_crval
	ldmia	r5, {r5, r6}
	mrc	p15, 0, r0, c1, c0		@ get control register v4
	bic	r0, r0, r5
	orr	r0, r0, r6
	mov	pc, lr
	.size	__arm920_setup, . - __arm920_setup

mov    r0, #0

        将r0寄存器赋值为0。

mcr    p15, 0, r0, c7, c7        @ invalidate I,D caches on v4

        将r0寄存器的值赋值到cp15处理器的c7寄存器,表示关闭处理器 的指令和数据cache。

mcr    p15, 0, r0, c7, c10, 4        @ drain write buffer on v4

        清空cache的写缓存。

mcr    p15, 0, r0, c8, c7        @ invalidate I,D TLBs on v4

        关闭指令和数据TLB。

adr    r5, arm920_crval

        将arm920_crval的地址赋值到r5,arm920_crval的定义如下:

arm920_crval:
	crval	clear=0x00003f3f, mmuset=0x00003135, ucset=0x00001130

        crval是一个宏,定义在./arch/arm/mm/proc-macros.S,如下:

	.macro	crval, clear, mmuset, ucset
#ifdef CONFIG_MMU
	.word	\clear
	.word	\mmuset
#else
	.word	\clear
	.word	\ucset
#endif
	.endm

        如果开启了CONFIG_MMU,arm920_crval展开为,如下:

.word	0x00003f3f
.word	0x00003135

ldmia    r5, {r5, r6}

        该指令是将r5 = 0x000003f3f,r6 = 0x00003135。

mrc    p15, 0, r0, c1, c0        @ get control register v4

        将CP15协处理器的C1寄存器的值赋值到r0寄存器中。

bic    r0, r0, r5

        将r0和r5进行按位异或操作的结果赋值到r0。

orr    r0, r0, r6

        将r0和r6进行或操作的结果保存到r0中。

mov    pc, lr

        此时lr的值是__enable_mmu,所以接下来执行__enable_mmu函数。

三.  __enable_mmu分析

__enable_mmu如下:

	.type	__enable_mmu, %function
__enable_mmu:
#ifdef CONFIG_ALIGNMENT_TRAP
	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
	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
	b	__turn_mmu_on

orr    r0, r0, #CR_A

        从前代码可知,r0是CP15处理器的C1寄存器读出的值,并且与0x00003f3f进行按位异或和与0x00003135进行或操作后的值,CR_A定义在include/asm/system.h中,如下:

#define CR_A  (1 << 1)        /* Alignment abort enable               */

        该指令是将r0的第一位置1。

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))

        domain_val是一个宏,定义在include/asm-arm/domain.h中,如下:

#define domain_val(dom,type)      ((type) << (2*(dom)))

        DOMAIN_USER,DOMAIN_KERNEL,DOMAIN_TABLE,DOMAIN_IO,DOMAIN_MANAGER,DOMAIN_CLIENT也定义在include/asm-arm/domain.h中,如下:

#define DOMAIN_KERNEL   0
#define DOMAIN_TABLE    0
#define DOMAIN_USER     1
#define DOMAIN_IO       2

#define DOMAIN_NOACCESS 0
#define DOMAIN_CLIENT   1
#define DOMAIN_MANAGER  3

#define domain_val(dom,type)    ((type) << (2*(dom)))

该条指令是将ARM处理器的域的配置赋值到r5寄存器中。

mcr    p15, 0, r5, c3, c0, 0        @ load domain access register

        将r5寄存器的值赋值到CP15协处理器的C3寄存器中。

mcr    p15, 0, r4, c2, c0, 0        @ load page table pointer

        r4是0x30004000,是页表的基地址,该条指令是设置页表的基地址。

b    __turn_mmu_on

        跳转到__turn_mmu_on,真正的开启MMU。

四. __turn_mmu_on分析

__turn_mmu_on:
	mov	r0, r0
	mcr	p15, 0, r0, c1, c0, 0		@ write control reg
	mrc	p15, 0, r3, c0, c0, 0		@ read id reg
	mov	r3, r3
	mov	r3, r3
	mov	pc, r13

mov    r0, r0

        这是一句空操作指令,相当于nop操作。

mcr    p15, 0, r0, c1, c0, 0        @ write control reg

        r0的值是从CP15协处理器C1寄存器中读出的,并经过了几个位操作。该指令将r0的值写到C1寄存器中,使能MMU

mrc    p15, 0, r3, c0, c0, 0        @ read id reg

        读出处理器的标识符。

mov    r3, r3 

mov    r3, r3

        上面两行是空操作指令。

mov    pc, r13

        r13的内容是__switch_data的地址,__switch_data的第一个4字节的内容是__mmap_switched函数的地址。所以接下来要跳到__mmap_switched函数执行。

五. __mmap_switched分析

__mmap_switched代码如下:

	.type	__mmap_switched, %function
__mmap_switched:
	adr	r3, __switch_data + 4

	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

	ldmia	r3, {r4, r5, r6, sp}
	str	r9, [r4]			@ Save processor ID
	str	r1, [r5]			@ Save machine type
	bic	r4, r0, #CR_A			@ Clear 'A' bit
	stmia	r6, {r0, r4}			@ Save control register values
	b	start_kernel

__switch_data代码如下:

	.type	__switch_data, %object
__switch_data:
	.long	__mmap_switched
	.long	__data_loc			@ r4
	.long	__data_start			@ r5
	.long	__bss_start			@ r6
	.long	_end				@ r7
	.long	processor_id			@ r4
	.long	__machine_arch_type		@ r5
	.long	cr_alignment			@ r6
	.long	init_thread_union + THREAD_START_SP @ sp

adr    r3, __switch_data + 4

        __switch_data + 4是__data_start的地址。所以r3 = __data_start。

ldmia    r3!, {r4, r5, r6, r7}

        由ldmia功能可知,

r4 = __data_loc,

r5 = __data_start,

r6 = __bss_start,

r7 = _end,

r3 = processor_id 。

cmp    r4, r5                @ Copy data segment if needed

        比较r4和r5的值是否相等。

1:    cmpne    r5, r6

        如果r4和r5不相等,则比较r5和r6的值。

ldrne    fp, [r4], #4

        如果r5和r6不相等,则将r4地址的值赋值到fp寄存器,r4 = r4 + 4。

strne    fp, [r5], #4

        如果r5不等于r6,将fp赋值到r5寄存器保存的地址,r5 = r5 + 4。

bne    1b

        如果r5不等于r6,则跳转到1:    cmpne    r5, r6指令。

以上的5行代码的功能是将__data_loc地址开始代码拷贝到__data_start。

mov    fp, #0                @ Clear BSS (and zero fp)

        将fp寄存器的值赋值为0。

1:    cmp    r6, r7

        比较r6和r7寄存器的值,r6 = __bss_start,r7 = _end。

strcc    fp, [r6],#4

        如果r6小于r7,将0赋值到r6指向的__bss_start的位置,r6 = r6 + 4。

bcc    1b

        如果r6小于r7,跳转到1:    cmp    r6, r7。

以上4行代码的功能是清除内核的bss段。

ldmia    r3, {r4, r5, r6, sp}

        此时,r3寄存器的值是processor_id,所以该行指令是将

r4 = processor_id,

r5 = __machine_arch_type,

r6 = cr_alignment,

sp = init_thread_union + THREAD_START_SP。

        cr_alignment定义在arch/arm/kernel/entry-armv.S,如下:

	.globl	cr_alignment
	.globl	cr_no_alignment
cr_alignment:
	.space	4
cr_no_alignment:
	.space	4

str    r9, [r4]            @ Save processor ID

        将r9的值保存到processor_id变量中,

str    r1, [r5]

        将机器型号的值保存到__machine_arch_type变量中。

bic    r4, r0, #CR_A            @ Clear 'A' bit

        将r0清除CR_A位清除。CR_A定义在./include/asm/system.h中,如下:

#define CR_A  (1 << 1)        /* Alignment abort enable               */

stmia    r6, {r0, r4}            @ Save control register values

        已知r6 = cr_alignment,该行指令将r0存储到cr_alignment,r4存储到cr_no_alignment。 

b    start_kernel

        跳转到start_kernel函数执行。

六. 总结

        本文的主要内容介绍的代码主要和设置MMU相关的配置的CP15协处理器的配置,后面直接进入C语言环境,后面继续分析

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值