Coverage Report

Created: 2023-06-07 07:11

/src/boringssl/crypto/cpu_intel.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
2
 * All rights reserved.
3
 *
4
 * This package is an SSL implementation written
5
 * by Eric Young (eay@cryptsoft.com).
6
 * The implementation was written so as to conform with Netscapes SSL.
7
 *
8
 * This library is free for commercial and non-commercial use as long as
9
 * the following conditions are aheared to.  The following conditions
10
 * apply to all code found in this distribution, be it the RC4, RSA,
11
 * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
12
 * included with this distribution is covered by the same copyright terms
13
 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
14
 *
15
 * Copyright remains Eric Young's, and as such any Copyright notices in
16
 * the code are not to be removed.
17
 * If this package is used in a product, Eric Young should be given attribution
18
 * as the author of the parts of the library used.
19
 * This can be in the form of a textual message at program startup or
20
 * in documentation (online or textual) provided with the package.
21
 *
22
 * Redistribution and use in source and binary forms, with or without
23
 * modification, are permitted provided that the following conditions
24
 * are met:
25
 * 1. Redistributions of source code must retain the copyright
26
 *    notice, this list of conditions and the following disclaimer.
27
 * 2. Redistributions in binary form must reproduce the above copyright
28
 *    notice, this list of conditions and the following disclaimer in the
29
 *    documentation and/or other materials provided with the distribution.
30
 * 3. All advertising materials mentioning features or use of this software
31
 *    must display the following acknowledgement:
32
 *    "This product includes cryptographic software written by
33
 *     Eric Young (eay@cryptsoft.com)"
34
 *    The word 'cryptographic' can be left out if the rouines from the library
35
 *    being used are not cryptographic related :-).
36
 * 4. If you include any Windows specific code (or a derivative thereof) from
37
 *    the apps directory (application code) you must include an acknowledgement:
38
 *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
39
 *
40
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
41
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
44
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50
 * SUCH DAMAGE.
51
 *
52
 * The licence and distribution terms for any publically available version or
53
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
54
 * copied and put under another distribution licence
55
 * [including the GNU Public Licence.] */
