powerpc e500系列,linux初始化的tlb汇编fsl_booke_entry_mapping.S,添加人肉代码注释

powerpc e500的内核启动,关于tlb的初始化可以说是重头戏。看懂这段代码后,powerpc的虚实映射基本不在话下。

这段初始化tlb要考虑的,主要是将boot可能初始化过的tlb全清零,然后自己建立一套PAGE_OFFSET的虚实映射,即为0xc打头的地址

建立映射。

inux kernel 3.10.7将这段初始化代码,全部放到fsl_booke_entry_mapping.S里,这段代码除了linux初始化会用,

也被kexec使用。下面先直接贴出代码,之后分析核心流程。

/* 1. Find the index of the entry we're executing in */
	bl	invstr				/* Find our address */
invstr:	mflr	r6				/* Make it accessible */
	mfmsr	r7
	rlwinm	r4,r7,27,31,31			/* extract MSR[IS] */ 
	mfspr	r7, SPRN_PID0   
	slwi	r7,r7,16
	or	r7,r7,r4
	mtspr	SPRN_MAS6,r7
	tlbsx	0,r6				/* search MSR[IS], SPID=PID0 */
	mfspr	r7,SPRN_MAS1
	andis.	r7,r7,MAS1_VALID@h  
	bne	match_TLB

	mfspr	r7,SPRN_MMUCFG
	rlwinm	r7,r7,21,28,31			/* extract MMUCFG[NPIDS] */
	cmpwi	r7,3
	bne	match_TLB			/* skip if NPIDS != 3 */

	mfspr	r7,SPRN_PID1
	slwi	r7,r7,16
	or	r7,r7,r4
	mtspr	SPRN_MAS6,r7
	tlbsx	0,r6				/* search MSR[IS], SPID=PID1 */
	mfspr	r7,SPRN_MAS1
	andis.	r7,r7,MAS1_VALID@h
	bne	match_TLB
	mfspr	r7, SPRN_PID2
	slwi	r7,r7,16
	or	r7,r7,r4
	mtspr	SPRN_MAS6,r7
	tlbsx	0,r6				/* Fall through, we had to match */

match_TLB:
	mfspr	r7,SPRN_MAS0
	rlwinm	r3,r7,16,20,31			/* Extract MAS0(Entry) */ 

	mfspr	r7,SPRN_MAS1			/* Insure IPROT set */ 
	oris	r7,r7,MAS1_IPROT@h
	mtspr	SPRN_MAS1,r7
	tlbwe

/* 2. Invalidate all entries except the entry we're executing in */
	mfspr	r9,SPRN_TLB1CFG
	andi.	r9,r9,0xfff  //r9 = tlb number
	li	r6,0				/* Set Entry counter to 0 */
1:	lis	r7,0x1000			/* Set MAS0(TLBSEL) = 1 */ 
	rlwimi	r7,r6,16,4,15			/* Setup MAS0 = TLBSEL | ESEL(r6) */
	mtspr	SPRN_MAS0,r7
	tlbre
	mfspr	r7,SPRN_MAS1
	rlwinm	r7,r7,0,2,31			/* Clear MAS1 Valid and IPROT */
	cmpw	r3,r6
	beq	skpinv				/* Dont update the current execution TLB */
	mtspr	SPRN_MAS1,r7
	tlbwe
	isync
skpinv:	addi	r6,r6,1				/* Increment */
	cmpw	r6,r9				/* Are we done? */
	bne	1b				/* If not, repeat */

	/* Invalidate TLB0 */
	li	r6,0x04
	tlbivax 0,r6
	TLBSYNC
	/* Invalidate TLB1 */
	li	r6,0x0c
	tlbivax 0,r6
	TLBSYNC

