| 1 | /* |
|---|
| 2 | * Authors: Frédéric Pétrot and Denis Hommais |
|---|
| 3 | * $Log: exception.s,v $ |
|---|
| 4 | * Revision 1.1 2005/03/02 12:37:22 buchmann |
|---|
| 5 | * Add "soft" directory. |
|---|
| 6 | * It allows to generate MIPS binary using cross compiler. |
|---|
| 7 | * |
|---|
| 8 | * Revision 1.2 2003/07/18 16:21:30 fred |
|---|
| 9 | * Now interprets the instruction that causes a DBE in order to print the |
|---|
| 10 | * address responsible for the failure. |
|---|
| 11 | * Cleaned up the code a little (mainly comments :) |
|---|
| 12 | * |
|---|
| 13 | * Revision 1.1 2003/03/10 13:39:07 fred |
|---|
| 14 | * Adding the hardware dependent stuff to smoothly handle multi |
|---|
| 15 | * architecture compilation of this kernel |
|---|
| 16 | * |
|---|
| 17 | * Revision 1.1.1.1 2002/02/28 12:58:55 disydent |
|---|
| 18 | * Creation of Disydent CVS Tree |
|---|
| 19 | * |
|---|
| 20 | * Revision 1.1.1.1 2001/11/19 16:55:37 pwet |
|---|
| 21 | * Changing the CVS tree structure of disydent |
|---|
| 22 | * |
|---|
| 23 | * Revision 1.3 2001/11/14 12:08:45 fred |
|---|
| 24 | * Making the ldscript being generated for correct path accesses |
|---|
| 25 | * Removing the syscall entry of the exception handler for now |
|---|
| 26 | * |
|---|
| 27 | * Revision 1.2 2001/11/09 14:51:03 fred |
|---|
| 28 | * Remove the reset executable and make the assembler be piped through |
|---|
| 29 | * cpp. |
|---|
| 30 | * The ldscript is not usable. I have to think before going on |
|---|
| 31 | * |
|---|
| 32 | * $Id: exception.S,v 1.1 2003/03/10 13:39:07 fred Exp |
|---|
| 33 | * Note : we do not check BD at any time yet! Beware, ... |
|---|
| 34 | * Rev 0 : minimal handler to support syscall for system calls |
|---|
| 35 | * implementation |
|---|
| 36 | * Rev 1 : added correct exception/interruption detection and messages |
|---|
| 37 | * Rev 2 : added mult and div interpretation in illegal instruction |
|---|
| 38 | * Rev 3 : Makes it compliant with multi-thread/multi-processor |
|---|
| 39 | * Stack is thread stack now (it must at least for preemptive |
|---|
| 40 | * scheduling stuffs |
|---|
| 41 | */ |
|---|
| 42 | .rdata |
|---|
| 43 | .align 2 |
|---|
| 44 | msg : .ascii "An exception occurred with the following cause :\n\000" |
|---|
| 45 | |
|---|
| 46 | jmptable: |
|---|
| 47 | .word Int |
|---|
| 48 | .word Uimp |
|---|
| 49 | .word Uimp |
|---|
| 50 | .word Uimp |
|---|
| 51 | .word AdEL |
|---|
| 52 | .word AdES |
|---|
| 53 | .word IBE |
|---|
| 54 | .word DBE |
|---|
| 55 | .word Sys |
|---|
| 56 | .word Bp |
|---|
| 57 | .word RI |
|---|
| 58 | .word CpU |
|---|
| 59 | .word Ovf |
|---|
| 60 | .word Ukn |
|---|
| 61 | .word Ukn |
|---|
| 62 | |
|---|
| 63 | .text |
|---|
| 64 | .align 2 |
|---|
| 65 | .ent excep |
|---|
| 66 | excep: |
|---|
| 67 | /* |
|---|
| 68 | * Here I save the whole C compatible register context, which is a |
|---|
| 69 | * waste of time if only assembly functions are to be used |
|---|
| 70 | * A more optimized way to do things would be to check for critical |
|---|
| 71 | * events (typically external interrupts) and do the saving only |
|---|
| 72 | * when an other events took place |
|---|
| 73 | * Note that this implies to check which registers to save according |
|---|
| 74 | * to the interrupt handling routines, that are usually user defined! |
|---|
| 75 | * |
|---|
| 76 | * Reminder: $26 and $27 are kernel registers that need not be saved |
|---|
| 77 | * This shall be executed in critical section since the stack pointer |
|---|
| 78 | * is not properly set until the subu! |
|---|
| 79 | */ |
|---|
| 80 | .set noreorder |
|---|
| 81 | /* Shall not touch $1 until it is saved */ |
|---|
| 82 | .set noat |
|---|
| 83 | |
|---|
| 84 | subu $29, $29, 26*4 /* Load kernel stack addr + stack update */ |
|---|
| 85 | sw $1, 1*4($29) /* save all temporaries*/ |
|---|
| 86 | .set at |
|---|
| 87 | sw $2, 2*4($29) /* others are saved when calling procedures*/ |
|---|
| 88 | sw $3, 3*4($29) |
|---|
| 89 | sw $4, 4*4($29) |
|---|
| 90 | sw $5, 5*4($29) |
|---|
| 91 | sw $6, 6*4($29) |
|---|
| 92 | sw $7, 7*4($29) |
|---|
| 93 | sw $8, 8*4($29) |
|---|
| 94 | sw $9, 9*4($29) |
|---|
| 95 | sw $10, 10*4($29) |
|---|
| 96 | sw $11, 11*4($29) |
|---|
| 97 | sw $12, 12*4($29) |
|---|
| 98 | sw $13, 13*4($29) |
|---|
| 99 | sw $14, 14*4($29) |
|---|
| 100 | sw $15, 15*4($29) |
|---|
| 101 | sw $24, 16*4($29) |
|---|
| 102 | sw $25, 17*4($29) |
|---|
| 103 | mfc0 $26, $14 /* load EPC into k0 (two cycles befor use)*/ |
|---|
| 104 | sw $29, 18*4($29) |
|---|
| 105 | sw $30, 19*4($29) |
|---|
| 106 | sw $31, 20*4($29) |
|---|
| 107 | sw $26, 21*4($29) /* and store it to be recallable*/ |
|---|
| 108 | mfhi $26 |
|---|
| 109 | sw $26, 22*4($29) |
|---|
| 110 | mflo $26 |
|---|
| 111 | sw $26, 23*4($29) |
|---|
| 112 | |
|---|
| 113 | /* Check the cause of the arrival in this code */ |
|---|
| 114 | |
|---|
| 115 | mfc0 $27, $13 /* load CAUSE into $27*/ |
|---|
| 116 | nop |
|---|
| 117 | andi $27, $27, 0x3c /* get the exception code (ExcCode)*/ |
|---|
| 118 | beqz $27, Int /* Test if it is an interrupt. If so*/ |
|---|
| 119 | sw $0, 24*4($29) /* return address is epc + 0 */ |
|---|
| 120 | /* it's faster.*/ |
|---|
| 121 | la $26, jmptable /* load the base of the jump table*/ |
|---|
| 122 | /* get the exception code (ExcCode)*/ |
|---|
| 123 | addu $26, $26, $27 /* load the offset in the table*/ |
|---|
| 124 | lw $27, ($26) /* load the pointed value*/ |
|---|
| 125 | li $26, 4 |
|---|
| 126 | j $27 /* and jump there*/ |
|---|
| 127 | sw $26, 24*4($29) /* faulty insn is at epc - 4 */ |
|---|
| 128 | nop /* delayed slot too*/ |
|---|
| 129 | |
|---|
| 130 | /* |
|---|
| 131 | * External interupts: |
|---|
| 132 | * The meaning of these interupts is for sure system dependent, so |
|---|
| 133 | * this routine must be adapted to each system particularities |
|---|
| 134 | * |
|---|
| 135 | * Right now we just find out what interrupt lead us here, and print |
|---|
| 136 | * a small message about it. |
|---|
| 137 | */ |
|---|
| 138 | .rdata |
|---|
| 139 | .align 2 |
|---|
| 140 | .text |
|---|
| 141 | Int : |
|---|
| 142 | .set noat |
|---|
| 143 | mfc0 $4, $13 /* load CAUSE into $27*/ |
|---|
| 144 | mfc0 $26, $12 /* load STATUS into $26*/ |
|---|
| 145 | andi $4, $4, 0xFF00 /* mask CAUSE to get the ITs only*/ |
|---|
| 146 | and $4, $4, $26 /* keep only the enabled ITs*/ |
|---|
| 147 | srl $4, $4, 8 /* and put them in the LSBees*/ |
|---|
| 148 | beqz $4, _endit /* If none, then what are we doing here? */ |
|---|
| 149 | nop |
|---|
| 150 | |
|---|
| 151 | .extern SwitchOnIt |
|---|
| 152 | |
|---|
| 153 | la $26, SwitchOnIt |
|---|
| 154 | jal $26 |
|---|
| 155 | nop |
|---|
| 156 | |
|---|
| 157 | _endit: |
|---|
| 158 | j _return /* go back to user*/ |
|---|
| 159 | nop |
|---|
| 160 | .set at |
|---|
| 161 | |
|---|
| 162 | /* |
|---|
| 163 | * Clearly fatal exceptions as far as we're concerned, ... |
|---|
| 164 | */ |
|---|
| 165 | .rdata |
|---|
| 166 | .align 2 |
|---|
| 167 | m2 : .ascii "Unimplemented TLB Exception!\n\000" |
|---|
| 168 | m2k : .ascii "Reserved Exception!\n\000" |
|---|
| 169 | m2o : .ascii "Integer Arithmetic Overflow!\n\000" |
|---|
| 170 | |
|---|
| 171 | .text |
|---|
| 172 | .align 2 |
|---|
| 173 | .set reorder |
|---|
| 174 | Uimp : la $4, m2 /* load the appropriate message,...*/ |
|---|
| 175 | j U /* */ |
|---|
| 176 | Ukn : la $4, m2k /* */ |
|---|
| 177 | j U /* */ |
|---|
| 178 | Ovf : la $4, m2o /* */ |
|---|
| 179 | U : la $2, uputs |
|---|
| 180 | jal $2 /* and print it!*/ |
|---|
| 181 | j _exit /* */ |
|---|
| 182 | .set noreorder |
|---|
| 183 | |
|---|
| 184 | /* |
|---|
| 185 | * Illegal instructions: |
|---|
| 186 | * we interpret mult/div and lwl/lwr here, and exit on an other code |
|---|
| 187 | * Before modifing this code think that NO REGISTER CONTENT should |
|---|
| 188 | * be touched before the end of the register loading routine |
|---|
| 189 | * This explains why some code seems duplicated (several mfc0) |
|---|
| 190 | */ |
|---|
| 191 | .rdata |
|---|
| 192 | m2i : .ascii "Illegal or Reserved Instruction!\n\000" |
|---|
| 193 | .text |
|---|
| 194 | .set noat |
|---|
| 195 | RI : mfc0 $26, $14 /* load EPC into $26*/ |
|---|
| 196 | nop /* */ |
|---|
| 197 | nop /* */ |
|---|
| 198 | lw $26, ($26) /* $26 contains the faulty ins*/ |
|---|
| 199 | lui $27, 0xFC00 /* mask for special*/ |
|---|
| 200 | and $27, $26, $27 /* */ |
|---|
| 201 | bne $27, $0, Ilg /* this is not a special trap*/ |
|---|
| 202 | |
|---|
| 203 | /* |
|---|
| 204 | * I do not handle the mult/div trap, as we have added the |
|---|
| 205 | * instructions in our R3000 model |
|---|
| 206 | */ |
|---|
| 207 | nop |
|---|
| 208 | |
|---|
| 209 | Ilg: la $4, m2i |
|---|
| 210 | la $2, uputs |
|---|
| 211 | jal $2 /* we got an illegal ins, so we quit*/ |
|---|
| 212 | nop |
|---|
| 213 | j _exit /* */ |
|---|
| 214 | nop |
|---|
| 215 | |
|---|
| 216 | /* |
|---|
| 217 | * Unaligned access: |
|---|
| 218 | * Quite fatal, I'm afraid |
|---|
| 219 | */ |
|---|
| 220 | .rdata |
|---|
| 221 | .align 2 |
|---|
| 222 | m3 : .ascii "Unaligned/unallowed Access while %s address 0x%x at pc 0x%x!\n\000" |
|---|
| 223 | m3l : .ascii "reading\000" |
|---|
| 224 | m3e : .ascii "writing\000" |
|---|
| 225 | |
|---|
| 226 | .text |
|---|
| 227 | .align 2 |
|---|
| 228 | .set reorder |
|---|
| 229 | AdEL : la $5, m3l |
|---|
| 230 | j AdE |
|---|
| 231 | AdES : la $5, m3e |
|---|
| 232 | AdE : la $4, m3 |
|---|
| 233 | .set noreorder |
|---|
| 234 | mfc0 $6, $8 /* load BAR into thrid arg ($6)*/ |
|---|
| 235 | mfc0 $7, $14 /* load EPC into fourth arg ($7)*/ |
|---|
| 236 | la $2, uputs |
|---|
| 237 | jal $2 /* print all that stuff*/ |
|---|
| 238 | nop |
|---|
| 239 | j _exit |
|---|
| 240 | nop |
|---|
| 241 | |
|---|
| 242 | /* |
|---|
| 243 | * Synchronous bus error |
|---|
| 244 | */ |
|---|
| 245 | .rdata |
|---|
| 246 | .align 2 |
|---|
| 247 | m4i: .ascii "Bus error while loading the instrunction at pc 0x%x!\n\000" |
|---|
| 248 | m4d: .ascii "Bus error while loading the datum at pc 0x%x for addr 0x%x!\n\000" |
|---|
| 249 | .data |
|---|
| 250 | .text |
|---|
| 251 | |
|---|
| 252 | .align 2 |
|---|
| 253 | .set reorder |
|---|
| 254 | IBE : mfc0 $5, $14 /* load EPC */ |
|---|
| 255 | la $4, m4i |
|---|
| 256 | j BE |
|---|
| 257 | .set noreorder |
|---|
| 258 | DBE : la $4, m4d |
|---|
| 259 | /* |
|---|
| 260 | * The instruction at EPC must be interpreted to get the |
|---|
| 261 | * address that caused the fault. |
|---|
| 262 | * The register value must be reloaded from the stack, and |
|---|
| 263 | * since the storage of the registers is quite exotic right |
|---|
| 264 | * now, this will work only for registers 1 to 15! |
|---|
| 265 | */ |
|---|
| 266 | mfc0 $5, $14 /* load EPC */ |
|---|
| 267 | lw $6, 0($5) /* load faulty insn */ |
|---|
| 268 | andi $8, $6, 0xFFFF /* get the imm field of it */ |
|---|
| 269 | srl $9, $6, 19 |
|---|
| 270 | andi $9, $9, 0x7c /* get the rs register index times 4 */ |
|---|
| 271 | add $9, $29, $9 /* compute the stack index of this register */ |
|---|
| 272 | lw $9, 0($9) |
|---|
| 273 | addu $6, $9, $8 /* get the address as computed */ |
|---|
| 274 | |
|---|
| 275 | BE : la $2, uputs |
|---|
| 276 | jal $2 /* print all that stuff*/ |
|---|
| 277 | nop |
|---|
| 278 | j _exit |
|---|
| 279 | nop |
|---|
| 280 | |
|---|
| 281 | /* |
|---|
| 282 | * Syscall: simple and straightforward. |
|---|
| 283 | * Even elegant : nothing's done at all anymore |
|---|
| 284 | */ |
|---|
| 285 | .rdata |
|---|
| 286 | .align 2 |
|---|
| 287 | m5 : .ascii "Syscall : no actions performed anymore\000" |
|---|
| 288 | .text |
|---|
| 289 | .align 2 |
|---|
| 290 | Sys : |
|---|
| 291 | la $4, m5 |
|---|
| 292 | la $2, uputs |
|---|
| 293 | jalr $2 |
|---|
| 294 | nop |
|---|
| 295 | j _ret_sys /* get back to caller */ |
|---|
| 296 | nop |
|---|
| 297 | #endif |
|---|
| 298 | |
|---|
| 299 | /* |
|---|
| 300 | * |
|---|
| 301 | * We just indicate we got a breakpoint |
|---|
| 302 | * This shall be filled on a system per system basis |
|---|
| 303 | * |
|---|
| 304 | */ |
|---|
| 305 | .rdata |
|---|
| 306 | .align 2 |
|---|
| 307 | m6 : .ascii "Breakpoint of code %d!\n\000" |
|---|
| 308 | |
|---|
| 309 | .text |
|---|
| 310 | .align 2 |
|---|
| 311 | Bp : la $4, m6 |
|---|
| 312 | mfc0 $3, $14 /* load EPC*/ |
|---|
| 313 | nop |
|---|
| 314 | nop |
|---|
| 315 | lw $5, ($3) /* load the 'break' instruction*/ |
|---|
| 316 | li $3, 0xF /* filled delayed slot (load a mask)*/ |
|---|
| 317 | srl $5, $5, 16 /* shift it*/ |
|---|
| 318 | and $5, $5, $3 /* and get the code from the insn*/ |
|---|
| 319 | la $2, uputs |
|---|
| 320 | jal $2 /* print the string*/ |
|---|
| 321 | nop |
|---|
| 322 | j _return |
|---|
| 323 | nop |
|---|
| 324 | |
|---|
| 325 | /* |
|---|
| 326 | * |
|---|
| 327 | * Kernel/user violation, or coprocessor unusable. |
|---|
| 328 | * that's pretty fatal, ain't it ? |
|---|
| 329 | * |
|---|
| 330 | */ |
|---|
| 331 | .rdata |
|---|
| 332 | .align 2 |
|---|
| 333 | m7 : .ascii "Unusable Coprocessor %d!\n\000" |
|---|
| 334 | m70: .ascii "Trying to use a kernel instruction in user mode!\n\000" |
|---|
| 335 | |
|---|
| 336 | .text |
|---|
| 337 | .align 2 |
|---|
| 338 | CpU : mfc0 $3, $13 /* load CAUSE*/ |
|---|
| 339 | nop |
|---|
| 340 | li $2, 0x30000000/* */ |
|---|
| 341 | and $2, $2, $3 /* mask to get the CE bits */ |
|---|
| 342 | bnez $2, cun /* not zero, so it is a CUN*/ |
|---|
| 343 | nop /* cannot put a 'la' here,...*/ |
|---|
| 344 | .set reorder |
|---|
| 345 | la $4, m70 /* here, it is a kernel violation*/ |
|---|
| 346 | j C |
|---|
| 347 | .set noreorder |
|---|
| 348 | cun: la $4, m7 |
|---|
| 349 | srl $5, $2, 28 /* LSByte contains now the copro number*/ |
|---|
| 350 | C: la $2, uputs |
|---|
| 351 | jal $2 |
|---|
| 352 | nop |
|---|
| 353 | j _exit |
|---|
| 354 | nop |
|---|
| 355 | |
|---|
| 356 | _exit: li $2, 100000 |
|---|
| 357 | _exit_l: bne $2, $0, _exit_l |
|---|
| 358 | addiu $2, $2, -1 |
|---|
| 359 | c0 00 |
|---|
| 360 | nop |
|---|
| 361 | |
|---|
| 362 | _return : |
|---|
| 363 | lw $2, 2*4($29) |
|---|
| 364 | j tmp |
|---|
| 365 | nop |
|---|
| 366 | _ret_sys: /* Special case for syscall : $2 is not restored */ |
|---|
| 367 | tmp: .set noat |
|---|
| 368 | lw $1, 1*4($29) |
|---|
| 369 | .set at |
|---|
| 370 | lw $3, 3*4($29) |
|---|
| 371 | lw $4, 4*4($29) |
|---|
| 372 | lw $5, 5*4($29) |
|---|
| 373 | lw $6, 6*4($29) |
|---|
| 374 | lw $7, 7*4($29) |
|---|
| 375 | lw $8, 8*4($29) |
|---|
| 376 | lw $9, 9*4($29) |
|---|
| 377 | lw $10, 10*4($29) |
|---|
| 378 | lw $11, 11*4($29) |
|---|
| 379 | lw $12, 12*4($29) |
|---|
| 380 | lw $13, 13*4($29) |
|---|
| 381 | lw $14, 14*4($29) |
|---|
| 382 | lw $15, 15*4($29) |
|---|
| 383 | lw $24, 16*4($29) |
|---|
| 384 | lw $25, 17*4($29) |
|---|
| 385 | lw $29, 18*4($29) |
|---|
| 386 | lw $30, 19*4($29) |
|---|
| 387 | lw $31, 20*4($29) |
|---|
| 388 | |
|---|
| 389 | /* Take care of hi and lo also */ |
|---|
| 390 | lw $26, 22*4($29) |
|---|
| 391 | mthi $26 |
|---|
| 392 | lw $26, 23*4($29) |
|---|
| 393 | mtlo $26 |
|---|
| 394 | |
|---|
| 395 | lw $26, 21*4($29) /* This is the epc of this context */ |
|---|
| 396 | lw $27, 24*4($29) /* This is the offset: 4 excep, 0 int */ |
|---|
| 397 | addu $29, $29, 26*4 /* Stack update : fill lw stall */ |
|---|
| 398 | addu $26, $26, $27 /* Addr for return from excep */ |
|---|
| 399 | j $26 /* Jump back where the exception occured*/ |
|---|
| 400 | rfe /* And restore the original mode*/ |
|---|
| 401 | .set reorder |
|---|
| 402 | .end excep |
|---|