Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARC: pt_regs update #4: r25 saved/restored unconditionally

(This is a VERY IMP change for low level interrupt/exception handling)

-----------------------------------------------------------------------
WHAT
-----------------------------------------------------------------------
* User 25 now saved in pt_regs->user_r25 (vs. tsk->thread_info.user_r25)

* This allows Low level interrupt code to unconditionally save r25
(vs. the prev version which would only do it for U->K transition).
Ofcourse for nested interrupts, only the pt_regs->user_r25 of
bottom-most frame is useful.

* simplifies the interrupt prologue/epilogue

* Needed for ARCv2 ISA code and done here to keep design similar with
ARCompact event handling

-----------------------------------------------------------------------
WHY
-------------------------------------------------------------------------
With CONFIG_ARC_CURR_IN_REG, r25 is used to cache "current" task pointer
in kernel mode. So when entering kernel mode from User Mode
- user r25 is specially safe-kept (it being a callee reg is NOT part of
pt_regs which are saved by default on each interrupt/trap/exception)
- r25 loaded with current task pointer.

Further, if interrupt was taken in kernel mode, this is skipped since we
know that r25 already has valid "current" pointer.

With 2 level of interrupts in ARCompact ISA, detecting this is difficult
but still possible, since we could be in kernel mode but r25 not already saved
(in fact the stack itself might not have been switched).

A. User mode
B. L1 IRQ taken
C. L2 IRQ taken (while on 1st line of L1 ISR)

So in #C, although in kernel mode, r25 not saved (infact SP not
switched at all)

Given that ARcompact has manual stack switching, we could use a bit of
trickey - The low level code would make sure that SP is only set to kernel
mode value at the very end (after saving r25). So a non kernel mode SP,
even if in kernel mode, meant r25 was NOT saved.

The same paradigm won't work in ARCv2 ISA since SP is auto-switched so
it's setting can't be delayed/constrained.

Signed-off-by: Vineet Gupta <vgupta@synopsys.com>

