blob: 7833fb8f9ddd6dc796d21c91ba42754738ec739c [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0 */
/*
* 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: