e_padlock.c: last x86_64 commit didn't work with some optimizers.
[openssl.git] / engines / e_padlock.c
index 3fbb51b585ee6f8720ba784cbb0a1a0340f173f1..f0142798f49564819165c9a4eb381b5b23b021cb 100644 (file)
    compiler choice is limited to GCC and Microsoft C. */
 #undef COMPILE_HW_PADLOCK
 #if !defined(I386_ONLY) && !defined(OPENSSL_NO_INLINE_ASM)
-# if (defined(__GNUC__) && (defined(__i386__) || defined(__i386))) || \
+# if (defined(__GNUC__) && __GNUC__>=2 && \
+       (defined(__i386__) || defined(__i386) || \
+        defined(__x86_64__) || defined(__x86_64)) \
+     ) || \
      (defined(_MSC_VER) && defined(_M_IX86))
 #  define COMPILE_HW_PADLOCK
+#  ifdef OPENSSL_NO_DYNAMIC_ENGINE
 static ENGINE *ENGINE_padlock (void);
+#  endif
 # endif
 #endif
 
+#ifdef OPENSSL_NO_DYNAMIC_ENGINE
+
 void ENGINE_load_padlock (void)
 {
 /* On non-x86 CPUs it just returns. */
@@ -120,16 +127,21 @@ void ENGINE_load_padlock (void)
 #endif
 }
 
+#endif
+
 #ifdef COMPILE_HW_PADLOCK
 /* We do these includes here to avoid header problems on platforms that
    do not have the VIA padlock anyway... */
+#include <stdlib.h>
 #ifdef _WIN32
 # include <malloc.h>
 # ifndef alloca
 #  define alloca _alloca
 # endif
-#else
-# include <stdlib.h>
+#elif defined(__GNUC__)
+# ifndef alloca
+#  define alloca(s) __builtin_alloca((s))
+# endif
 #endif
 
 /* Function for ENGINE detection and control */
@@ -190,6 +202,7 @@ padlock_bind_helper(ENGINE *e)
        return 1;
 }
 
+#ifdef OPENSSL_NO_DYNAMIC_ENGINE
 /* Constructor */
 static ENGINE *
 ENGINE_padlock(void)
@@ -207,6 +220,7 @@ ENGINE_padlock(void)
 
        return eng;
 }
+#endif
 
 /* Check availability of the engine */
 static int
@@ -233,8 +247,8 @@ padlock_bind_fn(ENGINE *e, const char *id)
        return 1;
 }
 
-IMPLEMENT_DYNAMIC_CHECK_FN ();
-IMPLEMENT_DYNAMIC_BIND_FN (padlock_bind_fn);
+IMPLEMENT_DYNAMIC_CHECK_FN()
+IMPLEMENT_DYNAMIC_BIND_FN (padlock_bind_fn)
 #endif /* DYNAMIC_ENGINE */
 
 /* ===== Here comes the "real" engine ===== */
@@ -291,6 +305,7 @@ static volatile struct padlock_cipher_data *padlock_saved_context;
  * =======================================================
  */
 #if defined(__GNUC__) && __GNUC__>=2
+#if defined(__i386__) || defined(__i386)
 /*
  * As for excessive "push %ebx"/"pop %ebx" found all over.
  * When generating position-independent code GCC won't let
@@ -370,21 +385,6 @@ padlock_available(void)
        return padlock_use_ace + padlock_use_rng;
 }
 
-#ifndef OPENSSL_NO_AES
-/* Our own htonl()/ntohl() */
-static inline void
-padlock_bswapl(AES_KEY *ks)
-{
-       size_t i = sizeof(ks->rd_key)/sizeof(ks->rd_key[0]);
-       unsigned int *key = ks->rd_key;
-
-       while (i--) {
-               asm volatile ("bswapl %0" : "+r"(*key));
-               key++;
-       }
-}
-#endif
-
 /* Force key reload from memory to the CPU microcode.
    Loading EFLAGS from the stack clears EFLAGS[30] 
    which does the trick. */
@@ -438,16 +438,137 @@ static inline void *name(size_t cnt,             \
                        rep_xcrypt "\n"         \
                "       popl    %%ebx"          \
                : "=a"(iv), "=c"(cnt), "=D"(out), "=S"(inp) \
-               : "0"(cdata), "1"(cnt), "2"(out), "3"(inp), "m"(*cdata)  \
+               : "0"(cdata), "1"(cnt), "2"(out), "3"(inp)  \
                : "edx", "cc", "memory");       \
        return iv;                              \
 }
