TLK: Clean up obsolete files
[3rdparty/ote_partner/tlk.git] / arch / arm / arm-m / thread.c
1 /*
2  * Copyright (c) 2012 Travis Geiselbrecht
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 #include <sys/types.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <debug.h>
27 #include <assert.h>
28 #include <kernel/thread.h>
29 #include <arch/arm.h>
30 #include <arch/arm/cm3.h>
31
32 #define LOCAL_TRACE 0
33
34 struct cm3_context_switch_frame {
35         uint32_t r4;
36         uint32_t r5;
37         uint32_t r6;
38         uint32_t r7;
39         uint32_t r8;
40         uint32_t r9;
41         uint32_t r10;
42         uint32_t r11;
43         uint32_t lr;
44 };
45
46 static void initial_thread_func(void) __NO_RETURN;
47 static void initial_thread_func(void)
48 {
49         int ret;
50
51         LTRACEF("thread %p calling %p with arg %p\n", current_thread, current_thread->entry, current_thread->arg);
52 #if LOCAL_TRACE
53         dump_thread(current_thread);
54 #endif
55
56         /* exit the implicit critical section we're within */
57         exit_critical_section();
58
59         ret = current_thread->entry(current_thread->arg);
60
61         LTRACEF("thread %p exiting with %d\n", current_thread, ret);
62
63         thread_exit(ret);
64 }
65
66 void arch_thread_initialize(struct thread *t)
67 {
68         LTRACEF("thread %p, stack %p\n", t, t->stack);
69
70         /* find the top of the stack and align it on an 8 byte boundary */
71         uint32_t *sp = (void *)ROUNDDOWN((vaddr_t)t->stack + t->stack_size, 8);
72
73         struct cm3_context_switch_frame *frame = (void *)sp;
74         frame--;
75
76         /* arrange for lr to point to our starting routine */
77         frame->lr = (uint32_t)&initial_thread_func;
78
79         t->arch.sp = (addr_t)frame;
80         t->arch.was_preempted = false;
81 }
82
83 volatile struct cm3_exception_frame_long *preempt_frame;
84
85 static void pendsv(struct cm3_exception_frame_long *frame)
86 {
87         arch_disable_ints();
88         inc_critical_section();
89
90         ASSERT(critical_section_count == 1);
91
92         LTRACEF("preempting thread %p (%s)\n", current_thread, current_thread->name);
93
94         /* save the iframe the pendsv fired on and hit the preemption code */
95         preempt_frame = frame;
96         thread_preempt();
97
98         LTRACEF("fell through\n");
99
100         /* if we got here, there wasn't anything to switch to, so just fall through and exit */
101         preempt_frame = NULL;
102
103         dec_critical_section();
104         arch_enable_ints();
105 }
106
107 /* 
108  * raw pendsv exception handler, triggered by interrupt glue to schedule
109  * a preemption check.
110  */
111 __NAKED void _pendsv(void)
112 {
113         __asm__ volatile(
114             "push       { r4-r11, lr };"
115             "mov        r0, sp;"
116             "bl         %0;"
117             "pop        { r4-r11, lr };"
118             "bx         lr;"
119             :: "i" (pendsv)
120         );
121         __UNREACHABLE;
122 }
123
124 /* 
125  * svc handler, used to hard switch the cpu into exception mode to return
126  * to preempted thread.
127  */
128 __NAKED void _svc(void)
129 {
130         __asm__ volatile(
131                 /* load the pointer to the original exception frame we want to restore */
132                 "mov    sp, r4;"
133                 "pop    { r4-r11, lr };"
134                 "bx             lr;"
135         );
136 }
137
138 __NAKED static void _half_save_and_svc(vaddr_t *fromsp, vaddr_t tosp)
139 {
140         __asm__ volatile(
141                 "push   { r4-r11, lr };"
142                 "str    sp, [r0];"
143
144                 /* make sure we load the destination sp here before we reenable interrupts */
145                 "mov    sp, r1;"
146
147                 "clrex;"
148                 "cpsie  i;"
149
150                 "mov    r4, r1;"
151                 "svc #0;" /* make a svc call to get us into handler mode */
152         );      
153 }
154
155 /* simple scenario where the to and from thread yielded */
156 __NAKED static void _arch_non_preempt_context_switch(vaddr_t *fromsp, vaddr_t tosp)
157 {
158         __asm__ volatile(
159                 "push   { r4-r11, lr };"
160                 "str    sp, [r0];"
161
162                 "mov    sp, r1;"
163                 "pop    { r4-r11, lr };"
164                 "clrex;"
165                 "bx             lr;"
166         );
167 }
168
169 __NAKED static void _thread_mode_bounce(void)
170 {
171         __asm__ volatile(
172                 "pop    { r4-r11, lr };"
173                 "bx             lr;"
174         );
175         __UNREACHABLE;
176 }
177
178 /*
179  * The raw context switch routine. Called by the scheduler when it decides to switch.
180  * Called either in the context of a thread yielding or blocking (interrupts disabled,
181  * on the system stack), or inside the pendsv handler on a thread that is being preempted
182  * (interrupts disabled, in handler mode). If preempt_frame is set the thread
183  * is being preempted.
184  */
185 void arch_context_switch(struct thread *oldthread, struct thread *newthread)
186 {
187         LTRACE_ENTRY;
188
189         if (newthread->arch.was_preempted) {
190                 /* we're about to return directly to a thread that was preempted (in user space),
191                  * so push its critical section count back down to zero
192                  */
193                 critical_section_count = newthread->saved_critical_section_count = 0;
194         }
195
196         /* if preempt_frame is set, we are being preempted */
197         if (preempt_frame) {
198                 oldthread->arch.was_preempted = true;
199                 oldthread->arch.sp = (addr_t)preempt_frame;
200                 preempt_frame = NULL;
201
202                 LTRACEF("we're preempted, new %d\n", newthread->arch.was_preempted);
203                 if (newthread->arch.was_preempted) {
204                         /* return directly to the preempted thread's iframe */
205                         __asm__ volatile(
206                             "mov        sp, %0;"
207                             "cpsie      i;"
208                             "pop        { r4-r11, lr };"
209                             "clrex;"
210                             "bx         lr;"
211                             :: "r"(newthread->arch.sp)
212                         );
213                         __UNREACHABLE;
214                 } else {
215                         /* we're inside a pendsv, switching to a user mode thread */
216                         /* set up a fake frame to exception return to */
217                         struct cm3_exception_frame_short *frame = (void *)newthread->arch.sp;
218                         frame--;
219
220                         frame->pc = (uint32_t)&_thread_mode_bounce;
221                         frame->psr = (1 << 24); /* thread bit set, IPSR 0 */
222                         frame->r0 = frame->r1 =  frame->r2 = frame->r3 = frame->r12 = frame->lr = 99;
223
224                         LTRACEF("iretting to user space\n");
225                         //hexdump(frame, sizeof(*frame) + 64);
226
227                         __asm__ volatile(
228                             "clrex;"
229                             "mov        sp, %0;"
230                             "bx         %1;"
231                             :: "r"(frame), "r"(0xfffffff9)
232                         );
233                         __UNREACHABLE;
234                 }
235         } else {
236                 oldthread->arch.was_preempted = false;
237
238                 if (newthread->arch.was_preempted) {
239                         LTRACEF("not being preempted, but switching to preempted thread\n");
240                         _half_save_and_svc(&oldthread->arch.sp, newthread->arch.sp);
241                 } else {
242                         /* fast path, both sides did not preempt */
243                         _arch_non_preempt_context_switch(&oldthread->arch.sp, newthread->arch.sp);
244                 }
245         }
246
247 }
248