| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * ppc64 MMU hashtable management routines | 
 | 3 |  * | 
 | 4 |  * (c) Copyright IBM Corp. 2003 | 
 | 5 |  * | 
 | 6 |  * Maintained by: Benjamin Herrenschmidt | 
 | 7 |  *                <benh@kernel.crashing.org> | 
 | 8 |  * | 
 | 9 |  * This file is covered by the GNU Public Licence v2 as | 
 | 10 |  * described in the kernel's COPYING file. | 
 | 11 |  */ | 
 | 12 |  | 
 | 13 | #include <asm/processor.h> | 
 | 14 | #include <asm/pgtable.h> | 
 | 15 | #include <asm/mmu.h> | 
 | 16 | #include <asm/page.h> | 
 | 17 | #include <asm/types.h> | 
 | 18 | #include <asm/ppc_asm.h> | 
 | 19 | #include <asm/offsets.h> | 
 | 20 | #include <asm/cputable.h> | 
 | 21 |  | 
 | 22 | 	.text | 
 | 23 |  | 
 | 24 | /* | 
 | 25 |  * Stackframe: | 
 | 26 |  *		 | 
 | 27 |  *         +-> Back chain			(SP + 256) | 
 | 28 |  *         |   General register save area	(SP + 112) | 
 | 29 |  *         |   Parameter save area		(SP + 48) | 
 | 30 |  *         |   TOC save area			(SP + 40) | 
 | 31 |  *         |   link editor doubleword		(SP + 32) | 
 | 32 |  *         |   compiler doubleword		(SP + 24) | 
 | 33 |  *         |   LR save area			(SP + 16) | 
 | 34 |  *         |   CR save area			(SP + 8) | 
 | 35 |  * SP ---> +-- Back chain			(SP + 0) | 
 | 36 |  */ | 
 | 37 | #define STACKFRAMESIZE	256 | 
 | 38 |  | 
 | 39 | /* Save parameters offsets */ | 
 | 40 | #define STK_PARM(i)	(STACKFRAMESIZE + 48 + ((i)-3)*8) | 
 | 41 |  | 
 | 42 | /* Save non-volatile offsets */ | 
 | 43 | #define STK_REG(i)	(112 + ((i)-14)*8) | 
 | 44 |  | 
 | 45 | /* | 
 | 46 |  * _hash_page(unsigned long ea, unsigned long access, unsigned long vsid, | 
 | 47 |  *		pte_t *ptep, unsigned long trap, int local) | 
 | 48 |  * | 
 | 49 |  * Adds a page to the hash table. This is the non-LPAR version for now | 
 | 50 |  */ | 
 | 51 |  | 
 | 52 | _GLOBAL(__hash_page) | 
 | 53 | 	mflr	r0 | 
 | 54 | 	std	r0,16(r1) | 
 | 55 | 	stdu	r1,-STACKFRAMESIZE(r1) | 
 | 56 | 	/* Save all params that we need after a function call */ | 
 | 57 | 	std	r6,STK_PARM(r6)(r1) | 
 | 58 | 	std	r8,STK_PARM(r8)(r1) | 
 | 59 | 	 | 
 | 60 | 	/* Add _PAGE_PRESENT to access */ | 
 | 61 | 	ori	r4,r4,_PAGE_PRESENT | 
 | 62 |  | 
 | 63 | 	/* Save non-volatile registers. | 
 | 64 | 	 * r31 will hold "old PTE" | 
 | 65 | 	 * r30 is "new PTE" | 
 | 66 | 	 * r29 is "va" | 
 | 67 | 	 * r28 is a hash value | 
 | 68 | 	 * r27 is hashtab mask (maybe dynamic patched instead ?) | 
 | 69 | 	 */ | 
 | 70 | 	std	r27,STK_REG(r27)(r1) | 
 | 71 | 	std	r28,STK_REG(r28)(r1) | 
 | 72 | 	std	r29,STK_REG(r29)(r1) | 
 | 73 | 	std	r30,STK_REG(r30)(r1) | 
 | 74 | 	std	r31,STK_REG(r31)(r1) | 
 | 75 | 	 | 
 | 76 | 	/* Step 1: | 
 | 77 | 	 * | 
 | 78 | 	 * Check permissions, atomically mark the linux PTE busy | 
 | 79 | 	 * and hashed. | 
 | 80 | 	 */  | 
 | 81 | 1: | 
 | 82 | 	ldarx	r31,0,r6 | 
 | 83 | 	/* Check access rights (access & ~(pte_val(*ptep))) */ | 
 | 84 | 	andc.	r0,r4,r31 | 
 | 85 | 	bne-	htab_wrong_access | 
 | 86 | 	/* Check if PTE is busy */ | 
 | 87 | 	andi.	r0,r31,_PAGE_BUSY | 
