98 lines
2.8 KiB
ArmAsm
98 lines
2.8 KiB
ArmAsm
/*
|
|
* Copyright (C) 2010 Imagination Technologies Ltd.
|
|
*
|
|
* This file contains code that can be accessed from userspace and can
|
|
* access certain kernel data structures without the overhead of a system
|
|
* call.
|
|
*/
|
|
|
|
#include <asm/metag_regs.h>
|
|
#include <asm/user_gateway.h>
|
|
|
|
/*
|
|
* User helpers.
|
|
*
|
|
* These are segment of kernel provided user code reachable from user space
|
|
* at a fixed address in kernel memory. This is used to provide user space
|
|
* with some operations which require kernel help because of unimplemented
|
|
* native feature and/or instructions in some Meta CPUs. The idea is for
|
|
* this code to be executed directly in user mode for best efficiency but
|
|
* which is too intimate with the kernel counter part to be left to user
|
|
* libraries. The kernel reserves the right to change this code as needed
|
|
* without warning. Only the entry points and their results are guaranteed
|
|
* to be stable.
|
|
*
|
|
* Each segment is 64-byte aligned. This mechanism should be used only for
|
|
* for things that are really small and justified, and not be abused freely.
|
|
*/
|
|
.text
|
|
.global ___user_gateway_start
|
|
___user_gateway_start:
|
|
|
|
/* get_tls
|
|
* Offset: 0
|
|
* Description: Get the TLS pointer for this process.
|
|
*/
|
|
.global ___kuser_get_tls
|
|
.type ___kuser_get_tls,function
|
|
___kuser_get_tls:
|
|
MOVT D1Ar1,#HI(USER_GATEWAY_PAGE + USER_GATEWAY_TLS)
|
|
ADD D1Ar1,D1Ar1,#LO(USER_GATEWAY_PAGE + USER_GATEWAY_TLS)
|
|
MOV D1Ar3,TXENABLE
|
|
AND D1Ar3,D1Ar3,#(TXENABLE_THREAD_BITS)
|
|
LSR D1Ar3,D1Ar3,#(TXENABLE_THREAD_S - 2)
|
|
GETD D0Re0,[D1Ar1+D1Ar3]
|
|
___kuser_get_tls_end: /* Beyond this point the read will complete */
|
|
MOV PC,D1RtP
|
|
.size ___kuser_get_tls,.-___kuser_get_tls
|
|
.global ___kuser_get_tls_end
|
|
|
|
/* cmpxchg
|
|
* Offset: 64
|
|
* Description: Replace the value at 'ptr' with 'newval' if the current
|
|
* value is 'oldval'. Return zero if we succeeded,
|
|
* non-zero otherwise.
|
|
*
|
|
* Reference prototype:
|
|
*
|
|
* int __kuser_cmpxchg(int oldval, int newval, unsigned long *ptr)
|
|
*
|
|
*/
|
|
.balign 64
|
|
.global ___kuser_cmpxchg
|
|
.type ___kuser_cmpxchg,function
|
|
___kuser_cmpxchg:
|
|
#ifdef CONFIG_SMP
|
|
/*
|
|
* We must use LNKGET/LNKSET with an SMP kernel because the other method
|
|
* does not provide atomicity across multiple CPUs.
|
|
*/
|
|
0: LNKGETD D0Re0,[D1Ar3]
|
|
CMP D0Re0,D1Ar1
|
|
LNKSETDZ [D1Ar3],D0Ar2
|
|
BNZ 1f
|
|
DEFR D0Re0,TXSTAT
|
|
ANDT D0Re0,D0Re0,#HI(0x3f000000)
|
|
CMPT D0Re0,#HI(0x02000000)
|
|
BNE 0b
|
|
#ifdef CONFIG_METAG_LNKGET_AROUND_CACHE
|
|
DCACHE [D1Ar3], D0Re0
|
|
#endif
|
|
1: MOV D0Re0,#1
|
|
XORZ D0Re0,D0Re0,D0Re0
|
|
MOV PC,D1RtP
|
|
#else
|
|
GETD D0Re0,[D1Ar3]
|
|
CMP D0Re0,D1Ar1
|
|
SETDZ [D1Ar3],D0Ar2
|
|
___kuser_cmpxchg_end: /* Beyond this point the write will complete */
|
|
MOV D0Re0,#1
|
|
XORZ D0Re0,D0Re0,D0Re0
|
|
MOV PC,D1RtP
|
|
#endif /* CONFIG_SMP */
|
|
.size ___kuser_cmpxchg,.-___kuser_cmpxchg
|
|
.global ___kuser_cmpxchg_end
|
|
|
|
.global ___user_gateway_end
|
|
___user_gateway_end:
|