--- /dev/null
+/*
+ * Copyright (c) 2007-2014 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+#include <machine/asm.h>
+#include <arm/proc_reg.h>
+#include <arm/pmap.h>
+#include <sys/errno.h>
+#include "assym.s"
+
+ .align 2
+ .globl EXT(machine_set_current_thread)
+LEXT(machine_set_current_thread)
+ mcr p15, 0, r0, c13, c0, 4 // Write TPIDRPRW
+ ldr r1, [r0, TH_CTH_SELF]
+ mrc p15, 0, r2, c13, c0, 3 // Read TPIDRURO
+ and r2, r2, #3 // Extract cpu number
+ orr r1, r1, r2 //
+ mcr p15, 0, r1, c13, c0, 3 // Write TPIDRURO
+ ldr r1, [r0, TH_CTH_DATA]
+ mcr p15, 0, r1, c13, c0, 2 // Write TPIDRURW
+ bx lr
+
+/*
+ * void machine_idle(void)
+ */
+ .text
+ .align 2
+ .globl EXT(machine_idle)
+LEXT(machine_idle)
+ cpsid if // Disable FIQ IRQ
+ mov ip, lr
+ bl EXT(Idle_context)
+ mov lr, ip
+ cpsie if // Enable FIQ IRQ
+ bx lr
+
+/*
+ * void cpu_idle_wfi(boolean_t wfi_fast):
+ * cpu_idle is the only function that should call this.
+ */
+ .text
+ .align 2
+ .globl EXT(cpu_idle_wfi)
+LEXT(cpu_idle_wfi)
+ mov r1, #32
+ mov r2, #1200
+ cmp r0, #0
+ beq 3f
+ mov r1, #1
+ b 2f
+ .align 5
+1:
+ add r0, r0, #1
+ mov r1, r2
+2:
+
+/*
+ * We export the address of the WFI instruction so that it can be patched; this will be
+ * ugly from a debugging perspective.
+ */
+
+#if (__ARM_ARCH__ >= 7)
+ dsb
+ .globl EXT(wfi_inst)
+LEXT(wfi_inst)
+ wfi
+#else
+ mcr p15, 0, r0, c7, c10, 4
+ .globl EXT(wfi_inst)
+LEXT(wfi_inst)
+ mcr p15, 0, r0, c7, c0, 4
+#endif
+3:
+ subs r1, r1, #1
+ bne 3b
+ nop
+ nop
+ nop
+ nop
+ nop
+ cmp r0, #0
+ beq 1b
+ bx lr
+
+ .align 2
+ .globl EXT(timer_grab)
+LEXT(timer_grab)
+0:
+ ldr r2, [r0, TIMER_HIGH]
+ ldr r3, [r0, TIMER_LOW]
+#if __ARM_SMP__
+ dmb ish // dmb ish
+#endif
+ ldr r1, [r0, TIMER_HIGHCHK]
+ cmp r1, r2
+ bne 0b
+ mov r0, r3
+ bx lr
+
+ .align 2
+ .globl EXT(timer_update)
+LEXT(timer_update)
+ str r1, [r0, TIMER_HIGHCHK]
+#if __ARM_SMP__
+ dmb ish // dmb ish
+#endif
+ str r2, [r0, TIMER_LOW]
+#if __ARM_SMP__
+ dmb ish // dmb ish
+#endif
+ str r1, [r0, TIMER_HIGH]
+ bx lr
+
+ .align 2
+ .globl EXT(get_vfp_enabled)
+LEXT(get_vfp_enabled)
+#if __ARM_VFP__
+ fmrx r0, fpexc
+ and r1, r0, #FPEXC_EN // Extact vfp enable previous state
+ mov r0, r1, LSR #FPEXC_EN_BIT // Return 1 if enabled, 0 if disabled
+#else
+ mov r0, #0 // return false
+#endif
+ bx lr
+
+/* This is no longer useful (but is exported, so this may require kext cleanup). */
+ .align 2
+ .globl EXT(enable_kernel_vfp_context)
+LEXT(enable_kernel_vfp_context)
+ bx lr
+
+/* uint32_t get_fpscr(void):
+ * Returns the current state of the FPSCR register.
+ */
+ .align 2
+ .globl EXT(get_fpscr)
+LEXT(get_fpscr)
+#if __ARM_VFP__
+ fmrx r0, fpscr
+#endif
+ bx lr
+ .align 2
+ .globl EXT(set_fpscr)
+/* void set_fpscr(uint32_t value):
+ * Set the FPSCR register.
+ */
+LEXT(set_fpscr)
+#if __ARM_VFP__
+ fmxr fpscr, r0
+#else
+ mov r0, #0
+#endif
+ bx lr
+
+#if (__ARM_VFP__ >= 3)
+ .align 2
+ .globl EXT(get_mvfr0)
+LEXT(get_mvfr0)
+ vmrs r0, mvfr0
+ bx lr
+ .globl EXT(get_mvfr1)
+LEXT(get_mvfr1)
+ vmrs r0, mvfr1
+ bx lr
+#endif
+
+/*
+ * void OSSynchronizeIO(void)
+ */
+ .text
+ .align 2
+ .globl EXT(OSSynchronizeIO)
+LEXT(OSSynchronizeIO)
+ .align 2
+ dsb
+ bx lr
+
+/*
+ * void flush_mmu_tlb(void)
+ *
+ * Flush all TLBs
+ */
+ .text
+ .align 2
+ .globl EXT(flush_mmu_tlb)
+LEXT(flush_mmu_tlb)
+ mov r0, #0
+#if __ARM_SMP__
+ mcr p15, 0, r0, c8, c3, 0 // Invalidate Inner Shareable entire TLBs
+#else
+ mcr p15, 0, r0, c8, c7, 0 // Invalidate entire TLB
+#endif
+ dsb ish
+ isb
+ bx lr
+
+/*
+ * void flush_core_tlb(void)
+ *
+ * Flush core TLB
+ */
+ .text
+ .align 2
+ .globl EXT(flush_core_tlb)
+LEXT(flush_core_tlb)
+ mov r0, #0
+ mcr p15, 0, r0, c8, c7, 0 // Invalidate entire TLB
+ dsb ish
+ isb
+ bx lr
+
+/*
+ * void flush_mmu_tlb_entry(uint32_t)
+ *
+ * Flush TLB entry
+ */
+ .text
+ .align 2
+ .globl EXT(flush_mmu_tlb_entry)
+LEXT(flush_mmu_tlb_entry)
+#if __ARM_SMP__
+ mcr p15, 0, r0, c8, c3, 1 // Invalidate TLB Inner Shareableentry
+#else
+ mcr p15, 0, r0, c8, c7, 1 // Invalidate TLB entry
+#endif
+ dsb ish
+ isb
+ bx lr
+
+/*
+ * void flush_mmu_tlb_entries(uint32_t, uint32_t)
+ *
+ * Flush TLB entries
+ */
+ .text
+ .align 2
+ .globl EXT(flush_mmu_tlb_entries)
+LEXT(flush_mmu_tlb_entries)
+1:
+#if __ARM_SMP__
+ mcr p15, 0, r0, c8, c3, 1 // Invalidate TLB Inner Shareable entry
+#else
+ mcr p15, 0, r0, c8, c7, 1 // Invalidate TLB entry
+#endif
+ add r0, r0, ARM_PGBYTES // Increment to the next page
+ cmp r0, r1 // Loop if current address < end address
+ blt 1b
+ dsb ish // Synchronize
+ isb
+ bx lr
+
+
+/*
+ * void flush_mmu_tlb_mva_entries(uint32_t)
+ *
+ * Flush TLB entries for mva
+ */
+ .text
+ .align 2
+ .globl EXT(flush_mmu_tlb_mva_entries)
+LEXT(flush_mmu_tlb_mva_entries)
+#if __ARM_SMP__
+ mcr p15, 0, r0, c8, c3, 3 // Invalidate TLB Inner Shareable entries by mva
+#else
+ mcr p15, 0, r0, c8, c7, 3 // Invalidate TLB Inner Shareable entries by mva
+#endif
+ dsb ish
+ isb
+ bx lr
+
+/*
+ * void flush_mmu_tlb_asid(uint32_t)
+ *
+ * Flush TLB entriesfor requested asid
+ */
+ .text
+ .align 2
+ .globl EXT(flush_mmu_tlb_asid)
+LEXT(flush_mmu_tlb_asid)
+#if __ARM_SMP__
+ mcr p15, 0, r0, c8, c3, 2 // Invalidate TLB Inner Shareable entries by asid
+#else
+ mcr p15, 0, r0, c8, c7, 2 // Invalidate TLB entries by asid
+#endif
+ dsb ish
+ isb
+ bx lr
+
+/*
+ * void flush_core_tlb_asid(uint32_t)
+ *
+ * Flush TLB entries for core for requested asid
+ */
+ .text
+ .align 2
+ .globl EXT(flush_core_tlb_asid)
+LEXT(flush_core_tlb_asid)
+ mcr p15, 0, r0, c8, c7, 2 // Invalidate TLB entries by asid
+ dsb ish
+ isb
+ bx lr
+
+/*
+ * Set MMU Translation Table Base
+ */
+ .text
+ .align 2
+ .globl EXT(set_mmu_ttb)
+LEXT(set_mmu_ttb)
+ orr r0, r0, #(TTBR_SETUP & 0xFF) // Setup PTWs memory attribute
+ orr r0, r0, #(TTBR_SETUP & 0xFF00) // Setup PTWs memory attribute
+ mcr p15, 0, r0, c2, c0, 0 // write r0 to translation table 0
+ dsb ish
+ isb
+ bx lr
+
+/*
+ * Set MMU Translation Table Base Alternate
+ */
+ .text
+ .align 2
+ .globl EXT(set_mmu_ttb_alternate)
+LEXT(set_mmu_ttb_alternate)
+ orr r0, r0, #(TTBR_SETUP & 0xFF) // Setup PTWs memory attribute
+ orr r0, r0, #(TTBR_SETUP & 0xFF00) // Setup PTWs memory attribute
+ mcr p15, 0, r0, c2, c0, 1 // write r0 to translation table 1
+ dsb ish
+ isb
+ bx lr
+
+/*
+ * Set MMU Translation Table Base
+ */
+ .text
+ .align 2
+ .globl EXT(get_mmu_ttb)
+LEXT(get_mmu_ttb)
+ mrc p15, 0, r0, c2, c0, 0 // translation table to r0
+ isb
+ bx lr
+
+/*
+ * get MMU control register
+ */
+ .text
+ .align 2
+ .globl EXT(get_aux_control)
+LEXT(get_aux_control)
+ mrc p15, 0, r0, c1, c0, 1 // read aux control into r0
+ bx lr // return old bits in r0
+
+/*
+ * set MMU control register
+ */
+ .text
+ .align 2
+ .globl EXT(set_aux_control)
+LEXT(set_aux_control)
+ mcr p15, 0, r0, c1, c0, 1 // write r0 back to aux control
+ isb
+ bx lr
+
+
+/*
+ * get MMU control register
+ */
+ .text
+ .align 2
+ .globl EXT(get_mmu_control)
+LEXT(get_mmu_control)
+ mrc p15, 0, r0, c1, c0, 0 // read mmu control into r0
+ bx lr // return old bits in r0
+
+/*
+ * set MMU control register
+ */
+ .text
+ .align 2
+ .globl EXT(set_mmu_control)
+LEXT(set_mmu_control)
+ mcr p15, 0, r0, c1, c0, 0 // write r0 back to mmu control
+ isb
+ bx lr
+
+/*
+ * MMU kernel virtual to physical address translation
+ */
+ .text
+ .align 2
+ .globl EXT(mmu_kvtop)
+LEXT(mmu_kvtop)
+ mrs r3, cpsr // Read cpsr
+ cpsid if // Disable FIQ IRQ
+ mov r1, r0
+ mcr p15, 0, r1, c7, c8, 0 // Write V2PCWPR
+ isb
+ mrc p15, 0, r0, c7, c4, 0 // Read PAR
+ ands r2, r0, #0x1 // Test conversion aborted
+ bne mmu_kvtophys_fail
+ ands r2, r0, #0x2 // Test super section
+ mvnne r2, #0xFF000000
+ moveq r2, #0x000000FF
+ orreq r2, r2, #0x00000F00
+ bics r0, r0, r2 // Clear lower bits
+ beq mmu_kvtophys_fail
+ and r1, r1, r2
+ orr r0, r0, r1
+ b mmu_kvtophys_ret
+mmu_kvtophys_fail:
+ mov r0, #0
+mmu_kvtophys_ret:
+ msr cpsr, r3 // Restore cpsr
+ bx lr
+
+/*
+ * MMU user virtual to physical address translation
+ */
+ .text
+ .align 2
+ .globl EXT(mmu_uvtop)
+LEXT(mmu_uvtop)
+ mrs r3, cpsr // Read cpsr
+ cpsid if // Disable FIQ IRQ
+ mov r1, r0
+ mcr p15, 0, r1, c7, c8, 2 // Write V2PCWUR
+ isb
+ mrc p15, 0, r0, c7, c4, 0 // Read PAR
+ ands r2, r0, #0x1 // Test conversion aborted
+ bne mmu_uvtophys_fail
+ ands r2, r0, #0x2 // Test super section
+ mvnne r2, #0xFF000000
+ moveq r2, #0x000000FF
+ orreq r2, r2, #0x00000F00
+ bics r0, r0, r2 // Clear lower bits
+ beq mmu_uvtophys_fail
+ and r1, r1, r2
+ orr r0, r0, r1
+ b mmu_uvtophys_ret
+mmu_uvtophys_fail:
+ mov r0, #0
+mmu_uvtophys_ret:
+ msr cpsr, r3 // Restore cpsr
+ bx lr
+
+/*
+ * MMU kernel virtual to physical address preflight write access
+ */
+ .text
+ .align 2
+ .globl EXT(mmu_kvtop_wpreflight)
+LEXT(mmu_kvtop_wpreflight)
+ mrs r3, cpsr // Read cpsr
+ cpsid if // Disable FIQ IRQ
+ mov r1, r0
+ mcr p15, 0, r1, c7, c8, 1 // Write V2PCWPW
+ isb
+ mrc p15, 0, r0, c7, c4, 0 // Read PAR
+ ands r2, r0, #0x1 // Test conversion aborted
+ bne mmu_kvtophys_wpreflight_fail
+ ands r2, r0, #0x2 // Test super section
+ mvnne r2, #0xFF000000
+ moveq r2, #0x000000FF
+ orreq r2, r2, #0x00000F00
+ bics r0, r0, r2 // Clear lower bits
+ beq mmu_kvtophys_wpreflight_fail // Sanity check: successful access must deliver zero low bits
+ and r1, r1, r2
+ orr r0, r0, r1
+ b mmu_kvtophys_wpreflight_ret
+mmu_kvtophys_wpreflight_fail:
+ mov r0, #0
+mmu_kvtophys_wpreflight_ret:
+ msr cpsr, r3 // Restore cpsr
+ bx lr
+
+/*
+ * set context id register
+ */
+/*
+ * set context id register
+ */
+ .text
+ .align 2
+ .globl EXT(set_context_id)
+LEXT(set_context_id)
+ mcr p15, 0, r0, c13, c0, 1
+ isb
+ bx lr
+
+#define COPYIO_HEADER(rUser, kLabel) \
+ /* test for zero len */ ;\
+ cmp r2, #0 ;\
+ moveq r0, #0 ;\
+ bxeq lr ;\
+ /* test user_addr, user_addr+len to see if it's in kernel space */ ;\
+ add r12, rUser, r2 ;\
+ cmp r12, KERNELBASE ;\
+ bhs kLabel ;\
+ cmp r12, rUser ;\
+ bcc kLabel
+
+#define COPYIO_VALIDATE(NAME, SIZE) \
+ /* branch around for small sizes */ ;\
+ cmp r2, #(SIZE) ;\
+ bls L##NAME##_validate_done ;\
+ /* call NAME_validate to check the arguments */ ;\
+ push {r0, r1, r2, r7, lr} ;\
+ add r7, sp, #12 ;\
+ blx EXT(NAME##_validate) ;\
+ cmp r0, #0 ;\
+ addne sp, #12 ;\
+ popne {r7, pc} ;\
+ pop {r0, r1, r2, r7, lr} ;\
+L##NAME##_validate_done:
+
+#define COPYIO_SET_RECOVER() \
+ /* set recovery address */ ;\
+ stmfd sp!, { r4, r5, r6 } ;\
+ adr r3, copyio_error ;\
+ mrc p15, 0, r12, c13, c0, 4 ;\
+ ldr r4, [r12, TH_RECOVER] ;\
+ str r3, [r12, TH_RECOVER]
+
+#if __ARM_USER_PROTECT__
+#define COPYIO_MAP_USER() \
+ /* disable interrupts to prevent expansion to 2GB at L1 ;\
+ * between loading ttep and storing it in ttbr0.*/ ;\
+ mrs r5, cpsr ;\
+ cpsid if ;\
+ ldr r3, [r12, ACT_UPTW_TTB] ;\
+ mcr p15, 0, r3, c2, c0, 0 ;\
+ msr cpsr, r5 ;\
+ ldr r3, [r12, ACT_ASID] ;\
+ mcr p15, 0, r3, c13, c0, 1 ;\
+ isb
+#else
+#define COPYIO_MAP_USER()
+#endif
+
+#define COPYIO_HEADER_KERN() ;\
+ /* test for zero len */ ;\
+ cmp r2, #0 ;\
+ moveq r0, #0 ;\
+ bxeq lr
+
+.macro COPYIO_BODY
+ /* if len is less than 16 bytes, just do a simple copy */
+ cmp r2, #16
+ blt L$0_bytewise
+ /* test for src and dest of the same word alignment */
+ orr r3, r0, r1
+ tst r3, #3
+ bne L$0_bytewise
+L$0_wordwise:
+ sub r2, r2, #16
+L$0_wordwise_loop:
+ /* 16 bytes at a time */
+ ldmia r0!, { r3, r5, r6, r12 }
+ stmia r1!, { r3, r5, r6, r12 }
+ subs r2, r2, #16
+ bge L$0_wordwise_loop
+ /* fixup the len and test for completion */
+ adds r2, r2, #16
+ beq L$0_noerror
+L$0_bytewise:
+ /* copy 2 bytes at a time */
+ subs r2, r2, #2
+ ldrb r3, [r0], #1
+ ldrbpl r12, [r0], #1
+ strb r3, [r1], #1
+ strbpl r12, [r1], #1
+ bhi L$0_bytewise
+L$0_noerror:
+ mov r0, #0
+.endmacro
+
+#if __ARM_USER_PROTECT__
+#define COPYIO_UNMAP_USER() \
+ mrc p15, 0, r12, c13, c0, 4 ;\
+ ldr r3, [r12, ACT_KPTW_TTB] ;\
+ mcr p15, 0, r3, c2, c0, 0 ;\
+ mov r3, #0 ;\
+ mcr p15, 0, r3, c13, c0, 1 ;\
+ isb
+#else
+#define COPYIO_UNMAP_USER() \
+ mrc p15, 0, r12, c13, c0, 4
+#endif
+
+#define COPYIO_RESTORE_RECOVER() \
+ /* restore the recovery address */ ;\
+ str r4, [r12, TH_RECOVER] ;\
+ ldmfd sp!, { r4, r5, r6 }
+
+/*
+ * int copyinstr(
+ * const user_addr_t user_addr,
+ * char *kernel_addr,
+ * vm_size_t max,
+ * vm_size_t *actual)
+ */
+ .text
+ .align 2
+ .globl EXT(copyinstr)
+LEXT(copyinstr)
+ stmfd sp!, { r4, r5, r6 }
+
+ mov r6, r3
+ add r3, r0, r2 // user_addr + max
+ cmp r3, KERNELBASE // Check KERNELBASE < user_addr + max
+ bhs copyinstr_param_error // Drop out if it is
+ cmp r3, r0 // Check we're copying from user space
+ bcc copyinstr_param_error // Drop out if we aren't
+ adr r3, copyinstr_error // Get address for recover
+ mrc p15, 0, r12, c13, c0, 4 // Read TPIDRPRW
+ ldr r4, [r12, TH_RECOVER] ;\
+ str r3, [r12, TH_RECOVER]
+ COPYIO_MAP_USER()
+ mov r12, #0 // Number of bytes copied so far
+ cmp r2, #0
+ beq copyinstr_too_long
+copyinstr_loop:
+ ldrb r3, [r0], #1 // Load a byte from the source (user)
+ strb r3, [r1], #1 // Store a byte to the destination (kernel)
+ add r12, r12, #1
+ cmp r3, #0
+ beq copyinstr_done
+ cmp r12, r2 // Room to copy more bytes?
+ bne copyinstr_loop
+//
+// Ran out of space in the destination buffer, so return ENAMETOOLONG.
+//
+copyinstr_too_long:
+ mov r3, #ENAMETOOLONG
+copyinstr_done:
+//
+// When we get here, we have finished copying the string. We came here from
+// either the "beq copyinstr_done" above, in which case r4 == 0 (which is also
+// the function result for success), or falling through from copyinstr_too_long,
+// in which case r4 == ENAMETOOLONG.
+//
+ str r12, [r6] // Save the count for actual
+ mov r0, r3 // Return error code from r3
+copyinstr_exit:
+ COPYIO_UNMAP_USER()
+ str r4, [r12, TH_RECOVER]
+copyinstr_exit2:
+ ldmfd sp!, { r4, r5, r6 }
+ bx lr
+
+copyinstr_error:
+ /* set error, exit routine */
+ mov r0, #EFAULT
+ b copyinstr_exit
+
+copyinstr_param_error:
+ /* set error, exit routine */
+ mov r0, #EFAULT
+ b copyinstr_exit2
+
+/*
+ * int copyin(const user_addr_t user_addr, char *kernel_addr, vm_size_t nbytes)
+ */
+ .text
+ .align 2
+ .globl EXT(copyin)
+LEXT(copyin)
+ COPYIO_HEADER(r0,copyio_kernel)
+ COPYIO_VALIDATE(copyin,4096)
+ COPYIO_SET_RECOVER()
+ COPYIO_MAP_USER()
+ COPYIO_BODY copyin
+ COPYIO_UNMAP_USER()
+ COPYIO_RESTORE_RECOVER()
+ bx lr
+
+/*
+ * int copyout(const char *kernel_addr, user_addr_t user_addr, vm_size_t nbytes)
+ */
+ .text
+ .align 2
+ .globl EXT(copyout)
+LEXT(copyout)
+ COPYIO_HEADER(r1,copyio_kernel)
+ COPYIO_VALIDATE(copyout,4096)
+ COPYIO_SET_RECOVER()
+ COPYIO_MAP_USER()
+ COPYIO_BODY copyout
+ COPYIO_UNMAP_USER()
+ COPYIO_RESTORE_RECOVER()
+ bx lr
+
+
+/*
+ * int copyin_word(const user_addr_t user_addr, uint64_t *kernel_addr, vm_size_t nbytes)
+ */
+ .text
+ .align 2
+ .globl EXT(copyin_word)
+LEXT(copyin_word)
+ cmp r2, #4 // Test if size is 4 or 8
+ cmpne r2, #8
+ bne L_copyin_invalid
+ sub r3, r2, #1
+ tst r0, r3 // Test alignment of user address
+ bne L_copyin_invalid
+
+ COPYIO_HEADER(r0,L_copyin_word_fault)
+ COPYIO_SET_RECOVER()
+ COPYIO_MAP_USER()
+
+ mov r3, #0 // Clear high register
+ cmp r2, #4 // If size is 4
+ ldreq r2, [r0] // Load word from user
+ ldrdne r2, r3, [r0] // Else Load double word from user
+ stm r1, {r2, r3} // Store to kernel_addr
+ mov r0, #0 // Success
+
+ COPYIO_UNMAP_USER()
+ COPYIO_RESTORE_RECOVER()
+ bx lr
+L_copyin_invalid:
+ mov r0, #EINVAL
+ bx lr
+L_copyin_word_fault:
+ mov r0, #EFAULT
+ bx lr
+
+
+copyio_error:
+ mov r0, #EFAULT
+ COPYIO_UNMAP_USER()
+ str r4, [r12, TH_RECOVER]
+ ldmfd sp!, { r4, r5, r6 }
+ bx lr
+
+/*
+ * int copyin_kern(const user_addr_t user_addr, char *kernel_addr, vm_size_t nbytes)
+ */
+ .text
+ .align 2
+ .globl EXT(copyin_kern)
+LEXT(copyin_kern)
+ COPYIO_HEADER_KERN()
+ b bypass_check
+
+/*
+ * int copyout_kern(const char *kernel_addr, user_addr_t user_addr, vm_size_t nbytes)
+ */
+ .text
+ .align 2
+ .globl EXT(copyout_kern)
+LEXT(copyout_kern)
+ COPYIO_HEADER_KERN()
+ b bypass_check
+
+copyio_kernel_error:
+ mov r0, #EFAULT
+ bx lr
+
+copyio_kernel:
+ /* if (current_thread()->map->pmap != kernel_pmap) return EFAULT */
+ mrc p15, 0, r12, c13, c0, 4 // Read TPIDRPRW
+ ldr r3, [r12, ACT_MAP]
+ ldr r3, [r3, MAP_PMAP]
+ LOAD_ADDR(ip, kernel_pmap_store)
+ cmp r3, ip
+ bne copyio_kernel_error
+
+bypass_check:
+ stmfd sp!, { r5, r6 }
+ COPYIO_BODY copyio_kernel
+ ldmfd sp!, { r5, r6 }
+ bx lr
+
+/*
+ * int copyinframe(const vm_address_t frame_addr, char *kernel_addr)
+ *
+ * Safely copy eight bytes (the fixed top of an ARM frame) from
+ * either user or kernel memory.
+ */
+ .text
+ .align 2
+ .globl EXT(copyinframe)
+LEXT(copyinframe)
+ COPYIO_SET_RECOVER()
+ COPYIO_MAP_USER()
+ ldmia r0, {r2, r3}
+ stmia r1, {r2, r3}
+ b Lcopyin_noerror
+
+/*
+ * uint32_t arm_debug_read_dscr(void)
+ */
+ .text
+ .align 2
+ .globl EXT(arm_debug_read_dscr)
+LEXT(arm_debug_read_dscr)
+#if __ARM_DEBUG__ >= 6
+ mrc p14, 0, r0, c0, c1
+#else
+ mov r0, #0
+#endif
+ bx lr
+
+/*
+ * void arm_debug_set_cp14(arm_debug_state_t *debug_state)
+ *
+ * Set debug registers to match the current thread state
+ * (NULL to disable). Assume 6 breakpoints and 2
+ * watchpoints, since that has been the case in all cores
+ * thus far.
+ */
+ .text
+ .align 2
+ .globl EXT(arm_debug_set_cp14)
+LEXT(arm_debug_set_cp14)
+#if __ARM_DEBUG__ >= 6
+ mrc p15, 0, r1, c13, c0, 4 // Read TPIDRPRW
+ ldr r2, [r1, ACT_CPUDATAP] // Get current cpu
+ str r0, [r2, CPU_USER_DEBUG] // Set current user debug
+
+ // Lock the debug registers
+ movw ip, #0xCE55
+ movt ip, #0xC5AC
+ mcr p14, 0, ip, c1, c0, 4
+
+ // enable monitor mode (needed to set and use debug registers)
+ mrc p14, 0, ip, c0, c1, 0
+ orr ip, ip, #0x8000 // set MDBGen = 1
+#if __ARM_DEBUG__ >= 7
+ mcr p14, 0, ip, c0, c2, 2
+#else
+ mcr p14, 0, ip, c0, c1, 0
+#endif
+ // first turn off all breakpoints/watchpoints
+ mov r1, #0
+ mcr p14, 0, r1, c0, c0, 5 // BCR0
+ mcr p14, 0, r1, c0, c1, 5 // BCR1
+ mcr p14, 0, r1, c0, c2, 5 // BCR2
+ mcr p14, 0, r1, c0, c3, 5 // BCR3
+ mcr p14, 0, r1, c0, c4, 5 // BCR4
+ mcr p14, 0, r1, c0, c5, 5 // BCR5
+ mcr p14, 0, r1, c0, c0, 7 // WCR0
+ mcr p14, 0, r1, c0, c1, 7 // WCR1
+ // if (debug_state == NULL) disable monitor mode and return;
+ cmp r0, #0
+ biceq ip, ip, #0x8000 // set MDBGen = 0
+#if __ARM_DEBUG__ >= 7
+ mcreq p14, 0, ip, c0, c2, 2
+#else
+ mcreq p14, 0, ip, c0, c1, 0
+#endif
+ bxeq lr
+ ldmia r0!, {r1, r2, r3, ip}
+ mcr p14, 0, r1, c0, c0, 4 // BVR0
+ mcr p14, 0, r2, c0, c1, 4 // BVR1
+ mcr p14, 0, r3, c0, c2, 4 // BVR2
+ mcr p14, 0, ip, c0, c3, 4 // BVR3
+ ldmia r0!, {r1, r2}
+ mcr p14, 0, r1, c0, c4, 4 // BVR4
+ mcr p14, 0, r2, c0, c5, 4 // BVR5
+ add r0, r0, #40 // advance to bcr[0]
+ ldmia r0!, {r1, r2, r3, ip}
+ mcr p14, 0, r1, c0, c0, 5 // BCR0
+ mcr p14, 0, r2, c0, c1, 5 // BCR1
+ mcr p14, 0, r3, c0, c2, 5 // BCR2
+ mcr p14, 0, ip, c0, c3, 5 // BCR3
+ ldmia r0!, {r1, r2}
+ mcr p14, 0, r1, c0, c4, 5 // BCR4
+ mcr p14, 0, r2, c0, c5, 5 // BCR5
+ add r0, r0, #40 // advance to wvr[0]
+ ldmia r0!, {r1, r2}
+ mcr p14, 0, r1, c0, c0, 6 // WVR0
+ mcr p14, 0, r2, c0, c1, 6 // WVR1
+ add r0, r0, #56 // advance to wcr[0]
+ ldmia r0!, {r1, r2}
+ mcr p14, 0, r1, c0, c0, 7 // WCR0
+ mcr p14, 0, r2, c0, c1, 7 // WCR1
+
+ // Unlock debug registers
+ mov ip, #0
+ mcr p14, 0, ip, c1, c0, 4
+#endif
+ bx lr
+
+/*
+ * void fiq_context_init(boolean_t enable_fiq)
+ */
+ .text
+ .align 2
+ .globl EXT(fiq_context_init)
+LEXT(fiq_context_init)
+ mrs r3, cpsr // Save current CPSR
+ cmp r0, #0 // Test enable_fiq
+ bicne r3, r3, #PSR_FIQF // Enable FIQ if not FALSE
+ mrc p15, 0, r12, c13, c0, 4 // Read TPIDRPRW
+ ldr r2, [r12, ACT_CPUDATAP] // Get current cpu data
+
+#if __ARM_TIME__
+ /* Despite the fact that we use the physical timebase
+ * register as the basis for time on our platforms, we
+ * end up using the virtual timer in order to manage
+ * deadlines. This is due to the fact that for our
+ * current platforms, the interrupt generated by the
+ * physical timer is not hooked up to anything, and is
+ * therefore dropped on the floor. Therefore, for
+ * timers to function they MUST be based on the virtual
+ * timer.
+ */
+
+ mov r0, #1 // Enable Timer
+ mcr p15, 0, r0, c14, c3, 1 // Write to CNTV_CTL
+
+ /* Enable USER access to the physical timebase (PL0PCTEN).
+ * The rationale for providing access to the physical
+ * timebase being that the virtual timebase is broken for
+ * some platforms. Maintaining the offset ourselves isn't
+ * expensive, so mandate that the userspace implementation
+ * do timebase_phys+offset rather than trying to propogate
+ * all of the informaiton about what works up to USER.
+ */
+ mcr p15, 0, r0, c14, c1, 0 // Set CNTKCTL.PL0PCTEN (CNTKCTL[0])
+
+#else /* ! __ARM_TIME__ */
+ msr cpsr_c, #(PSR_FIQ_MODE|PSR_FIQF|PSR_IRQF) // Change mode to FIQ with FIQ/IRQ disabled
+ mov r8, r2 // Load the BootCPUData address
+ ldr r9, [r2, CPU_GET_FIQ_HANDLER] // Load fiq function address
+ ldr r10, [r2, CPU_TBD_HARDWARE_ADDR] // Load the hardware address
+ ldr r11, [r2, CPU_TBD_HARDWARE_VAL] // Load the hardware value
+#endif /* __ARM_TIME__ */
+
+ msr cpsr_c, r3 // Restore saved CPSR
+ bx lr
+
+/*
+ * void reenable_async_aborts(void)
+ */
+ .text
+ .align 2
+ .globl EXT(reenable_async_aborts)
+LEXT(reenable_async_aborts)
+ cpsie a // Re-enable async aborts
+ bx lr
+
+/*
+ * uint64_t ml_get_timebase(void)
+ */
+ .text
+ .align 2
+ .globl EXT(ml_get_timebase)
+LEXT(ml_get_timebase)
+ mrc p15, 0, r12, c13, c0, 4 // Read TPIDRPRW
+ ldr r3, [r12, ACT_CPUDATAP] // Get current cpu data
+#if __ARM_TIME__ || __ARM_TIME_TIMEBASE_ONLY__
+ isb // Required by ARMV7C.b section B8.1.2, ARMv8 section D6.1.2.
+1:
+ mrrc p15, 0, r3, r1, c14 // Read the Time Base (CNTPCT), high => r1
+ mrrc p15, 0, r0, r3, c14 // Read the Time Base (CNTPCT), low => r0
+ mrrc p15, 0, r3, r2, c14 // Read the Time Base (CNTPCT), high => r2
+ cmp r1, r2
+ bne 1b // Loop until both high values are the same
+
+ ldr r3, [r12, ACT_CPUDATAP] // Get current cpu data
+ ldr r2, [r3, CPU_BASE_TIMEBASE_LOW] // Add in the offset to
+ adds r0, r0, r2 // convert to
+ ldr r2, [r3, CPU_BASE_TIMEBASE_HIGH] // mach_absolute_time
+ adc r1, r1, r2 //
+#else /* ! __ARM_TIME__ || __ARM_TIME_TIMEBASE_ONLY__ */
+1:
+ ldr r2, [r3, CPU_TIMEBASE_HIGH] // Get the saved TBU value
+ ldr r0, [r3, CPU_TIMEBASE_LOW] // Get the saved TBL value
+ ldr r1, [r3, CPU_TIMEBASE_HIGH] // Get the saved TBU value
+ cmp r1, r2 // Make sure TB has not rolled over
+ bne 1b
+#endif /* __ARM_TIME__ */
+ bx lr // return
+
+
+/*
+ * uint32_t ml_get_decrementer(void)
+ */
+ .text
+ .align 2
+ .globl EXT(ml_get_decrementer)
+LEXT(ml_get_decrementer)
+ mrc p15, 0, r12, c13, c0, 4 // Read TPIDRPRW
+ ldr r3, [r12, ACT_CPUDATAP] // Get current cpu data
+ ldr r2, [r3, CPU_GET_DECREMENTER_FUNC] // Get get_decrementer_func
+ cmp r2, #0
+ bxne r2 // Call it if there is one
+#if __ARM_TIME__
+ mrc p15, 0, r0, c14, c3, 0 // Read the Decrementer (CNTV_TVAL)
+#else
+ ldr r0, [r3, CPU_DECREMENTER] // Get the saved dec value
+#endif
+ bx lr // return
+
+
+/*
+ * void ml_set_decrementer(uint32_t dec_value)
+ */
+ .text
+ .align 2
+ .globl EXT(ml_set_decrementer)
+LEXT(ml_set_decrementer)
+ mrc p15, 0, r12, c13, c0, 4 // Read TPIDRPRW
+ ldr r3, [r12, ACT_CPUDATAP] // Get current cpu data
+ ldr r2, [r3, CPU_SET_DECREMENTER_FUNC] // Get set_decrementer_func
+ cmp r2, #0
+ bxne r2 // Call it if there is one
+#if __ARM_TIME__
+ str r0, [r3, CPU_DECREMENTER] // Save the new dec value
+ mcr p15, 0, r0, c14, c3, 0 // Write the Decrementer (CNTV_TVAL)
+#else
+ mrs r2, cpsr // Save current CPSR
+ msr cpsr_c, #(PSR_FIQ_MODE|PSR_FIQF|PSR_IRQF) // Change mode to FIQ with FIQ/IRQ disabled.
+ mov r12, r0 // Set the DEC value
+ str r12, [r8, CPU_DECREMENTER] // Store DEC
+ msr cpsr_c, r2 // Restore saved CPSR
+#endif
+ bx lr
+
+
+/*
+ * boolean_t ml_get_interrupts_enabled(void)
+ */
+ .text
+ .align 2
+ .globl EXT(ml_get_interrupts_enabled)
+LEXT(ml_get_interrupts_enabled)
+ mrs r2, cpsr
+ mov r0, #1
+ bic r0, r0, r2, lsr #PSR_IRQFb
+ bx lr
+
+/*
+ * Platform Specific Timebase & Decrementer Functions
+ *
+ */
+
+#if defined(ARM_BOARD_CLASS_S7002)
+ .text
+ .align 2
+ .globl EXT(fleh_fiq_s7002)
+LEXT(fleh_fiq_s7002)
+ str r11, [r10, #PMGR_INTERVAL_TMR_CTL_OFFSET] // Clear the decrementer interrupt
+ mvn r13, #0
+ str r13, [r8, CPU_DECREMENTER]
+ b EXT(fleh_dec)
+
+ .text
+ .align 2
+ .globl EXT(s7002_get_decrementer)
+LEXT(s7002_get_decrementer)
+ ldr ip, [r3, CPU_TBD_HARDWARE_ADDR] // Get the hardware address
+ add ip, ip, #PMGR_INTERVAL_TMR_OFFSET
+ ldr r0, [ip] // Get the Decrementer
+ bx lr
+
+ .text
+ .align 2
+ .globl EXT(s7002_set_decrementer)
+LEXT(s7002_set_decrementer)
+ str r0, [r3, CPU_DECREMENTER] // Save the new dec value
+ ldr ip, [r3, CPU_TBD_HARDWARE_ADDR] // Get the hardware address
+ str r0, [ip, #PMGR_INTERVAL_TMR_OFFSET] // Store the new Decrementer
+ bx lr
+#endif /* defined(ARM_BOARD_CLASS_S7002) */
+
+#if defined(ARM_BOARD_CLASS_T8002)
+ .text
+ .align 2
+ .globl EXT(fleh_fiq_t8002)
+LEXT(fleh_fiq_t8002)
+ mov r13, #kAICTmrIntStat
+ str r11, [r10, r13] // Clear the decrementer interrupt
+ mvn r13, #0
+ str r13, [r8, CPU_DECREMENTER]
+ b EXT(fleh_dec)
+
+ .text
+ .align 2
+ .globl EXT(t8002_get_decrementer)
+LEXT(t8002_get_decrementer)
+ ldr ip, [r3, CPU_TBD_HARDWARE_ADDR] // Get the hardware address
+ mov r0, #kAICTmrCnt
+ add ip, ip, r0
+ ldr r0, [ip] // Get the Decrementer
+ bx lr
+
+ .text
+ .align 2
+ .globl EXT(t8002_set_decrementer)
+LEXT(t8002_set_decrementer)
+ str r0, [r3, CPU_DECREMENTER] // Save the new dec value
+ ldr ip, [r3, CPU_TBD_HARDWARE_ADDR] // Get the hardware address
+ mov r5, #kAICTmrCnt
+ str r0, [ip, r5] // Store the new Decrementer
+ bx lr
+#endif /* defined(ARM_BOARD_CLASS_T8002) */
+
+LOAD_ADDR_GEN_DEF(kernel_pmap_store)
+
+#include "globals_asm.h"
+
+/* vim: set ts=4: */