+#endif
+
+#elif defined(__x86_64__) || defined(__x86_64)
+
+/* Load supported features of the CPU to see if
+   the PadLock is available. */
+static int
+padlock_available(void)
+{
+       char vendor_string[16];
+       unsigned int eax, edx;
+       size_t  scratch;
+
+       /* Are we running on the Centaur (VIA) CPU? */
+       eax = 0x00000000;
+       vendor_string[12] = 0;
+       asm volatile (
+               "movq   %%rbx,%1\n"
+               "cpuid\n"
+               "movl   %%ebx,(%2)\n"
+               "movl   %%edx,4(%2)\n"
+               "movl   %%ecx,8(%2)\n"
+               "movq   %1,%%rbx"
+               : "+a"(eax), "=&r"(scratch) : "r"(vendor_string) : "rcx", "rdx");
+       if (strcmp(vendor_string, "CentaurHauls") != 0)
+               return 0;
+
+       /* Check for Centaur Extended Feature Flags presence */
+       eax = 0xC0000000;
+       asm volatile ("movq %%rbx,%1; cpuid; movq %1,%%rbx"
+               : "+a"(eax), "=&r"(scratch) : : "rcx", "rdx");
+       if (eax < 0xC0000001)
+               return 0;
+
+       /* Read the Centaur Extended Feature Flags */
+       eax = 0xC0000001;
+       asm volatile ("movq %%rbx,%2; cpuid; movq %2,%%rbx"
+               : "+a"(eax), "=d"(edx), "=&r"(scratch) : : "rcx");
+
+       /* Fill up some flags */
+       padlock_use_ace = ((edx & (0x3<<6)) == (0x3<<6));
+       padlock_use_rng = ((edx & (0x3<<2)) == (0x3<<2));
+
+       return padlock_use_ace + padlock_use_rng;
+}
+
+/* Force key reload from memory to the CPU microcode.
+   Loading EFLAGS from the stack clears EFLAGS[30] 
+   which does the trick. */
+static inline void
+padlock_reload_key(void)
+{
+       asm volatile ("pushfq; popfq");
+}
+
+#ifndef OPENSSL_NO_AES
+/*
+ * This is heuristic key context tracing. At first one
+ * believes that one should use atomic swap instructions,
+ * but it's not actually necessary. Point is that if
+ * padlock_saved_context was changed by another thread
+ * after we've read it and before we compare it with cdata,
+ * our key *shall* be reloaded upon thread context switch
+ * and we are therefore set in either case...
+ */
+static inline void
+padlock_verify_context(struct padlock_cipher_data *cdata)
+{
+       asm volatile (
+       "pushfq\n"
+"      btl     $30,(%%rsp)\n"
+"      jnc     1f\n"
+"      cmpq    %2,%1\n"
+"      je      1f\n"
+"      popfq\n"
+"      subq    $8,%%rsp\n"
+"1:    addq    $8,%%rsp\n"
+"      movq    %2,%0"
+       :"+m"(padlock_saved_context)
+       : "r"(padlock_saved_context), "r"(cdata) : "cc");
+}
+
+/* Template for padlock_xcrypt_* modes */
+/* BIG FAT WARNING: 
+ *     The offsets used with 'leal' instructions
+ *     describe items of the 'padlock_cipher_data'
+ *     structure.
+ */
+#define PADLOCK_XCRYPT_ASM(name,rep_xcrypt)    \
+static inline void *name(size_t cnt,           \
+       struct padlock_cipher_data *cdata,      \
+       void *out, const void *inp)             \
+{      void *iv;                               \
+       size_t scratch;                         \
+       asm volatile ( "movq    %%rbx,%4\n"     \
+               "       leaq    16(%0),%%rdx\n" \
+               "       leaq    32(%0),%%rbx\n" \
+                       rep_xcrypt "\n"         \
+               "       movq    %%rbx,%4"       \
+               : "=a"(iv), "=c"(cnt), "=D"(out), "=S"(inp), "=&r"(scratch) \
+               : "0"(cdata), "1"(cnt), "2"(out), "3"(inp)  \
+               : "rdx", "cc", "memory");       \
+       return iv;                              \
+}
+#endif
+
+#endif /* cpu */
 
+#ifndef OPENSSL_NO_AES
 /* Generate all functions with appropriate opcodes */
 PADLOCK_XCRYPT_ASM(padlock_xcrypt_ecb, ".byte 0xf3,0x0f,0xa7,0xc8")    /* rep xcryptecb */
 PADLOCK_XCRYPT_ASM(padlock_xcrypt_cbc, ".byte 0xf3,0x0f,0xa7,0xd0")    /* rep xcryptcbc */
 PADLOCK_XCRYPT_ASM(padlock_xcrypt_cfb, ".byte 0xf3,0x0f,0xa7,0xe0")    /* rep xcryptcfb */
 PADLOCK_XCRYPT_ASM(padlock_xcrypt_ofb, ".byte 0xf3,0x0f,0xa7,0xe8")    /* rep xcryptofb */
+
+/* Our own htonl()/ntohl() */
+static inline void
+padlock_bswapl(AES_KEY *ks)
+{
+       size_t i = sizeof(ks->rd_key)/sizeof(ks->rd_key[0]);
+       unsigned int *key = ks->rd_key;
+
+       while (i--) {
+               asm volatile ("bswapl %0" : "+r"(*key));
+               key++;
+       }
+}
 #endif
 
 /* The RNG call itself */
@@ -478,8 +599,8 @@ padlock_xstore(void *addr, unsigned int edx_in)
 static inline unsigned char *
 padlock_memcpy(void *dst,const void *src,size_t n)
 {
-       long       *d=dst;
-       const long *s=src;
+       size_t       *d=dst;
+       const size_t *s=src;
 
        n /= sizeof(*d);
        do { *d++ = *s++; } while (--n);
@@ -1212,6 +1333,14 @@ static RAND_METHOD padlock_rand = {
        padlock_rand_status,    /* rand status */
 };
 
+#else  /* !COMPILE_HW_PADLOCK */
+#ifndef OPENSSL_NO_DYNAMIC_ENGINE
+OPENSSL_EXPORT
+int bind_engine(ENGINE *e, const char *id, const dynamic_fns *fns);
+OPENSSL_EXPORT
+int bind_engine(ENGINE *e, const char *id, const dynamic_fns *fns) { return 0; }
+IMPLEMENT_DYNAMIC_CHECK_FN()
+#endif
 #endif /* COMPILE_HW_PADLOCK */
 
 #endif /* !OPENSSL_NO_HW_PADLOCK */