| Olof Johansson | d03853d | 2005-05-01 08:58:45 -0700 | [diff] [blame] | 88 | 	/* If so, just bail out and refault if needed. Someone else | 
 | 89 | 	 * is changing this PTE anyway and might hash it. | 
 | 90 | 	 */ | 
 | 91 | 	bne-	bail_ok | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 92 | 	/* Prepare new PTE value (turn access RW into DIRTY, then | 
 | 93 | 	 * add BUSY,HASHPTE and ACCESSED) | 
 | 94 | 	 */ | 
 | 95 | 	rlwinm	r30,r4,32-9+7,31-7,31-7	/* _PAGE_RW -> _PAGE_DIRTY */ | 
 | 96 | 	or	r30,r30,r31 | 
 | 97 | 	ori	r30,r30,_PAGE_BUSY | _PAGE_ACCESSED | _PAGE_HASHPTE | 
 | 98 | 	/* Write the linux PTE atomically (setting busy) */ | 
 | 99 | 	stdcx.	r30,0,r6 | 
 | 100 | 	bne-	1b | 
 | 101 | 	isync | 
 | 102 |  | 
 | 103 | 	/* Step 2: | 
 | 104 | 	 * | 
 | 105 | 	 * Insert/Update the HPTE in the hash table. At this point, | 
 | 106 | 	 * r4 (access) is re-useable, we use it for the new HPTE flags | 
 | 107 | 	 */ | 
 | 108 |  | 
 | 109 | 	/* Calc va and put it in r29 */ | 
 | 110 | 	rldicr	r29,r5,28,63-28 | 
 | 111 | 	rldicl	r3,r3,0,36 | 
 | 112 | 	or	r29,r3,r29 | 
 | 113 |  | 
 | 114 | 	/* Calculate hash value for primary slot and store it in r28 */ | 
 | 115 | 	rldicl	r5,r5,0,25		/* vsid & 0x0000007fffffffff */ | 
 | 116 | 	rldicl	r0,r3,64-12,48		/* (ea >> 12) & 0xffff */ | 
 | 117 | 	xor	r28,r5,r0 | 
 | 118 |  | 
 | 119 | 	/* Convert linux PTE bits into HW equivalents */ | 
 | 120 | 	andi.	r3,r30,0x1fe		/* Get basic set of flags */ | 
 | 121 | 	xori	r3,r3,HW_NO_EXEC	/* _PAGE_EXEC -> NOEXEC */ | 
 | 122 | 	rlwinm	r0,r30,32-9+1,30,30	/* _PAGE_RW -> _PAGE_USER (r0) */ | 
 | 123 | 	rlwinm	r4,r30,32-7+1,30,30	/* _PAGE_DIRTY -> _PAGE_USER (r4) */ | 
 | 124 | 	and	r0,r0,r4		/* _PAGE_RW & _PAGE_DIRTY -> r0 bit 30 */ | 
 | 125 | 	andc	r0,r30,r0		/* r0 = pte & ~r0 */ | 
 | 126 | 	rlwimi	r3,r0,32-1,31,31	/* Insert result into PP lsb */ | 
 | 127 |  | 
 | 128 | 	/* We eventually do the icache sync here (maybe inline that | 
 | 129 | 	 * code rather than call a C function...)  | 
 | 130 | 	 */ | 
 | 131 | BEGIN_FTR_SECTION | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 132 | 	mr	r4,r30 | 
 | 133 | 	mr	r5,r7 | 
 | 134 | 	bl	.hash_page_do_lazy_icache | 
