Commit | Line | Data |
---|---|---|
e49ee290 RK |
1 | /* |
2 | * Event entry/exit for Hexagon | |
3 | * | |
e1858b2a | 4 | * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved. |
e49ee290 RK |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 and | |
8 | * only version 2 as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
18 | * 02110-1301, USA. | |
19 | */ | |
20 | ||
21 | #include <asm/asm-offsets.h> /* assembly-safer versions of C defines */ | |
22 | #include <asm/mem-layout.h> /* sigh, except for page_offset */ | |
23 | #include <asm/hexagon_vm.h> | |
24 | #include <asm/thread_info.h> | |
25 | ||
26 | /* | |
27 | * Entry into guest-mode Linux under Hexagon Virtual Machine. | |
28 | * Stack pointer points to event record - build pt_regs on top of it, | |
29 | * set up a plausible C stack frame, and dispatch to the C handler. | |
30 | * On return, do vmrte virtual instruction with SP where we started. | |
31 | * | |
32 | * VM Spec 0.5 uses a trap to fetch HVM record now. | |
33 | */ | |
34 | ||
35 | /* | |
36 | * Save full register state, while setting up thread_info struct | |
37 | * pointer derived from kernel stack pointer in THREADINFO_REG | |
38 | * register, putting prior thread_info.regs pointer in a callee-save | |
39 | * register (R24, which had better not ever be assigned to THREADINFO_REG), | |
40 | * and updating thread_info.regs to point to current stack frame, | |
41 | * so as to support nested events in kernel mode. | |
42 | * | |
43 | * As this is common code, we set the pt_regs system call number | |
44 | * to -1 for all events. It will be replaced with the system call | |
45 | * number in the case where we decode a system call (trap0(#1)). | |
46 | */ | |
47 | ||
60c4ba99 | 48 | #if CONFIG_HEXAGON_ARCH_VERSION < 4 |
e49ee290 | 49 | #define save_pt_regs()\ |
60c4ba99 RK |
50 | memd(R0 + #_PT_R3130) = R31:30; \ |
51 | { memw(R0 + #_PT_R2928) = R28; \ | |
52 | R31 = memw(R0 + #_PT_ER_VMPSP); }\ | |
53 | { memw(R0 + #(_PT_R2928 + 4)) = R31; \ | |
54 | R31 = ugp; } \ | |
55 | { memd(R0 + #_PT_R2726) = R27:26; \ | |
56 | R30 = gp ; } \ | |
57 | memd(R0 + #_PT_R2524) = R25:24; \ | |
58 | memd(R0 + #_PT_R2322) = R23:22; \ | |
59 | memd(R0 + #_PT_R2120) = R21:20; \ | |
60 | memd(R0 + #_PT_R1918) = R19:18; \ | |
61 | memd(R0 + #_PT_R1716) = R17:16; \ | |
62 | memd(R0 + #_PT_R1514) = R15:14; \ | |
63 | memd(R0 + #_PT_R1312) = R13:12; \ | |
64 | { memd(R0 + #_PT_R1110) = R11:10; \ | |
65 | R15 = lc0; } \ | |
66 | { memd(R0 + #_PT_R0908) = R9:8; \ | |
67 | R14 = sa0; } \ | |
68 | { memd(R0 + #_PT_R0706) = R7:6; \ | |
69 | R13 = lc1; } \ | |
70 | { memd(R0 + #_PT_R0504) = R5:4; \ | |
71 | R12 = sa1; } \ | |
72 | { memd(R0 + #_PT_GPUGP) = R31:30; \ | |
73 | R11 = m1; \ | |
74 | R2.H = #HI(_THREAD_SIZE); } \ | |
75 | { memd(R0 + #_PT_LC0SA0) = R15:14; \ | |
76 | R10 = m0; \ | |
77 | R2.L = #LO(_THREAD_SIZE); } \ | |
78 | { memd(R0 + #_PT_LC1SA1) = R13:12; \ | |
79 | R15 = p3:0; \ | |
80 | R2 = neg(R2); } \ | |
81 | { memd(R0 + #_PT_M1M0) = R11:10; \ | |
82 | R14 = usr; \ | |
83 | R2 = and(R0,R2); } \ | |
84 | { memd(R0 + #_PT_PREDSUSR) = R15:14; \ | |
85 | THREADINFO_REG = R2; } \ | |
86 | { r24 = memw(THREADINFO_REG + #_THREAD_INFO_PT_REGS); \ | |
87 | memw(THREADINFO_REG + #_THREAD_INFO_PT_REGS) = R0; \ | |
88 | R2 = #-1; } \ | |
89 | { memw(R0 + #_PT_SYSCALL_NR) = R2; \ | |
90 | R30 = #0; } | |
91 | #else | |
92 | /* V4+ */ | |
93 | /* the # ## # syntax inserts a literal ## */ | |
94 | #define save_pt_regs()\ | |
95 | { memd(R0 + #_PT_R3130) = R31:30; \ | |
96 | R30 = memw(R0 + #_PT_ER_VMPSP); }\ | |
e49ee290 | 97 | { memw(R0 + #_PT_R2928) = R28; \ |
60c4ba99 RK |
98 | memw(R0 + #(_PT_R2928 + 4)) = R30; }\ |
99 | { R31:30 = C11:10; \ | |
100 | memd(R0 + #_PT_R2726) = R27:26; \ | |
101 | memd(R0 + #_PT_R2524) = R25:24; }\ | |
102 | { memd(R0 + #_PT_R2322) = R23:22; \ | |
103 | memd(R0 + #_PT_R2120) = R21:20; }\ | |
104 | { memd(R0 + #_PT_R1918) = R19:18; \ | |
105 | memd(R0 + #_PT_R1716) = R17:16; }\ | |
106 | { memd(R0 + #_PT_R1514) = R15:14; \ | |
107 | memd(R0 + #_PT_R1312) = R13:12; \ | |
108 | R17:16 = C13:12; }\ | |
e49ee290 | 109 | { memd(R0 + #_PT_R1110) = R11:10; \ |
60c4ba99 RK |
110 | memd(R0 + #_PT_R0908) = R9:8; \ |
111 | R15:14 = C1:0; } \ | |
e49ee290 | 112 | { memd(R0 + #_PT_R0706) = R7:6; \ |
60c4ba99 RK |
113 | memd(R0 + #_PT_R0504) = R5:4; \ |
114 | R13:12 = C3:2; } \ | |
115 | { memd(R0 + #_PT_GPUGP) = R31:30; \ | |
116 | memd(R0 + #_PT_LC0SA0) = R15:14; \ | |
117 | R11:10 = C7:6; }\ | |
118 | { THREADINFO_REG = and(R0, # ## #-_THREAD_SIZE); \ | |
119 | memd(R0 + #_PT_LC1SA1) = R13:12; \ | |
120 | R15 = p3:0; }\ | |
e49ee290 | 121 | { memd(R0 + #_PT_M1M0) = R11:10; \ |
60c4ba99 | 122 | memw(R0 + #_PT_PREDSUSR + 4) = R15; }\ |
e49ee290 RK |
123 | { r24 = memw(THREADINFO_REG + #_THREAD_INFO_PT_REGS); \ |
124 | memw(THREADINFO_REG + #_THREAD_INFO_PT_REGS) = R0; \ | |
125 | R2 = #-1; } \ | |
126 | { memw(R0 + #_PT_SYSCALL_NR) = R2; \ | |
60c4ba99 | 127 | memd(R0 + #_PT_CS1CS0) = R17:16; \ |
e49ee290 | 128 | R30 = #0; } |
60c4ba99 | 129 | #endif |
e49ee290 RK |
130 | |
131 | /* | |
132 | * Restore registers and thread_info.regs state. THREADINFO_REG | |
133 | * is assumed to still be sane, and R24 to have been correctly | |
134 | * preserved. Don't restore R29 (SP) until later. | |
135 | */ | |
136 | ||
60c4ba99 | 137 | #if CONFIG_HEXAGON_ARCH_VERSION < 4 |
e49ee290 RK |
138 | #define restore_pt_regs() \ |
139 | { memw(THREADINFO_REG + #_THREAD_INFO_PT_REGS) = R24; \ | |
140 | R15:14 = memd(R0 + #_PT_PREDSUSR); } \ | |
141 | { R11:10 = memd(R0 + #_PT_M1M0); \ | |
142 | p3:0 = R15; } \ | |
143 | { R13:12 = memd(R0 + #_PT_LC1SA1); \ | |
144 | usr = R14; } \ | |
145 | { R15:14 = memd(R0 + #_PT_LC0SA0); \ | |
146 | m1 = R11; } \ | |
147 | { R3:2 = memd(R0 + #_PT_R0302); \ | |
148 | m0 = R10; } \ | |
149 | { R5:4 = memd(R0 + #_PT_R0504); \ | |
150 | lc1 = R13; } \ | |
151 | { R7:6 = memd(R0 + #_PT_R0706); \ | |
152 | sa1 = R12; } \ | |
153 | { R9:8 = memd(R0 + #_PT_R0908); \ | |
154 | lc0 = R15; } \ | |
155 | { R11:10 = memd(R0 + #_PT_R1110); \ | |
156 | sa0 = R14; } \ | |
157 | { R13:12 = memd(R0 + #_PT_R1312); \ | |
158 | R15:14 = memd(R0 + #_PT_R1514); } \ | |
159 | { R17:16 = memd(R0 + #_PT_R1716); \ | |
160 | R19:18 = memd(R0 + #_PT_R1918); } \ | |
161 | { R21:20 = memd(R0 + #_PT_R2120); \ | |
162 | R23:22 = memd(R0 + #_PT_R2322); } \ | |
163 | { R25:24 = memd(R0 + #_PT_R2524); \ | |
164 | R27:26 = memd(R0 + #_PT_R2726); } \ | |
60c4ba99 | 165 | R31:30 = memd(R0 + #_PT_GPUGP); \ |
e49ee290 RK |
166 | { R28 = memw(R0 + #_PT_R2928); \ |
167 | ugp = R31; } \ | |
168 | { R31:30 = memd(R0 + #_PT_R3130); \ | |
169 | gp = R30; } | |
60c4ba99 RK |
170 | #else |
171 | /* V4+ */ | |
172 | #define restore_pt_regs() \ | |
173 | { memw(THREADINFO_REG + #_THREAD_INFO_PT_REGS) = R24; \ | |
174 | R15:14 = memd(R0 + #_PT_PREDSUSR); } \ | |
175 | { R11:10 = memd(R0 + #_PT_M1M0); \ | |
176 | R13:12 = memd(R0 + #_PT_LC1SA1); \ | |
177 | p3:0 = R15; } \ | |
178 | { R15:14 = memd(R0 + #_PT_LC0SA0); \ | |
179 | R3:2 = memd(R0 + #_PT_R0302); \ | |
180 | usr = R14; } \ | |
181 | { R5:4 = memd(R0 + #_PT_R0504); \ | |
182 | R7:6 = memd(R0 + #_PT_R0706); \ | |
183 | C7:6 = R11:10; }\ | |
184 | { R9:8 = memd(R0 + #_PT_R0908); \ | |
185 | R11:10 = memd(R0 + #_PT_R1110); \ | |
186 | C3:2 = R13:12; }\ | |
187 | { R13:12 = memd(R0 + #_PT_R1312); \ | |
188 | R15:14 = memd(R0 + #_PT_R1514); \ | |
189 | C1:0 = R15:14; }\ | |
190 | { R17:16 = memd(R0 + #_PT_R1716); \ | |
191 | R19:18 = memd(R0 + #_PT_R1918); } \ | |
192 | { R21:20 = memd(R0 + #_PT_R2120); \ | |
193 | R23:22 = memd(R0 + #_PT_R2322); } \ | |
194 | { R25:24 = memd(R0 + #_PT_R2524); \ | |
195 | R27:26 = memd(R0 + #_PT_R2726); } \ | |
196 | R31:30 = memd(R0 + #_PT_CS1CS0); \ | |
197 | { C13:12 = R31:30; \ | |
198 | R31:30 = memd(R0 + #_PT_GPUGP) ; \ | |
199 | R28 = memw(R0 + #_PT_R2928); }\ | |
200 | { C11:10 = R31:30; \ | |
201 | R31:30 = memd(R0 + #_PT_R3130); } | |
202 | #endif | |
e49ee290 RK |
203 | |
204 | /* | |
205 | * Clears off enough space for the rest of pt_regs; evrec is a part | |
206 | * of pt_regs in HVM mode. Save R0/R1, set handler's address in R1. | |
207 | * R0 is the address of pt_regs and is the parameter to save_pt_regs. | |
208 | */ | |
209 | ||
210 | /* | |
211 | * Since the HVM isn't automagically pushing the EVREC onto the stack anymore, | |
212 | * we'll subract the entire size out and then fill it in ourselves. | |
213 | * Need to save off R0, R1, R2, R3 immediately. | |
214 | */ | |
215 | ||
60c4ba99 | 216 | #if CONFIG_HEXAGON_ARCH_VERSION < 4 |
e49ee290 RK |
217 | #define vm_event_entry(CHandler) \ |
218 | { \ | |
219 | R29 = add(R29, #-(_PT_REGS_SIZE)); \ | |
220 | memd(R29 + #(_PT_R0100 + -_PT_REGS_SIZE)) = R1:0; \ | |
221 | } \ | |
222 | { \ | |
223 | memd(R29 +#_PT_R0302) = R3:2; \ | |
224 | } \ | |
225 | trap1(#HVM_TRAP1_VMGETREGS); \ | |
226 | { \ | |
227 | memd(R29 + #_PT_ER_VMEL) = R1:0; \ | |
228 | R0 = R29; \ | |
229 | R1.L = #LO(CHandler); \ | |
230 | } \ | |
231 | { \ | |
232 | memd(R29 + #_PT_ER_VMPSP) = R3:2; \ | |
233 | R1.H = #HI(CHandler); \ | |
234 | jump event_dispatch; \ | |
235 | } | |
60c4ba99 RK |
236 | #else |
237 | /* V4+ */ | |
238 | /* turn on I$ prefetch early */ | |
239 | /* the # ## # syntax inserts a literal ## */ | |
240 | #define vm_event_entry(CHandler) \ | |
241 | { \ | |
242 | R29 = add(R29, #-(_PT_REGS_SIZE)); \ | |
243 | memd(R29 + #(_PT_R0100 + -_PT_REGS_SIZE)) = R1:0; \ | |
244 | memd(R29 + #(_PT_R0302 + -_PT_REGS_SIZE)) = R3:2; \ | |
245 | R0 = usr; \ | |
246 | } \ | |
247 | { \ | |
248 | memw(R29 + #_PT_PREDSUSR) = R0; \ | |
249 | R0 = setbit(R0, #16); \ | |
250 | } \ | |
251 | usr = R0; \ | |
252 | R1:0 = G1:0; \ | |
253 | { \ | |
254 | memd(R29 + #_PT_ER_VMEL) = R1:0; \ | |
255 | R1 = # ## #(CHandler); \ | |
256 | R3:2 = G3:2; \ | |
257 | } \ | |
258 | { \ | |
259 | R0 = R29; \ | |
260 | memd(R29 + #_PT_ER_VMPSP) = R3:2; \ | |
261 | jump event_dispatch; \ | |
262 | } | |
263 | #endif | |
e49ee290 RK |
264 | |
265 | .text | |
266 | /* | |
267 | * Do bulk save/restore in one place. | |
268 | * Adds a jump to dispatch latency, but | |
269 | * saves hundreds of bytes. | |
270 | */ | |
271 | ||
272 | event_dispatch: | |
273 | save_pt_regs() | |
274 | callr r1 | |
275 | ||
276 | /* | |
a11e67c2 RK |
277 | * Coming back from the C-world, our thread info pointer |
278 | * should be in the designated register (usually R19) | |
279 | * | |
e49ee290 RK |
280 | * If we were in kernel mode, we don't need to check scheduler |
281 | * or signals if CONFIG_PREEMPT is not set. If set, then it has | |
282 | * to jump to a need_resched kind of block. | |
283 | * BTW, CONFIG_PREEMPT is not supported yet. | |
284 | */ | |
285 | ||
286 | #ifdef CONFIG_PREEMPT | |
287 | R0 = #VM_INT_DISABLE | |
288 | trap1(#HVM_TRAP1_VMSETIE) | |
289 | #endif | |
290 | ||
291 | /* "Nested control path" -- if the previous mode was kernel */ | |
60c4ba99 | 292 | { |
a11e67c2 RK |
293 | R0 = memw(R29 + #_PT_ER_VMEST); |
294 | R16.L = #LO(do_work_pending); | |
60c4ba99 | 295 | } |
e49ee290 | 296 | { |
a11e67c2 RK |
297 | P0 = tstbit(R0, #HVM_VMEST_UM_SFT); |
298 | if (!P0.new) jump:nt restore_all; | |
299 | R16.H = #HI(do_work_pending); | |
300 | R0 = #VM_INT_DISABLE; | |
60c4ba99 | 301 | } |
e49ee290 RK |
302 | |
303 | /* | |
a11e67c2 RK |
304 | * Check also the return from fork/system call, normally coming back from |
305 | * user mode | |
306 | * | |
307 | * R16 needs to have do_work_pending, and R0 should have VM_INT_DISABLE | |
e49ee290 | 308 | */ |
e49ee290 | 309 | |
a11e67c2 RK |
310 | check_work_pending: |
311 | /* Disable interrupts while checking TIF */ | |
312 | trap1(#HVM_TRAP1_VMSETIE) | |
e49ee290 | 313 | { |
a11e67c2 RK |
314 | R0 = R29; /* regs should still be at top of stack */ |
315 | R1 = memw(THREADINFO_REG + #_THREAD_INFO_FLAGS); | |
316 | callr R16; | |
e49ee290 | 317 | } |
e49ee290 | 318 | |
60c4ba99 | 319 | { |
a11e67c2 RK |
320 | P0 = cmp.eq(R0, #0); if (!P0.new) jump:nt check_work_pending; |
321 | R0 = #VM_INT_DISABLE; | |
60c4ba99 | 322 | } |
e49ee290 RK |
323 | |
324 | restore_all: | |
a11e67c2 RK |
325 | /* |
326 | * Disable interrupts, if they weren't already, before reg restore. | |
327 | * R0 gets preloaded with #VM_INT_DISABLE before we get here. | |
328 | */ | |
e49ee290 RK |
329 | trap1(#HVM_TRAP1_VMSETIE) |
330 | ||
331 | /* do the setregs here for VM 0.5 */ | |
332 | /* R29 here should already be pointing at pt_regs */ | |
60c4ba99 RK |
333 | { |
334 | R1:0 = memd(R29 + #_PT_ER_VMEL); | |
335 | R3:2 = memd(R29 + #_PT_ER_VMPSP); | |
336 | } | |
337 | #if CONFIG_HEXAGON_ARCH_VERSION < 4 | |
e49ee290 | 338 | trap1(#HVM_TRAP1_VMSETREGS); |
60c4ba99 RK |
339 | #else |
340 | G1:0 = R1:0; | |
341 | G3:2 = R3:2; | |
342 | #endif | |
e49ee290 RK |
343 | |
344 | R0 = R29 | |
345 | restore_pt_regs() | |
60c4ba99 RK |
346 | { |
347 | R1:0 = memd(R29 + #_PT_R0100); | |
348 | R29 = add(R29, #_PT_REGS_SIZE); | |
349 | } | |
e49ee290 RK |
350 | trap1(#HVM_TRAP1_VMRTE) |
351 | /* Notreached */ | |
352 | ||
a11e67c2 | 353 | |
e49ee290 RK |
354 | .globl _K_enter_genex |
355 | _K_enter_genex: | |
356 | vm_event_entry(do_genex) | |
357 | ||
358 | .globl _K_enter_interrupt | |
359 | _K_enter_interrupt: | |
360 | vm_event_entry(arch_do_IRQ) | |
361 | ||
362 | .globl _K_enter_trap0 | |
363 | _K_enter_trap0: | |
364 | vm_event_entry(do_trap0) | |
365 | ||
366 | .globl _K_enter_machcheck | |
367 | _K_enter_machcheck: | |
368 | vm_event_entry(do_machcheck) | |
369 | ||
7777746c RK |
370 | .globl _K_enter_debug |
371 | _K_enter_debug: | |
372 | vm_event_entry(do_debug_exception) | |
e49ee290 RK |
373 | |
374 | .globl ret_from_fork | |
375 | ret_from_fork: | |
a11e67c2 RK |
376 | { |
377 | call schedule_tail; | |
378 | R16.H = #HI(do_work_pending); | |
379 | } | |
380 | { | |
381 | R16.L = #LO(do_work_pending); | |
382 | R0 = #VM_INT_DISABLE; | |
383 | jump check_work_pending; | |
384 | } |