6195eabaf31fd060eafc59f47bd12c56c2232d35
[barrelfish] / lib / barrelfish / arch / arm / dispatch.c
1 /**
2  * \file
3  * \brief Dispatcher architecture-specific implementation.
4  */
5
6 /*
7  * Copyright (c) 2007, 2008, 2009, 2010, 2012, ETH Zurich.
8  * All rights reserved.
9  *
10  * This file is distributed under the terms in the attached LICENSE file.
11  * If you do not find this file, copies can be found by writing to:
12  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13  */
14
15 #include <barrelfish/barrelfish.h>
16 #include <barrelfish/dispatch.h>
17 #include <barrelfish/dispatcher_arch.h>
18 #include <barrelfish/syscalls.h>
19 #include <barrelfish/static_assert.h>
20 #include "threads_priv.h"
21 #include <stdio.h>//for debugging printf
22
23 #include <asmoffsets.h>
24 #ifndef OFFSETOF_DISP_DISABLED
25 #error "Pants!"
26 #endif
27
28 /* entry points defined in assembler code */
29 extern void run_entry(void);
30 extern void pagefault_entry(void);
31 extern void disabled_pagefault_entry(void);
32 extern void trap_entry(void);
33
34 void __attribute__ ((visibility ("hidden"))) disp_resume_context_epilog(void);
35 void __attribute__ ((visibility ("hidden"))) disp_switch_epilog(void);
36 void __attribute__ ((visibility ("hidden"))) disp_save_epilog(void);
37 void __attribute__ ((visibility ("hidden"))) disp_save_rm_kcb_epilog(void);
38
39 ///////////////////////////////////////////////////////////////////////////////
40 //
41 // Low level "C" context switch related code
42 //
43
44 STATIC_ASSERT(CPSR_REG == 0,  "broken context assumption");
45 STATIC_ASSERT(NUM_REGS == 17, "broken context assumption");
46 STATIC_ASSERT(PC_REG   == 16, "broken context assumption");
47
48
49 /*
50  * XXX: there is no guarantee that the context has been set up by
51  * disp_save_context, so we can not cut corners by not restoring registers
52  * clobbered in disp_save_context.
53  * e.g. when a new thread is created, it is started using this function, with r0 and r1
54  * being arguments.
55  */
56 static void __attribute__((naked)) __attribute__((noinline))
57 disp_resume_context(struct dispatcher_shared_generic *disp, uint32_t *regs)
58 {
59     __asm volatile(
60         "    clrex\n\t"
61         /* Re-enable dispatcher */
62         "    mov     r2, #0                                             \n\t"
63         "    str     r2, [r0, # " XTR(OFFSETOF_DISP_DISABLED) "]        \n\t"
64         /* Restore cpsr condition bits  */
65         "    ldr     r0, [r1], #4                                       \n\t"
66         "    msr     cpsr, r0                                           \n\t"
67         /* Restore registers */
68         "    ldmia   r1, {r0-r15}                                       \n\t"
69         "disp_resume_context_epilog:                                    \n\t"
70         "    mov     r0, r0          ; nop                              \n\t"
71                   );
72 }
73
74
75 static void __attribute__((naked))
76 disp_save_context(uint32_t *regs)
77 {
78 //use normal arm assembly
79     __asm volatile(
80         "    clrex\n\t"
81         "    mrs     r1, cpsr                                           \n\t"
82         "    adr     r2, disp_save_context_resume                       \n\t"
83         "    stmib   r0, {r0-r14}                                       \n\t"
84         "    str     r1, [r0]                                           \n\t"
85         "    str     r2, [r0, # (" XTR(PC_REG) "  * 4)]                 \n\t"
86         "disp_save_context_resume:                                      \n\t"
87         "    bx      lr                                                 \n\t"
88         );
89 }
90
91 ///////////////////////////////////////////////////////////////////////////////
92
93 /**
94  * \brief Resume execution of a given register state
95  *
96  * This function resumes the execution of the given register state on the
97  * current dispatcher. It may only be called while the dispatcher is disabled.
98  *
99  * \param disp Current dispatcher pointer
100  * \param regs Register state snapshot
101  */
102 void
103 disp_resume(dispatcher_handle_t handle,
104             arch_registers_state_t *archregs)
105
106 {
107     struct dispatcher_shared_arm *disp =
108         get_dispatcher_shared_arm(handle);
109
110     // The definition of disp_resume_end is a totally flakey. The system
111     // uses the location of the PC to determine where to spill the thread
112     // context for exceptions and interrupts. There are two safe ways of doing
113     // this:
114     //
115     // 1) Write this entire function in assmebler.
116     // 2) Write this function in C and write a linker script to emit
117     //    function bounds.
118
119     assert_disabled(curdispatcher() == handle);
120     assert_disabled(disp->d.disabled);
121     assert_disabled(disp->d.haswork);
122
123 #ifdef CONFIG_DEBUG_DEADLOCKS
124     ((struct disp_priv *)disp)->yieldcount = 0;
125 #endif
126
127     disp_resume_context(&disp->d, archregs->regs);
128 }
129
130 /**
131  * \brief Switch execution between two register states
132  *
133  * This function saves as much as necessary of the current register state
134  * (which, when resumed will return to the caller), and switches execution
135  * by resuming the given register state.  It may only be called while the
136  * dispatcher is disabled.
137  *
138  * \param disp Current dispatcher pointer
139  * \param from_regs Location to save current register state
140  * \param to_regs Location from which to resume new register state
141  */
142 void disp_switch(dispatcher_handle_t handle,
143                  arch_registers_state_t *from_state,
144                  arch_registers_state_t *to_state)
145 {
146     struct dispatcher_shared_arm *disp =
147         get_dispatcher_shared_arm(handle);
148
149     // make sure arguments survive call to disp_save_context()
150     // not sure if our code has a subtle bug or whether ARMv7 GCC
151     // (arm-linux-gnueabi-gcc (Ubuntu/Linaro 5.3.1-13ubuntu3) 5.3.1 20160330)
152     // is overeager when optimizing code that calls into
153     // __attribute__((naked)) functions. -SG, 2016-04-06
154     __asm volatile("" : /*out*/ : /*in*/ : "r0", "r1", "r2" );
155
156     assert_disabled(curdispatcher() == handle);
157     assert_disabled(disp->d.disabled);
158     assert_disabled(disp->d.haswork);
159     assert_disabled(to_state != NULL);
160
161     disp_save_context(from_state->regs);
162     from_state->named.pc = (lvaddr_t)disp_switch_epilog;
163     disp_resume_context(&disp->d, to_state->regs);
164
165     __asm volatile("disp_switch_epilog:");
166 }
167
168 /**
169  * \brief Save the current register state and optionally yield the CPU
170  *
171  * This function saves as much as necessary of the current register state
172  * (which, when resumed will return to the caller), and then either
173  * re-enters the thread scheduler or yields the CPU.
174  * It may only be called while the dispatcher is disabled.
175  *
176  * \param disp Current dispatcher pointer
177  * \param regs Location to save current register state
178  * \param yield If true, yield CPU to kernel; otherwise re-run thread scheduler
179  * \param yield_to Endpoint capability for dispatcher to which we want to yield
180  */
181 void disp_save(dispatcher_handle_t handle,
182                arch_registers_state_t *state,
183                bool yield, capaddr_t yield_to)
184 {
185     struct dispatcher_shared_arm *disp =
186         get_dispatcher_shared_arm(handle);
187
188     // make sure arguments survive call to disp_save_context()
189     // not sure if our code has a subtle bug or whether ARMv7 GCC
190     // (arm-linux-gnueabi-gcc (Ubuntu/Linaro 5.3.1-13ubuntu3) 5.3.1 20160330)
191     // is overeager when optimizing code that calls into
192     // __attribute__((naked)) functions. -SG, 2016-04-06
193     __asm volatile("" : /*out*/ : /*in*/ : "r0", "r1", "r2" );
194
195     assert_disabled(curdispatcher() == handle);
196     assert_disabled(disp->d.disabled);
197
198     disp_save_context(state->regs);
199     state->named.pc = (lvaddr_t)disp_save_epilog;
200
201     if (yield) {
202         sys_yield(yield_to);
203         // may fail if target doesn't exist; if so, just fall through
204     }
205     // this code won't run if the yield succeeded
206
207     // enter thread scheduler again
208     // this doesn't return, and will call disp_yield if there's nothing to do
209     thread_run_disabled(handle);
210
211     __asm volatile("disp_save_epilog:");
212 }
213
214 void disp_save_rm_kcb(void)
215 {
216     dispatcher_handle_t handle = disp_disable();
217     struct dispatcher_shared_arm *disp =
218         get_dispatcher_shared_arm(handle);
219     arch_registers_state_t *state =
220         dispatcher_get_enabled_save_area(handle);
221
222     assert_disabled(curdispatcher() == handle);
223     assert_disabled(disp->d.disabled);
224
225     disp_save_context(state->regs);
226     state->named.pc = (lvaddr_t)disp_save_rm_kcb_epilog;
227
228     sys_suspend(false);
229
230     // enter thread scheduler again
231     // this doesn't return, and will call disp_yield if there's nothing to do
232     thread_run_disabled(handle);
233
234     __asm volatile("disp_save_rm_kcb_epilog:");
235 }
236
237
238 /**
239  * \brief Architecture-specific dispatcher initialisation
240  */
241 void disp_arch_init(dispatcher_handle_t handle)
242 {
243     struct dispatcher_shared_arm *disp =
244         get_dispatcher_shared_arm(handle);
245
246     disp->d.dispatcher_run                = (lvaddr_t)run_entry;
247     disp->d.dispatcher_pagefault          = (lvaddr_t)pagefault_entry;
248     disp->d.dispatcher_pagefault_disabled = (lvaddr_t)disabled_pagefault_entry;
249     disp->d.dispatcher_trap               = (lvaddr_t)trap_entry;
250     disp->crit_pc_low                     = (lvaddr_t)disp_resume_context;
251     disp->crit_pc_high                    = (lvaddr_t)disp_resume_context_epilog;
252 }