/* 3. Setup a temp mapping and jump to it */
	andi.	r5, r3, 0x1	/* Find an entry not used and is non-zero */
	addi	r5, r5, 0x1
	lis	r7,0x1000	/* Set MAS0(TLBSEL) = 1 */ 
	rlwimi	r7,r3,16,4,15	/* Setup MAS0 = TLBSEL | ESEL(r3) */
	mtspr	SPRN_MAS0,r7
	tlbre

	/* grab and fixup the RPN */
	mfspr	r6,SPRN_MAS1	/* extract MAS1[SIZE] */
	rlwinm	r6,r6,25,27,31   
	li	r8,-1
	addi	r6,r6,10
	slw	r6,r8,r6	/* convert to mask */ 

	bl	1f		/* Find our address */
1:	mflr	r7

	mfspr	r8,SPRN_MAS3
#ifdef CONFIG_PHYS_64BIT
	mfspr	r23,SPRN_MAS7
#endif
	and	r8,r6,r8
	subfic	r9,r6,-4096
	and	r9,r9,r7

	or	r25,r8,r9
	ori	r8,r25,(MAS3_SX|MAS3_SW|MAS3_SR)

	/* Just modify the entry ID and EPN for the temp mapping */
	lis	r7,0x1000	/* Set MAS0(TLBSEL) = 1 */
	rlwimi	r7,r5,16,4,15	/* Setup MAS0 = TLBSEL | ESEL(r5) */
	mtspr	SPRN_MAS0,r7
	xori	r6,r4,1		/* Setup TMP mapping in the other Address space */
	slwi	r6,r6,12
	oris	r6,r6,(MAS1_VALID|MAS1_IPROT)@h
	ori	r6,r6,(MAS1_TSIZE(BOOK3E_PAGESZ_4K))@l
	mtspr	SPRN_MAS1,r6
	mfspr	r6,SPRN_MAS2
	li	r7,0		/* temp EPN = 0 */ 
	rlwimi	r7,r6,0,20,31
	mtspr	SPRN_MAS2,r7
	mtspr	SPRN_MAS3,r8
	tlbwe

	xori	r6,r4,1
	slwi	r6,r6,5		/* setup new context with other address space */
	bl	1f		/* Find our address */
1:	mflr	r9

	rlwimi	r7,r9,0,20,31  
					
	addi	r7,r7,(2f - 1b)
	mtspr	SPRN_SRR0,r7
	mtspr	SPRN_SRR1,r6
	rfi
2:
/* 4. Clear out PIDs & Search info */
	li	r6,0
	mtspr   SPRN_MAS6,r6
	mtspr	SPRN_PID0,r6

	mfspr	r7,SPRN_MMUCFG
	rlwinm	r7,r7,21,28,31			/* extract MMUCFG[NPIDS] */
	cmpwi	r7,3
	bne	2f				/* skip if NPIDS != 3 */

	mtspr	SPRN_PID1,r6
	mtspr	SPRN_PID2,r6

/* 5. Invalidate mapping we started in */
2:
	lis	r7,0x1000	/* Set MAS0(TLBSEL) = 1 */
	rlwimi	r7,r3,16,4,15	/* Setup MAS0 = TLBSEL | ESEL(r3) */
	mtspr	SPRN_MAS0,r7
	tlbre
	mfspr	r6,SPRN_MAS1
	rlwinm	r6,r6,0,2,0	/* clear IPROT */
	mtspr	SPRN_MAS1,r6
	tlbwe
	/* Invalidate TLB1 */
	li	r9,0x0c
	tlbivax 0,r9
	TLBSYNC

/* The mapping only needs to be cache-coherent on SMP */
#ifdef CONFIG_SMP
#define M_IF_SMP	MAS2_M
#else
#define M_IF_SMP	0
#endif

#if defined(ENTRY_MAPPING_BOOT_SETUP)

/* 6. Setup KERNELBASE mapping in TLB1[0] */
	lis	r6,0x1000		/* Set MAS0(TLBSEL) = TLB1(1), ESEL = 0 */
	mtspr	SPRN_MAS0,r6
	lis	r6,(MAS1_VALID|MAS1_IPROT)@h
	ori	r6,r6,(MAS1_TSIZE(BOOK3E_PAGESZ_64M))@l
	mtspr	SPRN_MAS1,r6
	lis	r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, M_IF_SMP)@h
	ori	r6,r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, M_IF_SMP)@l
	mtspr	SPRN_MAS2,r6
	mtspr	SPRN_MAS3,r8
	tlbwe

