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