OSDN Git Service

ARM: kuser: move interface documentation out of the source code
authorNicolas Pitre <nicolas.pitre@linaro.org>
Mon, 20 Jun 2011 03:36:03 +0000 (23:36 -0400)
committerNicolas Pitre <nico@fluxnic.net>
Mon, 20 Jun 2011 14:49:24 +0000 (10:49 -0400)
Digging into some assembly file in order to get information about the
kuser helpers is not that convivial.  Let's move that information to
a better formatted file in Documentation/arm/ and improve on it a bit.

Thanks to Dave Martin <dave.martin@linaro.org> for the initial cleanup and
clarifications.

Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Acked-by: Dave Martin <dave.martin@linaro.org>
Documentation/arm/kernel_user_helpers.txt [new file with mode: 0644]
arch/arm/kernel/entry-armv.S

diff --git a/Documentation/arm/kernel_user_helpers.txt b/Documentation/arm/kernel_user_helpers.txt
new file mode 100644 (file)
index 0000000..0c33f72
--- /dev/null
@@ -0,0 +1,203 @@
+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).
index e8d8856..63f7907 100644 (file)
@@ -754,31 +754,12 @@ ENDPROC(__switch_to)
 /*
  * 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
  * 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
  * really small and justified, and not be abused freely.
  *
- * User space is expected to implement those things inline when optimizing
- * 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.
+ * See Documentation/arm/kernel_user_helpers.txt for formal definitions.
  */
  THUMB(        .arm    )
 
@@ -794,98 +775,12 @@ ENDPROC(__switch_to)
        .globl  __kuser_helper_start
 __kuser_helper_start:
 
-/*
- * Reference prototype:
- *
- *     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_memory_barrier:                                @ 0xffff0fa0
        smp_dmb arm
        usr_ret lr
 
        .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
 
 #if defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
@@ -959,39 +854,6 @@ kuser_cmpxchg_fixup:
 
        .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
        ldr     r0, [pc, #(16 - 8)]     @ read TLS, set in kuser_get_tls_init
        usr_ret lr
@@ -1000,19 +862,6 @@ __kuser_get_tls:                          @ 0xffff0fe0
        .word   0                       @ 0xffff0ff0 software TLS value, then
        .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
        .word   ((__kuser_helper_end - __kuser_helper_start) >> 5)