mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	Signed-off-by: Masanari Iida <standby24x7@gmail.com> Acked-by: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
		
			
				
	
	
		
			267 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			267 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
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).
 |