| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "mos/x86/cpu/cpu.hpp" |
| 4 | |
| 5 | #include "mos/assert.hpp" |
| 6 | #include "mos/platform/platform.hpp" |
| 7 | #include "mos/platform/platform_defs.hpp" |
| 8 | #include "mos/syslog/printk.hpp" |
| 9 | #include "mos/x86/cpu/cpuid.hpp" |
| 10 | #include "mos/x86/tasks/fpu_context.hpp" |
| 11 | |
| 12 | #include <mos_string.hpp> |
| 13 | |
| 14 | // clang-format off |
| 15 | #define do_static_assert(feat) MOS_STATIC_ASSERT(X86_CPUID_LEAF_ENUM(feat) >= 0); |
| 16 | #define test_ensure_all_leaves_are_supported(feature) do_static_assert(CPU_FEATURE_##feature) |
| 17 | FOR_ALL_CPU_FEATURES(test_ensure_all_leaves_are_supported) |
| 18 | #undef test_ensure_all_leaves_are_supported |
| 19 | #undef do_static_assert |
| 20 | // clang-format on |
| 21 | |
| 22 | void x86_cpu_initialise_caps(void) |
| 23 | { |
| 24 | platform_cpuinfo_t *cpuinfo = &per_cpu(platform_info->cpu)->cpuinfo; |
| 25 | memzero(s: cpuinfo, n: sizeof(platform_cpuinfo_t)); |
| 26 | |
| 27 | #define impl_fill_leaf(_l, _sl, _r) cpuinfo->cpuid[X86_CPUID_LEAF_ENUM(_l, _sl, _r, _)] = x86_cpuid(_r, _l, _sl); |
| 28 | FOR_ALL_SUPPORTED_CPUID_LEAF(impl_fill_leaf); |
| 29 | #undef impl_fill_leaf |
| 30 | |
| 31 | MOS_ASSERT_X(cpu_has_feature(CPU_FEATURE_FSGSBASE), "FSGSBASE is required" ); |
| 32 | MOS_ASSERT_X(cpu_has_feature(CPU_FEATURE_FXSR), "FXSR is required" ); |
| 33 | MOS_ASSERT_X(cpu_has_feature(CPU_FEATURE_SSE), "SSE is required" ); |
| 34 | MOS_ASSERT_X(cpu_has_feature(CPU_FEATURE_XSAVE), "XSAVE is required" ); |
| 35 | |
| 36 | x86_cpu_set_cr4(x86_cpu_get_cr4() | BIT(7) | BIT(11) | BIT(16)); // set CR4.PGE, CR4.FSGSBASE, CR4.UMIP |
| 37 | } |
| 38 | |
| 39 | void x86_cpu_setup_xsave_area(void) |
| 40 | { |
| 41 | pr_dinfo2(x86_startup, "setting up xsave area..." ); |
| 42 | |
| 43 | reg_t cr0 = x86_cpu_get_cr0(); |
| 44 | cr0 &= ~BIT(2); // clear coprocessor emulation CR0.EM |
| 45 | cr0 |= BIT(1); |
| 46 | x86_cpu_set_cr0(cr0); |
| 47 | |
| 48 | reg_t cr4 = x86_cpu_get_cr4(); |
| 49 | cr4 |= BIT(9) | BIT(10) | BIT(18); // set CR4.OSFXSR, CR4.OSXMMEXCPT and CR4.OSXSAVE |
| 50 | x86_cpu_set_cr4(cr4); |
| 51 | |
| 52 | reg_t xcr0 = XCR0_X87 | XCR0_SSE; // bit 0, 1 |
| 53 | size_t xsave_size = 512; // X87 + SSE |
| 54 | |
| 55 | // SSE |
| 56 | xcr0 |= XCR0_SSE; // bit 1 |
| 57 | xsave_size += 64; // XSAVE header |
| 58 | |
| 59 | // AVX |
| 60 | if (cpu_has_feature(CPU_FEATURE_AVX)) |
| 61 | xcr0 |= XCR0_AVX; |
| 62 | |
| 63 | static const char *const xcr0_names[] = { |
| 64 | /*0*/ "x87" , // |
| 65 | /*1*/ "SSE" , // |
| 66 | /*2*/ "AVX" , // |
| 67 | /*3*/ "MPX BNDREGS" , // |
| 68 | /*4*/ "MPX BNDCSR" , // |
| 69 | /*5*/ "AVX-512 OPMASK" , // |
| 70 | /*6*/ "AVX-512 ZMM0-15" , // |
| 71 | /*7*/ "AVX-512 ZMM16-31" , // |
| 72 | /*8*/ "PT" , // |
| 73 | /*9*/ "PKRU" , // |
| 74 | }; |
| 75 | |
| 76 | for (size_t state_component = 2; state_component < 64; state_component++) |
| 77 | { |
| 78 | reg32_t size, offset, ecx, edx; |
| 79 | __cpuid_count(0xd, state_component, size, offset, ecx, edx); |
| 80 | if (size && offset && ((ecx & BIT(0)) == 0)) |
| 81 | { |
| 82 | const char *const name = state_component < MOS_ARRAY_SIZE(xcr0_names) ? xcr0_names[state_component] : "<unknown>" ; |
| 83 | pr_dinfo2(x86_startup, "XSAVE state component '%s': size=%d, offset=%d" , name, size, offset); |
| 84 | |
| 85 | if (xcr0 & BIT(state_component)) |
| 86 | { |
| 87 | pr_dcont(x86_startup, " (enabled)" ); |
| 88 | xsave_size += size; |
| 89 | } |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | pr_dinfo2(x86_startup, "XSAVE area size: %zu" , xsave_size); |
| 94 | |
| 95 | __asm__ volatile("xsetbv" : : "c" (0), "a" (xcr0), "d" (xcr0 >> 32)); |
| 96 | xsave_area_slab.ent_size = xsave_size; |
| 97 | } |
| 98 | |