| David Gibson | 8913ca1 | 2005-07-27 15:47:23 +1000 | [diff] [blame] | 135 | END_FTR_SECTION(CPU_FTR_NOEXECUTE|CPU_FTR_COHERENT_ICACHE, CPU_FTR_NOEXECUTE) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 136 |  | 
 | 137 | 	/* At this point, r3 contains new PP bits, save them in | 
 | 138 | 	 * place of "access" in the param area (sic) | 
 | 139 | 	 */ | 
 | 140 | 	std	r3,STK_PARM(r4)(r1) | 
 | 141 |  | 
 | 142 | 	/* Get htab_hash_mask */ | 
 | 143 | 	ld	r4,htab_hash_mask@got(2) | 
 | 144 | 	ld	r27,0(r4)	/* htab_hash_mask -> r27 */ | 
 | 145 |  | 
 | 146 | 	/* Check if we may already be in the hashtable, in this case, we | 
 | 147 | 	 * go to out-of-line code to try to modify the HPTE | 
 | 148 | 	 */ | 
 | 149 | 	andi.	r0,r31,_PAGE_HASHPTE | 
 | 150 | 	bne	htab_modify_pte | 
 | 151 |  | 
 | 152 | htab_insert_pte: | 
 | 153 | 	/* Clear hpte bits in new pte (we also clear BUSY btw) and | 
 | 154 | 	 * add _PAGE_HASHPTE | 
 | 155 | 	 */ | 
 | 156 | 	lis	r0,_PAGE_HPTEFLAGS@h | 
 | 157 | 	ori	r0,r0,_PAGE_HPTEFLAGS@l | 
 | 158 | 	andc	r30,r30,r0 | 
 | 159 | 	ori	r30,r30,_PAGE_HASHPTE | 
 | 160 |  | 
 | 161 | 	/* page number in r5 */ | 
 | 162 | 	rldicl	r5,r31,64-PTE_SHIFT,PTE_SHIFT | 
 | 163 |  | 
 | 164 | 	/* Calculate primary group hash */ | 
 | 165 | 	and	r0,r28,r27 | 
 | 166 | 	rldicr	r3,r0,3,63-3	/* r0 = (hash & mask) << 3 */ | 
 | 167 |  | 
 | 168 | 	/* Call ppc_md.hpte_insert */ | 
 | 169 | 	ld	r7,STK_PARM(r4)(r1)	/* Retreive new pp bits */ | 
 | 170 | 	mr	r4,r29			/* Retreive va */ | 
| David Gibson | 96e2844 | 2005-07-13 01:11:42 -0700 | [diff] [blame] | 171 | 	li	r6,0			/* no vflags */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 172 | _GLOBAL(htab_call_hpte_insert1) | 
 | 173 | 	bl	.			/* Will be patched by htab_finish_init() */ | 
 | 174 | 	cmpdi	0,r3,0 | 
 | 175 | 	bge	htab_pte_insert_ok	/* Insertion successful */ | 
 | 176 | 	cmpdi	0,r3,-2			/* Critical failure */ | 
 | 177 | 	beq-	htab_pte_insert_failure | 
 | 178 |  | 
 | 179 | 	/* Now try secondary slot */ | 
 | 180 | 	 | 
 | 181 | 	/* page number in r5 */ | 
 | 182 | 	rldicl	r5,r31,64-PTE_SHIFT,PTE_SHIFT | 
 | 183 |  | 
 | 184 | 	/* Calculate secondary group hash */ | 
 | 185 | 	andc	r0,r27,r28 | 
 | 186 | 	rldicr	r3,r0,3,63-3	/* r0 = (~hash & mask) << 3 */ | 
 | 187 | 	 | 
 | 188 | 	/* Call ppc_md.hpte_insert */ | 
 | 189 | 	ld	r7,STK_PARM(r4)(r1)	/* Retreive new pp bits */ | 
 | 190 | 	mr	r4,r29			/* Retreive va */ | 
| David Gibson | 96e2844 | 2005-07-13 01:11:42 -0700 | [diff] [blame] | 191 | 	li	r6,HPTE_V_SECONDARY@l	/* secondary slot */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 192 | _GLOBAL(htab_call_hpte_insert2) | 
 | 193 | 	bl	.			/* Will be patched by htab_finish_init() */ | 
 | 194 | 	cmpdi	0,r3,0 | 
 | 195 | 	bge+	htab_pte_insert_ok	/* Insertion successful */ | 
 | 196 | 	cmpdi	0,r3,-2			/* Critical failure */ | 
 | 197 | 	beq-	htab_pte_insert_failure | 
 | 198 |  | 
 | 199 | 	/* Both are full, we need to evict something */ | 
 | 200 | 	mftb	r0 | 
 | 201 | 	/* Pick a random group based on TB */ | 
 | 202 | 	andi.	r0,r0,1 | 
 | 203 | 	mr	r5,r28 | 
 | 204 | 	bne	2f | 
 | 205 | 	not	r5,r5 | 
 | 206 | 2:	and	r0,r5,r27 | 
 | 207 | 	rldicr	r3,r0,3,63-3	/* r0 = (hash & mask) << 3 */	 | 
 | 208 | 	/* Call ppc_md.hpte_remove */ | 
 | 209 | _GLOBAL(htab_call_hpte_remove) | 
 | 210 | 	bl	.			/* Will be patched by htab_finish_init() */ | 
 | 211 |  | 
 | 212 | 	/* Try all again */ | 
 | 213 | 	b	htab_insert_pte	 | 
 | 214 |  | 
