xen64: fix calls into hypercall page
authorJeremy Fitzhardinge <jeremy@goop.org>
Tue, 8 Jul 2008 22:06:37 +0000 (15:06 -0700)
committerIngo Molnar <mingo@elte.hu>
Wed, 16 Jul 2008 08:57:00 +0000 (10:57 +0200)
The 64-bit calling convention for hypercalls uses different registers
from 32-bit.  Annoyingly, gcc's asm syntax doesn't have a way to
specify one of the extra numeric reigisters in a constraint, so we
must use explicitly placed register variables.  Given that we have to
do it for some args, may as well do it for all.

Also fix syntax gcc generates for the call instruction itself.  We
need a plain direct call, but the asm expansion which works on 32-bit
generates a rip-relative addressing mode in 64-bit, which is treated
as an indirect call.  The alternative is to pass the hypercall page
offset into the asm, and have it add it to the hypercall page start
address to generate the call.

Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Cc: Stephen Tweedie <sct@redhat.com>
Cc: Eduardo Habkost <ehabkost@redhat.com>
Cc: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
include/asm-x86/xen/hypercall.h

index 0551b9d82ec4c1595d643d3cc2466e6b536ad18a..d0c5dedcb00197559c5bdb8d000e1a60029d1146 100644 (file)
 #include <xen/interface/sched.h>
 #include <xen/interface/physdev.h>
 
+/*
+ * The hypercall asms have to meet several constraints:
+ * - Work on 32- and 64-bit.
+ *    The two architectures put their arguments in different sets of
+ *    registers.
+ *
+ * - Work around asm syntax quirks
+ *    It isn't possible to specify one of the rNN registers in a
+ *    constraint, so we use explicit register variables to get the
+ *    args into the right place.
+ *
+ * - Mark all registers as potentially clobbered
+ *    Even unused parameters can be clobbered by the hypervisor, so we
+ *    need to make sure gcc knows it.
+ *
+ * - Avoid compiler bugs.
+ *    This is the tricky part.  Because x86_32 has such a constrained
+ *    register set, gcc versions below 4.3 have trouble generating
+ *    code when all the arg registers and memory are trashed by the
+ *    asm.  There are syntactically simpler ways of achieving the
+ *    semantics below, but they cause the compiler to crash.
+ *
+ *    The only combination I found which works is:
+ *     - assign the __argX variables first
+ *     - list all actually used parameters as "+r" (__argX)
+ *     - clobber the rest
+ *
+ * The result certainly isn't pretty, and it really shows up cpp's
+ * weakness as as macro language.  Sorry.  (But let's just give thanks
+ * there aren't more than 5 arguments...)
+ */
+
 extern struct { char _entry[32]; } hypercall_page[];
 
