mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-30 05:15:03 +00:00
Merge branch 'cmpxchg64' of git://git.linaro.org/people/nico/linux into devel-stable
This commit is contained in:
commit
2ff0720933
267
Documentation/arm/kernel_user_helpers.txt
Normal file
267
Documentation/arm/kernel_user_helpers.txt
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
Kernel-provided 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 many ARM 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.
|
||||||
|
In fact this code might even differ from one CPU to another depending on
|
||||||
|
the available instruction set, or whether it is a SMP systems. In other
|
||||||
|
words, the kernel reserves the right to change this code as needed without
|
||||||
|
warning. Only the entry points and their results as documented here are
|
||||||
|
guaranteed to be stable.
|
||||||
|
|
||||||
|
This is different from (but doesn't preclude) a full blown VDSO
|
||||||
|
implementation, however a VDSO would prevent some assembly tricks with
|
||||||
|
constants that allows for efficient branching to those code segments. And
|
||||||
|
since those code segments only use a few cycles before returning to user
|
||||||
|
code, the overhead of a VDSO indirect far call would add a measurable
|
||||||
|
overhead to such minimalistic operations.
|
||||||
|
|
||||||
|
User space is expected to bypass those helpers and implement those things
|
||||||
|
inline (either in the code emitted directly by the compiler, or part of
|
||||||
|
the implementation of a library call) when optimizing for a recent enough
|
||||||
|
processor that has the necessary native support, but only if resulting
|
||||||
|
binaries are already to be incompatible with earlier ARM processors due to
|
||||||
|
useage of similar native instructions for other things. In other words
|
||||||
|
don't make binaries unable to run on earlier processors just for the sake
|
||||||
|
of not using these kernel helpers if your compiled code is not going to
|
||||||
|
use new instructions for other purpose.
|
||||||
|
|
||||||
|
New helpers may be added over time, so an older kernel may be missing some
|
||||||
|
helpers present in a newer kernel. For this reason, programs must check
|
||||||
|
the value of __kuser_helper_version (see below) before assuming that it is
|
||||||
|
safe to call any particular helper. This check should ideally be
|
||||||
|
performed only once at process startup time, and execution aborted early
|
||||||
|
if the required helpers are not provided by the kernel version that
|
||||||
|
process is running on.
|
||||||
|
|
||||||
|
kuser_helper_version
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Location: 0xffff0ffc
|
||||||
|
|
||||||
|
Reference declaration:
|
||||||
|
|
||||||
|
extern int32_t __kuser_helper_version;
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
This field contains the number of helpers being implemented by the
|
||||||
|
running kernel. User space may read this to determine the availability
|
||||||
|
of a particular helper.
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
|
||||||
|
#define __kuser_helper_version (*(int32_t *)0xffff0ffc)
|
||||||
|
|
||||||
|
void check_kuser_version(void)
|
||||||
|
{
|
||||||
|
if (__kuser_helper_version < 2) {
|
||||||
|
fprintf(stderr, "can't do atomic operations, kernel too old\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
User space may assume that the value of this field never changes
|
||||||
|
during the lifetime of any single process. This means that this
|
||||||
|
field can be read once during the initialisation of a library or
|
||||||
|
startup phase of a program.
|
||||||
|
|
||||||
|
kuser_get_tls
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Location: 0xffff0fe0
|
||||||
|
|
||||||
|
Reference prototype:
|
||||||
|
|
||||||
|
void * __kuser_get_tls(void);
|
||||||
|
|
||||||
|
Input:
|
||||||
|
|
||||||
|
lr = return address
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
r0 = TLS value
|
||||||
|
|
||||||
|
Clobbered registers:
|
||||||
|
|
||||||
|
none
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
Get the TLS value as previously set via the __ARM_NR_set_tls syscall.
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
|
||||||
|
typedef void * (__kuser_get_tls_t)(void);
|
||||||
|
#define __kuser_get_tls (*(__kuser_get_tls_t *)0xffff0fe0)
|
||||||
|
|
||||||
|
void foo()
|
||||||
|
{
|
||||||
|
void *tls = __kuser_get_tls();
|
||||||
|
printf("TLS = %p\n", tls);
|
||||||
|
}
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- Valid only if __kuser_helper_version >= 1 (from kernel version 2.6.12).
|
||||||
|
|
||||||
|
kuser_cmpxchg
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Location: 0xffff0fc0
|
||||||
|
|
||||||
|
Reference prototype:
|
||||||
|
|
||||||
|
int __kuser_cmpxchg(int32_t oldval, int32_t newval, volatile int32_t *ptr);
|
||||||
|
|
||||||
|
Input:
|
||||||
|
|
||||||
|
r0 = oldval
|
||||||
|
r1 = newval
|
||||||
|
r2 = ptr
|
||||||
|
lr = return address
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
r0 = success code (zero or non-zero)
|
||||||
|
C flag = set if r0 == 0, clear if r0 != 0
|
||||||
|
|
||||||
|
Clobbered registers:
|
||||||
|
|
||||||
|
r3, ip, flags
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
Atomically store newval in *ptr only if *ptr is equal to oldval.
|
||||||
|
Return zero if *ptr was changed or non-zero if no exchange happened.
|
||||||
|
The C flag is also set if *ptr was changed to allow for assembly
|
||||||
|
optimization in the calling code.
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
|
||||||
|
typedef int (__kuser_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
|
||||||
|
#define __kuser_cmpxchg (*(__kuser_cmpxchg_t *)0xffff0fc0)
|
||||||
|
|
||||||
|
int atomic_add(volatile int *ptr, int val)
|
||||||
|
{
|
||||||
|
int old, new;
|
||||||
|
|
||||||
|
do {
|
||||||
|
old = *ptr;
|
||||||
|
new = old + val;
|
||||||
|
} while(__kuser_cmpxchg(old, new, ptr));
|
||||||
|
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- This routine already includes memory barriers as needed.
|
||||||
|
|
||||||
|
- Valid only if __kuser_helper_version >= 2 (from kernel version 2.6.12).
|
||||||
|
|
||||||
|
kuser_memory_barrier
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Location: 0xffff0fa0
|
||||||
|
|
||||||
|
Reference prototype:
|
||||||
|
|
||||||
|
void __kuser_memory_barrier(void);
|
||||||
|
|
||||||
|
Input:
|
||||||
|
|
||||||
|
lr = return address
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
none
|
||||||
|
|
||||||
|
Clobbered registers:
|
||||||
|
|
||||||
|
none
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
Apply any needed memory barrier to preserve consistency with data modified
|
||||||
|
manually and __kuser_cmpxchg usage.
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
|
||||||
|
typedef void (__kuser_dmb_t)(void);
|
||||||
|
#define __kuser_dmb (*(__kuser_dmb_t *)0xffff0fa0)
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- Valid only if __kuser_helper_version >= 3 (from kernel version 2.6.15).
|
||||||
|
|
||||||
|
kuser_cmpxchg64
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Location: 0xffff0f60
|
||||||
|
|
||||||
|
Reference prototype:
|
||||||
|
|
||||||
|
int __kuser_cmpxchg64(const int64_t *oldval,
|
||||||
|
const int64_t *newval,
|
||||||
|
volatile int64_t *ptr);
|
||||||
|
|
||||||
|
Input:
|
||||||
|
|
||||||
|
r0 = pointer to oldval
|
||||||
|
r1 = pointer to newval
|
||||||
|
r2 = pointer to target value
|
||||||
|
lr = return address
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
r0 = success code (zero or non-zero)
|
||||||
|
C flag = set if r0 == 0, clear if r0 != 0
|
||||||
|
|
||||||
|
Clobbered registers:
|
||||||
|
|
||||||
|
r3, lr, flags
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
Atomically store the 64-bit value pointed by *newval in *ptr only if *ptr
|
||||||
|
is equal to the 64-bit value pointed by *oldval. Return zero if *ptr was
|
||||||
|
changed or non-zero if no exchange happened.
|
||||||
|
|
||||||
|
The C flag is also set if *ptr was changed to allow for assembly
|
||||||
|
optimization in the calling code.
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
|
||||||
|
typedef int (__kuser_cmpxchg64_t)(const int64_t *oldval,
|
||||||
|
const int64_t *newval,
|
||||||
|
volatile int64_t *ptr);
|
||||||
|
#define __kuser_cmpxchg64 (*(__kuser_cmpxchg64_t *)0xffff0f60)
|
||||||
|
|
||||||
|
int64_t atomic_add64(volatile int64_t *ptr, int64_t val)
|
||||||
|
{
|
||||||
|
int64_t old, new;
|
||||||
|
|
||||||
|
do {
|
||||||
|
old = *ptr;
|
||||||
|
new = old + val;
|
||||||
|
} while(__kuser_cmpxchg64(&old, &new, ptr));
|
||||||
|
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- This routine already includes memory barriers as needed.
|
||||||
|
|
||||||
|
- Due to the length of this sequence, this spans 2 conventional kuser
|
||||||
|
"slots", therefore 0xffff0f80 is not used as a valid entry point.
|
||||||
|
|
||||||
|
- Valid only if __kuser_helper_version >= 5 (from kernel version 3.1).
|
@ -383,7 +383,7 @@ ENDPROC(__pabt_svc)
|
|||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro kuser_cmpxchg_check
|
.macro kuser_cmpxchg_check
|
||||||
#if __LINUX_ARM_ARCH__ < 6 && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
|
#if !defined(CONFIG_CPU_32v6K) && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
|
||||||
#ifndef CONFIG_MMU
|
#ifndef CONFIG_MMU
|
||||||
#warning "NPTL on non MMU needs fixing"
|
#warning "NPTL on non MMU needs fixing"
|
||||||
#else
|
#else
|
||||||
@ -392,7 +392,7 @@ ENDPROC(__pabt_svc)
|
|||||||
@ perform a quick test inline since it should be false
|
@ perform a quick test inline since it should be false
|
||||||
@ 99.9999% of the time. The rest is done out of line.
|
@ 99.9999% of the time. The rest is done out of line.
|
||||||
cmp r2, #TASK_SIZE
|
cmp r2, #TASK_SIZE
|
||||||
blhs kuser_cmpxchg_fixup
|
blhs kuser_cmpxchg64_fixup
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
.endm
|
.endm
|
||||||
@ -758,31 +758,12 @@ ENDPROC(__switch_to)
|
|||||||
/*
|
/*
|
||||||
* User helpers.
|
* 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 many ARM 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. In fact this code might even differ from one CPU to another
|
|
||||||
* depending on the available instruction set and restrictions like on
|
|
||||||
* SMP systems. In other words, 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 32-byte aligned and will be moved to the top of the high
|
* Each segment is 32-byte aligned and will be moved to the top of the high
|
||||||
* vector page. New segments (if ever needed) must be added in front of
|
* vector page. New segments (if ever needed) must be added in front of
|
||||||
* existing ones. This mechanism should be used only for things that are
|
* existing ones. This mechanism should be used only for things that are
|
||||||
* really small and justified, and not be abused freely.
|
* really small and justified, and not be abused freely.
|
||||||
*
|
*
|
||||||
* User space is expected to implement those things inline when optimizing
|
* See Documentation/arm/kernel_user_helpers.txt for formal definitions.
|
||||||
* for a processor that has the necessary native support, but only if such
|
|
||||||
* resulting binaries are already to be incompatible with earlier ARM
|
|
||||||
* processors due to the use of unsupported instructions other than what
|
|
||||||
* is provided here. In other words don't make binaries unable to run on
|
|
||||||
* earlier processors just for the sake of not using these kernel helpers
|
|
||||||
* if your compiled code is not going to use the new instructions for other
|
|
||||||
* purpose.
|
|
||||||
*/
|
*/
|
||||||
THUMB( .arm )
|
THUMB( .arm )
|
||||||
|
|
||||||
@ -799,97 +780,104 @@ ENDPROC(__switch_to)
|
|||||||
__kuser_helper_start:
|
__kuser_helper_start:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reference prototype:
|
* Due to the length of some sequences, __kuser_cmpxchg64 spans 2 regular
|
||||||
*
|
* kuser "slots", therefore 0xffff0f80 is not used as a valid entry point.
|
||||||
* void __kernel_memory_barrier(void)
|
|
||||||
*
|
|
||||||
* Input:
|
|
||||||
*
|
|
||||||
* lr = return address
|
|
||||||
*
|
|
||||||
* Output:
|
|
||||||
*
|
|
||||||
* none
|
|
||||||
*
|
|
||||||
* Clobbered:
|
|
||||||
*
|
|
||||||
* none
|
|
||||||
*
|
|
||||||
* Definition and user space usage example:
|
|
||||||
*
|
|
||||||
* typedef void (__kernel_dmb_t)(void);
|
|
||||||
* #define __kernel_dmb (*(__kernel_dmb_t *)0xffff0fa0)
|
|
||||||
*
|
|
||||||
* Apply any needed memory barrier to preserve consistency with data modified
|
|
||||||
* manually and __kuser_cmpxchg usage.
|
|
||||||
*
|
|
||||||
* This could be used as follows:
|
|
||||||
*
|
|
||||||
* #define __kernel_dmb() \
|
|
||||||
* asm volatile ( "mov r0, #0xffff0fff; mov lr, pc; sub pc, r0, #95" \
|
|
||||||
* : : : "r0", "lr","cc" )
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
__kuser_cmpxchg64: @ 0xffff0f60
|
||||||
|
|
||||||
|
#if defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Poor you. No fast solution possible...
|
||||||
|
* The kernel itself must perform the operation.
|
||||||
|
* A special ghost syscall is used for that (see traps.c).
|
||||||
|
*/
|
||||||
|
stmfd sp!, {r7, lr}
|
||||||
|
ldr r7, 1f @ it's 20 bits
|
||||||
|
swi __ARM_NR_cmpxchg64
|
||||||
|
ldmfd sp!, {r7, pc}
|
||||||
|
1: .word __ARM_NR_cmpxchg64
|
||||||
|
|
||||||
|
#elif defined(CONFIG_CPU_32v6K)
|
||||||
|
|
||||||
|
stmfd sp!, {r4, r5, r6, r7}
|
||||||
|
ldrd r4, r5, [r0] @ load old val
|
||||||
|
ldrd r6, r7, [r1] @ load new val
|
||||||
|
smp_dmb arm
|
||||||
|
1: ldrexd r0, r1, [r2] @ load current val
|
||||||
|
eors r3, r0, r4 @ compare with oldval (1)
|
||||||
|
eoreqs r3, r1, r5 @ compare with oldval (2)
|
||||||
|
strexdeq r3, r6, r7, [r2] @ store newval if eq
|
||||||
|
teqeq r3, #1 @ success?
|
||||||
|
beq 1b @ if no then retry
|
||||||
|
smp_dmb arm
|
||||||
|
rsbs r0, r3, #0 @ set returned val and C flag
|
||||||
|
ldmfd sp!, {r4, r5, r6, r7}
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
#elif !defined(CONFIG_SMP)
|
||||||
|
|
||||||
|
#ifdef CONFIG_MMU
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The only thing that can break atomicity in this cmpxchg64
|
||||||
|
* implementation is either an IRQ or a data abort exception
|
||||||
|
* causing another process/thread to be scheduled in the middle of
|
||||||
|
* the critical sequence. The same strategy as for cmpxchg is used.
|
||||||
|
*/
|
||||||
|
stmfd sp!, {r4, r5, r6, lr}
|
||||||
|
ldmia r0, {r4, r5} @ load old val
|
||||||
|
ldmia r1, {r6, lr} @ load new val
|
||||||
|
1: ldmia r2, {r0, r1} @ load current val
|
||||||
|
eors r3, r0, r4 @ compare with oldval (1)
|
||||||
|
eoreqs r3, r1, r5 @ compare with oldval (2)
|
||||||
|
2: stmeqia r2, {r6, lr} @ store newval if eq
|
||||||
|
rsbs r0, r3, #0 @ set return val and C flag
|
||||||
|
ldmfd sp!, {r4, r5, r6, pc}
|
||||||
|
|
||||||
|
.text
|
||||||
|
kuser_cmpxchg64_fixup:
|
||||||
|
@ Called from kuser_cmpxchg_fixup.
|
||||||
|
@ r2 = address of interrupted insn (must be preserved).
|
||||||
|
@ sp = saved regs. r7 and r8 are clobbered.
|
||||||
|
@ 1b = first critical insn, 2b = last critical insn.
|
||||||
|
@ If r2 >= 1b and r2 <= 2b then saved pc_usr is set to 1b.
|
||||||
|
mov r7, #0xffff0fff
|
||||||
|
sub r7, r7, #(0xffff0fff - (0xffff0f60 + (1b - __kuser_cmpxchg64)))
|
||||||
|
subs r8, r2, r7
|
||||||
|
rsbcss r8, r8, #(2b - 1b)
|
||||||
|
strcs r7, [sp, #S_PC]
|
||||||
|
#if __LINUX_ARM_ARCH__ < 6
|
||||||
|
bcc kuser_cmpxchg32_fixup
|
||||||
|
#endif
|
||||||
|
mov pc, lr
|
||||||
|
.previous
|
||||||
|
|
||||||
|
#else
|
||||||
|
#warning "NPTL on non MMU needs fixing"
|
||||||
|
mov r0, #-1
|
||||||
|
adds r0, r0, #0
|
||||||
|
usr_ret lr
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "incoherent kernel configuration"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* pad to next slot */
|
||||||
|
.rept (16 - (. - __kuser_cmpxchg64)/4)
|
||||||
|
.word 0
|
||||||
|
.endr
|
||||||
|
|
||||||
|
.align 5
|
||||||
|
|
||||||
__kuser_memory_barrier: @ 0xffff0fa0
|
__kuser_memory_barrier: @ 0xffff0fa0
|
||||||
smp_dmb arm
|
smp_dmb arm
|
||||||
usr_ret lr
|
usr_ret lr
|
||||||
|
|
||||||
.align 5
|
.align 5
|
||||||
|
|
||||||
/*
|
|
||||||
* Reference prototype:
|
|
||||||
*
|
|
||||||
* int __kernel_cmpxchg(int oldval, int newval, int *ptr)
|
|
||||||
*
|
|
||||||
* Input:
|
|
||||||
*
|
|
||||||
* r0 = oldval
|
|
||||||
* r1 = newval
|
|
||||||
* r2 = ptr
|
|
||||||
* lr = return address
|
|
||||||
*
|
|
||||||
* Output:
|
|
||||||
*
|
|
||||||
* r0 = returned value (zero or non-zero)
|
|
||||||
* C flag = set if r0 == 0, clear if r0 != 0
|
|
||||||
*
|
|
||||||
* Clobbered:
|
|
||||||
*
|
|
||||||
* r3, ip, flags
|
|
||||||
*
|
|
||||||
* Definition and user space usage example:
|
|
||||||
*
|
|
||||||
* typedef int (__kernel_cmpxchg_t)(int oldval, int newval, int *ptr);
|
|
||||||
* #define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0)
|
|
||||||
*
|
|
||||||
* Atomically store newval in *ptr if *ptr is equal to oldval for user space.
|
|
||||||
* Return zero if *ptr was changed or non-zero if no exchange happened.
|
|
||||||
* The C flag is also set if *ptr was changed to allow for assembly
|
|
||||||
* optimization in the calling code.
|
|
||||||
*
|
|
||||||
* Notes:
|
|
||||||
*
|
|
||||||
* - This routine already includes memory barriers as needed.
|
|
||||||
*
|
|
||||||
* For example, a user space atomic_add implementation could look like this:
|
|
||||||
*
|
|
||||||
* #define atomic_add(ptr, val) \
|
|
||||||
* ({ register unsigned int *__ptr asm("r2") = (ptr); \
|
|
||||||
* register unsigned int __result asm("r1"); \
|
|
||||||
* asm volatile ( \
|
|
||||||
* "1: @ atomic_add\n\t" \
|
|
||||||
* "ldr r0, [r2]\n\t" \
|
|
||||||
* "mov r3, #0xffff0fff\n\t" \
|
|
||||||
* "add lr, pc, #4\n\t" \
|
|
||||||
* "add r1, r0, %2\n\t" \
|
|
||||||
* "add pc, r3, #(0xffff0fc0 - 0xffff0fff)\n\t" \
|
|
||||||
* "bcc 1b" \
|
|
||||||
* : "=&r" (__result) \
|
|
||||||
* : "r" (__ptr), "rIL" (val) \
|
|
||||||
* : "r0","r3","ip","lr","cc","memory" ); \
|
|
||||||
* __result; })
|
|
||||||
*/
|
|
||||||
|
|
||||||
__kuser_cmpxchg: @ 0xffff0fc0
|
__kuser_cmpxchg: @ 0xffff0fc0
|
||||||
|
|
||||||
#if defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
|
#if defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
|
||||||
@ -925,7 +913,7 @@ __kuser_cmpxchg: @ 0xffff0fc0
|
|||||||
usr_ret lr
|
usr_ret lr
|
||||||
|
|
||||||
.text
|
.text
|
||||||
kuser_cmpxchg_fixup:
|
kuser_cmpxchg32_fixup:
|
||||||
@ Called from kuser_cmpxchg_check macro.
|
@ Called from kuser_cmpxchg_check macro.
|
||||||
@ r2 = address of interrupted insn (must be preserved).
|
@ r2 = address of interrupted insn (must be preserved).
|
||||||
@ sp = saved regs. r7 and r8 are clobbered.
|
@ sp = saved regs. r7 and r8 are clobbered.
|
||||||
@ -963,39 +951,6 @@ kuser_cmpxchg_fixup:
|
|||||||
|
|
||||||
.align 5
|
.align 5
|
||||||
|
|
||||||
/*
|
|
||||||
* Reference prototype:
|
|
||||||
*
|
|
||||||
* int __kernel_get_tls(void)
|
|
||||||
*
|
|
||||||
* Input:
|
|
||||||
*
|
|
||||||
* lr = return address
|
|
||||||
*
|
|
||||||
* Output:
|
|
||||||
*
|
|
||||||
* r0 = TLS value
|
|
||||||
*
|
|
||||||
* Clobbered:
|
|
||||||
*
|
|
||||||
* none
|
|
||||||
*
|
|
||||||
* Definition and user space usage example:
|
|
||||||
*
|
|
||||||
* typedef int (__kernel_get_tls_t)(void);
|
|
||||||
* #define __kernel_get_tls (*(__kernel_get_tls_t *)0xffff0fe0)
|
|
||||||
*
|
|
||||||
* Get the TLS value as previously set via the __ARM_NR_set_tls syscall.
|
|
||||||
*
|
|
||||||
* This could be used as follows:
|
|
||||||
*
|
|
||||||
* #define __kernel_get_tls() \
|
|
||||||
* ({ register unsigned int __val asm("r0"); \
|
|
||||||
* asm( "mov r0, #0xffff0fff; mov lr, pc; sub pc, r0, #31" \
|
|
||||||
* : "=r" (__val) : : "lr","cc" ); \
|
|
||||||
* __val; })
|
|
||||||
*/
|
|
||||||
|
|
||||||
__kuser_get_tls: @ 0xffff0fe0
|
__kuser_get_tls: @ 0xffff0fe0
|
||||||
ldr r0, [pc, #(16 - 8)] @ read TLS, set in kuser_get_tls_init
|
ldr r0, [pc, #(16 - 8)] @ read TLS, set in kuser_get_tls_init
|
||||||
usr_ret lr
|
usr_ret lr
|
||||||
@ -1004,19 +959,6 @@ __kuser_get_tls: @ 0xffff0fe0
|
|||||||
.word 0 @ 0xffff0ff0 software TLS value, then
|
.word 0 @ 0xffff0ff0 software TLS value, then
|
||||||
.endr @ pad up to __kuser_helper_version
|
.endr @ pad up to __kuser_helper_version
|
||||||
|
|
||||||
/*
|
|
||||||
* Reference declaration:
|
|
||||||
*
|
|
||||||
* extern unsigned int __kernel_helper_version;
|
|
||||||
*
|
|
||||||
* Definition and user space usage example:
|
|
||||||
*
|
|
||||||
* #define __kernel_helper_version (*(unsigned int *)0xffff0ffc)
|
|
||||||
*
|
|
||||||
* User space may read this to determine the curent number of helpers
|
|
||||||
* available.
|
|
||||||
*/
|
|
||||||
|
|
||||||
__kuser_helper_version: @ 0xffff0ffc
|
__kuser_helper_version: @ 0xffff0ffc
|
||||||
.word ((__kuser_helper_end - __kuser_helper_start) >> 5)
|
.word ((__kuser_helper_end - __kuser_helper_start) >> 5)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user