| Olof Johansson | d03853d | 2005-05-01 08:58:45 -0700 | [diff] [blame] | 215 | bail_ok: | 
 | 216 | 	li	r3,0 | 
 | 217 | 	b	bail | 
 | 218 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 219 | htab_pte_insert_ok: | 
 | 220 | 	/* Insert slot number & secondary bit in PTE */ | 
 | 221 | 	rldimi	r30,r3,12,63-15 | 
 | 222 | 		 | 
 | 223 | 	/* Write out the PTE with a normal write | 
 | 224 | 	 * (maybe add eieio may be good still ?) | 
 | 225 | 	 */ | 
 | 226 | htab_write_out_pte: | 
 | 227 | 	ld	r6,STK_PARM(r6)(r1) | 
 | 228 | 	std	r30,0(r6) | 
 | 229 | 	li	r3, 0 | 
 | 230 | bail: | 
 | 231 | 	ld	r27,STK_REG(r27)(r1) | 
 | 232 | 	ld	r28,STK_REG(r28)(r1) | 
 | 233 | 	ld	r29,STK_REG(r29)(r1) | 
 | 234 | 	ld      r30,STK_REG(r30)(r1) | 
 | 235 | 	ld      r31,STK_REG(r31)(r1) | 
 | 236 | 	addi    r1,r1,STACKFRAMESIZE | 
 | 237 | 	ld      r0,16(r1) | 
 | 238 | 	mtlr    r0 | 
 | 239 | 	blr | 
 | 240 |  | 
 | 241 | htab_modify_pte: | 
 | 242 | 	/* Keep PP bits in r4 and slot idx from the PTE around in r3 */ | 
 | 243 | 	mr	r4,r3 | 
 | 244 | 	rlwinm	r3,r31,32-12,29,31 | 
 | 245 |  | 
 | 246 | 	/* Secondary group ? if yes, get a inverted hash value */ | 
 | 247 | 	mr	r5,r28 | 
 | 248 | 	andi.	r0,r31,_PAGE_SECONDARY | 
 | 249 | 	beq	1f | 
 | 250 | 	not	r5,r5 | 
 | 251 | 1: | 
 | 252 | 	/* Calculate proper slot value for ppc_md.hpte_updatepp */ | 
 | 253 | 	and	r0,r5,r27 | 
 | 254 | 	rldicr	r0,r0,3,63-3	/* r0 = (hash & mask) << 3 */ | 
 | 255 | 	add	r3,r0,r3	/* add slot idx */ | 
 | 256 |  | 
 | 257 | 	/* Call ppc_md.hpte_updatepp */ | 
 | 258 | 	mr	r5,r29			/* va */ | 
 | 259 | 	li	r6,0			/* large is 0 */ | 
 | 260 | 	ld	r7,STK_PARM(r8)(r1)	/* get "local" param */ | 
 | 261 | _GLOBAL(htab_call_hpte_updatepp) | 
 | 262 | 	bl	.			/* Will be patched by htab_finish_init() */ | 
 | 263 |  | 
 | 264 | 	/* if we failed because typically the HPTE wasn't really here | 
 | 265 | 	 * we try an insertion.  | 
 | 266 | 	 */ | 
 | 267 | 	cmpdi	0,r3,-1 | 
 | 268 | 	beq-	htab_insert_pte | 
 | 269 |  | 
 | 270 | 	/* Clear the BUSY bit and Write out the PTE */ | 
 | 271 | 	li	r0,_PAGE_BUSY | 
 | 272 | 	andc	r30,r30,r0 | 
 | 273 | 	b	htab_write_out_pte | 
 | 274 |  | 
 | 275 | htab_wrong_access: | 
 | 276 | 	/* Bail out clearing reservation */ | 
 | 277 | 	stdcx.	r31,0,r6 | 
 | 278 | 	li	r3,1 | 
 | 279 | 	b	bail | 
 | 280 |  | 
 | 281 | htab_pte_insert_failure: | 
 | 282 | 	/* Bail out restoring old PTE */ | 
 | 283 | 	ld	r6,STK_PARM(r6)(r1) | 
 | 284 | 	std	r31,0(r6) | 
 | 285 | 	li	r3,-1 | 
 | 286 | 	b	bail | 
 | 287 |  | 
 | 288 |  |