|  | 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 | 
|  | usage 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). |