| 1 | /*
|
| 2 | * Low-level system-call handling, trap handlers and context-switching
|
| 3 | *
|
| 4 | * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
|
| 5 | * Copyright (C) 2008-2009 PetaLogix
|
| 6 | * Copyright (C) 2003 John Williams <jwilliams@itee.uq.edu.au>
|
| 7 | * Copyright (C) 2001,2002 NEC Corporation
|
| 8 | * Copyright (C) 2001,2002 Miles Bader <miles@gnu.org>
|
| 9 | *
|
| 10 | * This file is subject to the terms and conditions of the GNU General
|
| 11 | * Public License. See the file COPYING in the main directory of this
|
| 12 | * archive for more details.
|
| 13 | *
|
| 14 | * Written by Miles Bader <miles@gnu.org>
|
| 15 | * Heavily modified by John Williams for Microblaze
|
| 16 | */
|
| 17 |
|
| 18 | |
| 19 | |
| 20 |
|
| 21 | |
| 22 | |
| 23 | |
| 24 | |
| 25 | |
| 26 | |
| 27 |
|
| 28 | |
| 29 | |
| 30 | |
| 31 |
|
| 32 | |
| 33 | |
| 34 | |
| 35 |
|
| 36 | |
| 37 |
|
| 38 | |
| 39 | /* Create space for syscalls counting. */
|
| 40 | .section .data
|
| 41 | .global syscall_debug_table
|
| 42 | .align 4
|
| 43 | syscall_debug_table:
|
| 44 | .space (__NR_syscalls * 4)
|
| 45 | |
| 46 |
|
| 47 | |
| 48 |
|
| 49 | /*
|
| 50 | * Various ways of setting and clearing BIP in flags reg.
|
| 51 | * This is mucky, but necessary using microblaze version that
|
| 52 | * allows msr ops to write to BIP
|
| 53 | */
|
| 54 | |
| 55 | .macro clear_bip
|
| 56 | msrclr r0, MSR_BIP
|
| 57 | .endm
|
| 58 |
|
| 59 | .macro set_bip
|
| 60 | msrset r0, MSR_BIP
|
| 61 | .endm
|
| 62 |
|
| 63 | .macro clear_eip
|
| 64 | msrclr r0, MSR_EIP
|
| 65 | .endm
|
| 66 |
|
| 67 | .macro set_ee
|
| 68 | msrset r0, MSR_EE
|
| 69 | .endm
|
| 70 |
|
| 71 | .macro disable_irq
|
| 72 | msrclr r0, MSR_IE
|
| 73 | .endm
|
| 74 |
|
| 75 | .macro enable_irq
|
| 76 | msrset r0, MSR_IE
|
| 77 | .endm
|
| 78 |
|
| 79 | .macro set_ums
|
| 80 | msrset r0, MSR_UMS
|
| 81 | msrclr r0, MSR_VMS
|
| 82 | .endm
|
| 83 |
|
| 84 | .macro set_vms
|
| 85 | msrclr r0, MSR_UMS
|
| 86 | msrset r0, MSR_VMS
|
| 87 | .endm
|
| 88 |
|
| 89 | .macro clear_ums
|
| 90 | msrclr r0, MSR_UMS
|
| 91 | .endm
|
| 92 |
|
| 93 | .macro clear_vms_ums
|
| 94 | msrclr r0, MSR_VMS | MSR_UMS
|
| 95 | .endm
|
| 96 | |
| 97 | .macro clear_bip
|
| 98 | mfs r11, rmsr
|
| 99 | andi r11, r11, ~MSR_BIP
|
| 100 | mts rmsr, r11
|
| 101 | .endm
|
| 102 |
|
| 103 | .macro set_bip
|
| 104 | mfs r11, rmsr
|
| 105 | ori r11, r11, MSR_BIP
|
| 106 | mts rmsr, r11
|
| 107 | .endm
|
| 108 |
|
| 109 | .macro clear_eip
|
| 110 | mfs r11, rmsr
|
| 111 | andi r11, r11, ~MSR_EIP
|
| 112 | mts rmsr, r11
|
| 113 | .endm
|
| 114 |
|
| 115 | .macro set_ee
|
| 116 | mfs r11, rmsr
|
| 117 | ori r11, r11, MSR_EE
|
| 118 | mts rmsr, r11
|
| 119 | .endm
|
| 120 |
|
| 121 | .macro disable_irq
|
| 122 | mfs r11, rmsr
|
| 123 | andi r11, r11, ~MSR_IE
|
| 124 | mts rmsr, r11
|
| 125 | .endm
|
| 126 |
|
| 127 | .macro enable_irq
|
| 128 | mfs r11, rmsr
|
| 129 | ori r11, r11, MSR_IE
|
| 130 | mts rmsr, r11
|
| 131 | .endm
|
| 132 |
|
| 133 | .macro set_ums
|
| 134 | mfs r11, rmsr
|
| 135 | ori r11, r11, MSR_VMS
|
| 136 | andni r11, r11, MSR_UMS
|
| 137 | mts rmsr, r11
|
| 138 | .endm
|
| 139 |
|
| 140 | .macro set_vms
|
| 141 | mfs r11, rmsr
|
| 142 | ori r11, r11, MSR_VMS
|
| 143 | andni r11, r11, MSR_UMS
|
| 144 | mts rmsr, r11
|
| 145 | .endm
|
| 146 |
|
| 147 | .macro clear_ums
|
| 148 | mfs r11, rmsr
|
| 149 | andni r11, r11, MSR_UMS
|
| 150 | mts rmsr,r11
|
| 151 | .endm
|
| 152 |
|
| 153 | .macro clear_vms_ums
|
| 154 | mfs r11, rmsr
|
| 155 | andni r11, r11, (MSR_VMS|MSR_UMS)
|
| 156 | mts rmsr,r11
|
| 157 | .endm
|
| 158 | |
| 159 |
|
| 160 | /* Define how to call high-level functions. With MMU, virtual mode must be
|
| 161 | * enabled when calling the high-level function. Clobbers R11.
|
| 162 | * VM_ON, VM_OFF, DO_JUMP_BIPCLR, DO_CALL
|
| 163 | */
|
| 164 |
|
| 165 | /* turn on virtual protected mode save */
|
| 166 | |
| 167 | set_ums; \
|
| 168 | rted r0, 2f; \
|
| 169 | nop; \
|
| 170 | 2:
|
| 171 |
|
| 172 | /* turn off virtual protected mode save and user mode save*/
|
| 173 | |
| 174 | clear_vms_ums; \
|
| 175 | rted r0, TOPHYS(1f); \
|
| 176 | nop; \
|
| 177 | 1:
|
| 178 |
|
| 179 | |
| 180 | swi r2, r1, PTO+PT_R2; /* Save SDA */ \
|
| 181 | swi r3, r1, PTO+PT_R3; \
|
| 182 | swi r4, r1, PTO+PT_R4; \
|
| 183 | swi r5, r1, PTO+PT_R5; \
|
| 184 | swi r6, r1, PTO+PT_R6; \
|
| 185 | swi r7, r1, PTO+PT_R7; \
|
| 186 | swi r8, r1, PTO+PT_R8; \
|
| 187 | swi r9, r1, PTO+PT_R9; \
|
| 188 | swi r10, r1, PTO+PT_R10; \
|
| 189 | swi r11, r1, PTO+PT_R11; /* save clobbered regs after rval */\
|
| 190 | swi r12, r1, PTO+PT_R12; \
|
| 191 | swi r13, r1, PTO+PT_R13; /* Save SDA2 */ \
|
| 192 | swi r14, r1, PTO+PT_PC; /* PC, before IRQ/trap */ \
|
| 193 | swi r15, r1, PTO+PT_R15; /* Save LP */ \
|
| 194 | swi r16, r1, PTO+PT_R16; \
|
| 195 | swi r17, r1, PTO+PT_R17; \
|
| 196 | swi r18, r1, PTO+PT_R18; /* Save asm scratch reg */ \
|
| 197 | swi r19, r1, PTO+PT_R19; \
|
| 198 | swi r20, r1, PTO+PT_R20; \
|
| 199 | swi r21, r1, PTO+PT_R21; \
|
| 200 | swi r22, r1, PTO+PT_R22; \
|
| 201 | swi r23, r1, PTO+PT_R23; \
|
| 202 | swi r24, r1, PTO+PT_R24; \
|
| 203 | swi r25, r1, PTO+PT_R25; \
|
| 204 | swi r26, r1, PTO+PT_R26; \
|
| 205 | swi r27, r1, PTO+PT_R27; \
|
| 206 | swi r28, r1, PTO+PT_R28; \
|
| 207 | swi r29, r1, PTO+PT_R29; \
|
| 208 | swi r30, r1, PTO+PT_R30; \
|
| 209 | swi r31, r1, PTO+PT_R31; /* Save current task reg */ \
|
| 210 | mfs r11, rmsr; /* save MSR */ \
|
| 211 | swi r11, r1, PTO+PT_MSR;
|
| 212 |
|
| 213 | |
| 214 | lwi r2, r1, PTO+PT_R2; /* restore SDA */ \
|
| 215 | lwi r3, r1, PTO+PT_R3; \
|
| 216 | lwi r4, r1, PTO+PT_R4; \
|
| 217 | lwi r5, r1, PTO+PT_R5; \
|
| 218 | lwi r6, r1, PTO+PT_R6; \
|
| 219 | lwi r7, r1, PTO+PT_R7; \
|
| 220 | lwi r8, r1, PTO+PT_R8; \
|
| 221 | lwi r9, r1, PTO+PT_R9; \
|
| 222 | lwi r10, r1, PTO+PT_R10; \
|
| 223 | lwi r11, r1, PTO+PT_R11; /* restore clobbered regs after rval */\
|
| 224 | lwi r12, r1, PTO+PT_R12; \
|
| 225 | lwi r13, r1, PTO+PT_R13; /* restore SDA2 */ \
|
| 226 | lwi r14, r1, PTO+PT_PC; /* RESTORE_LINK PC, before IRQ/trap */\
|
| 227 | lwi r15, r1, PTO+PT_R15; /* restore LP */ \
|
| 228 | lwi r16, r1, PTO+PT_R16; \
|
| 229 | lwi r17, r1, PTO+PT_R17; \
|
| 230 | lwi r18, r1, PTO+PT_R18; /* restore asm scratch reg */ \
|
| 231 | lwi r19, r1, PTO+PT_R19; \
|
| 232 | lwi r20, r1, PTO+PT_R20; \
|
| 233 | lwi r21, r1, PTO+PT_R21; \
|
| 234 | lwi r22, r1, PTO+PT_R22; \
|
| 235 | lwi r23, r1, PTO+PT_R23; \
|
| 236 | lwi r24, r1, PTO+PT_R24; \
|
| 237 | lwi r25, r1, PTO+PT_R25; \
|
| 238 | lwi r26, r1, PTO+PT_R26; \
|
| 239 | lwi r27, r1, PTO+PT_R27; \
|
| 240 | lwi r28, r1, PTO+PT_R28; \
|
| 241 | lwi r29, r1, PTO+PT_R29; \
|
| 242 | lwi r30, r1, PTO+PT_R30; \
|
| 243 | lwi r31, r1, PTO+PT_R31; /* Restore cur task reg */
|
| 244 |
|
| 245 | |
| 246 | lwi r11, r1, PTO+PT_MSR; \
|
| 247 | mts rmsr , r11; \
|
| 248 | RESTORE_REGS_GP
|
| 249 |
|
| 250 | |
| 251 | lwi r11, r1, PTO+PT_MSR; \
|
| 252 | andni r11, r11, MSR_EIP; /* clear EIP */ \
|
| 253 | ori r11, r11, MSR_EE | MSR_BIP; /* set EE and BIP */ \
|
| 254 | mts rmsr , r11; \
|
| 255 | RESTORE_REGS_GP
|
| 256 |
|
| 257 | |
| 258 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* save stack */ \
|
| 259 | /* See if already in kernel mode.*/ \
|
| 260 | mfs r1, rmsr; \
|
| 261 | andi r1, r1, MSR_UMS; \
|
| 262 | bnei r1, 1f; \
|
| 263 | /* Kernel-mode state save. */ \
|
| 264 | /* Reload kernel stack-ptr. */ \
|
| 265 | lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); \
|
| 266 | /* FIXME: I can add these two lines to one */ \
|
| 267 | /* tophys(r1,r1); */ \
|
| 268 | /* addik r1, r1, -STATE_SAVE_SIZE; */ \
|
| 269 | addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE; \
|
| 270 | SAVE_REGS \
|
| 271 | brid 2f; \
|
| 272 | swi r1, r1, PTO+PT_MODE; \
|
| 273 | 1: /* User-mode state save. */ \
|
| 274 | lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\
|
| 275 | tophys(r1,r1); \
|
| 276 | lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ \
|
| 277 | /* MS these three instructions can be added to one */ \
|
| 278 | /* addik r1, r1, THREAD_SIZE; */ \
|
| 279 | /* tophys(r1,r1); */ \
|
| 280 | /* addik r1, r1, -STATE_SAVE_SIZE; */ \
|
| 281 | addik r1, r1, THREAD_SIZE + CONFIG_KERNEL_BASE_ADDR \
|
| 282 | - CONFIG_KERNEL_START - STATE_SAVE_SIZE; \
|
| 283 | SAVE_REGS \
|
| 284 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \
|
| 285 | swi r11, r1, PTO+PT_R1; /* Store user SP. */ \
|
| 286 | swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */ \
|
| 287 | /* MS: I am clearing UMS even in case when I come from kernel space */ \
|
| 288 | clear_ums; \
|
| 289 | 2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
|
| 290 |
|
| 291 | .text
|
| 292 |
|
| 293 | .extern cpuinfo
|
| 294 |
|
| 295 | C_ENTRY(mb_flush_dcache):
|
| 296 | addik r1, r1, -STATE_SAVE_SIZE
|
| 297 | SAVE_REGS
|
| 298 |
|
| 299 | addik r3, r0, cpuinfo
|
| 300 | lwi r7, r3, CI_DCS
|
| 301 | lwi r8, r3, CI_DCL
|
| 302 | sub r9, r7, r8
|
| 303 | 1:
|
| 304 | wdc.flush r9, r0
|
| 305 | bgtid r9, 1b
|
| 306 | addk r9, r9, r8
|
| 307 |
|
| 308 | RESTORE_REGS
|
| 309 | addik r1, r1, STATE_SAVE_SIZE
|
| 310 | rtsd r15, 8
|
| 311 | nop
|
| 312 |
|
| 313 | C_ENTRY(mb_invalidate_icache):
|
| 314 | addik r1, r1, -STATE_SAVE_SIZE
|
| 315 | SAVE_REGS
|
| 316 |
|
| 317 | addik r3, r0, cpuinfo
|
| 318 | lwi r7, r3, CI_ICS
|
| 319 | lwi r8, r3, CI_ICL
|
| 320 | sub r9, r7, r8
|
| 321 | 1:
|
| 322 | wic r9, r0
|
| 323 | bgtid r9, 1b
|
| 324 | addk r9, r9, r8
|
| 325 |
|
| 326 | RESTORE_REGS
|
| 327 | addik r1, r1, STATE_SAVE_SIZE
|
| 328 | rtsd r15, 8
|
| 329 | nop
|
| 330 |
|
| 331 | /*
|
| 332 | * User trap.
|
| 333 | *
|
| 334 | * System calls are handled here.
|
| 335 | *
|
| 336 | * Syscall protocol:
|
| 337 | * Syscall number in r12, args in r5-r10
|
| 338 | * Return value in r3
|
| 339 | *
|
| 340 | * Trap entered via brki instruction, so BIP bit is set, and interrupts
|
| 341 | * are masked. This is nice, means we don't have to CLI before state save
|
| 342 | */
|
| 343 | C_ENTRY(_user_exception):
|
| 344 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */
|
| 345 | addi r14, r14, 4 /* return address is 4 byte after call */
|
| 346 |
|
| 347 | lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
|
| 348 | tophys(r1,r1);
|
| 349 | lwi r1, r1, TS_THREAD_INFO; /* get stack from task_struct */
|
| 350 | /* calculate kernel stack pointer from task struct 8k */
|
| 351 | addik r1, r1, THREAD_SIZE;
|
| 352 | tophys(r1,r1);
|
| 353 |
|
| 354 | addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */
|
| 355 | SAVE_REGS
|
| 356 | swi r0, r1, PTO+PT_R3
|
| 357 | swi r0, r1, PTO+PT_R4
|
| 358 |
|
| 359 | swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */
|
| 360 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
|
| 361 | swi r11, r1, PTO+PT_R1; /* Store user SP. */
|
| 362 | clear_ums;
|
| 363 | 2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
|
| 364 | /* Save away the syscall number. */
|
| 365 | swi r12, r1, PTO+PT_R0;
|
| 366 | tovirt(r1,r1)
|
| 367 |
|
| 368 | /* where the trap should return need -8 to adjust for rtsd r15, 8*/
|
| 369 | /* Jump to the appropriate function for the system call number in r12
|
| 370 | * (r12 is not preserved), or return an error if r12 is not valid. The LP
|
| 371 | * register should point to the location where
|
| 372 | * the called function should return. [note that MAKE_SYS_CALL uses label 1] */
|
| 373 |
|
| 374 | /* Step into virtual mode */
|
| 375 | rtbd r0, 3f
|
| 376 | nop
|
| 377 | 3:
|
| 378 | lwi r11, CURRENT_TASK, TS_THREAD_INFO /* get thread info */
|
| 379 | lwi r11, r11, TI_FLAGS /* get flags in thread info */
|
| 380 | andi r11, r11, _TIF_WORK_SYSCALL_MASK
|
| 381 | beqi r11, 4f
|
| 382 |
|
| 383 | addik r3, r0, -ENOSYS
|
| 384 | swi r3, r1, PTO+PT_R3
|
| 385 | brlid r15, do_syscall_trace_enter
|
| 386 | addik r5, r1, PTO+PT_R0
|
| 387 |
|
| 388 | # do_syscall_trace_enter returns the new syscall nr.
|
| 389 | addk r12, r0, r3
|
| 390 | lwi r5, r1, PTO+PT_R5;
|
| 391 | lwi r6, r1, PTO+PT_R6;
|
| 392 | lwi r7, r1, PTO+PT_R7;
|
| 393 | lwi r8, r1, PTO+PT_R8;
|
| 394 | lwi r9, r1, PTO+PT_R9;
|
| 395 | lwi r10, r1, PTO+PT_R10;
|
| 396 | 4:
|
| 397 | /* Jump to the appropriate function for the system call number in r12
|
| 398 | * (r12 is not preserved), or return an error if r12 is not valid.
|
| 399 | * The LP register should point to the location where the called function
|
| 400 | * should return. [note that MAKE_SYS_CALL uses label 1] */
|
| 401 | /* See if the system call number is valid */
|
| 402 | blti r12, 5f
|
| 403 | addi r11, r12, -__NR_syscalls;
|
| 404 | bgei r11, 5f;
|
| 405 | /* Figure out which function to use for this system call. */
|
| 406 | /* Note Microblaze barrel shift is optional, so don't rely on it */
|
| 407 | add r12, r12, r12; /* convert num -> ptr */
|
| 408 | add r12, r12, r12;
|
| 409 | addi r30, r0, 1 /* restarts allowed */
|
| 410 |
|
| 411 | |
| 412 | /* Trac syscalls and stored them to syscall_debug_table */
|
| 413 | /* The first syscall location stores total syscall number */
|
| 414 | lwi r3, r0, syscall_debug_table
|
| 415 | addi r3, r3, 1
|
| 416 | swi r3, r0, syscall_debug_table
|
| 417 | lwi r3, r12, syscall_debug_table
|
| 418 | addi r3, r3, 1
|
| 419 | swi r3, r12, syscall_debug_table
|
| 420 | |
| 421 |
|
| 422 | |
| 423 | lwi r12, r12, sys_call_table
|
| 424 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */
|
| 425 | addi r15, r0, ret_from_trap-8
|
| 426 | bra r12
|
| 427 |
|
| 428 | /* The syscall number is invalid, return an error. */
|
| 429 | 5:
|
| 430 | braid ret_from_trap
|
| 431 | addi r3, r0, -ENOSYS;
|
| 432 |
|
| 433 | /* Entry point used to return from a syscall/trap */
|
| 434 | /* We re-enable BIP bit before state restore */
|
| 435 | C_ENTRY(ret_from_trap):
|
| 436 | swi r3, r1, PTO+PT_R3
|
| 437 | swi r4, r1, PTO+PT_R4
|
| 438 | /*
|
| 439 | * Entry point for returns that must not store r3/r4 back into pt_regs,
|
| 440 | * i.e. rt_sigreturn, which has already restored them from the signal
|
| 441 | * context. Reached as "rtsd r15, 8" with r15 set to this label minus 8,
|
| 442 | * so it stays correct if the number of stores above ever changes.
|
| 443 | */
|
| 444 | ret_from_trap_no_rval:
|
| 445 | lwi r11, r1, PTO+PT_MODE;
|
| 446 | /* See if returning to kernel mode, if so, skip resched &c. */
|
| 447 | bnei r11, 2f;
|
| 448 | /* We're returning to user mode, so check for various conditions that
|
| 449 | * trigger rescheduling. */
|
| 450 | /* FIXME: Restructure all these flag checks. */
|
| 451 | lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */
|
| 452 | lwi r11, r11, TI_FLAGS; /* get flags in thread info */
|
| 453 | andi r11, r11, _TIF_WORK_SYSCALL_MASK
|
| 454 | beqi r11, 1f
|
| 455 |
|
| 456 | brlid r15, do_syscall_trace_leave
|
| 457 | addik r5, r1, PTO+PT_R0
|
| 458 | 1:
|
| 459 | /* We're returning to user mode, so check for various conditions that
|
| 460 | * trigger rescheduling. */
|
| 461 | /* get thread info from current task */
|
| 462 | lwi r11, CURRENT_TASK, TS_THREAD_INFO;
|
| 463 | lwi r19, r11, TI_FLAGS; /* get flags in thread info */
|
| 464 | andi r11, r19, _TIF_NEED_RESCHED;
|
| 465 | beqi r11, 5f;
|
| 466 |
|
| 467 | bralid r15, schedule; /* Call scheduler */
|
| 468 | nop; /* delay slot */
|
| 469 | bri 1b
|
| 470 |
|
| 471 | /* Maybe handle a signal */
|
| 472 | 5:
|
| 473 | andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME;
|
| 474 | beqi r11, 4f; /* Signals to handle, handle them */
|
| 475 |
|
| 476 | addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */
|
| 477 | bralid r15, do_notify_resume; /* Handle any signals */
|
| 478 | add r6, r30, r0; /* Arg 2: int in_syscall */
|
| 479 | add r30, r0, r0 /* no more restarts */
|
| 480 | bri 1b
|
| 481 |
|
| 482 | /* Finally, return to user state. */
|
| 483 | 4: set_bip; /* Ints masked for state restore */
|
| 484 | swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */
|
| 485 | VM_OFF;
|
| 486 | tophys(r1,r1);
|
| 487 | RESTORE_REGS_RTBD;
|
| 488 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */
|
| 489 | /*
|
| 490 | * Deliberately PT_SIZE, not STATE_SAVE_SIZE: r1 is back at the stack
|
| 491 | * top, so PTO cancels and the saved SP sits at PT_R1 - PT_SIZE.
|
| 492 | */
|
| 493 | lwi r1, r1, PT_R1 - PT_SIZE;/* Restore user stack pointer. */
|
| 494 | bri 6f;
|
| 495 |
|
| 496 | /* Return to kernel state. */
|
| 497 | 2: set_bip; /* Ints masked for state restore */
|
| 498 | VM_OFF;
|
| 499 | tophys(r1,r1);
|
| 500 | RESTORE_REGS_RTBD;
|
| 501 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */
|
| 502 | tovirt(r1,r1);
|
| 503 | 6:
|
| 504 | TRAP_return: /* Make global symbol for debugging */
|
| 505 | rtbd r14, 0; /* Instructions to return from an IRQ */
|
| 506 | nop;
|
| 507 |
|
| 508 |
|
| 509 | /* This the initial entry point for a new child thread, with an appropriate
|
| 510 | stack in place that makes it look like the child is in the middle of a
|
| 511 | syscall. This function is actually `returned to' from switch_thread
|
| 512 | (copy_thread makes ret_from_fork the return address in each new thread's
|
| 513 | saved context). */
|
| 514 | C_ENTRY(ret_from_fork):
|
| 515 | bralid r15, schedule_tail; /* ...which is schedule_tail's arg */
|
| 516 | add r5, r3, r0; /* switch_thread returns the prev task */
|
| 517 | /* ( in the delay slot ) */
|
| 518 | brid ret_from_trap; /* Do normal trap return */
|
| 519 | add r3, r0, r0; /* Child's fork call should return 0. */
|
| 520 |
|
| 521 | C_ENTRY(ret_from_kernel_thread):
|
| 522 | bralid r15, schedule_tail; /* ...which is schedule_tail's arg */
|
| 523 | add r5, r3, r0; /* switch_thread returns the prev task */
|
| 524 | /* ( in the delay slot ) */
|
| 525 | brald r15, r20 /* fn was left in r20 */
|
| 526 | addk r5, r0, r19 /* ... and argument - in r19 */
|
| 527 | brid ret_from_trap
|
| 528 | add r3, r0, r0
|
| 529 |
|
| 530 | C_ENTRY(sys_rt_sigreturn_wrapper):
|
| 531 | addik r30, r0, 0 /* no restarts */
|
| 532 | /*
|
| 533 | * rt_sigreturn restores the full register set from the signal
|
| 534 | * context, so it must skip the r3/r4 syscall-return stores at the
|
| 535 | * head of ret_from_trap which would otherwise overwrite the
|
| 536 | * just-restored r3/r4. Every C function returns with "rtsd r15, 8"
|
| 537 | * -- the ABI return, where the 8 skips the caller's branch and its
|
| 538 | * delay slot -- so bias r15 by -8 to land on ret_from_trap_no_rval.
|
| 539 | */
|
| 540 | addik r15, r0, ret_from_trap_no_rval - 8
|
| 541 | brid sys_rt_sigreturn /* Do real work */
|
| 542 | addik r5, r1, PTO; /* add user context as 1st arg */
|
| 543 |
|
| 544 | /*
|
| 545 | * HW EXCEPTION rutine start
|
| 546 | */
|
| 547 | C_ENTRY(full_exception_trap):
|
| 548 | /* adjust exception address for privileged instruction
|
| 549 | * for finding where is it */
|
| 550 | addik r17, r17, -4
|
| 551 | SAVE_STATE /* Save registers */
|
| 552 | /* PC, before IRQ/trap - this is one instruction above */
|
| 553 | swi r17, r1, PTO+PT_PC;
|
| 554 | tovirt(r1,r1)
|
| 555 | /* FIXME this can be store directly in PT_ESR reg.
|
| 556 | * I tested it but there is a fault */
|
| 557 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */
|
| 558 | addik r15, r0, ret_from_exc - 8
|
| 559 | mfs r6, resr
|
| 560 | mfs r7, rfsr; /* save FSR */
|
| 561 | mts rfsr, r0; /* Clear sticky fsr */
|
| 562 | rted r0, full_exception
|
| 563 | addik r5, r1, PTO /* parameter struct pt_regs * regs */
|
| 564 |
|
| 565 | /*
|
| 566 | * Unaligned data trap.
|
| 567 | *
|
| 568 | * Unaligned data trap last on 4k page is handled here.
|
| 569 | *
|
| 570 | * Trap entered via exception, so EE bit is set, and interrupts
|
| 571 | * are masked. This is nice, means we don't have to CLI before state save
|
| 572 | *
|
| 573 | * The assembler routine is in "arch/microblaze/kernel/hw_exception_handler.S"
|
| 574 | */
|
| 575 | C_ENTRY(unaligned_data_trap):
|
| 576 | /* MS: I have to save r11 value and then restore it because
|
| 577 | * set_bit, clear_eip, set_ee use r11 as temp register if MSR
|
| 578 | * instructions are not used. We don't need to do if MSR instructions
|
| 579 | * are used and they use r0 instead of r11.
|
| 580 | * I am using ENTRY_SP which should be primary used only for stack
|
| 581 | * pointer saving. */
|
| 582 | swi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
|
| 583 | set_bip; /* equalize initial state for all possible entries */
|
| 584 | clear_eip;
|
| 585 | set_ee;
|
| 586 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
|
| 587 | SAVE_STATE /* Save registers.*/
|
| 588 | /* PC, before IRQ/trap - this is one instruction above */
|
| 589 | swi r17, r1, PTO+PT_PC;
|
| 590 | tovirt(r1,r1)
|
| 591 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */
|
| 592 | addik r15, r0, ret_from_exc-8
|
| 593 | mfs r3, resr /* ESR */
|
| 594 | mfs r4, rear /* EAR */
|
| 595 | rtbd r0, _unaligned_data_exception
|
| 596 | addik r7, r1, PTO /* parameter struct pt_regs * regs */
|
| 597 |
|
| 598 | /*
|
| 599 | * Page fault traps.
|
| 600 | *
|
| 601 | * If the real exception handler (from hw_exception_handler.S) didn't find
|
| 602 | * the mapping for the process, then we're thrown here to handle such situation.
|
| 603 | *
|
| 604 | * Trap entered via exceptions, so EE bit is set, and interrupts
|
| 605 | * are masked. This is nice, means we don't have to CLI before state save
|
| 606 | *
|
| 607 | * Build a standard exception frame for TLB Access errors. All TLB exceptions
|
| 608 | * will bail out to this point if they can't resolve the lightweight TLB fault.
|
| 609 | *
|
| 610 | * The C function called is in "arch/microblaze/mm/fault.c", declared as:
|
| 611 | * void do_page_fault(struct pt_regs *regs,
|
| 612 | * unsigned long address,
|
| 613 | * unsigned long error_code)
|
| 614 | */
|
| 615 | /* data and intruction trap - which is choose is resolved int fault.c */
|
| 616 | C_ENTRY(page_fault_data_trap):
|
| 617 | SAVE_STATE /* Save registers.*/
|
| 618 | /* PC, before IRQ/trap - this is one instruction above */
|
| 619 | swi r17, r1, PTO+PT_PC;
|
| 620 | tovirt(r1,r1)
|
| 621 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */
|
| 622 | addik r15, r0, ret_from_exc-8
|
| 623 | mfs r6, rear /* parameter unsigned long address */
|
| 624 | mfs r7, resr /* parameter unsigned long error_code */
|
| 625 | rted r0, do_page_fault
|
| 626 | addik r5, r1, PTO /* parameter struct pt_regs * regs */
|
| 627 |
|
| 628 | C_ENTRY(page_fault_instr_trap):
|
| 629 | SAVE_STATE /* Save registers.*/
|
| 630 | /* PC, before IRQ/trap - this is one instruction above */
|
| 631 | swi r17, r1, PTO+PT_PC;
|
| 632 | tovirt(r1,r1)
|
| 633 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */
|
| 634 | addik r15, r0, ret_from_exc-8
|
| 635 | mfs r6, rear /* parameter unsigned long address */
|
| 636 | ori r7, r0, 0 /* parameter unsigned long error_code */
|
| 637 | rted r0, do_page_fault
|
| 638 | addik r5, r1, PTO /* parameter struct pt_regs * regs */
|
| 639 |
|
| 640 | /* Entry point used to return from an exception. */
|
| 641 | C_ENTRY(ret_from_exc):
|
| 642 | lwi r11, r1, PTO+PT_MODE;
|
| 643 | bnei r11, 2f; /* See if returning to kernel mode, */
|
| 644 | /* ... if so, skip resched &c. */
|
| 645 |
|
| 646 | /* We're returning to user mode, so check for various conditions that
|
| 647 | trigger rescheduling. */
|
| 648 | 1:
|
| 649 | lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */
|
| 650 | lwi r19, r11, TI_FLAGS; /* get flags in thread info */
|
| 651 | andi r11, r19, _TIF_NEED_RESCHED;
|
| 652 | beqi r11, 5f;
|
| 653 |
|
| 654 | /* Call the scheduler before returning from a syscall/trap. */
|
| 655 | bralid r15, schedule; /* Call scheduler */
|
| 656 | nop; /* delay slot */
|
| 657 | bri 1b
|
| 658 |
|
| 659 | /* Maybe handle a signal */
|
| 660 | 5: andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME;
|
| 661 | beqi r11, 4f; /* Signals to handle, handle them */
|
| 662 |
|
| 663 | /*
|
| 664 | * Handle a signal return; Pending signals should be in r18.
|
| 665 | *
|
| 666 | * Not all registers are saved by the normal trap/interrupt entry
|
| 667 | * points (for instance, call-saved registers (because the normal
|
| 668 | * C-compiler calling sequence in the kernel makes sure they're
|
| 669 | * preserved), and call-clobbered registers in the case of
|
| 670 | * traps), but signal handlers may want to examine or change the
|
| 671 | * complete register state. Here we save anything not saved by
|
| 672 | * the normal entry sequence, so that it may be safely restored
|
| 673 | * (in a possibly modified form) after do_notify_resume returns. */
|
| 674 | addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */
|
| 675 | bralid r15, do_notify_resume; /* Handle any signals */
|
| 676 | addi r6, r0, 0; /* Arg 2: int in_syscall */
|
| 677 | bri 1b
|
| 678 |
|
| 679 | /* Finally, return to user state. */
|
| 680 | 4: set_bip; /* Ints masked for state restore */
|
| 681 | swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */
|
| 682 | VM_OFF;
|
| 683 | tophys(r1,r1);
|
| 684 |
|
| 685 | RESTORE_REGS_RTBD;
|
| 686 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */
|
| 687 |
|
| 688 | lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer. */
|
| 689 | bri 6f;
|
| 690 | /* Return to kernel state. */
|
| 691 | 2: set_bip; /* Ints masked for state restore */
|
| 692 | VM_OFF;
|
| 693 | tophys(r1,r1);
|
| 694 | RESTORE_REGS_RTBD;
|
| 695 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */
|
| 696 |
|
| 697 | tovirt(r1,r1);
|
| 698 | 6:
|
| 699 | EXC_return: /* Make global symbol for debugging */
|
| 700 | rtbd r14, 0; /* Instructions to return from an IRQ */
|
| 701 | nop;
|
| 702 |
|
| 703 | /*
|
| 704 | * HW EXCEPTION rutine end
|
| 705 | */
|
| 706 |
|
| 707 | /*
|
| 708 | * Hardware maskable interrupts.
|
| 709 | *
|
| 710 | * The stack-pointer (r1) should have already been saved to the memory
|
| 711 | * location PER_CPU(ENTRY_SP).
|
| 712 | */
|
| 713 | C_ENTRY(_interrupt):
|
| 714 | /* MS: we are in physical address */
|
| 715 | /* Save registers, switch to proper stack, convert SP to virtual.*/
|
| 716 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP))
|
| 717 | /* MS: See if already in kernel mode. */
|
| 718 | mfs r1, rmsr
|
| 719 | nop
|
| 720 | andi r1, r1, MSR_UMS
|
| 721 | bnei r1, 1f
|
| 722 |
|
| 723 | /* Kernel-mode state save. */
|
| 724 | lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP))
|
| 725 | tophys(r1,r1); /* MS: I have in r1 physical address where stack is */
|
| 726 | /* save registers */
|
| 727 | /* MS: Make room on the stack -> activation record */
|
| 728 | addik r1, r1, -STATE_SAVE_SIZE;
|
| 729 | SAVE_REGS
|
| 730 | brid 2f;
|
| 731 | swi r1, r1, PTO+PT_MODE; /* 0 - user mode, 1 - kernel mode */
|
| 732 | 1:
|
| 733 | /* User-mode state save. */
|
| 734 | /* MS: get the saved current */
|
| 735 | lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
|
| 736 | tophys(r1,r1);
|
| 737 | lwi r1, r1, TS_THREAD_INFO;
|
| 738 | addik r1, r1, THREAD_SIZE;
|
| 739 | tophys(r1,r1);
|
| 740 | /* save registers */
|
| 741 | addik r1, r1, -STATE_SAVE_SIZE;
|
| 742 | SAVE_REGS
|
| 743 | /* calculate mode */
|
| 744 | swi r0, r1, PTO+PT_MODE;
|
| 745 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
|
| 746 | swi r11, r1, PTO+PT_R1;
|
| 747 | clear_ums;
|
| 748 | 2:
|
| 749 | lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
|
| 750 | tovirt(r1,r1)
|
| 751 | addik r15, r0, irq_call;
|
| 752 | irq_call:rtbd r0, do_IRQ;
|
| 753 | addik r5, r1, PTO;
|
| 754 |
|
| 755 | /* MS: we are in virtual mode */
|
| 756 | ret_from_irq:
|
| 757 | lwi r11, r1, PTO+PT_MODE;
|
| 758 | bnei r11, 2f;
|
| 759 |
|
| 760 | 1:
|
| 761 | lwi r11, CURRENT_TASK, TS_THREAD_INFO;
|
| 762 | lwi r19, r11, TI_FLAGS; /* MS: get flags from thread info */
|
| 763 | andi r11, r19, _TIF_NEED_RESCHED;
|
| 764 | beqi r11, 5f
|
| 765 | bralid r15, schedule;
|
| 766 | nop; /* delay slot */
|
| 767 | bri 1b
|
| 768 |
|
| 769 | /* Maybe handle a signal */
|
| 770 | 5: andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME;
|
| 771 | beqid r11, no_intr_resched
|
| 772 | /* Handle a signal return; Pending signals should be in r18. */
|
| 773 | addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */
|
| 774 | bralid r15, do_notify_resume; /* Handle any signals */
|
| 775 | addi r6, r0, 0; /* Arg 2: int in_syscall */
|
| 776 | bri 1b
|
| 777 |
|
| 778 | /* Finally, return to user state. */
|
| 779 | no_intr_resched:
|
| 780 | /* Disable interrupts, we are now committed to the state restore */
|
| 781 | disable_irq
|
| 782 | swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE);
|
| 783 | VM_OFF;
|
| 784 | tophys(r1,r1);
|
| 785 | RESTORE_REGS
|
| 786 | addik r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */
|
| 787 | lwi r1, r1, PT_R1 - PT_SIZE;
|
| 788 | bri 6f;
|
| 789 | /* MS: Return to kernel state. */
|
| 790 | 2:
|
| 791 | |
| 792 | lwi r11, CURRENT_TASK, TS_THREAD_INFO;
|
| 793 | /* MS: get preempt_count from thread info */
|
| 794 | lwi r5, r11, TI_PREEMPT_COUNT;
|
| 795 | bgti r5, restore;
|
| 796 |
|
| 797 | lwi r5, r11, TI_FLAGS; /* get flags in thread info */
|
| 798 | andi r5, r5, _TIF_NEED_RESCHED;
|
| 799 | beqi r5, restore /* if zero jump over */
|
| 800 |
|
| 801 | /* interrupts are off that's why I am calling preempt_chedule_irq */
|
| 802 | bralid r15, preempt_schedule_irq
|
| 803 | nop
|
| 804 | restore:
|
| 805 | #endif
|
| 806 | VM_OFF /* MS: turn off MMU */
|
| 807 | tophys(r1,r1)
|
| 808 | RESTORE_REGS
|
| 809 | addik r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */
|
| 810 | tovirt(r1,r1);
|
| 811 | 6:
|
| 812 | IRQ_return: /* MS: Make global symbol for debugging */
|
| 813 | rtid r14, 0
|
| 814 | nop
|
| 815 |
|
| 816 | #ifdef CONFIG_MB_MANAGER
|
| 817 |
|
| 818 | #define PT_PID PT_SIZE
|
| 819 | #define PT_TLBI PT_SIZE + 4
|
| 820 | #define PT_ZPR PT_SIZE + 8
|
| 821 | #define PT_TLBL0 PT_SIZE + 12
|
| 822 | #define PT_TLBH0 PT_SIZE + 16
|
| 823 |
|
| 824 | C_ENTRY(_xtmr_manager_reset):
|
| 825 | lwi r1, r0, xmb_manager_stackpointer
|
| 826 |
|
| 827 | /* Restore MSR */
|
| 828 | lwi r2, r1, PTO+PT_MSR
|
| 829 | mts rmsr, r2
|
| 830 | bri 4
|
| 831 |
|
| 832 | /* restore Special purpose registers */
|
| 833 | lwi r2, r1, PTO+PT_PID
|
| 834 | mts rpid, r2
|
| 835 |
|
| 836 | lwi r2, r1, PTO+PT_TLBI
|
| 837 | mts rtlbx, r2
|
| 838 |
|
| 839 | lwi r2, r1, PTO+PT_ZPR
|
| 840 | mts rzpr, r2
|
| 841 |
|
| 842 | #if CONFIG_XILINX_MICROBLAZE0_USE_FPU
|
| 843 | lwi r2, r1, PTO+PT_FSR
|
| 844 | mts rfsr, r2
|
| 845 | #endif
|
| 846 |
|
| 847 | /* restore all the tlb's */
|
| 848 | addik r3, r0, TOPHYS(tlb_skip)
|
| 849 | addik r6, r0, PTO+PT_TLBL0
|
| 850 | addik r7, r0, PTO+PT_TLBH0
|
| 851 | restore_tlb:
|
| 852 | add r6, r6, r1
|
| 853 | add r7, r7, r1
|
| 854 | lwi r2, r6, 0
|
| 855 | mts rtlblo, r2
|
| 856 | lwi r2, r7, 0
|
| 857 | mts rtlbhi, r2
|
| 858 | addik r6, r6, 4
|
| 859 | addik r7, r7, 4
|
| 860 | bgtid r3, restore_tlb
|
| 861 | addik r3, r3, -1
|
| 862 |
|
| 863 | lwi r5, r0, TOPHYS(xmb_manager_dev)
|
| 864 | lwi r8, r0, TOPHYS(xmb_manager_reset_callback)
|
| 865 | set_vms
|
| 866 | /* return from reset need -8 to adjust for rtsd r15, 8 */
|
| 867 | addik r15, r0, ret_from_reset - 8
|
| 868 | rtbd r8, 0
|
| 869 | nop
|
| 870 |
|
| 871 | ret_from_reset:
|
| 872 | set_bip /* Ints masked for state restore */
|
| 873 | VM_OFF
|
| 874 | /* MS: Restore all regs */
|
| 875 | RESTORE_REGS
|
| 876 | lwi r14, r1, PTO+PT_R14
|
| 877 | lwi r16, r1, PTO+PT_PC
|
| 878 | addik r1, r1, STATE_SAVE_SIZE + 36
|
| 879 | rtbd r16, 0
|
| 880 | nop
|
| 881 |
|
| 882 | /*
|
| 883 | * Break handler for MB Manager. Enter to _xmb_manager_break by
|
| 884 | * injecting fault in one of the TMR Microblaze core.
|
| 885 | * FIXME: This break handler supports getting
|
| 886 | * called from kernel space only.
|
| 887 | */
|
| 888 | C_ENTRY(_xmb_manager_break):
|
| 889 | /*
|
| 890 | * Reserve memory in the stack for context store/restore
|
| 891 | * (which includes memory for storing tlbs (max two tlbs))
|
| 892 | */
|
| 893 | addik r1, r1, -STATE_SAVE_SIZE - 36
|
| 894 | swi r1, r0, xmb_manager_stackpointer
|
| 895 | SAVE_REGS
|
| 896 | swi r14, r1, PTO+PT_R14 /* rewrite saved R14 value */
|
| 897 | swi r16, r1, PTO+PT_PC; /* PC and r16 are the same */
|
| 898 |
|
| 899 | lwi r6, r0, TOPHYS(xmb_manager_baseaddr)
|
| 900 | lwi r7, r0, TOPHYS(xmb_manager_crval)
|
| 901 | /*
|
| 902 | * When the break vector gets asserted because of error injection,
|
| 903 | * the break signal must be blocked before exiting from the
|
| 904 | * break handler, below code configures the tmr manager
|
| 905 | * control register to block break signal.
|
| 906 | */
|
| 907 | swi r7, r6, 0
|
| 908 |
|
| 909 | /* Save the special purpose registers */
|
| 910 | mfs r2, rpid
|
| 911 | swi r2, r1, PTO+PT_PID
|
| 912 |
|
| 913 | mfs r2, rtlbx
|
| 914 | swi r2, r1, PTO+PT_TLBI
|
| 915 |
|
| 916 | mfs r2, rzpr
|
| 917 | swi r2, r1, PTO+PT_ZPR
|
| 918 |
|
| 919 | |
| 920 | mfs r2, rfsr
|
| 921 | swi r2, r1, PTO+PT_FSR
|
| 922 | |
| 923 | mfs r2, rmsr
|
| 924 | swi r2, r1, PTO+PT_MSR
|
| 925 |
|
| 926 | /* Save all the tlb's */
|
| 927 | addik r3, r0, TOPHYS(tlb_skip)
|
| 928 | addik r6, r0, PTO+PT_TLBL0
|
| 929 | addik r7, r0, PTO+PT_TLBH0
|
| 930 | save_tlb:
|
| 931 | add r6, r6, r1
|
| 932 | add r7, r7, r1
|
| 933 | mfs r2, rtlblo
|
| 934 | swi r2, r6, 0
|
| 935 | mfs r2, rtlbhi
|
| 936 | swi r2, r7, 0
|
| 937 | addik r6, r6, 4
|
| 938 | addik r7, r7, 4
|
| 939 | bgtid r3, save_tlb
|
| 940 | addik r3, r3, -1
|
| 941 |
|
| 942 | lwi r5, r0, TOPHYS(xmb_manager_dev)
|
| 943 | lwi r8, r0, TOPHYS(xmb_manager_callback)
|
| 944 | /* return from break need -8 to adjust for rtsd r15, 8 */
|
| 945 | addik r15, r0, ret_from_break - 8
|
| 946 | rtbd r8, 0
|
| 947 | nop
|
| 948 |
|
| 949 | ret_from_break:
|
| 950 | /* flush the d-cache */
|
| 951 | bralid r15, mb_flush_dcache
|
| 952 | nop
|
| 953 |
|
| 954 | /*
|
| 955 | * To make sure microblaze i-cache is in a proper state
|
| 956 | * invalidate the i-cache.
|
| 957 | */
|
| 958 | bralid r15, mb_invalidate_icache
|
| 959 | nop
|
| 960 |
|
| 961 | set_bip; /* Ints masked for state restore */
|
| 962 | VM_OFF;
|
| 963 | mbar 1
|
| 964 | mbar 2
|
| 965 | bri 4
|
| 966 | mbar 24 /* suspend */
|
| 967 | nop
|
| 968 | #endif
|
| 969 |
|
| 970 | /*
|
| 971 | * Debug trap for KGDB. Enter to _debug_exception by brki r16, 0x18
|
| 972 | * and call handling function with saved pt_regs
|
| 973 | */
|
| 974 | C_ENTRY(_debug_exception):
|
| 975 | /* BIP bit is set on entry, no interrupts can occur */
|
| 976 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP))
|
| 977 |
|
| 978 | mfs r1, rmsr
|
| 979 | nop
|
| 980 | andi r1, r1, MSR_UMS
|
| 981 | bnei r1, 1f
|
| 982 | /* MS: Kernel-mode state save - kgdb */
|
| 983 | lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/
|
| 984 |
|
| 985 | /* BIP bit is set on entry, no interrupts can occur */
|
| 986 | addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE;
|
| 987 | SAVE_REGS;
|
| 988 | /* save all regs to pt_reg structure */
|
| 989 | swi r0, r1, PTO+PT_R0; /* R0 must be saved too */
|
| 990 | swi r14, r1, PTO+PT_R14 /* rewrite saved R14 value */
|
| 991 | swi r16, r1, PTO+PT_PC; /* PC and r16 are the same */
|
| 992 | /* save special purpose registers to pt_regs */
|
| 993 | mfs r11, rear;
|
| 994 | swi r11, r1, PTO+PT_EAR;
|
| 995 | mfs r11, resr;
|
| 996 | swi r11, r1, PTO+PT_ESR;
|
| 997 | mfs r11, rfsr;
|
| 998 | swi r11, r1, PTO+PT_FSR;
|
| 999 |
|
| 1000 | /* stack pointer is in physical address at it is decrease
|
| 1001 | * by STATE_SAVE_SIZE but we need to get correct R1 value */
|
| 1002 | addik r11, r1, CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR + STATE_SAVE_SIZE;
|
| 1003 | swi r11, r1, PTO+PT_R1
|
| 1004 | /* MS: r31 - current pointer isn't changed */
|
| 1005 | tovirt(r1,r1)
|
| 1006 | |
| 1007 | addi r5, r1, PTO /* pass pt_reg address as the first arg */
|
| 1008 | addik r15, r0, dbtrap_call; /* return address */
|
| 1009 | rtbd r0, microblaze_kgdb_break
|
| 1010 | nop;
|
| 1011 | |
| 1012 | /* MS: Place handler for brki from kernel space if KGDB is OFF.
|
| 1013 | * It is very unlikely that another brki instruction is called. */
|
| 1014 | bri 0
|
| 1015 |
|
| 1016 | /* MS: User-mode state save - gdb */
|
| 1017 | 1: lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
|
| 1018 | tophys(r1,r1);
|
| 1019 | lwi r1, r1, TS_THREAD_INFO; /* get the thread info */
|
| 1020 | addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */
|
| 1021 | tophys(r1,r1);
|
| 1022 |
|
| 1023 | addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */
|
| 1024 | SAVE_REGS;
|
| 1025 | swi r16, r1, PTO+PT_PC; /* Save LP */
|
| 1026 | swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */
|
| 1027 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
|
| 1028 | swi r11, r1, PTO+PT_R1; /* Store user SP. */
|
| 1029 | lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
|
| 1030 | tovirt(r1,r1)
|
| 1031 | set_vms;
|
| 1032 | addik r5, r1, PTO;
|
| 1033 | addik r15, r0, dbtrap_call;
|
| 1034 | dbtrap_call: /* Return point for kernel/user entry + 8 because of rtsd r15, 8 */
|
| 1035 | rtbd r0, sw_exception
|
| 1036 | nop
|
| 1037 |
|
| 1038 | /* MS: The first instruction for the second part of the gdb/kgdb */
|
| 1039 | set_bip; /* Ints masked for state restore */
|
| 1040 | lwi r11, r1, PTO+PT_MODE;
|
| 1041 | bnei r11, 2f;
|
| 1042 | /* MS: Return to user space - gdb */
|
| 1043 | 1:
|
| 1044 | /* Get current task ptr into r11 */
|
| 1045 | lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */
|
| 1046 | lwi r19, r11, TI_FLAGS; /* get flags in thread info */
|
| 1047 | andi r11, r19, _TIF_NEED_RESCHED;
|
| 1048 | beqi r11, 5f;
|
| 1049 |
|
| 1050 | /* Call the scheduler before returning from a syscall/trap. */
|
| 1051 | bralid r15, schedule; /* Call scheduler */
|
| 1052 | nop; /* delay slot */
|
| 1053 | bri 1b
|
| 1054 |
|
| 1055 | /* Maybe handle a signal */
|
| 1056 | 5: andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME;
|
| 1057 | beqi r11, 4f; /* Signals to handle, handle them */
|
| 1058 |
|
| 1059 | addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */
|
| 1060 | bralid r15, do_notify_resume; /* Handle any signals */
|
| 1061 | addi r6, r0, 0; /* Arg 2: int in_syscall */
|
| 1062 | bri 1b
|
| 1063 |
|
| 1064 | /* Finally, return to user state. */
|
| 1065 | 4: swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */
|
| 1066 | VM_OFF;
|
| 1067 | tophys(r1,r1);
|
| 1068 | /* MS: Restore all regs */
|
| 1069 | RESTORE_REGS_RTBD
|
| 1070 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space */
|
| 1071 | lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer */
|
| 1072 | DBTRAP_return_user: /* MS: Make global symbol for debugging */
|
| 1073 | rtbd r16, 0; /* MS: Instructions to return from a debug trap */
|
| 1074 | nop;
|
| 1075 |
|
| 1076 | /* MS: Return to kernel state - kgdb */
|
| 1077 | 2: VM_OFF;
|
| 1078 | tophys(r1,r1);
|
| 1079 | /* MS: Restore all regs */
|
| 1080 | RESTORE_REGS_RTBD
|
| 1081 | lwi r14, r1, PTO+PT_R14;
|
| 1082 | lwi r16, r1, PTO+PT_PC;
|
| 1083 | addik r1, r1, STATE_SAVE_SIZE; /* MS: Clean up stack space */
|
| 1084 | tovirt(r1,r1);
|
| 1085 | DBTRAP_return_kernel: /* MS: Make global symbol for debugging */
|
| 1086 | rtbd r16, 0; /* MS: Instructions to return from a debug trap */
|
| 1087 | nop;
|
| 1088 |
|
| 1089 |
|
| 1090 | ENTRY(_switch_to)
|
| 1091 | /* prepare return value */
|
| 1092 | addk r3, r0, CURRENT_TASK
|
| 1093 |
|
| 1094 | /* save registers in cpu_context */
|
| 1095 | /* use r11 and r12, volatile registers, as temp register */
|
| 1096 | /* give start of cpu_context for previous process */
|
| 1097 | addik r11, r5, TI_CPU_CONTEXT
|
| 1098 | swi r1, r11, CC_R1
|
| 1099 | swi r2, r11, CC_R2
|
| 1100 | /* skip volatile registers.
|
| 1101 | * they are saved on stack when we jumped to _switch_to() */
|
| 1102 | /* dedicated registers */
|
| 1103 | swi r13, r11, CC_R13
|
| 1104 | swi r14, r11, CC_R14
|
| 1105 | swi r15, r11, CC_R15
|
| 1106 | swi r16, r11, CC_R16
|
| 1107 | swi r17, r11, CC_R17
|
| 1108 | swi r18, r11, CC_R18
|
| 1109 | /* save non-volatile registers */
|
| 1110 | swi r19, r11, CC_R19
|
| 1111 | swi r20, r11, CC_R20
|
| 1112 | swi r21, r11, CC_R21
|
| 1113 | swi r22, r11, CC_R22
|
| 1114 | swi r23, r11, CC_R23
|
| 1115 | swi r24, r11, CC_R24
|
| 1116 | swi r25, r11, CC_R25
|
| 1117 | swi r26, r11, CC_R26
|
| 1118 | swi r27, r11, CC_R27
|
| 1119 | swi r28, r11, CC_R28
|
| 1120 | swi r29, r11, CC_R29
|
| 1121 | swi r30, r11, CC_R30
|
| 1122 | /* special purpose registers */
|
| 1123 | mfs r12, rmsr
|
| 1124 | swi r12, r11, CC_MSR
|
| 1125 | mfs r12, rear
|
| 1126 | swi r12, r11, CC_EAR
|
| 1127 | mfs r12, resr
|
| 1128 | swi r12, r11, CC_ESR
|
| 1129 | mfs r12, rfsr
|
| 1130 | swi r12, r11, CC_FSR
|
| 1131 |
|
| 1132 | /* update r31, the current-give me pointer to task which will be next */
|
| 1133 | lwi CURRENT_TASK, r6, TI_TASK
|
| 1134 | /* stored it to current_save too */
|
| 1135 | swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE)
|
| 1136 |
|
| 1137 | /* get new process' cpu context and restore */
|
| 1138 | /* give me start where start context of next task */
|
| 1139 | addik r11, r6, TI_CPU_CONTEXT
|
| 1140 |
|
| 1141 | /* non-volatile registers */
|
| 1142 | lwi r30, r11, CC_R30
|
| 1143 | lwi r29, r11, CC_R29
|
| 1144 | lwi r28, r11, CC_R28
|
| 1145 | lwi r27, r11, CC_R27
|
| 1146 | lwi r26, r11, CC_R26
|
| 1147 | lwi r25, r11, CC_R25
|
| 1148 | lwi r24, r11, CC_R24
|
| 1149 | lwi r23, r11, CC_R23
|
| 1150 | lwi r22, r11, CC_R22
|
| 1151 | lwi r21, r11, CC_R21
|
| 1152 | lwi r20, r11, CC_R20
|
| 1153 | lwi r19, r11, CC_R19
|
| 1154 | /* dedicated registers */
|
| 1155 | lwi r18, r11, CC_R18
|
| 1156 | lwi r17, r11, CC_R17
|
| 1157 | lwi r16, r11, CC_R16
|
| 1158 | lwi r15, r11, CC_R15
|
| 1159 | lwi r14, r11, CC_R14
|
| 1160 | lwi r13, r11, CC_R13
|
| 1161 | /* skip volatile registers */
|
| 1162 | lwi r2, r11, CC_R2
|
| 1163 | lwi r1, r11, CC_R1
|
| 1164 |
|
| 1165 | /* special purpose registers */
|
| 1166 | lwi r12, r11, CC_FSR
|
| 1167 | mts rfsr, r12
|
| 1168 | lwi r12, r11, CC_MSR
|
| 1169 | mts rmsr, r12
|
| 1170 |
|
| 1171 | rtsd r15, 8
|
| 1172 | nop
|
| 1173 |
|
| 1174 | #ifdef CONFIG_MB_MANAGER
|
| 1175 | .global xmb_inject_err
|
| 1176 | .section .text
|
| 1177 | .align 2
|
| 1178 | .ent xmb_inject_err
|
| 1179 | .type xmb_inject_err, @function
|
| 1180 | xmb_inject_err:
|
| 1181 | addik r1, r1, -STATE_SAVE_SIZE
|
| 1182 | SAVE_REGS
|
| 1183 |
|
| 1184 | /* Switch to real mode */
|
| 1185 | VM_OFF;
|
| 1186 | set_bip;
|
| 1187 | mbar 1
|
| 1188 | mbar 2
|
| 1189 | bralid r15, XMB_INJECT_ERR_OFFSET
|
| 1190 | nop;
|
| 1191 |
|
| 1192 | /* enable virtual mode */
|
| 1193 | set_vms;
|
| 1194 | /* barrier for instructions and data accesses */
|
| 1195 | mbar 1
|
| 1196 | mbar 2
|
| 1197 | /*
|
| 1198 | * Enable Interrupts, Virtual Protected Mode, equalize
|
| 1199 | * initial state for all possible entries.
|
| 1200 | */
|
| 1201 | rtbd r0, 1f
|
| 1202 | nop;
|
| 1203 | 1:
|
| 1204 | RESTORE_REGS
|
| 1205 | addik r1, r1, STATE_SAVE_SIZE
|
| 1206 | rtsd r15, 8;
|
| 1207 | nop;
|
| 1208 | .end xmb_inject_err
|
| 1209 |
|
| 1210 | .section .data
|
| 1211 | .global xmb_manager_dev
|
| 1212 | .global xmb_manager_baseaddr
|
| 1213 | .global xmb_manager_crval
|
| 1214 | .global xmb_manager_callback
|
| 1215 | .global xmb_manager_reset_callback
|
| 1216 | .global xmb_manager_stackpointer
|
| 1217 | .align 4
|
| 1218 | xmb_manager_dev:
|
| 1219 | .long 0
|
| 1220 | xmb_manager_baseaddr:
|
| 1221 | .long 0
|
| 1222 | xmb_manager_crval:
|
| 1223 | .long 0
|
| 1224 | xmb_manager_callback:
|
| 1225 | .long 0
|
| 1226 | xmb_manager_reset_callback:
|
| 1227 | .long 0
|
| 1228 | xmb_manager_stackpointer:
|
| 1229 | .long 0
|
| 1230 |
|
| 1231 | /*
|
| 1232 | * When the break vector gets asserted because of error injection,
|
| 1233 | * the break signal must be blocked before exiting from the
|
| 1234 | * break handler, Below api updates the manager address and
|
| 1235 | * control register and error count callback arguments,
|
| 1236 | * which will be used by the break handler to block the
|
| 1237 | * break and call the callback function.
|
| 1238 | */
|
| 1239 | .global xmb_manager_register
|
| 1240 | .section .text
|
| 1241 | .align 2
|
| 1242 | .ent xmb_manager_register
|
| 1243 | .type xmb_manager_register, @function
|
| 1244 | xmb_manager_register:
|
| 1245 | swi r5, r0, xmb_manager_baseaddr
|
| 1246 | swi r6, r0, xmb_manager_crval
|
| 1247 | swi r7, r0, xmb_manager_callback
|
| 1248 | swi r8, r0, xmb_manager_dev
|
| 1249 | swi r9, r0, xmb_manager_reset_callback
|
| 1250 |
|
| 1251 | rtsd r15, 8;
|
| 1252 | nop;
|
| 1253 | .end xmb_manager_register
|
| 1254 | #endif
|
| 1255 |
|
| 1256 | ENTRY(_reset)
|
| 1257 | VM_OFF
|
| 1258 | brai 0; /* Jump to reset vector */
|
| 1259 |
|
| 1260 | /* These are compiled and loaded into high memory, then
|
| 1261 | * copied into place in mach_early_setup */
|
| 1262 | .section .init.ivt, "ax"
|
| 1263 | #if CONFIG_MANUAL_RESET_VECTOR && !defined(CONFIG_MB_MANAGER)
|
| 1264 | .org 0x0
|
| 1265 | brai CONFIG_MANUAL_RESET_VECTOR
|
| 1266 | #elif defined(CONFIG_MB_MANAGER)
|
| 1267 | .org 0x0
|
| 1268 | brai TOPHYS(_xtmr_manager_reset);
|
| 1269 | #endif
|
| 1270 | .org 0x8
|
| 1271 | brai TOPHYS(_user_exception); /* syscall handler */
|
| 1272 | .org 0x10
|
| 1273 | brai TOPHYS(_interrupt); /* Interrupt handler */
|
| 1274 | #ifdef CONFIG_MB_MANAGER
|
| 1275 | .org 0x18
|
| 1276 | brai TOPHYS(_xmb_manager_break); /* microblaze manager break handler */
|
| 1277 | #else
|
| 1278 | .org 0x18
|
| 1279 | brai TOPHYS(_debug_exception); /* debug trap handler */
|
| 1280 | #endif
|
| 1281 | .org 0x20
|
| 1282 | brai TOPHYS(_hw_exception_handler); /* HW exception handler */
|
| 1283 |
|
| 1284 | #ifdef CONFIG_MB_MANAGER
|
| 1285 | /*
|
| 1286 | * For TMR Inject API which injects the error should
|
| 1287 | * be executed from LMB.
|
| 1288 | * TMR Inject is programmed with address of 0x200 so that
|
| 1289 | * when program counter matches with this address error will
|
| 1290 | * be injected. 0x200 is expected to be next available bram
|
| 1291 | * offset, hence used for this api.
|
| 1292 | */
|
| 1293 | .org XMB_INJECT_ERR_OFFSET
|
| 1294 | xmb_inject_error:
|
| 1295 | nop
|
| 1296 | rtsd r15, 8
|
| 1297 | nop
|
| 1298 | #endif
|
| 1299 |
|
| 1300 | .section .rodata,"a"
|
| 1301 | #include "syscall_table.S"
|
| 1302 |
|
| 1303 | syscall_table_size=(.-sys_call_table)
|
| 1304 |
|
| 1305 | type_SYSCALL:
|
| 1306 | .ascii "SYSCALL\0"
|
| 1307 | type_IRQ:
|
| 1308 | .ascii "IRQ\0"
|
| 1309 | type_IRQ_PREEMPT:
|
| 1310 | .ascii "IRQ (PREEMPTED)\0"
|
| 1311 | type_SYSCALL_PREEMPT:
|
| 1312 | .ascii " SYSCALL (PREEMPTED)\0"
|
| 1313 |
|
| 1314 | /*
|
| 1315 | * Trap decoding for stack unwinder
|
| 1316 | * Tuples are (start addr, end addr, string)
|
| 1317 | * If return address lies on [start addr, end addr],
|
| 1318 | * unwinder displays 'string'
|
| 1319 | */
|
| 1320 |
|
| 1321 | .align 4
|
| 1322 | .global microblaze_trap_handlers
|
| 1323 | microblaze_trap_handlers:
|
| 1324 | /* Exact matches come first */
|
| 1325 | .word ret_from_trap; .word ret_from_trap ; .word type_SYSCALL
|
| 1326 | .word ret_from_irq ; .word ret_from_irq ; .word type_IRQ
|
| 1327 | /* Fuzzy matches go here */
|
| 1328 | .word ret_from_irq ; .word no_intr_resched ; .word type_IRQ_PREEMPT
|
| 1329 | .word ret_from_trap; .word TRAP_return ; .word type_SYSCALL_PREEMPT
|
| 1330 | /* End of table */
|
| 1331 | .word 0 ; .word 0 ; .word 0
|