56
57
#include <openssl/base.h>
58
59
#if !defined(OPENSSL_NO_ASM) && (defined(OPENSSL_X86) || defined(OPENSSL_X86_64))
60
61
#include <inttypes.h>
62
#include <stdio.h>
63
#include <stdlib.h>
64
#include <string.h>
65
66
#if defined(_MSC_VER)
67
OPENSSL_MSVC_PRAGMA(warning(push, 3))
68
#include <immintrin.h>
69
#include <intrin.h>
70
OPENSSL_MSVC_PRAGMA(warning(pop))
71
#endif
72
73
#include "internal.h"
74
75
76
// OPENSSL_cpuid runs the cpuid instruction. |leaf| is passed in as EAX and ECX
77
// is set to zero. It writes EAX, EBX, ECX, and EDX to |*out_eax| through
78
// |*out_edx|.
79
static void OPENSSL_cpuid(uint32_t *out_eax, uint32_t *out_ebx,
80
6
                          uint32_t *out_ecx, uint32_t *out_edx, uint32_t leaf) {
81
#if defined(_MSC_VER)
82
  int tmp[4];
83
  __cpuid(tmp, (int)leaf);
84
  *out_eax = (uint32_t)tmp[0];
85
  *out_ebx = (uint32_t)tmp[1];
86
  *out_ecx = (uint32_t)tmp[2];
87
  *out_edx = (uint32_t)tmp[3];
88
#elif defined(__pic__) && defined(OPENSSL_32_BIT)
89
  // Inline assembly may not clobber the PIC register. For 32-bit, this is EBX.
90
  // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47602.
91
  __asm__ volatile (
92
    "xor %%ecx, %%ecx\n"
93
    "mov %%ebx, %%edi\n"
94
    "cpuid\n"
95
    "xchg %%edi, %%ebx\n"
96
    : "=a"(*out_eax), "=D"(*out_ebx), "=c"(*out_ecx), "=d"(*out_edx)
97
    : "a"(leaf)
98
  );
99
#else
100
6
  __asm__ volatile (
101
6
    "xor %%ecx, %%ecx\n"
102
6
    "cpuid\n"
103
6
    : "=a"(*out_eax), "=b"(*out_ebx), "=c"(*out_ecx), "=d"(*out_edx)
104
6
    : "a"(leaf)
105
6
  );
106
6
#endif
107
6
}
108
109
// OPENSSL_xgetbv returns the value of an Intel Extended Control Register (XCR).
110
// Currently only XCR0 is defined by Intel so |xcr| should always be zero.
111
2
static uint64_t OPENSSL_xgetbv(uint32_t xcr) {
112
#if defined(_MSC_VER)
113
  return (uint64_t)_xgetbv(xcr);
114
#else
115
2
  uint32_t eax, edx;
116
2
  __asm__ volatile ("xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr));
117
2
  return (((uint64_t)edx) << 32) | eax;
118
2
#endif
119
2
}
120
121
// handle_cpu_env applies the value from |in| to the CPUID values in |out[0]|
122
// and |out[1]|. See the comment in |OPENSSL_cpuid_setup| about this.
123
0
static void handle_cpu_env(uint32_t *out, const char *in) {
124
0
  const int invert = in[0] == '~';
125
0
  const int or = in[0] == '|';
126
0
  const int skip_first_byte = invert || or;
127
0
  const int hex = in[skip_first_byte] == '0' && in[skip_first_byte+1] == 'x';
128
129
0
  int sscanf_result;
130
0
  uint64_t v;
131
0
  if (hex) {
132
0
    sscanf_result = sscanf(in + invert + 2, "%" PRIx64, &v);
133
0
  } else {
134
0
    sscanf_result = sscanf(in + invert, "%" PRIu64, &v);
135
0
  }
136
137
0
  if (!sscanf_result) {
138
0
    return;
139
0
  }
140
141
0
  if (invert) {
142
0
    out[0] &= ~v;
143
0
    out[1] &= ~(v >> 32);
144
0
  } else if (or) {
145
0
    out[0] |= v;
146
0
    out[1] |= (v >> 32);
147
0
  } else {
148
0
    out[0] = v;
149
0
    out[1] = v >> 32;
150
0
  }
151
0
}
152
153
2
void OPENSSL_cpuid_setup(void) {
154
  // Determine the vendor and maximum input value.
155
2
  uint32_t eax, ebx, ecx, edx;
156
2
  OPENSSL_cpuid(&eax, &ebx, &ecx, &edx, 0);
157
158
2
  uint32_t num_ids = eax;
159
160
2
  int is_intel = ebx == 0x756e6547 /* Genu */ &&
161
2
                 edx == 0x49656e69 /* ineI */ &&
162
2
                 ecx == 0x6c65746e /* ntel */;
163
2
  int is_amd = ebx == 0x68747541 /* Auth */ &&
164
2
               edx == 0x69746e65 /* enti */ &&
165
2
               ecx == 0x444d4163 /* cAMD */;
166
167
2
  uint32_t extended_features[2] = {0};
168
2
  if (num_ids >= 7) {
169
2
    OPENSSL_cpuid(&eax, &ebx, &ecx, &edx, 7);
170
2
    extended_features[0] = ebx;
171
2
    extended_features[1] = ecx;
172
2
  }
173
174
2
  OPENSSL_cpuid(&eax, &ebx, &ecx, &edx, 1);
175
176
2
  if (is_amd) {
177
    // See https://www.amd.com/system/files/TechDocs/25481.pdf, page 10.
178
0
    const uint32_t base_family = (eax >> 8) & 15;
179
0
    const uint32_t base_model = (eax >> 4) & 15;
180
181
0
    uint32_t family = base_family;
182
0
    uint32_t model = base_model;
183
0
    if (base_family == 0xf) {
184
0
      const uint32_t ext_family = (eax >> 20) & 255;
185
0
      family += ext_family;
186
0
      const uint32_t ext_model = (eax >> 16) & 15;
187
0
      model |= ext_model << 4;
188
0
    }
189
190
0
    if (family < 0x17 || (family == 0x17 && 0x70 <= model && model <= 0x7f)) {
191
      // Disable RDRAND on AMD families before 0x17 (Zen) due to reported
192
      // failures after suspend.
193
      // https://bugzilla.redhat.com/show_bug.cgi?id=1150286
194
      // Also disable for family 0x17, models 0x70–0x7f, due to possible RDRAND
195
      // failures there too.
196
0
      ecx &= ~(1u << 30);
197
0
    }
198
0
  }
199
200
  // Force the hyper-threading bit so that the more conservative path is always
201
  // chosen.
202
2
  edx |= 1u << 28;
203
204
  // Reserved bit #20 was historically repurposed to control the in-memory
205
  // representation of RC4 state. Always set it to zero.
206
2
  edx &= ~(1u << 20);
207
208
  // Reserved bit #30 is repurposed to signal an Intel CPU.
209
2
  if (is_intel) {
210
2
    edx |= (1u << 30);
211
212
    // Clear the XSAVE bit on Knights Landing to mimic Silvermont. This enables
213
    // some Silvermont-specific codepaths which perform better. See OpenSSL
214
    // commit 64d92d74985ebb3d0be58a9718f9e080a14a8e7f.
215
2
    if ((eax & 0x0fff0ff0) == 0x00050670 /* Knights Landing */ ||
216
2
        (eax & 0x0fff0ff0) == 0x00080650 /* Knights Mill (per SDE) */) {
217
0
      ecx &= ~(1u << 26);
218
0
    }
219
2
  } else {
220
0
    edx &= ~(1u << 30);
221
0
  }
222
223
  // The SDBG bit is repurposed to denote AMD XOP support. Don't ever use AMD
224
  // XOP code paths.
225
2
  ecx &= ~(1u << 11);
226
227
2
  uint64_t xcr0 = 0;
228
2
  if (ecx & (1u << 27)) {
229
    // XCR0 may only be queried if the OSXSAVE bit is set.
230
2
    xcr0 = OPENSSL_xgetbv(0);
231
2
  }
232
  // See Intel manual, volume 1, section 14.3.
233
2
  if ((xcr0 & 6) != 6) {
234
    // YMM registers cannot be used.
235
0
    ecx &= ~(1u << 28);  // AVX
236
0
    ecx &= ~(1u << 12);  // FMA
237
0
    ecx &= ~(1u << 11);  // AMD XOP
238
    // Clear AVX2 and AVX512* bits.
239
    //
240
    // TODO(davidben): Should bits 17 and 26-28 also be cleared? Upstream
241
    // doesn't clear those.
242
0
    extended_features[0] &=
243
0
        ~((1u << 5) | (1u << 16) | (1u << 21) | (1u << 30) | (1u << 31));
244
0
  }
245
  // See Intel manual, volume 1, section 15.2.
246
2
  if ((xcr0 & 0xe6) != 0xe6) {
247
    // Clear AVX512F. Note we don't touch other AVX512 extensions because they
248
    // can be used with YMM.
249
2
    extended_features[0] &= ~(1u << 16);
250
2
  }
251
252
  // Disable ADX instructions on Knights Landing. See OpenSSL commit
253
  // 64d92d74985ebb3d0be58a9718f9e080a14a8e7f.
254
2
  if ((ecx & (1u << 26)) == 0) {
255
0
    extended_features[0] &= ~(1u << 19);
256
0
  }
257
258
2
  OPENSSL_ia32cap_P[0] = edx;
259
2
  OPENSSL_ia32cap_P[1] = ecx;
260
2
  OPENSSL_ia32cap_P[2] = extended_features[0];
261
2
  OPENSSL_ia32cap_P[3] = extended_features[1];
262
263
2
  const char *env1, *env2;
264
2
  env1 = getenv("OPENSSL_ia32cap");
265
2
  if (env1 == NULL) {
266
2
    return;
267
2
  }
268
269
  // OPENSSL_ia32cap can contain zero, one or two values, separated with a ':'.
270
  // Each value is a 64-bit, unsigned value which may start with "0x" to
271
  // indicate a hex value. Prior to the 64-bit value, a '~' or '|' may be given.
272
  //
273
  // If the '~' prefix is present:
274
  //   the value is inverted and ANDed with the probed CPUID result
275
  // If the '|' prefix is present:
276
  //   the value is ORed with the probed CPUID result
277
  // Otherwise:
278
  //   the value is taken as the result of the CPUID
279
  //
280
  // The first value determines OPENSSL_ia32cap_P[0] and [1]. The second [2]
281
  // and [3].
282
283
0
  handle_cpu_env(&OPENSSL_ia32cap_P[0], env1);
284
0
  env2 = strchr(env1, ':');
285
0
  if (env2 != NULL) {
286
0
    handle_cpu_env(&OPENSSL_ia32cap_P[2], env2 + 1);
287
0
  }
288
0
}
289
290
#endif  // !OPENSSL_NO_ASM && (OPENSSL_X86 || OPENSSL_X86_64)