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