| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 1 | /* | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 2 | * Kernel execution entry point code. | 
|  | 3 | * | 
|  | 4 | *    Copyright (c) 1995-1996 Gary Thomas <gdt@linuxppc.org> | 
|  | 5 | *      Initial PowerPC version. | 
|  | 6 | *    Copyright (c) 1996 Cort Dougan <cort@cs.nmt.edu> | 
|  | 7 | *      Rewritten for PReP | 
|  | 8 | *    Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au> | 
|  | 9 | *      Low-level exception handers, MMU support, and rewrite. | 
|  | 10 | *    Copyright (c) 1997 Dan Malek <dmalek@jlc.net> | 
|  | 11 | *      PowerPC 8xx modifications. | 
|  | 12 | *    Copyright (c) 1998-1999 TiVo, Inc. | 
|  | 13 | *      PowerPC 403GCX modifications. | 
|  | 14 | *    Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu> | 
|  | 15 | *      PowerPC 403GCX/405GP modifications. | 
|  | 16 | *    Copyright 2000 MontaVista Software Inc. | 
|  | 17 | *	PPC405 modifications | 
|  | 18 | *      PowerPC 403GCX/405GP modifications. | 
|  | 19 | * 	Author: MontaVista Software, Inc. | 
|  | 20 | *         	frank_rowand@mvista.com or source@mvista.com | 
|  | 21 | * 	   	debbie_chu@mvista.com | 
|  | 22 | *    Copyright 2002-2005 MontaVista Software, Inc. | 
|  | 23 | *      PowerPC 44x support, Matt Porter <mporter@kernel.crashing.org> | 
|  | 24 | * | 
|  | 25 | * This program is free software; you can redistribute  it and/or modify it | 
|  | 26 | * under  the terms of  the GNU General  Public License as published by the | 
|  | 27 | * Free Software Foundation;  either version 2 of the  License, or (at your | 
|  | 28 | * option) any later version. | 
|  | 29 | */ | 
|  | 30 |  | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 31 | #include <asm/processor.h> | 
|  | 32 | #include <asm/page.h> | 
|  | 33 | #include <asm/mmu.h> | 
|  | 34 | #include <asm/pgtable.h> | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 35 | #include <asm/cputable.h> | 
|  | 36 | #include <asm/thread_info.h> | 
|  | 37 | #include <asm/ppc_asm.h> | 
|  | 38 | #include <asm/asm-offsets.h> | 
|  | 39 | #include "head_booke.h" | 
|  | 40 |  | 
|  | 41 |  | 
|  | 42 | /* As with the other PowerPC ports, it is expected that when code | 
|  | 43 | * execution begins here, the following registers contain valid, yet | 
|  | 44 | * optional, information: | 
|  | 45 | * | 
|  | 46 | *   r3 - Board info structure pointer (DRAM, frequency, MAC address, etc.) | 
|  | 47 | *   r4 - Starting address of the init RAM disk | 
|  | 48 | *   r5 - Ending address of the init RAM disk | 
|  | 49 | *   r6 - Start of kernel command line string (e.g. "mem=128") | 
|  | 50 | *   r7 - End of kernel command line string | 
|  | 51 | * | 
|  | 52 | */ | 
|  | 53 | .text | 
|  | 54 | _GLOBAL(_stext) | 
|  | 55 | _GLOBAL(_start) | 
|  | 56 | /* | 
|  | 57 | * Reserve a word at a fixed location to store the address | 
|  | 58 | * of abatron_pteptrs | 
|  | 59 | */ | 
|  | 60 | nop | 
|  | 61 | /* | 
|  | 62 | * Save parameters we are passed | 
|  | 63 | */ | 
|  | 64 | mr	r31,r3 | 
|  | 65 | mr	r30,r4 | 
|  | 66 | mr	r29,r5 | 
|  | 67 | mr	r28,r6 | 
|  | 68 | mr	r27,r7 | 
|  | 69 | li	r24,0		/* CPU number */ | 
|  | 70 |  | 
|  | 71 | /* | 
|  | 72 | * Set up the initial MMU state | 
|  | 73 | * | 
|  | 74 | * We are still executing code at the virtual address | 
|  | 75 | * mappings set by the firmware for the base of RAM. | 
|  | 76 | * | 
|  | 77 | * We first invalidate all TLB entries but the one | 
|  | 78 | * we are running from.  We then load the KERNELBASE | 
|  | 79 | * mappings so we can begin to use kernel addresses | 
|  | 80 | * natively and so the interrupt vector locations are | 
|  | 81 | * permanently pinned (necessary since Book E | 
|  | 82 | * implementations always have translation enabled). | 
|  | 83 | * | 
|  | 84 | * TODO: Use the known TLB entry we are running from to | 
|  | 85 | *	 determine which physical region we are located | 
|  | 86 | *	 in.  This can be used to determine where in RAM | 
|  | 87 | *	 (on a shared CPU system) or PCI memory space | 
|  | 88 | *	 (on a DRAMless system) we are located. | 
|  | 89 | *       For now, we assume a perfect world which means | 
|  | 90 | *	 we are located at the base of DRAM (physical 0). | 
|  | 91 | */ | 
|  | 92 |  | 
|  | 93 | /* | 
|  | 94 | * Search TLB for entry that we are currently using. | 
|  | 95 | * Invalidate all entries but the one we are using. | 
|  | 96 | */ | 
|  | 97 | /* Load our current PID->MMUCR TID and MSR IS->MMUCR STS */ | 
|  | 98 | mfspr	r3,SPRN_PID			/* Get PID */ | 
|  | 99 | mfmsr	r4				/* Get MSR */ | 
|  | 100 | andi.	r4,r4,MSR_IS@l			/* TS=1? */ | 
|  | 101 | beq	wmmucr				/* If not, leave STS=0 */ | 
|  | 102 | oris	r3,r3,PPC44x_MMUCR_STS@h	/* Set STS=1 */ | 
|  | 103 | wmmucr:	mtspr	SPRN_MMUCR,r3			/* Put MMUCR */ | 
|  | 104 | sync | 
|  | 105 |  | 
|  | 106 | bl	invstr				/* Find our address */ | 
|  | 107 | invstr:	mflr	r5				/* Make it accessible */ | 
|  | 108 | tlbsx	r23,0,r5			/* Find entry we are in */ | 
|  | 109 | li	r4,0				/* Start at TLB entry 0 */ | 
|  | 110 | li	r3,0				/* Set PAGEID inval value */ | 
|  | 111 | 1:	cmpw	r23,r4				/* Is this our entry? */ | 
|  | 112 | beq	skpinv				/* If so, skip the inval */ | 
|  | 113 | tlbwe	r3,r4,PPC44x_TLB_PAGEID		/* If not, inval the entry */ | 
|  | 114 | skpinv:	addi	r4,r4,1				/* Increment */ | 
|  | 115 | cmpwi	r4,64				/* Are we done? */ | 
|  | 116 | bne	1b				/* If not, repeat */ | 
|  | 117 | isync					/* If so, context change */ | 
|  | 118 |  | 
|  | 119 | /* | 
|  | 120 | * Configure and load pinned entry into TLB slot 63. | 
|  | 121 | */ | 
|  | 122 |  | 
|  | 123 | lis	r3,KERNELBASE@h		/* Load the kernel virtual address */ | 
|  | 124 | ori	r3,r3,KERNELBASE@l | 
|  | 125 |  | 
|  | 126 | /* Kernel is at the base of RAM */ | 
|  | 127 | li r4, 0			/* Load the kernel physical address */ | 
|  | 128 |  | 
|  | 129 | /* Load the kernel PID = 0 */ | 
|  | 130 | li	r0,0 | 
|  | 131 | mtspr	SPRN_PID,r0 | 
|  | 132 | sync | 
|  | 133 |  | 
|  | 134 | /* Initialize MMUCR */ | 
|  | 135 | li	r5,0 | 
|  | 136 | mtspr	SPRN_MMUCR,r5 | 
|  | 137 | sync | 
|  | 138 |  | 
|  | 139 | /* pageid fields */ | 
|  | 140 | clrrwi	r3,r3,10		/* Mask off the effective page number */ | 
|  | 141 | ori	r3,r3,PPC44x_TLB_VALID | PPC44x_TLB_256M | 
|  | 142 |  | 
|  | 143 | /* xlat fields */ | 
|  | 144 | clrrwi	r4,r4,10		/* Mask off the real page number */ | 
|  | 145 | /* ERPN is 0 for first 4GB page */ | 
|  | 146 |  | 
|  | 147 | /* attrib fields */ | 
|  | 148 | /* Added guarded bit to protect against speculative loads/stores */ | 
|  | 149 | li	r5,0 | 
|  | 150 | ori	r5,r5,(PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G) | 
|  | 151 |  | 
|  | 152 | li      r0,63                    /* TLB slot 63 */ | 
|  | 153 |  | 
|  | 154 | tlbwe	r3,r0,PPC44x_TLB_PAGEID	/* Load the pageid fields */ | 
|  | 155 | tlbwe	r4,r0,PPC44x_TLB_XLAT	/* Load the translation fields */ | 
|  | 156 | tlbwe	r5,r0,PPC44x_TLB_ATTRIB	/* Load the attrib/access fields */ | 
|  | 157 |  | 
|  | 158 | /* Force context change */ | 
|  | 159 | mfmsr	r0 | 
|  | 160 | mtspr	SPRN_SRR1, r0 | 
|  | 161 | lis	r0,3f@h | 
|  | 162 | ori	r0,r0,3f@l | 
|  | 163 | mtspr	SPRN_SRR0,r0 | 
|  | 164 | sync | 
|  | 165 | rfi | 
|  | 166 |  | 
|  | 167 | /* If necessary, invalidate original entry we used */ | 
|  | 168 | 3:	cmpwi	r23,63 | 
|  | 169 | beq	4f | 
|  | 170 | li	r6,0 | 
|  | 171 | tlbwe   r6,r23,PPC44x_TLB_PAGEID | 
|  | 172 | isync | 
|  | 173 |  | 
|  | 174 | 4: | 
|  | 175 | #ifdef CONFIG_SERIAL_TEXT_DEBUG | 
|  | 176 | /* | 
|  | 177 | * Add temporary UART mapping for early debug. | 
|  | 178 | * We can map UART registers wherever we want as long as they don't | 
|  | 179 | * interfere with other system mappings (e.g. with pinned entries). | 
|  | 180 | * For an example of how we handle this - see ocotea.h.       --ebs | 
|  | 181 | */ | 
|  | 182 | /* pageid fields */ | 
|  | 183 | lis	r3,UART0_IO_BASE@h | 
|  | 184 | ori	r3,r3,PPC44x_TLB_VALID | PPC44x_TLB_4K | 
|  | 185 |  | 
|  | 186 | /* xlat fields */ | 
|  | 187 | lis	r4,UART0_PHYS_IO_BASE@h		/* RPN depends on SoC */ | 
|  | 188 | #ifndef CONFIG_440EP | 
|  | 189 | ori	r4,r4,0x0001		/* ERPN is 1 for second 4GB page */ | 
|  | 190 | #endif | 
|  | 191 |  | 
|  | 192 | /* attrib fields */ | 
|  | 193 | li	r5,0 | 
|  | 194 | ori	r5,r5,(PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_I | PPC44x_TLB_G) | 
|  | 195 |  | 
|  | 196 | li      r0,0                    /* TLB slot 0 */ | 
|  | 197 |  | 
|  | 198 | tlbwe	r3,r0,PPC44x_TLB_PAGEID	/* Load the pageid fields */ | 
|  | 199 | tlbwe	r4,r0,PPC44x_TLB_XLAT	/* Load the translation fields */ | 
|  | 200 | tlbwe	r5,r0,PPC44x_TLB_ATTRIB	/* Load the attrib/access fields */ | 
|  | 201 |  | 
|  | 202 | /* Force context change */ | 
|  | 203 | isync | 
|  | 204 | #endif /* CONFIG_SERIAL_TEXT_DEBUG */ | 
|  | 205 |  | 
|  | 206 | /* Establish the interrupt vector offsets */ | 
|  | 207 | SET_IVOR(0,  CriticalInput); | 
|  | 208 | SET_IVOR(1,  MachineCheck); | 
|  | 209 | SET_IVOR(2,  DataStorage); | 
|  | 210 | SET_IVOR(3,  InstructionStorage); | 
|  | 211 | SET_IVOR(4,  ExternalInput); | 
|  | 212 | SET_IVOR(5,  Alignment); | 
|  | 213 | SET_IVOR(6,  Program); | 
|  | 214 | SET_IVOR(7,  FloatingPointUnavailable); | 
|  | 215 | SET_IVOR(8,  SystemCall); | 
|  | 216 | SET_IVOR(9,  AuxillaryProcessorUnavailable); | 
|  | 217 | SET_IVOR(10, Decrementer); | 
|  | 218 | SET_IVOR(11, FixedIntervalTimer); | 
|  | 219 | SET_IVOR(12, WatchdogTimer); | 
|  | 220 | SET_IVOR(13, DataTLBError); | 
|  | 221 | SET_IVOR(14, InstructionTLBError); | 
|  | 222 | SET_IVOR(15, Debug); | 
|  | 223 |  | 
|  | 224 | /* Establish the interrupt vector base */ | 
|  | 225 | lis	r4,interrupt_base@h	/* IVPR only uses the high 16-bits */ | 
|  | 226 | mtspr	SPRN_IVPR,r4 | 
|  | 227 |  | 
|  | 228 | #ifdef CONFIG_440EP | 
|  | 229 | /* Clear DAPUIB flag in CCR0 (enable APU between CPU and FPU) */ | 
|  | 230 | mfspr	r2,SPRN_CCR0 | 
|  | 231 | lis	r3,0xffef | 
|  | 232 | ori	r3,r3,0xffff | 
|  | 233 | and	r2,r2,r3 | 
|  | 234 | mtspr	SPRN_CCR0,r2 | 
|  | 235 | isync | 
|  | 236 | #endif | 
|  | 237 |  | 
|  | 238 | /* | 
|  | 239 | * This is where the main kernel code starts. | 
|  | 240 | */ | 
|  | 241 |  | 
|  | 242 | /* ptr to current */ | 
|  | 243 | lis	r2,init_task@h | 
|  | 244 | ori	r2,r2,init_task@l | 
|  | 245 |  | 
|  | 246 | /* ptr to current thread */ | 
|  | 247 | addi	r4,r2,THREAD	/* init task's THREAD */ | 
|  | 248 | mtspr	SPRN_SPRG3,r4 | 
|  | 249 |  | 
|  | 250 | /* stack */ | 
|  | 251 | lis	r1,init_thread_union@h | 
|  | 252 | ori	r1,r1,init_thread_union@l | 
|  | 253 | li	r0,0 | 
|  | 254 | stwu	r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1) | 
|  | 255 |  | 
|  | 256 | bl	early_init | 
|  | 257 |  | 
|  | 258 | /* | 
|  | 259 | * Decide what sort of machine this is and initialize the MMU. | 
|  | 260 | */ | 
|  | 261 | mr	r3,r31 | 
|  | 262 | mr	r4,r30 | 
|  | 263 | mr	r5,r29 | 
|  | 264 | mr	r6,r28 | 
|  | 265 | mr	r7,r27 | 
|  | 266 | bl	machine_init | 
|  | 267 | bl	MMU_init | 
|  | 268 |  | 
|  | 269 | /* Setup PTE pointers for the Abatron bdiGDB */ | 
|  | 270 | lis	r6, swapper_pg_dir@h | 
|  | 271 | ori	r6, r6, swapper_pg_dir@l | 
|  | 272 | lis	r5, abatron_pteptrs@h | 
|  | 273 | ori	r5, r5, abatron_pteptrs@l | 
|  | 274 | lis	r4, KERNELBASE@h | 
|  | 275 | ori	r4, r4, KERNELBASE@l | 
|  | 276 | stw	r5, 0(r4)	/* Save abatron_pteptrs at a fixed location */ | 
|  | 277 | stw	r6, 0(r5) | 
|  | 278 |  | 
|  | 279 | /* Let's move on */ | 
|  | 280 | lis	r4,start_kernel@h | 
|  | 281 | ori	r4,r4,start_kernel@l | 
|  | 282 | lis	r3,MSR_KERNEL@h | 
|  | 283 | ori	r3,r3,MSR_KERNEL@l | 
|  | 284 | mtspr	SPRN_SRR0,r4 | 
|  | 285 | mtspr	SPRN_SRR1,r3 | 
|  | 286 | rfi			/* change context and jump to start_kernel */ | 
|  | 287 |  | 
|  | 288 | /* | 
|  | 289 | * Interrupt vector entry code | 
|  | 290 | * | 
|  | 291 | * The Book E MMUs are always on so we don't need to handle | 
|  | 292 | * interrupts in real mode as with previous PPC processors. In | 
|  | 293 | * this case we handle interrupts in the kernel virtual address | 
|  | 294 | * space. | 
|  | 295 | * | 
|  | 296 | * Interrupt vectors are dynamically placed relative to the | 
|  | 297 | * interrupt prefix as determined by the address of interrupt_base. | 
|  | 298 | * The interrupt vectors offsets are programmed using the labels | 
|  | 299 | * for each interrupt vector entry. | 
|  | 300 | * | 
|  | 301 | * Interrupt vectors must be aligned on a 16 byte boundary. | 
|  | 302 | * We align on a 32 byte cache line boundary for good measure. | 
|  | 303 | */ | 
|  | 304 |  | 
|  | 305 | interrupt_base: | 
|  | 306 | /* Critical Input Interrupt */ | 
| Stephen Rothwell | dc1c1ca | 2005-10-01 18:43:42 +1000 | [diff] [blame] | 307 | CRITICAL_EXCEPTION(0x0100, CriticalInput, unknown_exception) | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 308 |  | 
|  | 309 | /* Machine Check Interrupt */ | 
|  | 310 | #ifdef CONFIG_440A | 
| Stephen Rothwell | dc1c1ca | 2005-10-01 18:43:42 +1000 | [diff] [blame] | 311 | MCHECK_EXCEPTION(0x0200, MachineCheck, machine_check_exception) | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 312 | #else | 
| Stephen Rothwell | dc1c1ca | 2005-10-01 18:43:42 +1000 | [diff] [blame] | 313 | CRITICAL_EXCEPTION(0x0200, MachineCheck, machine_check_exception) | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 314 | #endif | 
|  | 315 |  | 
|  | 316 | /* Data Storage Interrupt */ | 
|  | 317 | START_EXCEPTION(DataStorage) | 
|  | 318 | mtspr	SPRN_SPRG0, r10		/* Save some working registers */ | 
|  | 319 | mtspr	SPRN_SPRG1, r11 | 
|  | 320 | mtspr	SPRN_SPRG4W, r12 | 
|  | 321 | mtspr	SPRN_SPRG5W, r13 | 
|  | 322 | mfcr	r11 | 
|  | 323 | mtspr	SPRN_SPRG7W, r11 | 
|  | 324 |  | 
|  | 325 | /* | 
|  | 326 | * Check if it was a store fault, if not then bail | 
|  | 327 | * because a user tried to access a kernel or | 
|  | 328 | * read-protected page.  Otherwise, get the | 
|  | 329 | * offending address and handle it. | 
|  | 330 | */ | 
|  | 331 | mfspr	r10, SPRN_ESR | 
|  | 332 | andis.	r10, r10, ESR_ST@h | 
|  | 333 | beq	2f | 
|  | 334 |  | 
|  | 335 | mfspr	r10, SPRN_DEAR		/* Get faulting address */ | 
|  | 336 |  | 
|  | 337 | /* If we are faulting a kernel address, we have to use the | 
|  | 338 | * kernel page tables. | 
|  | 339 | */ | 
|  | 340 | lis	r11, TASK_SIZE@h | 
|  | 341 | cmplw	r10, r11 | 
|  | 342 | blt+	3f | 
|  | 343 | lis	r11, swapper_pg_dir@h | 
|  | 344 | ori	r11, r11, swapper_pg_dir@l | 
|  | 345 |  | 
|  | 346 | mfspr   r12,SPRN_MMUCR | 
|  | 347 | rlwinm	r12,r12,0,0,23		/* Clear TID */ | 
|  | 348 |  | 
|  | 349 | b	4f | 
|  | 350 |  | 
|  | 351 | /* Get the PGD for the current thread */ | 
|  | 352 | 3: | 
|  | 353 | mfspr	r11,SPRN_SPRG3 | 
|  | 354 | lwz	r11,PGDIR(r11) | 
|  | 355 |  | 
|  | 356 | /* Load PID into MMUCR TID */ | 
|  | 357 | mfspr	r12,SPRN_MMUCR		/* Get MMUCR */ | 
|  | 358 | mfspr   r13,SPRN_PID		/* Get PID */ | 
|  | 359 | rlwimi	r12,r13,0,24,31		/* Set TID */ | 
|  | 360 |  | 
|  | 361 | 4: | 
|  | 362 | mtspr   SPRN_MMUCR,r12 | 
|  | 363 |  | 
|  | 364 | rlwinm  r12, r10, 13, 19, 29    /* Compute pgdir/pmd offset */ | 
|  | 365 | lwzx    r11, r12, r11           /* Get pgd/pmd entry */ | 
|  | 366 | rlwinm. r12, r11, 0, 0, 20      /* Extract pt base address */ | 
|  | 367 | beq     2f                      /* Bail if no table */ | 
|  | 368 |  | 
|  | 369 | rlwimi  r12, r10, 23, 20, 28    /* Compute pte address */ | 
|  | 370 | lwz     r11, 4(r12)             /* Get pte entry */ | 
|  | 371 |  | 
|  | 372 | andi.	r13, r11, _PAGE_RW	/* Is it writeable? */ | 
|  | 373 | beq	2f			/* Bail if not */ | 
|  | 374 |  | 
|  | 375 | /* Update 'changed'. | 
|  | 376 | */ | 
|  | 377 | ori	r11, r11, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE | 
|  | 378 | stw	r11, 4(r12)		/* Update Linux page table */ | 
|  | 379 |  | 
|  | 380 | li	r13, PPC44x_TLB_SR@l	/* Set SR */ | 
|  | 381 | rlwimi	r13, r11, 29, 29, 29	/* SX = _PAGE_HWEXEC */ | 
|  | 382 | rlwimi	r13, r11, 0, 30, 30	/* SW = _PAGE_RW */ | 
|  | 383 | rlwimi	r13, r11, 29, 28, 28	/* UR = _PAGE_USER */ | 
|  | 384 | rlwimi	r12, r11, 31, 26, 26	/* (_PAGE_USER>>1)->r12 */ | 
|  | 385 | rlwimi	r12, r11, 29, 30, 30	/* (_PAGE_USER>>3)->r12 */ | 
|  | 386 | and	r12, r12, r11		/* HWEXEC/RW & USER */ | 
|  | 387 | rlwimi	r13, r12, 0, 26, 26	/* UX = HWEXEC & USER */ | 
|  | 388 | rlwimi	r13, r12, 3, 27, 27	/* UW = RW & USER */ | 
|  | 389 |  | 
|  | 390 | rlwimi	r11,r13,0,26,31		/* Insert static perms */ | 
|  | 391 |  | 
|  | 392 | rlwinm	r11,r11,0,20,15		/* Clear U0-U3 */ | 
|  | 393 |  | 
|  | 394 | /* find the TLB index that caused the fault.  It has to be here. */ | 
|  | 395 | tlbsx	r10, 0, r10 | 
|  | 396 |  | 
|  | 397 | tlbwe	r11, r10, PPC44x_TLB_ATTRIB	/* Write ATTRIB */ | 
|  | 398 |  | 
|  | 399 | /* Done...restore registers and get out of here. | 
|  | 400 | */ | 
|  | 401 | mfspr	r11, SPRN_SPRG7R | 
|  | 402 | mtcr	r11 | 
|  | 403 | mfspr	r13, SPRN_SPRG5R | 
|  | 404 | mfspr	r12, SPRN_SPRG4R | 
|  | 405 |  | 
|  | 406 | mfspr	r11, SPRN_SPRG1 | 
|  | 407 | mfspr	r10, SPRN_SPRG0 | 
|  | 408 | rfi			/* Force context change */ | 
|  | 409 |  | 
|  | 410 | 2: | 
|  | 411 | /* | 
|  | 412 | * The bailout.  Restore registers to pre-exception conditions | 
|  | 413 | * and call the heavyweights to help us out. | 
|  | 414 | */ | 
|  | 415 | mfspr	r11, SPRN_SPRG7R | 
|  | 416 | mtcr	r11 | 
|  | 417 | mfspr	r13, SPRN_SPRG5R | 
|  | 418 | mfspr	r12, SPRN_SPRG4R | 
|  | 419 |  | 
|  | 420 | mfspr	r11, SPRN_SPRG1 | 
|  | 421 | mfspr	r10, SPRN_SPRG0 | 
|  | 422 | b	data_access | 
|  | 423 |  | 
|  | 424 | /* Instruction Storage Interrupt */ | 
|  | 425 | INSTRUCTION_STORAGE_EXCEPTION | 
|  | 426 |  | 
|  | 427 | /* External Input Interrupt */ | 
|  | 428 | EXCEPTION(0x0500, ExternalInput, do_IRQ, EXC_XFER_LITE) | 
|  | 429 |  | 
|  | 430 | /* Alignment Interrupt */ | 
|  | 431 | ALIGNMENT_EXCEPTION | 
|  | 432 |  | 
|  | 433 | /* Program Interrupt */ | 
|  | 434 | PROGRAM_EXCEPTION | 
|  | 435 |  | 
|  | 436 | /* Floating Point Unavailable Interrupt */ | 
|  | 437 | #ifdef CONFIG_PPC_FPU | 
|  | 438 | FP_UNAVAILABLE_EXCEPTION | 
|  | 439 | #else | 
| Stephen Rothwell | dc1c1ca | 2005-10-01 18:43:42 +1000 | [diff] [blame] | 440 | EXCEPTION(0x2010, FloatingPointUnavailable, unknown_exception, EXC_XFER_EE) | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 441 | #endif | 
|  | 442 |  | 
|  | 443 | /* System Call Interrupt */ | 
|  | 444 | START_EXCEPTION(SystemCall) | 
|  | 445 | NORMAL_EXCEPTION_PROLOG | 
|  | 446 | EXC_XFER_EE_LITE(0x0c00, DoSyscall) | 
|  | 447 |  | 
|  | 448 | /* Auxillary Processor Unavailable Interrupt */ | 
| Stephen Rothwell | dc1c1ca | 2005-10-01 18:43:42 +1000 | [diff] [blame] | 449 | EXCEPTION(0x2020, AuxillaryProcessorUnavailable, unknown_exception, EXC_XFER_EE) | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 450 |  | 
|  | 451 | /* Decrementer Interrupt */ | 
|  | 452 | DECREMENTER_EXCEPTION | 
|  | 453 |  | 
|  | 454 | /* Fixed Internal Timer Interrupt */ | 
|  | 455 | /* TODO: Add FIT support */ | 
| Stephen Rothwell | dc1c1ca | 2005-10-01 18:43:42 +1000 | [diff] [blame] | 456 | EXCEPTION(0x1010, FixedIntervalTimer, unknown_exception, EXC_XFER_EE) | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 457 |  | 
|  | 458 | /* Watchdog Timer Interrupt */ | 
|  | 459 | /* TODO: Add watchdog support */ | 
|  | 460 | #ifdef CONFIG_BOOKE_WDT | 
|  | 461 | CRITICAL_EXCEPTION(0x1020, WatchdogTimer, WatchdogException) | 
|  | 462 | #else | 
| Stephen Rothwell | dc1c1ca | 2005-10-01 18:43:42 +1000 | [diff] [blame] | 463 | CRITICAL_EXCEPTION(0x1020, WatchdogTimer, unknown_exception) | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 464 | #endif | 
|  | 465 |  | 
|  | 466 | /* Data TLB Error Interrupt */ | 
|  | 467 | START_EXCEPTION(DataTLBError) | 
|  | 468 | mtspr	SPRN_SPRG0, r10		/* Save some working registers */ | 
|  | 469 | mtspr	SPRN_SPRG1, r11 | 
|  | 470 | mtspr	SPRN_SPRG4W, r12 | 
|  | 471 | mtspr	SPRN_SPRG5W, r13 | 
|  | 472 | mfcr	r11 | 
|  | 473 | mtspr	SPRN_SPRG7W, r11 | 
|  | 474 | mfspr	r10, SPRN_DEAR		/* Get faulting address */ | 
|  | 475 |  | 
|  | 476 | /* If we are faulting a kernel address, we have to use the | 
|  | 477 | * kernel page tables. | 
|  | 478 | */ | 
|  | 479 | lis	r11, TASK_SIZE@h | 
|  | 480 | cmplw	r10, r11 | 
|  | 481 | blt+	3f | 
|  | 482 | lis	r11, swapper_pg_dir@h | 
|  | 483 | ori	r11, r11, swapper_pg_dir@l | 
|  | 484 |  | 
|  | 485 | mfspr	r12,SPRN_MMUCR | 
|  | 486 | rlwinm	r12,r12,0,0,23		/* Clear TID */ | 
|  | 487 |  | 
|  | 488 | b	4f | 
|  | 489 |  | 
|  | 490 | /* Get the PGD for the current thread */ | 
|  | 491 | 3: | 
|  | 492 | mfspr	r11,SPRN_SPRG3 | 
|  | 493 | lwz	r11,PGDIR(r11) | 
|  | 494 |  | 
|  | 495 | /* Load PID into MMUCR TID */ | 
|  | 496 | mfspr	r12,SPRN_MMUCR | 
|  | 497 | mfspr   r13,SPRN_PID		/* Get PID */ | 
|  | 498 | rlwimi	r12,r13,0,24,31		/* Set TID */ | 
|  | 499 |  | 
|  | 500 | 4: | 
|  | 501 | mtspr	SPRN_MMUCR,r12 | 
|  | 502 |  | 
|  | 503 | rlwinm 	r12, r10, 13, 19, 29	/* Compute pgdir/pmd offset */ | 
|  | 504 | lwzx	r11, r12, r11		/* Get pgd/pmd entry */ | 
|  | 505 | rlwinm.	r12, r11, 0, 0, 20	/* Extract pt base address */ | 
|  | 506 | beq	2f			/* Bail if no table */ | 
|  | 507 |  | 
|  | 508 | rlwimi	r12, r10, 23, 20, 28	/* Compute pte address */ | 
|  | 509 | lwz	r11, 4(r12)		/* Get pte entry */ | 
|  | 510 | andi.	r13, r11, _PAGE_PRESENT	/* Is the page present? */ | 
|  | 511 | beq	2f			/* Bail if not present */ | 
|  | 512 |  | 
|  | 513 | ori	r11, r11, _PAGE_ACCESSED | 
|  | 514 | stw	r11, 4(r12) | 
|  | 515 |  | 
|  | 516 | /* Jump to common tlb load */ | 
|  | 517 | b	finish_tlb_load | 
|  | 518 |  | 
|  | 519 | 2: | 
|  | 520 | /* The bailout.  Restore registers to pre-exception conditions | 
|  | 521 | * and call the heavyweights to help us out. | 
|  | 522 | */ | 
|  | 523 | mfspr	r11, SPRN_SPRG7R | 
|  | 524 | mtcr	r11 | 
|  | 525 | mfspr	r13, SPRN_SPRG5R | 
|  | 526 | mfspr	r12, SPRN_SPRG4R | 
|  | 527 | mfspr	r11, SPRN_SPRG1 | 
|  | 528 | mfspr	r10, SPRN_SPRG0 | 
|  | 529 | b	data_access | 
|  | 530 |  | 
|  | 531 | /* Instruction TLB Error Interrupt */ | 
|  | 532 | /* | 
|  | 533 | * Nearly the same as above, except we get our | 
|  | 534 | * information from different registers and bailout | 
|  | 535 | * to a different point. | 
|  | 536 | */ | 
|  | 537 | START_EXCEPTION(InstructionTLBError) | 
|  | 538 | mtspr	SPRN_SPRG0, r10		/* Save some working registers */ | 
|  | 539 | mtspr	SPRN_SPRG1, r11 | 
|  | 540 | mtspr	SPRN_SPRG4W, r12 | 
|  | 541 | mtspr	SPRN_SPRG5W, r13 | 
|  | 542 | mfcr	r11 | 
|  | 543 | mtspr	SPRN_SPRG7W, r11 | 
|  | 544 | mfspr	r10, SPRN_SRR0		/* Get faulting address */ | 
|  | 545 |  | 
|  | 546 | /* If we are faulting a kernel address, we have to use the | 
|  | 547 | * kernel page tables. | 
|  | 548 | */ | 
|  | 549 | lis	r11, TASK_SIZE@h | 
|  | 550 | cmplw	r10, r11 | 
|  | 551 | blt+	3f | 
|  | 552 | lis	r11, swapper_pg_dir@h | 
|  | 553 | ori	r11, r11, swapper_pg_dir@l | 
|  | 554 |  | 
|  | 555 | mfspr	r12,SPRN_MMUCR | 
|  | 556 | rlwinm	r12,r12,0,0,23		/* Clear TID */ | 
|  | 557 |  | 
|  | 558 | b	4f | 
|  | 559 |  | 
|  | 560 | /* Get the PGD for the current thread */ | 
|  | 561 | 3: | 
|  | 562 | mfspr	r11,SPRN_SPRG3 | 
|  | 563 | lwz	r11,PGDIR(r11) | 
|  | 564 |  | 
|  | 565 | /* Load PID into MMUCR TID */ | 
|  | 566 | mfspr	r12,SPRN_MMUCR | 
|  | 567 | mfspr   r13,SPRN_PID		/* Get PID */ | 
|  | 568 | rlwimi	r12,r13,0,24,31		/* Set TID */ | 
|  | 569 |  | 
|  | 570 | 4: | 
|  | 571 | mtspr	SPRN_MMUCR,r12 | 
|  | 572 |  | 
|  | 573 | rlwinm	r12, r10, 13, 19, 29	/* Compute pgdir/pmd offset */ | 
|  | 574 | lwzx	r11, r12, r11		/* Get pgd/pmd entry */ | 
|  | 575 | rlwinm.	r12, r11, 0, 0, 20	/* Extract pt base address */ | 
|  | 576 | beq	2f			/* Bail if no table */ | 
|  | 577 |  | 
|  | 578 | rlwimi	r12, r10, 23, 20, 28	/* Compute pte address */ | 
|  | 579 | lwz	r11, 4(r12)		/* Get pte entry */ | 
|  | 580 | andi.	r13, r11, _PAGE_PRESENT	/* Is the page present? */ | 
|  | 581 | beq	2f			/* Bail if not present */ | 
|  | 582 |  | 
|  | 583 | ori	r11, r11, _PAGE_ACCESSED | 
|  | 584 | stw	r11, 4(r12) | 
|  | 585 |  | 
|  | 586 | /* Jump to common TLB load point */ | 
|  | 587 | b	finish_tlb_load | 
|  | 588 |  | 
|  | 589 | 2: | 
|  | 590 | /* The bailout.  Restore registers to pre-exception conditions | 
|  | 591 | * and call the heavyweights to help us out. | 
|  | 592 | */ | 
|  | 593 | mfspr	r11, SPRN_SPRG7R | 
|  | 594 | mtcr	r11 | 
|  | 595 | mfspr	r13, SPRN_SPRG5R | 
|  | 596 | mfspr	r12, SPRN_SPRG4R | 
|  | 597 | mfspr	r11, SPRN_SPRG1 | 
|  | 598 | mfspr	r10, SPRN_SPRG0 | 
|  | 599 | b	InstructionStorage | 
|  | 600 |  | 
|  | 601 | /* Debug Interrupt */ | 
|  | 602 | DEBUG_EXCEPTION | 
|  | 603 |  | 
|  | 604 | /* | 
|  | 605 | * Local functions | 
|  | 606 | */ | 
|  | 607 | /* | 
|  | 608 | * Data TLB exceptions will bail out to this point | 
|  | 609 | * if they can't resolve the lightweight TLB fault. | 
|  | 610 | */ | 
|  | 611 | data_access: | 
|  | 612 | NORMAL_EXCEPTION_PROLOG | 
|  | 613 | mfspr	r5,SPRN_ESR		/* Grab the ESR, save it, pass arg3 */ | 
|  | 614 | stw	r5,_ESR(r11) | 
|  | 615 | mfspr	r4,SPRN_DEAR		/* Grab the DEAR, save it, pass arg2 */ | 
|  | 616 | EXC_XFER_EE_LITE(0x0300, handle_page_fault) | 
|  | 617 |  | 
|  | 618 | /* | 
|  | 619 |  | 
|  | 620 | * Both the instruction and data TLB miss get to this | 
|  | 621 | * point to load the TLB. | 
|  | 622 | * 	r10 - EA of fault | 
|  | 623 | * 	r11 - available to use | 
|  | 624 | *	r12 - Pointer to the 64-bit PTE | 
|  | 625 | *	r13 - available to use | 
|  | 626 | *	MMUCR - loaded with proper value when we get here | 
|  | 627 | *	Upon exit, we reload everything and RFI. | 
|  | 628 | */ | 
|  | 629 | finish_tlb_load: | 
|  | 630 | /* | 
|  | 631 | * We set execute, because we don't have the granularity to | 
|  | 632 | * properly set this at the page level (Linux problem). | 
|  | 633 | * If shared is set, we cause a zero PID->TID load. | 
|  | 634 | * Many of these bits are software only.  Bits we don't set | 
|  | 635 | * here we (properly should) assume have the appropriate value. | 
|  | 636 | */ | 
|  | 637 |  | 
|  | 638 | /* Load the next available TLB index */ | 
|  | 639 | lis	r13, tlb_44x_index@ha | 
|  | 640 | lwz	r13, tlb_44x_index@l(r13) | 
|  | 641 | /* Load the TLB high watermark */ | 
|  | 642 | lis	r11, tlb_44x_hwater@ha | 
|  | 643 | lwz	r11, tlb_44x_hwater@l(r11) | 
|  | 644 |  | 
|  | 645 | /* Increment, rollover, and store TLB index */ | 
|  | 646 | addi	r13, r13, 1 | 
|  | 647 | cmpw	0, r13, r11			/* reserve entries */ | 
|  | 648 | ble	7f | 
|  | 649 | li	r13, 0 | 
|  | 650 | 7: | 
|  | 651 | /* Store the next available TLB index */ | 
|  | 652 | lis	r11, tlb_44x_index@ha | 
|  | 653 | stw	r13, tlb_44x_index@l(r11) | 
|  | 654 |  | 
|  | 655 | lwz	r11, 0(r12)			/* Get MS word of PTE */ | 
|  | 656 | lwz	r12, 4(r12)			/* Get LS word of PTE */ | 
|  | 657 | rlwimi	r11, r12, 0, 0 , 19		/* Insert RPN */ | 
|  | 658 | tlbwe	r11, r13, PPC44x_TLB_XLAT	/* Write XLAT */ | 
|  | 659 |  | 
|  | 660 | /* | 
|  | 661 | * Create PAGEID. This is the faulting address, | 
|  | 662 | * page size, and valid flag. | 
|  | 663 | */ | 
|  | 664 | li	r11, PPC44x_TLB_VALID | PPC44x_TLB_4K | 
|  | 665 | rlwimi	r10, r11, 0, 20, 31		/* Insert valid and page size */ | 
|  | 666 | tlbwe	r10, r13, PPC44x_TLB_PAGEID	/* Write PAGEID */ | 
|  | 667 |  | 
|  | 668 | li	r10, PPC44x_TLB_SR@l		/* Set SR */ | 
|  | 669 | rlwimi	r10, r12, 0, 30, 30		/* Set SW = _PAGE_RW */ | 
|  | 670 | rlwimi	r10, r12, 29, 29, 29		/* SX = _PAGE_HWEXEC */ | 
|  | 671 | rlwimi	r10, r12, 29, 28, 28		/* UR = _PAGE_USER */ | 
|  | 672 | rlwimi	r11, r12, 31, 26, 26		/* (_PAGE_USER>>1)->r12 */ | 
|  | 673 | and	r11, r12, r11			/* HWEXEC & USER */ | 
|  | 674 | rlwimi	r10, r11, 0, 26, 26		/* UX = HWEXEC & USER */ | 
|  | 675 |  | 
|  | 676 | rlwimi	r12, r10, 0, 26, 31		/* Insert static perms */ | 
|  | 677 | rlwinm	r12, r12, 0, 20, 15		/* Clear U0-U3 */ | 
|  | 678 | tlbwe	r12, r13, PPC44x_TLB_ATTRIB	/* Write ATTRIB */ | 
|  | 679 |  | 
|  | 680 | /* Done...restore registers and get out of here. | 
|  | 681 | */ | 
|  | 682 | mfspr	r11, SPRN_SPRG7R | 
|  | 683 | mtcr	r11 | 
|  | 684 | mfspr	r13, SPRN_SPRG5R | 
|  | 685 | mfspr	r12, SPRN_SPRG4R | 
|  | 686 | mfspr	r11, SPRN_SPRG1 | 
|  | 687 | mfspr	r10, SPRN_SPRG0 | 
|  | 688 | rfi					/* Force context change */ | 
|  | 689 |  | 
|  | 690 | /* | 
|  | 691 | * Global functions | 
|  | 692 | */ | 
|  | 693 |  | 
|  | 694 | /* | 
|  | 695 | * extern void giveup_altivec(struct task_struct *prev) | 
|  | 696 | * | 
|  | 697 | * The 44x core does not have an AltiVec unit. | 
|  | 698 | */ | 
|  | 699 | _GLOBAL(giveup_altivec) | 
|  | 700 | blr | 
|  | 701 |  | 
|  | 702 | /* | 
|  | 703 | * extern void giveup_fpu(struct task_struct *prev) | 
|  | 704 | * | 
|  | 705 | * The 44x core does not have an FPU. | 
|  | 706 | */ | 
|  | 707 | #ifndef CONFIG_PPC_FPU | 
|  | 708 | _GLOBAL(giveup_fpu) | 
|  | 709 | blr | 
|  | 710 | #endif | 
|  | 711 |  | 
|  | 712 | /* | 
|  | 713 | * extern void abort(void) | 
|  | 714 | * | 
|  | 715 | * At present, this routine just applies a system reset. | 
|  | 716 | */ | 
|  | 717 | _GLOBAL(abort) | 
|  | 718 | mfspr   r13,SPRN_DBCR0 | 
|  | 719 | oris    r13,r13,DBCR0_RST_SYSTEM@h | 
|  | 720 | mtspr   SPRN_DBCR0,r13 | 
|  | 721 |  | 
|  | 722 | _GLOBAL(set_context) | 
|  | 723 |  | 
|  | 724 | #ifdef CONFIG_BDI_SWITCH | 
|  | 725 | /* Context switch the PTE pointer for the Abatron BDI2000. | 
|  | 726 | * The PGDIR is the second parameter. | 
|  | 727 | */ | 
|  | 728 | lis	r5, abatron_pteptrs@h | 
|  | 729 | ori	r5, r5, abatron_pteptrs@l | 
|  | 730 | stw	r4, 0x4(r5) | 
|  | 731 | #endif | 
|  | 732 | mtspr	SPRN_PID,r3 | 
|  | 733 | isync			/* Force context change */ | 
|  | 734 | blr | 
|  | 735 |  | 
|  | 736 | /* | 
|  | 737 | * We put a few things here that have to be page-aligned. This stuff | 
|  | 738 | * goes at the beginning of the data segment, which is page-aligned. | 
|  | 739 | */ | 
|  | 740 | .data | 
| Kumar Gala | ea703ce | 2005-10-11 23:54:00 -0500 | [diff] [blame] | 741 | .align	12 | 
|  | 742 | .globl	sdata | 
|  | 743 | sdata: | 
|  | 744 | .globl	empty_zero_page | 
|  | 745 | empty_zero_page: | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 746 | .space	4096 | 
|  | 747 |  | 
|  | 748 | /* | 
|  | 749 | * To support >32-bit physical addresses, we use an 8KB pgdir. | 
|  | 750 | */ | 
| Kumar Gala | ea703ce | 2005-10-11 23:54:00 -0500 | [diff] [blame] | 751 | .globl	swapper_pg_dir | 
|  | 752 | swapper_pg_dir: | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 753 | .space	8192 | 
|  | 754 |  | 
|  | 755 | /* Reserved 4k for the critical exception stack & 4k for the machine | 
|  | 756 | * check stack per CPU for kernel mode exceptions */ | 
|  | 757 | .section .bss | 
|  | 758 | .align 12 | 
|  | 759 | exception_stack_bottom: | 
|  | 760 | .space	BOOKE_EXCEPTION_STACK_SIZE | 
| Kumar Gala | ea703ce | 2005-10-11 23:54:00 -0500 | [diff] [blame] | 761 | .globl	exception_stack_top | 
|  | 762 | exception_stack_top: | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 763 |  | 
|  | 764 | /* | 
|  | 765 | * This space gets a copy of optional info passed to us by the bootstrap | 
|  | 766 | * which is used to pass parameters into the kernel like root=/dev/sda1, etc. | 
|  | 767 | */ | 
| Kumar Gala | ea703ce | 2005-10-11 23:54:00 -0500 | [diff] [blame] | 768 | .globl	cmd_line | 
|  | 769 | cmd_line: | 
| Paul Mackerras | 14cf11a | 2005-09-26 16:04:21 +1000 | [diff] [blame] | 770 | .space	512 | 
|  | 771 |  | 
|  | 772 | /* | 
|  | 773 | * Room for two PTE pointers, usually the kernel and current user pointers | 
|  | 774 | * to their respective root page table. | 
|  | 775 | */ | 
|  | 776 | abatron_pteptrs: | 
|  | 777 | .space	8 |