1 // ppc_power8.cpp - written and placed in the public domain by
2 //                  Jeffrey Walton, Uri Blumenthal and Marcel Raad.
3 //
4 //    This source file uses intrinsics and built-ins to gain access to
5 //    Power8 instructions. A separate source file is needed because
6 //    additional CXXFLAGS are required to enable the appropriate
7 //    instructions sets in some build configurations.
8 
9 #include "pch.h"
10 #include "config.h"
11 
12 #ifdef CRYPTOPP_GNU_STYLE_INLINE_ASSEMBLY
13 # include <signal.h>
14 # include <setjmp.h>
15 #endif
16 
17 #if defined(_ARCH_PWR8) || defined(__CRYPTO__)
18 # include "ppc_simd.h"
19 #endif
20 
21 // Squash MS LNK4221 and libtool warnings
22 extern const char PPC_POWER8_FNAME[] = __FILE__;
23 
NAMESPACE_BEGIN(CryptoPP)24 NAMESPACE_BEGIN(CryptoPP)
25 
26 // ************************* Feature Probes ************************* //
27 
28 #ifdef CRYPTOPP_GNU_STYLE_INLINE_ASSEMBLY
29 extern "C" {
30     typedef void (*SigHandler)(int);
31 
32     static jmp_buf s_jmpSIGILL;
33     static void SigIllHandler(int)
34     {
35         longjmp(s_jmpSIGILL, 1);
36     }
37 }
38 #endif  // CRYPTOPP_MS_STYLE_INLINE_ASSEMBLY
39 
40 #if (CRYPTOPP_BOOL_PPC32 || CRYPTOPP_BOOL_PPC64)
41 
CPU_ProbePower8()42 bool CPU_ProbePower8()
43 {
44 #if defined(CRYPTOPP_NO_CPU_FEATURE_PROBES)
45     return false;
46 #elif (_ARCH_PWR8) && defined(CRYPTOPP_POWER8_AVAILABLE)
47 # if defined(CRYPTOPP_GNU_STYLE_INLINE_ASSEMBLY)
48 
49     // longjmp and clobber warnings. Volatile is required.
50     // http://github.com/weidai11/cryptopp/issues/24 and http://stackoverflow.com/q/7721854
51     volatile int result = true;
52 
53     volatile SigHandler oldHandler = signal(SIGILL, SigIllHandler);
54     if (oldHandler == SIG_ERR)
55         return false;
56 
57     volatile sigset_t oldMask;
58     if (sigprocmask(0, NULLPTR, (sigset_t*)&oldMask))
59     {
60         signal(SIGILL, oldHandler);
61         return false;
62     }
63 
64     if (setjmp(s_jmpSIGILL))
65         result = false;
66     else
67     {
68         // POWER8 added 64-bit SIMD operations
69         const word64 x = W64LIT(0xffffffffffffffff);
70         word64 w1[2] = {x, x}, w2[2] = {4, 6}, w3[2];
71 
72         // Specifically call the VSX loads and stores with 64-bit types
73         #if defined(__ibmxl__) || (defined(_AIX) && defined(__xlC__)) || defined(__clang__)
74         const uint64x2_p v1 = vec_xl(0, (unsigned long long*)w1);
75         const uint64x2_p v2 = vec_xl(0, (unsigned long long*)w2);
76         const uint64x2_p v3 = vec_add(v1, v2);  // 64-bit add
77         vec_xst(v3, 0, (unsigned long long*)w3);
78         #else
79         const uint64x2_p v1 = (uint64x2_p)vec_vsx_ld(0, (const byte*)w1);
80         const uint64x2_p v2 = (uint64x2_p)vec_vsx_ld(0, (const byte*)w2);
81         const uint64x2_p v3 = vec_add(v1, v2);  // 64-bit add
82         vec_vsx_st((uint8x16_p)v3, 0, (byte*)w3);
83         #endif
84 
85         // Relies on integer wrap
86         result = (w3[0] == 3 && w3[1] == 5);
87     }
88 
89     sigprocmask(SIG_SETMASK, (sigset_t*)&oldMask, NULLPTR);
90     signal(SIGILL, oldHandler);
91     return result;
92 # endif
93 #else
94     return false;
95 #endif  // _ARCH_PWR8
96 }
97 
CPU_ProbeAES()98 bool CPU_ProbeAES()
99 {
100 #if defined(CRYPTOPP_NO_CPU_FEATURE_PROBES)
101     return false;
102 #elif (__CRYPTO__) && defined(CRYPTOPP_POWER8_AES_AVAILABLE)
103 # if defined(CRYPTOPP_GNU_STYLE_INLINE_ASSEMBLY)
104 
105     // longjmp and clobber warnings. Volatile is required.
106     // http://github.com/weidai11/cryptopp/issues/24 and http://stackoverflow.com/q/7721854
107     volatile int result = true;
108 
109     volatile SigHandler oldHandler = signal(SIGILL, SigIllHandler);
110     if (oldHandler == SIG_ERR)
111         return false;
112 
113     volatile sigset_t oldMask;
114     if (sigprocmask(0, NULLPTR, (sigset_t*)&oldMask))
115     {
116         signal(SIGILL, oldHandler);
117         return false;
118     }
119 
120     if (setjmp(s_jmpSIGILL))
121         result = false;
122     else
123     {
124         byte key[16] = {0xA0, 0xFA, 0xFE, 0x17, 0x88, 0x54, 0x2c, 0xb1,
125                         0x23, 0xa3, 0x39, 0x39, 0x2a, 0x6c, 0x76, 0x05};
126         byte state[16] = {0x19, 0x3d, 0xe3, 0xb3, 0xa0, 0xf4, 0xe2, 0x2b,
127                           0x9a, 0xc6, 0x8d, 0x2a, 0xe9, 0xf8, 0x48, 0x08};
128         byte r[16] = {255}, z[16] = {};
129 
130         uint8x16_p k = (uint8x16_p)VecLoad(0, key);
131         uint8x16_p s = (uint8x16_p)VecLoad(0, state);
132         s = VecEncrypt(s, k);
133         s = VecEncryptLast(s, k);
134         s = VecDecrypt(s, k);
135         s = VecDecryptLast(s, k);
136         VecStore(s, r);
137 
138         result = (0 != std::memcmp(r, z, 16));
139     }
140 
141     sigprocmask(SIG_SETMASK, (sigset_t*)&oldMask, NULLPTR);
142     signal(SIGILL, oldHandler);
143     return result;
144 # endif
145 #else
146     return false;
147 #endif  // __CRYPTO__
148 }
149 
CPU_ProbeSHA256()150 bool CPU_ProbeSHA256()
151 {
152 #if defined(CRYPTOPP_NO_CPU_FEATURE_PROBES)
153     return false;
154 #elif (__CRYPTO__) && defined(CRYPTOPP_POWER8_SHA_AVAILABLE)
155 # if defined(CRYPTOPP_GNU_STYLE_INLINE_ASSEMBLY)
156 
157     // longjmp and clobber warnings. Volatile is required.
158     // http://github.com/weidai11/cryptopp/issues/24 and http://stackoverflow.com/q/7721854
159     volatile int result = false;
160 
161     volatile SigHandler oldHandler = signal(SIGILL, SigIllHandler);
162     if (oldHandler == SIG_ERR)
163         return false;
164 
165     volatile sigset_t oldMask;
166     if (sigprocmask(0, NULLPTR, (sigset_t*)&oldMask))
167     {
168         signal(SIGILL, oldHandler);
169         return false;
170     }
171 
172     if (setjmp(s_jmpSIGILL))
173         result = false;
174     else
175     {
176         byte r[16], z[16] = {0};
177         uint8x16_p x = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
178 
179         x = VecSHA256<0,0>(x);
180         x = VecSHA256<0,0xf>(x);
181         x = VecSHA256<1,0>(x);
182         x = VecSHA256<1,0xf>(x);
183         VecStore(x, r);
184 
185         result = (0 == std::memcmp(r, z, 16));
186     }
187 
188     sigprocmask(SIG_SETMASK, (sigset_t*)&oldMask, NULLPTR);
189     signal(SIGILL, oldHandler);
190     return result;
191 # endif
192 #else
193     return false;
194 #endif  // CRYPTOPP_ALTIVEC_AVAILABLE
195 }
196 
CPU_ProbeSHA512()197 bool CPU_ProbeSHA512()
198 {
199 #if defined(CRYPTOPP_NO_CPU_FEATURE_PROBES)
200     return false;
201 #elif (__CRYPTO__) && defined(CRYPTOPP_POWER8_SHA_AVAILABLE)
202 # if defined(CRYPTOPP_GNU_STYLE_INLINE_ASSEMBLY)
203 
204     // longjmp and clobber warnings. Volatile is required.
205     // http://github.com/weidai11/cryptopp/issues/24 and http://stackoverflow.com/q/7721854
206     volatile int result = false;
207 
208     volatile SigHandler oldHandler = signal(SIGILL, SigIllHandler);
209     if (oldHandler == SIG_ERR)
210         return false;
211 
212     volatile sigset_t oldMask;
213     if (sigprocmask(0, NULLPTR, (sigset_t*)&oldMask))
214     {
215         signal(SIGILL, oldHandler);
216         return false;
217     }
218 
219     if (setjmp(s_jmpSIGILL))
220         result = false;
221     else
222     {
223         byte r[16], z[16] = {0};
224         uint8x16_p x = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
225 
226         x = VecSHA512<0,0>(x);
227         x = VecSHA512<0,0xf>(x);
228         x = VecSHA512<1,0>(x);
229         x = VecSHA512<1,0xf>(x);
230         VecStore(x, r);
231 
232         result = (0 == std::memcmp(r, z, 16));
233     }
234 
235     sigprocmask(SIG_SETMASK, (sigset_t*)&oldMask, NULLPTR);
236     signal(SIGILL, oldHandler);
237     return result;
238 # endif
239 #else
240     return false;
241 #endif  // CRYPTOPP_POWER8_AVAILABLE
242 }
243 
244 #endif  // PPC32 or PPC64
245 
246 NAMESPACE_END
247