+#define __HYPERCALL            "call hypercall_page+%c[offset]"
+#define __HYPERCALL_ENTRY(x)                                           \
+       [offset] "i" (__HYPERVISOR_##x * sizeof(hypercall_page[0]))
+
+#ifdef CONFIG_X86_32
+#define __HYPERCALL_RETREG     "eax"
+#define __HYPERCALL_ARG1REG    "ebx"
+#define __HYPERCALL_ARG2REG    "ecx"
+#define __HYPERCALL_ARG3REG    "edx"
+#define __HYPERCALL_ARG4REG    "esi"
+#define __HYPERCALL_ARG5REG    "edi"
+#else
+#define __HYPERCALL_RETREG     "rax"
+#define __HYPERCALL_ARG1REG    "rdi"
+#define __HYPERCALL_ARG2REG    "rsi"
+#define __HYPERCALL_ARG3REG    "rdx"
+#define __HYPERCALL_ARG4REG    "r10"
+#define __HYPERCALL_ARG5REG    "r8"
+#endif
+
+#define __HYPERCALL_DECLS                                              \
+       register unsigned long __res  asm(__HYPERCALL_RETREG);          \
+       register unsigned long __arg1 asm(__HYPERCALL_ARG1REG) = __arg1; \
+       register unsigned long __arg2 asm(__HYPERCALL_ARG2REG) = __arg2; \
+       register unsigned long __arg3 asm(__HYPERCALL_ARG3REG) = __arg3; \
+       register unsigned long __arg4 asm(__HYPERCALL_ARG4REG) = __arg4; \
+       register unsigned long __arg5 asm(__HYPERCALL_ARG5REG) = __arg5;
+
+#define __HYPERCALL_0PARAM     "=r" (__res)
+#define __HYPERCALL_1PARAM     __HYPERCALL_0PARAM, "+r" (__arg1)
+#define __HYPERCALL_2PARAM     __HYPERCALL_1PARAM, "+r" (__arg2)
+#define __HYPERCALL_3PARAM     __HYPERCALL_2PARAM, "+r" (__arg3)
+#define __HYPERCALL_4PARAM     __HYPERCALL_3PARAM, "+r" (__arg4)
+#define __HYPERCALL_5PARAM     __HYPERCALL_4PARAM, "+r" (__arg5)
+
+#define __HYPERCALL_0ARG()
+#define __HYPERCALL_1ARG(a1)                                           \
+       __HYPERCALL_0ARG()              __arg1 = (unsigned long)(a1);
+#define __HYPERCALL_2ARG(a1,a2)                                                \
+       __HYPERCALL_1ARG(a1)            __arg2 = (unsigned long)(a2);
+#define __HYPERCALL_3ARG(a1,a2,a3)                                     \
+       __HYPERCALL_2ARG(a1,a2)         __arg3 = (unsigned long)(a3);
+#define __HYPERCALL_4ARG(a1,a2,a3,a4)                                  \
+       __HYPERCALL_3ARG(a1,a2,a3)      __arg4 = (unsigned long)(a4);
+#define __HYPERCALL_5ARG(a1,a2,a3,a4,a5)                               \
+       __HYPERCALL_4ARG(a1,a2,a3,a4)   __arg5 = (unsigned long)(a5);
+
+#define __HYPERCALL_CLOBBER5   "memory"
+#define __HYPERCALL_CLOBBER4   __HYPERCALL_CLOBBER5, __HYPERCALL_ARG5REG
+#define __HYPERCALL_CLOBBER3   __HYPERCALL_CLOBBER4, __HYPERCALL_ARG4REG
+#define __HYPERCALL_CLOBBER2   __HYPERCALL_CLOBBER3, __HYPERCALL_ARG3REG
+#define __HYPERCALL_CLOBBER1   __HYPERCALL_CLOBBER2, __HYPERCALL_ARG2REG
+#define __HYPERCALL_CLOBBER0   __HYPERCALL_CLOBBER1, __HYPERCALL_ARG1REG
+
 #define _hypercall0(type, name)                                                \
 ({                                                                     \
-       long __res;                                                     \
-       asm volatile (                                                  \
-               "call %[call]"                                          \
-               : "=a" (__res)                                          \
-               : [call] "m" (hypercall_page[__HYPERVISOR_##name])      \
-               : "memory" );                                           \
+       __HYPERCALL_DECLS;                                              \
+       __HYPERCALL_0ARG();                                             \
+       asm volatile (__HYPERCALL                                       \
+                     : __HYPERCALL_0PARAM                              \
+                     : __HYPERCALL_ENTRY(name)                         \
+                     : __HYPERCALL_CLOBBER0);                          \
        (type)__res;                                                    \
 })
 
 #define _hypercall1(type, name, a1)                                    \
 ({                                                                     \
-       long __res, __ign1;                                             \
-       asm volatile (                                                  \
-               "call %[call]"                                          \
-               : "=a" (__res), "=b" (__ign1)                           \
-               : "1" ((long)(a1)),                                     \
-                 [call] "m" (hypercall_page[__HYPERVISOR_##name])      \
-               : "memory" );                                           \
+       __HYPERCALL_DECLS;                                              \
+       __HYPERCALL_1ARG(a1);                                           \
+       asm volatile (__HYPERCALL                                       \
+                     : __HYPERCALL_1PARAM                              \
+                     : __HYPERCALL_ENTRY(name)                         \
+                     : __HYPERCALL_CLOBBER1);                          \
        (type)__res;                                                    \
 })
 
 #define _hypercall2(type, name, a1, a2)                                        \
 ({                                                                     \
-       long __res, __ign1, __ign2;                                     \
-       asm volatile (                                                  \
-               "call %[call]"                                          \
-               : "=a" (__res), "=b" (__ign1), "=c" (__ign2)            \
-               : "1" ((long)(a1)), "2" ((long)(a2)),                   \
-                 [call] "m" (hypercall_page[__HYPERVISOR_##name])      \
-               : "memory" );                                           \
+       __HYPERCALL_DECLS;                                              \
+       __HYPERCALL_2ARG(a1, a2);                                       \
+       asm volatile (__HYPERCALL                                       \
+                     : __HYPERCALL_2PARAM                              \
+                     : __HYPERCALL_ENTRY(name)                         \
+                     : __HYPERCALL_CLOBBER2);                          \
        (type)__res;                                                    \
 })
 
 #define _hypercall3(type, name, a1, a2, a3)                            \
 ({                                                                     \
-       long __res, __ign1, __ign2, __ign3;                             \
-       asm volatile (                                                  \
-               "call %[call]"                                          \
-               : "=a" (__res), "=b" (__ign1), "=c" (__ign2),           \
-               "=d" (__ign3)                                           \
-               : "1" ((long)(a1)), "2" ((long)(a2)),                   \
-                 "3" ((long)(a3)),                                     \
-                 [call] "m" (hypercall_page[__HYPERVISOR_##name])      \
-               : "memory" );                                           \
+       __HYPERCALL_DECLS;                                              \
+       __HYPERCALL_3ARG(a1, a2, a3);                                   \
+       asm volatile (__HYPERCALL                                       \
+                     : __HYPERCALL_3PARAM                              \
+                     : __HYPERCALL_ENTRY(name)                         \
+                     : __HYPERCALL_CLOBBER3);                          \
        (type)__res;                                                    \
 })
 
 #define _hypercall4(type, name, a1, a2, a3, a4)                                \
 ({                                                                     \
-       long __res, __ign1, __ign2, __ign3, __ign4;                     \
-       asm volatile (                                                  \
-               "call %[call]"                                          \
-               : "=a" (__res), "=b" (__ign1), "=c" (__ign2),           \
-               "=d" (__ign3), "=S" (__ign4)                            \
-               : "1" ((long)(a1)), "2" ((long)(a2)),                   \
-                 "3" ((long)(a3)), "4" ((long)(a4)),                   \
-                 [call] "m" (hypercall_page[__HYPERVISOR_##name])      \
-               : "memory" );                                           \
+       __HYPERCALL_DECLS;                                              \
+       __HYPERCALL_4ARG(a1, a2, a3, a4);                               \
+       asm volatile (__HYPERCALL                                       \
+                     : __HYPERCALL_4PARAM                              \
+                     : __HYPERCALL_ENTRY(name)                         \
+                     : __HYPERCALL_CLOBBER4);                          \
        (type)__res;                                                    \
 })
 
 #define _hypercall5(type, name, a1, a2, a3, a4, a5)                    \
 ({                                                                     \
-       long __res, __ign1, __ign2, __ign3, __ign4, __ign5;             \
-       asm volatile (                                                  \
-               "call %[call]"                                          \
-               : "=a" (__res), "=b" (__ign1), "=c" (__ign2),           \
-               "=d" (__ign3), "=S" (__ign4), "=D" (__ign5)             \
-               : "1" ((long)(a1)), "2" ((long)(a2)),                   \
-                 "3" ((long)(a3)), "4" ((long)(a4)),                   \
-                 "5" ((long)(a5)),                                     \
-                 [call] "m" (hypercall_page[__HYPERVISOR_##name])      \
-               : "memory" );                                           \
+       __HYPERCALL_DECLS;                                              \
+       __HYPERCALL_5ARG(a1, a2, a3, a4, a5);                           \
+       asm volatile (__HYPERCALL                                       \
+                     : __HYPERCALL_5PARAM                              \
+                     : __HYPERCALL_ENTRY(name)                         \
+                     : __HYPERCALL_CLOBBER5);                          \
        (type)__res;                                                    \
 })