/* 7. Jump to KERNELBASE mapping */
	lis	r6,(KERNELBASE & ~0xfff)@h
	ori	r6,r6,(KERNELBASE & ~0xfff)@l

#elif defined(ENTRY_MAPPING_KEXEC_SETUP)
/*
 * 6. Setup a 1:1 mapping in TLB1. Esel 0 is unsued, 1 or 2 contains the tmp
 * mapping so we start at 3. We setup 8 mappings, each 256MiB in size. This
 * will cover the first 2GiB of memory.
 */

	lis r10, (MAS1_VALID|MAS1_IPROT)@h
	ori r10,r10, (MAS1_TSIZE(BOOK3E_PAGESZ_256M))@l
	li  r11, 0
	li  r0, 8
	mtctr   r0

next_tlb_setup:
	addi	r0, r11, 3
	rlwinm  r0, r0, 16, 4, 15  // Compute esel
	rlwinm  r9, r11, 28, 0, 3   // Compute [ER]PN
	oris    r0, r0, (MAS0_TLBSEL(1))@h
	mtspr   SPRN_MAS0,r0
	mtspr   SPRN_MAS1,r10
	mtspr   SPRN_MAS2,r9
	ori r9, r9, (MAS3_SX|MAS3_SW|MAS3_SR)
	mtspr   SPRN_MAS3,r9
	tlbwe
	addi    r11, r11, 1
	bdnz+   next_tlb_setup

/* 7. Jump to our 1:1 mapping */
	mr	r6, r25
#else
	#error You need to specify the mapping or not use this at all.
#endif

	lis	r7,MSR_KERNEL@h
	ori	r7,r7,MSR_KERNEL@l
	bl	1f			/* Find our address */
1:	mflr	r9
	rlwimi	r6,r9,0,20,31
	addi	r6,r6,(2f - 1b)
	mtspr	SPRN_SRR0,r6
	mtspr	SPRN_SRR1,r7
	rfi				/* start execution out of TLB1[0] entry */

/* 8. Clear out the temp mapping */
2:	lis	r7,0x1000	/* Set MAS0(TLBSEL) = 1 */
	rlwimi	r7,r5,16,4,15	/* Setup MAS0 = TLBSEL | ESEL(r5) */
	mtspr	SPRN_MAS0,r7
	tlbre
	mfspr	r8,SPRN_MAS1
	rlwinm	r8,r8,0,2,0	/* clear IPROT */
	mtspr	SPRN_MAS1,r8
	tlbwe
	/* Invalidate TLB1 */
	li	r9,0x0c
	tlbivax 0,r9
	TLBSYNC

