disable sstrip when using musl
[lede.git] / target / linux / ubicom32 / files / arch / ubicom32 / kernel / ubicom32_context_switch.S
1 /*
2  * arch/ubicom32/kernel/ubicom32_context_switch.S
3  *      Implements context switch and return functions.
4  *
5  * (C) Copyright 2009, Ubicom, Inc.
6  *
7  * This file is part of the Ubicom32 Linux Kernel Port.
8  *
9  * The Ubicom32 Linux Kernel Port is free software: you can redistribute
10  * it and/or modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation, either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * The Ubicom32 Linux Kernel Port is distributed in the hope that it
15  * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
17  * the GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with the Ubicom32 Linux Kernel Port.  If not,
21  * see <http://www.gnu.org/licenses/>.
22  *
23  * Ubicom32 implementation derived from (with many thanks):
24  *   arch/m68knommu
25  *   arch/blackfin
26  *   arch/parisc
27  */
28 #include <linux/sys.h>
29 #include <linux/linkage.h>
30 #include <asm/asm-offsets.h>
31 #include <asm/ubicom32-common.h>
32 #include <asm/ip5000.h>
33 #include <asm/range-protect.h>
34
35 /*
36  * begin_restore_context()
37  *      Restore most of the context from sp (struct pt_reg *)
38  *
39  * This *can* be called without the global atomic lock. (because sp is
40  * not restored!)  Only d15 and a3 are allowed to be used after this
41  * before calling complete_restore_context
42  */
43 .macro begin_restore_context
44         move.4  d0, PT_D0(sp)
45         move.4  d1, PT_D1(sp)
46         move.4  d2, PT_D2(sp)
47         move.4  d3, PT_D3(sp)
48         move.4  d4, PT_D4(sp)
49         move.4  d5, PT_D5(sp)
50         move.4  d6, PT_D6(sp)
51         move.4  d7, PT_D7(sp)
52         move.4  d8, PT_D8(sp)
53         move.4  d9, PT_D9(sp)
54         move.4  d10, PT_D10(sp)
55         move.4  d11, PT_D11(sp)
56         move.4  d12, PT_D12(sp)
57         move.4  d13, PT_D13(sp)
58         move.4  d14, PT_D14(sp)
59 ;;      move.4  d15, PT_D15(sp)
60         move.4  a0, PT_A0(sp)
61         move.4  a1, PT_A1(sp)
62         move.4  a2, PT_A2(sp)
63 ;;      move.4  a3, PT_A3(sp)
64         move.4  a4, PT_A4(sp)
65         move.4  a5, PT_A5(sp)
66         move.4  a6, PT_A6(sp)
67         move.4  acc0_hi, PT_ACC0HI(sp)
68         move.4  acc0_lo, PT_ACC0LO(sp)
69         move.4  mac_rc16, PT_MAC_RC16(sp)
70         move.4  acc1_hi, PT_ACC1HI(sp)
71         move.4  acc1_lo, PT_ACC1LO(sp)
72         move.4  source3, PT_SOURCE3(sp)
73         move.4  int_mask0, PT_INT_MASK0(sp)
74         move.4  int_mask1, PT_INT_MASK1(sp)
75 .endm
76
77 /*
78  * complete_restore_context()
79  *      Completely restore the context from sp (struct pt_reg *)
80  *
81  * Note: Recovered PC and CSR are saved on the stack and are to be
82  * popped off before returning.
83  */
84 .macro complete_restore_context
85         move.4  a3, sp
86         move.4  d15, PT_D15(sp)
87         move.4  sp, PT_SP(a3)           ; Recover Stack pointer from save area
88         move.4  -4(sp)++, PT_PC(a3)     ; Recover saved PC and save to stack
89         move.4  -4(sp)++, PT_CSR(a3)    ; Recover saved csr and save to stack
90         move.4  a3, PT_A3(a3)
91 .endm
92
93 /*
94  * old restore_context macro
95  */
96 .macro restore_context
97         begin_restore_context
98         complete_restore_context
99 .endm
100
101 /*
102  * ldsr_thread_enable_interrupts()
103  *      An assembly version of the enable interrupts function.
104  *
105  * The stack is fair game but all registers MUST be preserved.
106  *
107  */
108 .macro ldsr_thread_enable_interrupts
109         move.4  -4(sp)++, d3    ; Push d3
110         move.4  -4(sp)++, a3    ; Push a3
111
112         /*
113          * Read the ROSR and obtain ~(1 << tid)
114          */
115         lsr.4   d3, rosr, #0x2  ; Move the thread portion of ROSR into d3
116         lsl.4   d3, #1, d3      ; perform a (1 << tid)
117         not.4   d3, d3          ; Negate the value of d3 == ~(1 << threadid)
118
119         /*
120          * Get the value of the ldsr_soft_irq_mask
121          */
122         moveai  a3, #%hi(ldsr_soft_irq_mask)
123         move.4  a3, %lo(ldsr_soft_irq_mask)(a3)
124
125         /*
126          * Now re-enable interrupts for this thread and then
127          * wakeup the LDSR.
128          */
129         and.4   scratchpad1, scratchpad1, d3
130         move.4  int_set0, a3
131
132         /*
133          * Restore the registers.
134          */
135         move.4  a3, (sp)4++
136         move.4  d3, (sp)4++
137 .endm
138
139 /*
140  * ret_from_interrupt_to_kernel()
141  *      RFI function that is where do_IRQ() returns to if the thread was
142  *      in kernel space.
143  */
144         .section .text.ret_from_interrupt_to_kernel, "ax", @progbits
145         .global ret_from_interrupt_to_kernel
146 ret_from_interrupt_to_kernel:
147         begin_restore_context           ; Restore the thread context
148         atomic_lock_acquire             ; Enter critical section
149         complete_restore_context        ; Restore the thread context
150         atomic_lock_release             ; Leave critical section
151         ldsr_thread_enable_interrupts   ; enable the threads interrupts
152         move.4  csr, (sp)4++            ; Restore csr from the stack
153         ret     (sp)4++
154
155 /*
156  * ret_from_interrupt_to_user()
157  *      RFI function that is where do_IRQ() returns to if the thread was
158  *      in user space.
159  *
160  * TODO: Do we really need the critical section handling in this code?
161  *
162  */
163         .section .text.ret_from_interrupt_to_user, "ax", @progbits
164         .global ret_from_interrupt_to_user
165 ret_from_interrupt_to_user:
166         ldsr_thread_enable_interrupts                   ; enable the threads interrupts
167         /*
168          * Set a1 to the thread info pointer, no need to save it as we are
169          * restoring userspace and will never return
170          */
171         movei   d0, #(~(ASM_THREAD_SIZE-1))
172         and.4   a1, sp, d0
173
174         /*
175          * Test if the scheduler needs to be called.
176          */
177         btst    TI_FLAGS(a1), #ASM_TIF_NEED_RESCHED
178         jmpeq.t 2f
179         call    a5, schedule                    ; Call the scheduler. I will come back here.
180
181         /*
182          * See if we have pending signals and call do_signal
183          * if needed.
184          */
185 2:
186         btst    TI_FLAGS(a1), #ASM_TIF_SIGPENDING       ; Any signals needed?
187         jmpeq.t 1f
188
189         /*
190          * Now call do_signal()
191          */
192         move.4  d0, #0                                  ; oldset pointer is NULL
193         move.4  d1, sp                                  ; d1 is the regs pointer
194         call    a5, do_signal                           ; Call do_signal()
195
196         /*
197          * Back from do_signal(), re-enter critical section.
198          */
199 1:
200         begin_restore_context                           ; Restore the thread context
201         atomic_lock_acquire                             ; Enter critical section
202         call a3, __complete_and_return_to_userspace     ; jump to unprotected section
203
204 /*
205  * restore_all_registers()
206  *
207  * restore_all_registers will be the alternate exit route for
208  * preempted processes that have called a signal handler
209  * and are returning back to user space.
210  */
211         .section .text.restore_all_registers, "ax", @progbits
212         .global restore_all_registers
213 restore_all_registers:
214         begin_restore_context                   ; Restore the thread context
215         atomic_lock_acquire                     ; Enter critical section
216         call a3, __complete_and_return_to_userspace
217
218 /*
219  * __complete_and_return_to_userspace
220  *
221  * restores the second half of the context and returns
222  * You must have the atomic lock when you call this function
223  */
224         .section .kernel_unprotected, "ax", @progbits
225 __complete_and_return_to_userspace:
226         disable_kernel_ranges_for_current d15   ; disable kernel ranges
227         complete_restore_context                ; restore previous context
228         atomic_lock_release                     ; Leave critical section
229         move.4  csr, (sp)4++                    ; Restore csr from the stack
230         ret     (sp)4++
231
232 /*
233  * ret_from_fork()
234  *      Called on the child's return from fork system call.
235  */
236         .section .text.ret_from_fork, "ax", @progbits
237         .global ret_from_fork
238 ret_from_fork:
239         ;;;  d0 contains the arg for schedule_tail
240         ;;;  the others we don't care about as they are in PT_REGS (sp)
241         call   a5, schedule_tail
242
243         atomic_lock_acquire             ; Enter critical section
244
245         move.4  a3, sp
246         move.4  d0, PT_D0(a3)           ; Restore D0
247         move.4  d1, PT_D1(a3)           ; Restore D1
248         move.4  d2, PT_D2(a3)           ; Restore D2
249         move.4  d3, PT_D3(a3)           ; Restore D3
250         move.4  d10, PT_D10(a3)         ; Restore D10
251         move.4  d11, PT_D11(a3)         ; Restore D11
252         move.4  d12, PT_D12(a3)         ; Restore D12
253         move.4  d13, PT_D13(a3)         ; Restore D13
254         move.4  a1, PT_A1(a3)           ; Restore A1
255         move.4  a2, PT_A2(a3)           ; Restore A2
256         move.4  a5, PT_A5(a3)           ; Restore A5
257         move.4  a6, PT_A6(a3)           ; Restore A6
258         ;;  I think atomic_lock_acquire could be moved here..
259         move.4  sp, PT_SP(a3)           ; Restore sp
260         move.4  a4, PT_PC(a3)           ; Restore pc in register a4
261         move.4  PT_FRAME_TYPE(a3), #0   ; Clear frame_type to indicate it is invalid.
262
263 #ifdef CONFIG_PROTECT_KERNEL
264         call a3, __ret_from_fork_bottom_half
265         .section .kernel_unprotected, "ax", @progbits
266 __ret_from_fork_bottom_half:
267         disable_kernel_ranges_for_current d15
268 #endif
269         atomic_lock_release             ; Leave critical section
270         calli   a4, 0(a4)               ; Return.
271
272 /*
273  * __switch_to()
274  *
275  * Call with:
276  *      void *__switch_to(struct task_struct *prev, struct thread_struct *prev_switch,
277  *                              struct thread_struct *next_switch)
278  */
279         .section .text.__switch_to, "ax", @progbits
280         .global __switch_to
281 __switch_to:
282
283         /*
284          * Set up register a3 to point to save area.
285          */
286         movea   a3, d1                  ; a3 now holds prev_switch
287         move.4  (a3)4++, d10
288         move.4  (a3)4++, d11
289         move.4  (a3)4++, d12
290         move.4  (a3)4++, d13
291         move.4  (a3)4++, a1
292         move.4  (a3)4++, a2
293         move.4  (a3)4++, a5
294         move.4  (a3)4++, a6
295         move.4  (a3)4++, a7
296
297         /*
298          * Set up register a3 to point to restore area.
299          */
300         movea   a3, d2                  ; a3 now holds next_switch
301         move.4  d10 , (a3)4++
302         move.4  d11 , (a3)4++
303         move.4  d12 , (a3)4++
304         move.4  d13 , (a3)4++
305         move.4  a1 , (a3)4++
306         move.4  a2 , (a3)4++
307         move.4  a5 , (a3)4++
308         move.4  a6 , (a3)4++
309         move.4  a7 , (a3)4++
310
311         /*
312          * Load the sw_ksp with the proper thread_info pointer.
313          */
314         movei   d15, #(~(ASM_THREAD_SIZE-1))
315         and.4   a3, sp, d15             ; a3 now has the thread info pointer
316         moveai  a4, #%hi(sw_ksp)
317         lea.1   a4, %lo(sw_ksp)(a4)     ; a4 now has the base address of sw_ksp array
318         lsr.4   d15, ROSR, #2           ; Thread number - bit's 6 through 31 are zeroes anyway.
319         move.4  (a4, d15), a3           ; Load the thread info pointer into the hw_ksp array..
320
321         /*
322          * We are done with context switch. Time to return..
323          */
324         calli   a5, 0(a5)
325         .size __switch_to, . - __switch_to
326
327 /*
328  * ubicom32_emulate_insn()
329  *      Emulates the instruction.
330  *
331  * Call with:
332  *      unsigned int ubicom32_emulate_insn(int source1, int source2, int source3, int *save_acc, int *save_csr);
333  */
334         .section .text.ubicom32_emulate_insn, "ax", @progbits
335         .global ubicom32_emulate_insn
336         .global trap_emulate
337 ubicom32_emulate_insn:
338         movea   a3, d3          ; a3 holds save_acc pointer
339         movea   a4, d4          ; a4 hods save_csr pointer
340         move.4  source3, d2
341         move.4  acc0_lo, (a3)
342         move.4  acc0_hi, 4(a3)
343         move.4  acc1_lo, 8(a3)
344         move.4  acc1_hi, 12(a3)
345         move.4  mac_rc16, 16(a3)
346         move.4  CSR, (a4)
347         setcsr_flush 0
348
349 trap_emulate:
350         move.4  d0, d1
351         setcsr_flush 0
352         move.4  (a4), CSR       ; Save csr
353         move.4  (a3), acc0_lo
354         move.4  4(a3), acc0_hi
355         move.4  8(a3), acc1_lo
356         move.4  12(a3), acc1_hi
357         move.4  16(a3), mac_rc16
358         ret     a5
359         .size ubicom32_emulate_insn, . - ubicom32_emulate_insn