| Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2012 The Chromium Authors |
| [email protected] | 14cd2e6 | 2011-02-24 09:20:16 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| [email protected] | 14cd2e6 | 2011-02-24 09:20:16 | [diff] [blame] | 5 | #include "base/cpu.h" |
| André Kempe | 1994caac | 2024-04-05 15:01:39 | [diff] [blame] | 6 | |
| Jan Wilken Dörrie | b5a41c3 | 2020-12-09 18:55:47 | [diff] [blame] | 7 | #include "base/containers/contains.h" |
| Sergio Solano | 2cd743b | 2025-11-05 20:14:29 | [diff] [blame] | 8 | #include "base/containers/span.h" |
| Richard Townsend | 8cb7ba0b | 2020-11-26 23:23:22 | [diff] [blame] | 9 | #include "base/logging.h" |
| André Kempe | 1994caac | 2024-04-05 15:01:39 | [diff] [blame] | 10 | #include "base/memory/protected_memory_buildflags.h" |
| Robert Sesek | fd71c38 | 2021-01-14 18:40:52 | [diff] [blame] | 11 | #include "base/strings/string_util.h" |
| André Kempe | 1994caac | 2024-04-05 15:01:39 | [diff] [blame] | 12 | #include "base/test/gtest_util.h" |
| [email protected] | d1811bc | 2012-03-31 07:08:53 | [diff] [blame] | 13 | #include "build/build_config.h" |
| [email protected] | 14cd2e6 | 2011-02-24 09:20:16 | [diff] [blame] | 14 | #include "testing/gtest/include/gtest/gtest.h" |
| 15 | |
| 16 | // Tests whether we can run extended instructions represented by the CPU |
| 17 | // information. This test actually executes some extended instructions (such as |
| 18 | // MMX, SSE, etc.) supported by the CPU and sees we can run them without |
| 19 | // "undefined instruction" exceptions. That is, this test succeeds when this |
| 20 | // test finishes without a crash. |
| 21 | TEST(CPU, RunExtendedInstructions) { |
| [email protected] | 14cd2e6 | 2011-02-24 09:20:16 | [diff] [blame] | 22 | // Retrieve the CPU information. |
| 23 | base::CPU cpu; |
| Richard Townsend | 8cb7ba0b | 2020-11-26 23:23:22 | [diff] [blame] | 24 | #if defined(ARCH_CPU_X86_FAMILY) |
| [email protected] | 14cd2e6 | 2011-02-24 09:20:16 | [diff] [blame] | 25 | ASSERT_TRUE(cpu.has_mmx()); |
| fbarchard | 0ce41ae | 2015-10-02 03:23:19 | [diff] [blame] | 26 | ASSERT_TRUE(cpu.has_sse()); |
| 27 | ASSERT_TRUE(cpu.has_sse2()); |
| Victor Costan | 5bb2864 | 2020-11-14 06:42:49 | [diff] [blame] | 28 | ASSERT_TRUE(cpu.has_sse3()); |
| [email protected] | 14cd2e6 | 2011-02-24 09:20:16 | [diff] [blame] | 29 | |
| [email protected] | 14cd2e6 | 2011-02-24 09:20:16 | [diff] [blame] | 30 | // Execute an MMX instruction. |
| 31 | __asm__ __volatile__("emms\n" : : : "mm0"); |
| 32 | |
| fbarchard | 0ce41ae | 2015-10-02 03:23:19 | [diff] [blame] | 33 | // Execute an SSE instruction. |
| 34 | __asm__ __volatile__("xorps %%xmm0, %%xmm0\n" : : : "xmm0"); |
| [email protected] | 14cd2e6 | 2011-02-24 09:20:16 | [diff] [blame] | 35 | |
| fbarchard | 0ce41ae | 2015-10-02 03:23:19 | [diff] [blame] | 36 | // Execute an SSE 2 instruction. |
| 37 | __asm__ __volatile__("psrldq $0, %%xmm0\n" : : : "xmm0"); |
| [email protected] | 14cd2e6 | 2011-02-24 09:20:16 | [diff] [blame] | 38 | |
| Victor Costan | 5bb2864 | 2020-11-14 06:42:49 | [diff] [blame] | 39 | // Execute an SSE 3 instruction. |
| 40 | __asm__ __volatile__("addsubpd %%xmm0, %%xmm0\n" : : : "xmm0"); |
| [email protected] | 14cd2e6 | 2011-02-24 09:20:16 | [diff] [blame] | 41 | |
| 42 | if (cpu.has_ssse3()) { |
| 43 | // Execute a Supplimental SSE 3 instruction. |
| 44 | __asm__ __volatile__("psignb %%xmm0, %%xmm0\n" : : : "xmm0"); |
| 45 | } |
| 46 | |
| 47 | if (cpu.has_sse41()) { |
| 48 | // Execute an SSE 4.1 instruction. |
| 49 | __asm__ __volatile__("pmuldq %%xmm0, %%xmm0\n" : : : "xmm0"); |
| 50 | } |
| 51 | |
| 52 | if (cpu.has_sse42()) { |
| 53 | // Execute an SSE 4.2 instruction. |
| 54 | __asm__ __volatile__("crc32 %%eax, %%eax\n" : : : "eax"); |
| 55 | } |
| fbarchard | 0ce41ae | 2015-10-02 03:23:19 | [diff] [blame] | 56 | |
| ilevy | b7d2f408 | 2016-10-30 20:46:57 | [diff] [blame] | 57 | if (cpu.has_popcnt()) { |
| 58 | // Execute a POPCNT instruction. |
| 59 | __asm__ __volatile__("popcnt %%eax, %%eax\n" : : : "eax"); |
| 60 | } |
| 61 | |
| fbarchard | 0ce41ae | 2015-10-02 03:23:19 | [diff] [blame] | 62 | if (cpu.has_avx()) { |
| 63 | // Execute an AVX instruction. |
| 64 | __asm__ __volatile__("vzeroupper\n" : : : "xmm0"); |
| 65 | } |
| 66 | |
| Ng Zhi An | 03baf5e | 2021-10-04 22:56:52 | [diff] [blame] | 67 | if (cpu.has_fma3()) { |
| 68 | // Execute a FMA3 instruction. |
| 69 | __asm__ __volatile__("vfmadd132ps %%xmm0, %%xmm0, %%xmm0\n" : : : "xmm0"); |
| 70 | } |
| 71 | |
| fbarchard | 0ce41ae | 2015-10-02 03:23:19 | [diff] [blame] | 72 | if (cpu.has_avx2()) { |
| 73 | // Execute an AVX 2 instruction. |
| 74 | __asm__ __volatile__("vpunpcklbw %%ymm0, %%ymm0, %%ymm0\n" : : : "xmm0"); |
| 75 | } |
| Stephen Roettger | 6f1a424 | 2022-10-07 10:04:25 | [diff] [blame] | 76 | |
| James Zern | 8a904ed | 2024-05-08 23:28:27 | [diff] [blame] | 77 | if (cpu.has_avx_vnni()) { |
| 78 | // Execute an AVX VNNI instruction. {vex} prevents EVEX encoding, which |
| 79 | // would shift it to AVX512 VNNI. |
| 80 | __asm__ __volatile__("%{vex%} vpdpbusd %%ymm0, %%ymm0, %%ymm0\n" |
| 81 | : |
| 82 | : |
| 83 | : "ymm0"); |
| 84 | } |
| 85 | |
| 86 | if (cpu.has_avx512_f()) { |
| 87 | // Execute an AVX-512 Foundation (F) instruction. |
| 88 | __asm__ __volatile__("vpxorq %%zmm0, %%zmm0, %%zmm0\n" : : : "zmm0"); |
| 89 | } |
| 90 | |
| 91 | if (cpu.has_avx512_bw()) { |
| 92 | // Execute an AVX-512 Byte & Word (BW) instruction. |
| 93 | __asm__ __volatile__("vpabsw %%zmm0, %%zmm0\n" : : : "zmm0"); |
| 94 | } |
| 95 | |
| 96 | if (cpu.has_avx512_vnni()) { |
| 97 | // Execute an AVX-512 VNNI instruction. |
| 98 | __asm__ __volatile__("vpdpbusd %%zmm0, %%zmm0, %%zmm0\n" : : : "zmm0"); |
| 99 | } |
| 100 | |
| Stephen Roettger | 6f1a424 | 2022-10-07 10:04:25 | [diff] [blame] | 101 | if (cpu.has_pku()) { |
| 102 | // rdpkru |
| 103 | uint32_t pkru; |
| 104 | __asm__ __volatile__(".byte 0x0f,0x01,0xee\n" |
| 105 | : "=a"(pkru) |
| 106 | : "c"(0), "d"(0)); |
| 107 | } |
| fbarchard | 0ce41ae | 2015-10-02 03:23:19 | [diff] [blame] | 108 | #endif // defined(ARCH_CPU_X86_FAMILY) |
| Richard Townsend | 8cb7ba0b | 2020-11-26 23:23:22 | [diff] [blame] | 109 | |
| 110 | #if defined(ARCH_CPU_ARM64) |
| 111 | // Check that the CPU is correctly reporting support for the Armv8.5-A memory |
| 112 | // tagging extension. The new MTE instructions aren't encoded in NOP space |
| 113 | // like BTI/Pointer Authentication and will crash older cores with a SIGILL if |
| 114 | // used incorrectly. This test demonstrates how it should be done and that |
| 115 | // this approach works. |
| 116 | if (cpu.has_mte()) { |
| 117 | #if !defined(__ARM_FEATURE_MEMORY_TAGGING) |
| 118 | // In this section, we're running on an MTE-compatible core, but we're |
| 119 | // building this file without MTE support. Fail this test to indicate that |
| 120 | // there's a problem with the base/ build configuration. |
| 121 | GTEST_FAIL() |
| 122 | << "MTE support detected (but base/ built without MTE support)"; |
| 123 | #else |
| 124 | char ptr[32]; |
| 125 | uint64_t val; |
| 126 | // Execute a trivial MTE instruction. Normally, MTE should be used via the |
| 127 | // intrinsics documented at |
| 128 | // https://developer.arm.com/documentation/101028/0012/10--Memory-tagging-intrinsics, |
| 129 | // this test uses the irg (Insert Random Tag) instruction directly to make |
| 130 | // sure that it's not optimized out by the compiler. |
| 131 | __asm__ __volatile__("irg %0, %1" : "=r"(val) : "r"(ptr)); |
| 132 | #endif // __ARM_FEATURE_MEMORY_TAGGING |
| Richard Townsend | 8cb7ba0b | 2020-11-26 23:23:22 | [diff] [blame] | 133 | } |
| 134 | #endif // ARCH_CPU_ARM64 |
| [email protected] | 14cd2e6 | 2011-02-24 09:20:16 | [diff] [blame] | 135 | } |
| Lei Zhang | 49c4b2a | 2017-11-03 21:34:23 | [diff] [blame] | 136 | |
| 137 | // For https://crbug.com/249713 |
| 138 | TEST(CPU, BrandAndVendorContainsNoNUL) { |
| 139 | base::CPU cpu; |
| Jan Wilken Dörrie | f61e74c | 2019-06-07 08:20:02 | [diff] [blame] | 140 | EXPECT_FALSE(base::Contains(cpu.cpu_brand(), '\0')); |
| 141 | EXPECT_FALSE(base::Contains(cpu.vendor_name(), '\0')); |
| Lei Zhang | 49c4b2a | 2017-11-03 21:34:23 | [diff] [blame] | 142 | } |
| Gabriel Marin | 8fdd777 | 2019-08-17 00:28:09 | [diff] [blame] | 143 | |
| 144 | #if defined(ARCH_CPU_X86_FAMILY) |
| 145 | // Tests that we compute the correct CPU family and model based on the vendor |
| 146 | // and CPUID signature. |
| 147 | TEST(CPU, X86FamilyAndModel) { |
| Lei Zhang | 1b1116c5 | 2021-05-14 22:54:38 | [diff] [blame] | 148 | base::internal::X86ModelInfo info; |
| Gabriel Marin | 8fdd777 | 2019-08-17 00:28:09 | [diff] [blame] | 149 | |
| 150 | // Check with an Intel Skylake signature. |
| Lei Zhang | 1b1116c5 | 2021-05-14 22:54:38 | [diff] [blame] | 151 | info = base::internal::ComputeX86FamilyAndModel("GenuineIntel", 0x000406e3); |
| 152 | EXPECT_EQ(info.family, 6); |
| 153 | EXPECT_EQ(info.model, 78); |
| 154 | EXPECT_EQ(info.ext_family, 0); |
| 155 | EXPECT_EQ(info.ext_model, 4); |
| Gabriel Marin | 8fdd777 | 2019-08-17 00:28:09 | [diff] [blame] | 156 | |
| 157 | // Check with an Intel Airmont signature. |
| Lei Zhang | 1b1116c5 | 2021-05-14 22:54:38 | [diff] [blame] | 158 | info = base::internal::ComputeX86FamilyAndModel("GenuineIntel", 0x000406c2); |
| 159 | EXPECT_EQ(info.family, 6); |
| 160 | EXPECT_EQ(info.model, 76); |
| 161 | EXPECT_EQ(info.ext_family, 0); |
| 162 | EXPECT_EQ(info.ext_model, 4); |
| Gabriel Marin | 8fdd777 | 2019-08-17 00:28:09 | [diff] [blame] | 163 | |
| 164 | // Check with an Intel Prescott signature. |
| Lei Zhang | 1b1116c5 | 2021-05-14 22:54:38 | [diff] [blame] | 165 | info = base::internal::ComputeX86FamilyAndModel("GenuineIntel", 0x00000f31); |
| 166 | EXPECT_EQ(info.family, 15); |
| 167 | EXPECT_EQ(info.model, 3); |
| 168 | EXPECT_EQ(info.ext_family, 0); |
| 169 | EXPECT_EQ(info.ext_model, 0); |
| Gabriel Marin | 8fdd777 | 2019-08-17 00:28:09 | [diff] [blame] | 170 | |
| 171 | // Check with an AMD Excavator signature. |
| Lei Zhang | 1b1116c5 | 2021-05-14 22:54:38 | [diff] [blame] | 172 | info = base::internal::ComputeX86FamilyAndModel("AuthenticAMD", 0x00670f00); |
| 173 | EXPECT_EQ(info.family, 21); |
| 174 | EXPECT_EQ(info.model, 112); |
| 175 | EXPECT_EQ(info.ext_family, 6); |
| 176 | EXPECT_EQ(info.ext_model, 7); |
| Gabriel Marin | 8fdd777 | 2019-08-17 00:28:09 | [diff] [blame] | 177 | } |
| 178 | #endif // defined(ARCH_CPU_X86_FAMILY) |
| Robert Sesek | fd71c38 | 2021-01-14 18:40:52 | [diff] [blame] | 179 | |
| André Kempe | 1994caac | 2024-04-05 15:01:39 | [diff] [blame] | 180 | #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) |
| 181 | TEST(CPUDeathTest, VerifyModifyingCPUInstanceNoAllocationCrashes) { |
| 182 | const base::CPU& cpu = base::CPU::GetInstanceNoAllocation(); |
| Sergio Solano | 2cd743b | 2025-11-05 20:14:29 | [diff] [blame] | 183 | // SAFETY: This test explicitly checks the functionality of protected memory. |
| 184 | // Any write to any byte of CPU should crash. Thus this test is used to |
| 185 | // precisely manipulate and target the memory to cause a crash. Here we just |
| 186 | // wrap it into a span for convenience. |
| 187 | // TODO(sergiosolano): Use base::byte_span_from_ref() here once base::CPU is |
| 188 | // trivially copyable. |
| 189 | const base::span<uint8_t> bytes = UNSAFE_BUFFERS( |
| 190 | base::span(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(&cpu)), |
| 191 | sizeof(cpu))); |
| André Kempe | 1994caac | 2024-04-05 15:01:39 | [diff] [blame] | 192 | // We try and flip a couple of bits and expect the test to die immediately. |
| 193 | // Checks are limited to every 15th byte, otherwise the tests run into |
| 194 | // time-outs. |
| 195 | for (size_t byte_index = 0; byte_index < sizeof(cpu); byte_index += 15) { |
| 196 | const size_t local_bit_index = byte_index % 8; |
| 197 | EXPECT_CHECK_DEATH_WITH(bytes[byte_index] ^= (0x01 << local_bit_index), ""); |
| 198 | } |
| 199 | } |
| 200 | #endif |