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