+24 -40
+20 -23
arch/arc/include/asm/entry.h
··· 102 102 POP r2 103 103 POP r1 104 104 POP r0 105 + 106 + #ifdef CONFIG_ARC_CURR_IN_REG 107 + ld r25, [sp, 12] 108 + #endif 105 109 .endm 106 110 107 111 /*-------------------------------------------------------------- ··· 142 138 POP r13 143 139 .endm 144 140 141 + #define OFF_USER_R25_FROM_R24 (SZ_CALLEE_REGS + SZ_PT_REGS - 8)/4 145 142 146 143 /*-------------------------------------------------------------- 147 144 * Collect User Mode callee regs as struct callee_regs - needed by ··· 160 155 161 156 #ifdef CONFIG_ARC_CURR_IN_REG 162 157 ; Retrieve orig r25 and save it on stack 163 - ld r12, [r25, TASK_THREAD + THREAD_USER_R25] 158 + ld.as r12, [sp, OFF_USER_R25_FROM_R24] 164 159 st.a r12, [sp, -4] 165 160 #else 166 161 PUSH r25 ··· 209 204 210 205 #ifdef CONFIG_ARC_CURR_IN_REG 211 206 ld.ab r12, [sp, 4] 212 - st r12, [r25, TASK_THREAD + THREAD_USER_R25] 207 + st.as r12, [sp, OFF_USER_R25_FROM_R24] 213 208 #else 214 209 POP r25 215 210 #endif ··· 221 216 *-------------------------------------------------------------*/ 222 217 .macro DISCARD_CALLEE_SAVED_USER 223 218 add sp, sp, SZ_CALLEE_REGS 224 - .endm 225 - 226 - /*-------------------------------------------------------------- 227 - * Restore User mode r25 saved in task_struct->thread.user_r25 228 - *-------------------------------------------------------------*/ 229 - .macro RESTORE_USER_R25 230 - ld r25, [r25, TASK_THREAD + THREAD_USER_R25] 231 219 .endm 232 220 233 221 /*------------------------------------------------------------- ··· 295 297 296 298 GET_CURR_TASK_ON_CPU r9 297 299 298 - #ifdef CONFIG_ARC_CURR_IN_REG 299 - 300 - /* If current task pointer cached in r25, time to 301 - * -safekeep USER r25 in task->thread_struct->user_r25 302 - * -load r25 with current task ptr 303 - */ 304 - st.as r25, [r9, (TASK_THREAD + THREAD_USER_R25)/4] 305 - mov r25, r9 306 - #endif 307 - 308 300 /* With current tsk in r9, get it's kernel mode stack base */ 309 301 GET_TSK_STACK_BASE r9, r9 310 302 311 303 66: 304 + #ifdef CONFIG_ARC_CURR_IN_REG 305 + /* 306 + * Treat r25 as scratch reg, save it on stack first 307 + * Load it with current task pointer 308 + */ 309 + st r25, [r9, -4] 310 + GET_CURR_TASK_ON_CPU r25 311 + #endif 312 + 312 313 /* Save Pre Intr/Exception User SP on kernel stack */ 313 - st.a sp, [r9, -12] ; Make room for orig_r0 and orig_r8 314 + st.a sp, [r9, -16] ; Make room for orig_r0, orig_r8, user_r25 314 315 315 316 /* CAUTION: 316 317 * SP should be set at the very end when we are done with everything ··· 463 466 RESTORE_R12_TO_R0 464 467 465 468 ld sp, [sp] /* restore original sp */ 466 - /* orig_r0 and orig_r8 skipped automatically */ 469 + /* orig_r0, orig_r8, user_r25 skipped automatically */ 467 470 .endm 468 471 469 472 ··· 546 549 RESTORE_R12_TO_R0 547 550 548 551 ld sp, [sp] /* restore original sp */ 549 - /* orig_r0 and orig_r8 skipped automatically */ 552 + /* orig_r0, orig_r8, user_r25 skipped automatically */ 550 553 .endm 551 554 552 555 .macro RESTORE_ALL_INT2 ··· 565 568 RESTORE_R12_TO_R0 566 569 567 570 ld sp, [sp] /* restore original sp */ 568 - /* orig_r0 and orig_r8 skipped automatically */ 571 + /* orig_r0, orig_r8, user_r25 skipped automatically */ 569 572 .endm 570 573 571 574
-3
arch/arc/include/asm/processor.h
··· 30 30 unsigned long callee_reg; /* pointer to callee regs */ 31 31 unsigned long fault_address; /* dbls as brkpt holder as well */ 32 32 unsigned long cause_code; /* Exception Cause Code (ECR) */ 33 - #ifdef CONFIG_ARC_CURR_IN_REG 34 - unsigned long user_r25; 35 - #endif 36 33 #ifdef CONFIG_ARC_FPU_SAVE_RESTORE 37 34 struct arc_fpu fpu; 38 35 #endif
+2
arch/arc/include/asm/ptrace.h
··· 54 54 #endif 55 55 long orig_r8_word; 56 56 }; 57 + 58 + long user_r25; 57 59 }; 58 60 59 61 /* Callee saved registers - need to be saved only when you are scheduled out */
+1 -3
arch/arc/kernel/asm-offsets.c
··· 24 24 25 25 DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp)); 26 26 DEFINE(THREAD_CALLEE_REG, offsetof(struct thread_struct, callee_reg)); 27 - #ifdef CONFIG_ARC_CURR_IN_REG 28 - DEFINE(THREAD_USER_R25, offsetof(struct thread_struct, user_r25)); 29 - #endif 30 27 DEFINE(THREAD_FAULT_ADDR, 31 28 offsetof(struct thread_struct, fault_address)); 32 29 ··· 58 61 DEFINE(PT_r7, offsetof(struct pt_regs, r7)); 59 62 60 63 DEFINE(SZ_CALLEE_REGS, sizeof(struct callee_regs)); 64 + DEFINE(SZ_PT_REGS, sizeof(struct pt_regs)); 61 65 return 0; 62 66 }
-11
arch/arc/kernel/entry.S
··· 680 680 ; XXX can this be optimised out 681 681 IRQ_DISABLE_SAVE r9, r10 ;@r10 has prisitine (pre-disable) copy 682 682 683 - #ifdef CONFIG_ARC_CURR_IN_REG 684 - ; Restore User R25 685 - ; Earlier this used to be only for returning to user mode 686 - ; However with 2 levels of IRQ this can also happen even if 687 - ; in kernel mode 688 - ld r9, [sp, PT_sp] 689 - brhs r9, VMALLOC_START, 8f 690 - RESTORE_USER_R25 691 - 8: 692 - #endif 693 - 694 683 ; Restore REG File. In case multiple Events outstanding, 695 684 ; use the same priorty as rtie: EXCPN, L2 IRQ, L1 IRQ, None 696 685 ; Note that we use realtime STATUS32 (not pt_regs->status32) to
+1
arch/arc/kernel/process.c
··· 77 77 * | SP | 78 78 * | orig_r0 | 79 79 * | orig_r8 | 80 + * | user_r25 | 80 81 * ------------------ <===== END of PAGE 81 82 */ 82 83 int copy_thread(unsigned long clone_flags,