| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * arch/ppc64/mm/slb_low.S | 
|  | 3 | * | 
|  | 4 | * Low-level SLB routines | 
|  | 5 | * | 
|  | 6 | * Copyright (C) 2004 David Gibson <dwg@au.ibm.com>, IBM | 
|  | 7 | * | 
|  | 8 | * Based on earlier C version: | 
|  | 9 | * Dave Engebretsen and Mike Corrigan {engebret|mikejc}@us.ibm.com | 
|  | 10 | *    Copyright (c) 2001 Dave Engebretsen | 
|  | 11 | * Copyright (C) 2002 Anton Blanchard <anton@au.ibm.com>, IBM | 
|  | 12 | * | 
|  | 13 | *  This program is free software; you can redistribute it and/or | 
|  | 14 | *  modify it under the terms of the GNU General Public License | 
|  | 15 | *  as published by the Free Software Foundation; either version | 
|  | 16 | *  2 of the License, or (at your option) any later version. | 
|  | 17 | */ | 
|  | 18 |  | 
|  | 19 | #include <linux/config.h> | 
|  | 20 | #include <asm/processor.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 21 | #include <asm/ppc_asm.h> | 
| Sam Ravnborg | 0013a85 | 2005-09-09 20:57:26 +0200 | [diff] [blame] | 22 | #include <asm/asm-offsets.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 23 | #include <asm/cputable.h> | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 24 | #include <asm/page.h> | 
|  | 25 | #include <asm/mmu.h> | 
|  | 26 | #include <asm/pgtable.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 27 |  | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 28 | /* void slb_allocate_realmode(unsigned long ea); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 29 | * | 
|  | 30 | * Create an SLB entry for the given EA (user or kernel). | 
|  | 31 | * 	r3 = faulting address, r13 = PACA | 
|  | 32 | *	r9, r10, r11 are clobbered by this function | 
|  | 33 | * No other registers are examined or changed. | 
|  | 34 | */ | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 35 | _GLOBAL(slb_allocate_realmode) | 
|  | 36 | /* r3 = faulting address */ | 
|  | 37 |  | 
|  | 38 | srdi	r9,r3,60		/* get region */ | 
|  | 39 | srdi	r10,r3,28		/* get esid */ | 
| Michael Ellerman | b5666f7 | 2005-12-05 10:24:33 -0600 | [diff] [blame] | 40 | cmpldi	cr7,r9,0xc		/* cmp PAGE_OFFSET for later use */ | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 41 |  | 
| Michael Ellerman | b5666f7 | 2005-12-05 10:24:33 -0600 | [diff] [blame] | 42 | /* r3 = address, r10 = esid, cr7 = <> PAGE_OFFSET */ | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 43 | blt	cr7,0f			/* user or kernel? */ | 
|  | 44 |  | 
|  | 45 | /* kernel address: proto-VSID = ESID */ | 
|  | 46 | /* WARNING - MAGIC: we don't use the VSID 0xfffffffff, but | 
|  | 47 | * this code will generate the protoVSID 0xfffffffff for the | 
|  | 48 | * top segment.  That's ok, the scramble below will translate | 
|  | 49 | * it to VSID 0, which is reserved as a bad VSID - one which | 
|  | 50 | * will never have any pages in it.  */ | 
|  | 51 |  | 
|  | 52 | /* Check if hitting the linear mapping of the vmalloc/ioremap | 
|  | 53 | * kernel space | 
|  | 54 | */ | 
|  | 55 | bne	cr7,1f | 
|  | 56 |  | 
|  | 57 | /* Linear mapping encoding bits, the "li" instruction below will | 
|  | 58 | * be patched by the kernel at boot | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 59 | */ | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 60 | _GLOBAL(slb_miss_kernel_load_linear) | 
|  | 61 | li	r11,0 | 
|  | 62 | b	slb_finish_load | 
|  | 63 |  | 
|  | 64 | 1:	/* vmalloc/ioremap mapping encoding bits, the "li" instruction below | 
|  | 65 | * will be patched by the kernel at boot | 
|  | 66 | */ | 
|  | 67 | _GLOBAL(slb_miss_kernel_load_virtual) | 
|  | 68 | li	r11,0 | 
|  | 69 | b	slb_finish_load | 
|  | 70 |  | 
|  | 71 |  | 
|  | 72 | 0:	/* user address: proto-VSID = context << 15 | ESID. First check | 
|  | 73 | * if the address is within the boundaries of the user region | 
|  | 74 | */ | 
|  | 75 | srdi.	r9,r10,USER_ESID_BITS | 
|  | 76 | bne-	8f			/* invalid ea bits set */ | 
|  | 77 |  | 
|  | 78 | /* Figure out if the segment contains huge pages */ | 
|  | 79 | #ifdef CONFIG_HUGETLB_PAGE | 
|  | 80 | BEGIN_FTR_SECTION | 
|  | 81 | b	1f | 
|  | 82 | END_FTR_SECTION_IFCLR(CPU_FTR_16M_PAGE) | 
| David Gibson | 7d24f0b | 2005-11-07 00:57:52 -0800 | [diff] [blame] | 83 | cmpldi	r10,16 | 
|  | 84 |  | 
|  | 85 | lhz	r9,PACALOWHTLBAREAS(r13) | 
|  | 86 | mr	r11,r10 | 
|  | 87 | blt	5f | 
|  | 88 |  | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 89 | lhz	r9,PACAHIGHHTLBAREAS(r13) | 
|  | 90 | srdi	r11,r10,(HTLB_AREA_SHIFT-SID_SHIFT) | 
| David Gibson | 7d24f0b | 2005-11-07 00:57:52 -0800 | [diff] [blame] | 91 |  | 
|  | 92 | 5:	srd	r9,r9,r11 | 
|  | 93 | andi.	r9,r9,1 | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 94 | beq	1f | 
|  | 95 | _GLOBAL(slb_miss_user_load_huge) | 
|  | 96 | li	r11,0 | 
|  | 97 | b	2f | 
|  | 98 | 1: | 
|  | 99 | #endif /* CONFIG_HUGETLB_PAGE */ | 
|  | 100 |  | 
|  | 101 | _GLOBAL(slb_miss_user_load_normal) | 
|  | 102 | li	r11,0 | 
|  | 103 |  | 
|  | 104 | 2: | 
|  | 105 | ld	r9,PACACONTEXTID(r13) | 
|  | 106 | rldimi	r10,r9,USER_ESID_BITS,0 | 
|  | 107 | b	slb_finish_load | 
|  | 108 |  | 
|  | 109 | 8:	/* invalid EA */ | 
|  | 110 | li	r10,0			/* BAD_VSID */ | 
|  | 111 | li	r11,SLB_VSID_USER	/* flags don't much matter */ | 
|  | 112 | b	slb_finish_load | 
|  | 113 |  | 
|  | 114 | #ifdef __DISABLED__ | 
|  | 115 |  | 
|  | 116 | /* void slb_allocate_user(unsigned long ea); | 
|  | 117 | * | 
|  | 118 | * Create an SLB entry for the given EA (user or kernel). | 
|  | 119 | * 	r3 = faulting address, r13 = PACA | 
|  | 120 | *	r9, r10, r11 are clobbered by this function | 
|  | 121 | * No other registers are examined or changed. | 
|  | 122 | * | 
|  | 123 | * It is called with translation enabled in order to be able to walk the | 
|  | 124 | * page tables. This is not currently used. | 
|  | 125 | */ | 
|  | 126 | _GLOBAL(slb_allocate_user) | 
|  | 127 | /* r3 = faulting address */ | 
|  | 128 | srdi	r10,r3,28		/* get esid */ | 
|  | 129 |  | 
|  | 130 | crset	4*cr7+lt		/* set "user" flag for later */ | 
|  | 131 |  | 
|  | 132 | /* check if we fit in the range covered by the pagetables*/ | 
|  | 133 | srdi.	r9,r3,PGTABLE_EADDR_SIZE | 
|  | 134 | crnot	4*cr0+eq,4*cr0+eq | 
|  | 135 | beqlr | 
|  | 136 |  | 
|  | 137 | /* now we need to get to the page tables in order to get the page | 
|  | 138 | * size encoding from the PMD. In the future, we'll be able to deal | 
|  | 139 | * with 1T segments too by getting the encoding from the PGD instead | 
|  | 140 | */ | 
|  | 141 | ld	r9,PACAPGDIR(r13) | 
|  | 142 | cmpldi	cr0,r9,0 | 
|  | 143 | beqlr | 
|  | 144 | rlwinm	r11,r10,8,25,28 | 
|  | 145 | ldx	r9,r9,r11		/* get pgd_t */ | 
|  | 146 | cmpldi	cr0,r9,0 | 
|  | 147 | beqlr | 
|  | 148 | rlwinm	r11,r10,3,17,28 | 
|  | 149 | ldx	r9,r9,r11		/* get pmd_t */ | 
|  | 150 | cmpldi	cr0,r9,0 | 
|  | 151 | beqlr | 
|  | 152 |  | 
|  | 153 | /* build vsid flags */ | 
|  | 154 | andi.	r11,r9,SLB_VSID_LLP | 
|  | 155 | ori	r11,r11,SLB_VSID_USER | 
|  | 156 |  | 
|  | 157 | /* get context to calculate proto-VSID */ | 
|  | 158 | ld	r9,PACACONTEXTID(r13) | 
|  | 159 | rldimi	r10,r9,USER_ESID_BITS,0 | 
|  | 160 |  | 
|  | 161 | /* fall through slb_finish_load */ | 
|  | 162 |  | 
|  | 163 | #endif /* __DISABLED__ */ | 
|  | 164 |  | 
|  | 165 |  | 
|  | 166 | /* | 
|  | 167 | * Finish loading of an SLB entry and return | 
|  | 168 | * | 
| Michael Ellerman | b5666f7 | 2005-12-05 10:24:33 -0600 | [diff] [blame] | 169 | * r3 = EA, r10 = proto-VSID, r11 = flags, clobbers r9, cr7 = <> PAGE_OFFSET | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 170 | */ | 
|  | 171 | slb_finish_load: | 
|  | 172 | ASM_VSID_SCRAMBLE(r10,r9) | 
|  | 173 | rldimi	r11,r10,SLB_VSID_SHIFT,16	/* combine VSID and flags */ | 
|  | 174 |  | 
|  | 175 | /* r3 = EA, r11 = VSID data */ | 
|  | 176 | /* | 
|  | 177 | * Find a slot, round robin. Previously we tried to find a | 
|  | 178 | * free slot first but that took too long. Unfortunately we | 
|  | 179 | * dont have any LRU information to help us choose a slot. | 
|  | 180 | */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 181 | #ifdef CONFIG_PPC_ISERIES | 
|  | 182 | /* | 
|  | 183 | * On iSeries, the "bolted" stack segment can be cast out on | 
|  | 184 | * shared processor switch so we need to check for a miss on | 
|  | 185 | * it and restore it to the right slot. | 
|  | 186 | */ | 
|  | 187 | ld	r9,PACAKSAVE(r13) | 
|  | 188 | clrrdi	r9,r9,28 | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 189 | clrrdi	r3,r3,28 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 190 | li	r10,SLB_NUM_BOLTED-1	/* Stack goes in last bolted slot */ | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 191 | cmpld	r9,r3 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 192 | beq	3f | 
|  | 193 | #endif /* CONFIG_PPC_ISERIES */ | 
|  | 194 |  | 
|  | 195 | ld	r10,PACASTABRR(r13) | 
|  | 196 | addi	r10,r10,1 | 
|  | 197 | /* use a cpu feature mask if we ever change our slb size */ | 
|  | 198 | cmpldi	r10,SLB_NUM_ENTRIES | 
|  | 199 |  | 
|  | 200 | blt+	4f | 
|  | 201 | li	r10,SLB_NUM_BOLTED | 
|  | 202 |  | 
|  | 203 | 4: | 
|  | 204 | std	r10,PACASTABRR(r13) | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 205 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 206 | 3: | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 207 | rldimi	r3,r10,0,36		/* r3= EA[0:35] | entry */ | 
|  | 208 | oris	r10,r3,SLB_ESID_V@h	/* r3 |= SLB_ESID_V */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 209 |  | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 210 | /* r3 = ESID data, r11 = VSID data */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 211 |  | 
|  | 212 | /* | 
|  | 213 | * No need for an isync before or after this slbmte. The exception | 
|  | 214 | * we enter with and the rfid we exit with are context synchronizing. | 
|  | 215 | */ | 
|  | 216 | slbmte	r11,r10 | 
|  | 217 |  | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 218 | /* we're done for kernel addresses */ | 
|  | 219 | crclr	4*cr0+eq		/* set result to "success" */ | 
|  | 220 | bgelr	cr7 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 221 |  | 
|  | 222 | /* Update the slb cache */ | 
|  | 223 | lhz	r3,PACASLBCACHEPTR(r13)	/* offset = paca->slb_cache_ptr */ | 
|  | 224 | cmpldi	r3,SLB_CACHE_ENTRIES | 
|  | 225 | bge	1f | 
|  | 226 |  | 
|  | 227 | /* still room in the slb cache */ | 
|  | 228 | sldi	r11,r3,1		/* r11 = offset * sizeof(u16) */ | 
|  | 229 | rldicl	r10,r10,36,28		/* get low 16 bits of the ESID */ | 
|  | 230 | add	r11,r11,r13		/* r11 = (u16 *)paca + offset */ | 
|  | 231 | sth	r10,PACASLBCACHE(r11)	/* paca->slb_cache[offset] = esid */ | 
|  | 232 | addi	r3,r3,1			/* offset++ */ | 
|  | 233 | b	2f | 
|  | 234 | 1:					/* offset >= SLB_CACHE_ENTRIES */ | 
|  | 235 | li	r3,SLB_CACHE_ENTRIES+1 | 
|  | 236 | 2: | 
|  | 237 | sth	r3,PACASLBCACHEPTR(r13)	/* paca->slb_cache_ptr = offset */ | 
| Benjamin Herrenschmidt | 3c726f8 | 2005-11-07 11:06:55 +1100 | [diff] [blame] | 238 | crclr	4*cr0+eq		/* set result to "success" */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 239 | blr | 
|  | 240 |  |