1/*
2 * Copyright 2013, winocm. <winocm@icloud.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 *   Redistributions of source code must retain the above copyright notice, this
9 *   list of conditions and the following disclaimer.
10 *
11 *   Redistributions in binary form must reproduce the above copyright notice, this
12 *   list of conditions and the following disclaimer in the documentation and/or
13 *   other materials provided with the distribution.
14 *
15 *   If you are going to use this software in any form that does not involve
16 *   releasing the source to this project or improving it, let me know beforehand.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/*
30 * ARM machine routines
31 */
32
33#include <arm/arch.h>
34#include <arm/asm_help.h>
35#include <assym.s>
36#include <mach/arm/asm.h>
37
38/*
39 * This is ARMv6 and later ONLY.
40 */
41
42#if __ARM_ARCH == 7
43#undef EnterThumb
44#define EnterThumb EnterARM
45#endif
46
47/**
48 * lck_mtx_ilk_unlock
49 */
50EnterARM(lck_mtx_ilk_unlock)
51    ldr     r2, [r0]
52    bic     r3, r2, #1
53    str     r3, [r0]
54    b       __enable_preemption
55
56/**
57 * machine_idle
58 *
59 * Idle loop.
60 */
61EnterARM(machine_idle)
62    /* See if first we have power saving enabled. */
63    LOAD_ADDR(r0, do_power_save)
64    ldr     r0, [r0]
65
66    /* Disabled? We leave. */
67    teq     r0, #0
68    beq     .Lmachine_idle_return
69
70    /* Disable all interrupts and go into wfi mode. */
71.Lmachine_idle_wfi:
72    cpsid   if
73    wfi
74    cpsie   if
75
76.Lmachine_idle_return:
77    bx      lr
78
79/**
80 * machine_signal_idle
81 */
82EnterThumb(machine_signal_idle)
83    bx      lr
84
85/**
86 * machine_callstack
87 */
88EnterThumb(machine_callstack)
89    bx      lr
90
91
92/**
93 * machine_trace_thread
94 */
95EnterARM(machine_trace_thread)
96    movs    r0, #0
97    bx      lr
98
99/**
100 * machine_trace_thread64
101 */
102EnterARM(machine_trace_thread64)
103    movs    r0, #0
104    bx      lr
105
106/**
107 * ml_set_interrupts_enabled
108 *
109 * Disable or enable interrupts based on input boolean.
110 */
111EnterARM(ml_set_interrupts_enabled)
112    mrs     r2, cpsr
113
114    cmp     r0, #0
115    beq     disable_interrupts
116
117astloop:
118    mrc     p15, 0, r0, c13, c0, 4
119    ldr     r1, [r0, MACHINE_THREAD_CPU_DATA]
120    ldr     r0, [r1, CPU_PENDING_AST]
121
122    stmfd   sp!,{r0,r2,r7,lr}
123    bl      _get_preemption_level
124    cmp     r0, #0
125    ldmfd   sp!,{r0,r2,r7,lr}
126
127    bne     enable_interrupts
128
129    and     r0, r0, #4
130    cmp     r0, #0
131    beq     enable_interrupts
132
133    stmfd   sp!,{r0-r7,lr}
134    movs    r0, #7
135    movs    r1, #0
136    blx     _ast_taken
137    ldmfd   sp!,{r0-r7,lr}
138
139    b       astloop
140
141enable_interrupts:
142    cpsie   i
143    b       out
144
145disable_interrupts:
146    cpsid   i
147    b       out
148
149out:
150    mov     r0, #1
151    bic     r0, r0, r2, lsr#7
152    bx      lr
153
154/**
155 * ml_get_interrupts_enabled
156 *
157 * Get current interrupt state.
158 */
159EnterARM(ml_get_interrupts_enabled)
160    mrs     r1, cpsr
161    mov     r0, #1
162    bic     r0, r0, r1, lsr#7
163    bx      lr
164
165/**
166 * __disable_preemption
167 *
168 * Disable preemption for a specified thread.
169 */
170EnterARM(__disable_preemption)
171EnterARM(_disable_preemption)
172EnterARM(disable_preemption)
173    LoadThreadRegister(r12)
174    IncrementPreemptLevel(r12, r2)
175    bx      lr
176
177/**
178 * get_preemption_level
179 *
180 * Get current thread's preemption level.
181 */
182EnterARM(get_preemption_level)
183    LoadThreadRegister(r12)
184    ldr     r0, [r12, MACHINE_THREAD_PREEMPT_COUNT]
185    bx      lr
186
187/**
188 * __enable_preemption
189 *
190 * Enable preemption for a specified thread.
191 */
192EnterARM(__enable_preemption)
193EnterARM(_enable_preemption)
194EnterARM(enable_preemption)
195    /* Get thread ID */
196    LoadThreadRegister(r12)
197    ldr     r2, [r12, MACHINE_THREAD_PREEMPT_COUNT]
198    subs    r2, r2, #1
199    strne   r2, [r12, MACHINE_THREAD_PREEMPT_COUNT]
200    bxne    lr
201
202    /* Preempt. */
203    cpsid   if
204
205    /* Check for interrupts */
206    mrs     r3, cpsr
207    tst     r3, #0x80
208    strne   r2, [r12, MACHINE_THREAD_PREEMPT_COUNT]
209    bxne    lr
210
211    /* Get CPU data and add an AST. */
212    ldr     r1, [r12, MACHINE_THREAD_CPU_DATA]
213    ldr     r0, [r1, CPU_PENDING_AST]
214    str     r2, [r12, MACHINE_THREAD_PREEMPT_COUNT]
215
216    ands    r1, r0, #4
217    bne     __preempt
218    msr     cpsr_cf, r3
219    bx      lr
220
221__preempt:
222    /* Reenable interrupts */
223    cpsie   f
224    stmfd   sp!,{r7,lr}
225    add     r7, sp, #8
226    mov     r0, #7
227    mov     r1, #1
228    blx     _ast_taken
229    ldmfd   sp!,{r7,lr}
230    bx      lr
231
232/**
233 * current_thread
234 *
235 * Return the core thread structure of the currently executing thread.
236 * The reason this doesn't use the thread register is because the current
237 * "executing" thread may not be the one in the register. Just get the one
238 * saved from machine_set_current_thread.
239 */
240EnterARM(current_thread)
241    mrc     p15, 0, r0, c13, c0, 4
242    bx      lr
243
244/**
245 * set_mmu_ttb/ttb_alt/ttbcr
246 *
247 * Set the translation table base register (and alternate TTBR) to point
248 * to the physical address of a translation-table.
249 *
250 * Set the current TTB control register for a split.
251 */
252EnterARM(set_mmu_ttb)
253    orr     r0, r0, #0x18
254    mcr     p15, 0, r0, c2, c0, 0
255    bx      lr
256EnterARM(set_mmu_ttb_alt)
257    orr     r0, r0, #0x18
258    mcr     p15, 0, r0, c2, c0, 1
259    bx      lr
260EnterARM(set_mmu_ttbcr)
261    mcr     p15, 0, r0, c2, c0, 2
262    bx      lr
263
264/**
265 * flush_mmu_single
266 *
267 * Flush a MVA specific entry from the TLB.
268 */
269 EnterARM(flush_mmu_single)
270    /* Gated. */
271    mov     r0, #0
272    mcr     p15, 0, r0, c8, c7, 0
273    mcr     p15, 0, r0, c7, c5, 0
274#if __ARM_ARCH == 7
275    isb     sy
276    dsb     sy
277#else
278    mcr     p15, 0, r0, c7, c5, 4
279    mcr     p15, 0, r0, c7, c10, 4
280#endif
281    bx      lr
282
283    /* Shove the lowest 12-bits off the VA */
284    mov     r0, r0, lsr #12
285    mov     r0, r0, lsl #12
286    /* Clean it */
287    mcr     p15, 0, r0, c8, c7, 1
288#if __ARM_ARCH == 7
289    isb     sy
290    dsb     sy
291#else
292    mcr     p15, 0, r0, c7, c5, 4
293    mcr     p15, 0, r0, c7, c10, 4
294#endif
295    bx      lr
296
297/**
298 * ml_cause_interrupt
299 */
300EnterThumb(ml_cause_interrupt)
301    bx      lr
302
303/**
304 * Halt_system
305 *
306 * Halt the system entirely.
307 */
308EnterARM(Halt_system)
309    /* Disable interruptions. */
310    cpsid    if
311
312.L_deadloop:
313    /* Drain write buffer. */
314#if __ARM_ARCH == 7
315    dsb      sy
316#else
317    mov      r0, #0
318    mcr      p15, 0, r0, c7, c10, 4
319#endif
320
321    /* Wait for interrupts, reduce voltage and power state. */
322#if __ARM_ARCH == 7
323    wfi
324#else
325    mov      r0, #0
326    mcr      p15, 0, r0, c7, c0, 4
327#endif
328
329    /* Try again for a halt. */
330    b        .L_deadloop
331
332LOAD_ADDR_GEN_DEF(do_power_save)
333