line2, bl是相对跳转,跳转到invstr标签处,将此标签虚拟地址存入r6;
line10,调用tlbsx 0,r6查找出当前的1:1映射tlb条目的编号,跳转至match_tlb处;
line35,先将MAS0的值存入r7,然后将r7逻辑循环左移16位,然后提取32+20至32+31的内容
存入r3,剩下的bit位全部清零。其实就是取得MAS0的ESEL字段。其中,ESEL字段位于bit44-bit47,
将MAS0的高32位循环左移16位后,就位于bit52-bit63。
(powerpc是大端,左移指的是将数据往低地址方向移动。)
这里第一次说到M-Form的rlwinm指令,
该指令主要用来提取寄存器的某些位,然后放到结果寄存器里。
rlwinm r1, r2, SH, A, B (A < B)
如:r2 = 0x12345678, SH=8, A=3,B=9 则:r1 = 0x1440 0000.
计算过程:MASK(A,B) = 0x1fc0 0000. r2 <- 8 = 0x34567812.
line10执行完后,r3保存tlb entry编号;
line38到line41,将r3对应的tlb条目置一个IPROT属性保护起来。
line44到line60,遍历所有的tlb1,主要编号不是r3的,就将其保护位IPROT清除;
line62到line69,将tlb0和tlb1的所有tlb置无效,目前只剩一个我们正在执行的tlb条目了;
接下来,在另外一个地址空间,建立一个临时的tlb,让代码跳转过去临时跑一会:
line72到line73,找一个不同的tlb,算法是r5=(r3&1)+1,假如r3是偶数,则r5=1;假如r3是奇数,则r5是2;
line74到line77,将r3对应的tlb信息读出到MAS系列寄存器;
line80到line84,对RPN进行4k调整对齐之类的操作;
line100到line114,填充临时tlb的信息:
line102,指定写r5对应的tlb,line104到line108,置MAS1的ts位为非当前空间,置IPROT保护位,置页面大小为4k;
line109获取当前MAS2的内容,存放到r6,然后获取r6的bit52-bit63位,平移到r7中,然后写回MAS2。这句的意思就是
保留tlb条目的cache属性,然后将epn清零。
line113将RPN按页对齐调整过后的值写入MAS3,然后写入此temp tlb;
line116到line117,将MSR的TS置为与当前空间相反,与line102相对应;
line121将当前地址的bit52-bit63更新到r7(即把之前r7存放的MAS2里的cache属性位覆盖),
而其余位仍然是r7的原值(这是与rlwinm唯一不同的地方),原值就是epn为0,并取当前运行地址的

bit52-bit63位,即低4k。也就是说,这个临时空间要求从代码段开头到line121

的长度应小于4k,且代码段必须被加载到物理地址4k的倍数,这样temp tlb才能正确的映射当前运行地址空间,

例如,假设代码段被加载到物理地址16k,则line121所在的物理地址为16k+121×4,

假设运行到此时,代码段所在tlb的虚实映射为(virt:16k,phy:16k,size:64M),则经line121的操作,需要建立的临时映射为

(virt:16k,phy:16k,size:4k),可以看出,line121离16k必须小于4k才能被正确映射;

line123算出2f的偏移,并在line124将地址存入SPR0,将MSR存入SPR1;
line126跳转至临时空间;
到了line143,这时候可以放心大胆的清除刚进内核时的唯一一个tlb条目了,这里不再罗嗦;
line166-line175,在空间0建立一个64M、虚拟映射为PAGE_OFFSET:0的tlb条目;
line178至line179,为r6存放一个内核态偏移,然后line218将当前运行地址的低4k,平移到r6中,
最后line222跳转至此地址,即从此运行在PAGE_OFFSET:0的地址空间了;
line225至line236,清除最先我们建立在另一个空间的临时映射;


line181至line209是kexec跳转前,为2G的物理地址空间建立1:1映射,这样做主要是

因为kexec e500架构下的最终内核页面拷贝都是假设关闭了mmu的,即misc_32.S里的

relocate_new_kernel操作的都是物理地址;

line191至line192,设置ctrk寄存器为8,表示接下来的循环有8次;

line195,从第3个tlb条目开始,依次设置tlb属性,每个tlb有256M,直到第11个tlb,一共2G的空间;

line196的rlwinm  r0, r0, 16, 4, 15, 表示将r0左移16位,再取bit4到bit15,当r0为3的时候,0x3<<16=0x30000,

再取bit4到bit15,由于powerpc是大端,因此从左边开始取第4到第15bit,即掩码为0000 1111 1111 1111 0000 0000 0000 0000 =0xfff0000,

再与0x30000相与得到0x30000再在line199存入MAS0;

至于line197的rlwinm  r9, r11, 28, 0, 3,则是将r11(0递增至8)左移28位(即乘以256M)后取bit0-bit3再在line201写入MAS2。

所以,对于kexec需要注意的是,这段代码并不完善,如果系统用到超过2G的内存,则这里的2G映射并不够。需要修改循环设置tlb的次数,

为什么不干脆就直接设置单位为1G的tlb条目?如果这样的话,line197就不是左移28位而是32位了,这对于32位指令系统显然是不可能的,所以这里

设置256M是有道理的。

你可能感兴趣的:(tlb,PowerPC,e500)