/* * Copyright 2013, winocm. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * If you are going to use this software in any form that does not involve * releasing the source to this project or improving it, let me know beforehand. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * ARM machine routines */ #include #include #include #include /* * This is ARMv6 and later ONLY. */ #if __ARM_ARCH == 7 #undef EnterThumb #define EnterThumb EnterARM #endif /** * lck_mtx_ilk_unlock */ EnterARM(lck_mtx_ilk_unlock) ldr r2, [r0] bic r3, r2, #1 str r3, [r0] b __enable_preemption /** * machine_idle * * Idle loop. */ EnterARM(machine_idle) /* See if first we have power saving enabled. */ LOAD_ADDR(r0, do_power_save) ldr r0, [r0] /* Disabled? We leave. */ teq r0, #0 beq .Lmachine_idle_return /* Disable all interrupts and go into wfi mode. */ .Lmachine_idle_wfi: cpsid if wfi cpsie if .Lmachine_idle_return: bx lr /** * machine_signal_idle */ EnterThumb(machine_signal_idle) bx lr /** * machine_callstack */ EnterThumb(machine_callstack) bx lr /** * machine_trace_thread */ EnterARM(machine_trace_thread) movs r0, #0 bx lr /** * machine_trace_thread64 */ EnterARM(machine_trace_thread64) movs r0, #0 bx lr /** * ml_set_interrupts_enabled * * Disable or enable interrupts based on input boolean. */ EnterARM(ml_set_interrupts_enabled) mrs r2, cpsr cmp r0, #0 beq disable_interrupts astloop: mrc p15, 0, r0, c13, c0, 4 ldr r1, [r0, MACHINE_THREAD_CPU_DATA] ldr r0, [r1, CPU_PENDING_AST] stmfd sp!,{r0,r2,r7,lr} bl _get_preemption_level cmp r0, #0 ldmfd sp!,{r0,r2,r7,lr} bne enable_interrupts and r0, r0, #4 cmp r0, #0 beq enable_interrupts stmfd sp!,{r0-r7,lr} movs r0, #7 movs r1, #0 blx _ast_taken ldmfd sp!,{r0-r7,lr} b astloop enable_interrupts: cpsie i b out disable_interrupts: cpsid i b out out: mov r0, #1 bic r0, r0, r2, lsr#7 bx lr /** * ml_get_interrupts_enabled * * Get current interrupt state. */ EnterARM(ml_get_interrupts_enabled) mrs r1, cpsr mov r0, #1 bic r0, r0, r1, lsr#7 bx lr /** * __disable_preemption * * Disable preemption for a specified thread. */ EnterARM(__disable_preemption) EnterARM(_disable_preemption) EnterARM(disable_preemption) LoadThreadRegister(r12) IncrementPreemptLevel(r12, r2) bx lr /** * get_preemption_level * * Get current thread's preemption level. */ EnterARM(get_preemption_level) LoadThreadRegister(r12) ldr r0, [r12, MACHINE_THREAD_PREEMPT_COUNT] bx lr /** * __enable_preemption * * Enable preemption for a specified thread. */ EnterARM(__enable_preemption) EnterARM(_enable_preemption) EnterARM(enable_preemption) /* Get thread ID */ LoadThreadRegister(r12) ldr r2, [r12, MACHINE_THREAD_PREEMPT_COUNT] subs r2, r2, #1 strne r2, [r12, MACHINE_THREAD_PREEMPT_COUNT] bxne lr /* Preempt. */ cpsid if /* Check for interrupts */ mrs r3, cpsr tst r3, #0x80 strne r2, [r12, MACHINE_THREAD_PREEMPT_COUNT] bxne lr /* Get CPU data and add an AST. */ ldr r1, [r12, MACHINE_THREAD_CPU_DATA] ldr r0, [r1, CPU_PENDING_AST] str r2, [r12, MACHINE_THREAD_PREEMPT_COUNT] ands r1, r0, #4 bne __preempt msr cpsr_cf, r3 bx lr __preempt: /* Reenable interrupts */ cpsie f stmfd sp!,{r7,lr} add r7, sp, #8 mov r0, #7 mov r1, #1 blx _ast_taken ldmfd sp!,{r7,lr} bx lr /** * current_thread * * Return the core thread structure of the currently executing thread. * The reason this doesn't use the thread register is because the current * "executing" thread may not be the one in the register. Just get the one * saved from machine_set_current_thread. */ EnterARM(current_thread) mrc p15, 0, r0, c13, c0, 4 bx lr /** * set_mmu_ttb/ttb_alt/ttbcr * * Set the translation table base register (and alternate TTBR) to point * to the physical address of a translation-table. * * Set the current TTB control register for a split. */ EnterARM(set_mmu_ttb) orr r0, r0, #0x18 mcr p15, 0, r0, c2, c0, 0 bx lr EnterARM(set_mmu_ttb_alt) orr r0, r0, #0x18 mcr p15, 0, r0, c2, c0, 1 bx lr EnterARM(set_mmu_ttbcr) mcr p15, 0, r0, c2, c0, 2 bx lr /** * flush_mmu_single * * Flush a MVA specific entry from the TLB. */ EnterARM(flush_mmu_single) /* Gated. */ mov r0, #0 mcr p15, 0, r0, c8, c7, 0 mcr p15, 0, r0, c7, c5, 0 #if __ARM_ARCH == 7 isb sy dsb sy #else mcr p15, 0, r0, c7, c5, 4 mcr p15, 0, r0, c7, c10, 4 #endif bx lr /* Shove the lowest 12-bits off the VA */ mov r0, r0, lsr #12 mov r0, r0, lsl #12 /* Clean it */ mcr p15, 0, r0, c8, c7, 1 #if __ARM_ARCH == 7 isb sy dsb sy #else mcr p15, 0, r0, c7, c5, 4 mcr p15, 0, r0, c7, c10, 4 #endif bx lr /** * ml_cause_interrupt */ EnterThumb(ml_cause_interrupt) bx lr /** * Halt_system * * Halt the system entirely. */ EnterARM(Halt_system) /* Disable interruptions. */ cpsid if .L_deadloop: /* Drain write buffer. */ #if __ARM_ARCH == 7 dsb sy #else mov r0, #0 mcr p15, 0, r0, c7, c10, 4 #endif /* Wait for interrupts, reduce voltage and power state. */ #if __ARM_ARCH == 7 wfi #else mov r0, #0 mcr p15, 0, r0, c7, c0, 4 #endif /* Try again for a halt. */ b .L_deadloop LOAD_ADDR_GEN_DEF(do_power_save)