diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/include/asm/processor.h | 18 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/amd.c | 60 |
2 files changed, 78 insertions, 0 deletions
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 7e5c6a60b8ee..5084c2f5ac20 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h | |||
@@ -1025,4 +1025,22 @@ unsigned long calc_aperfmperf_ratio(struct aperfmperf *old, | |||
1025 | return ratio; | 1025 | return ratio; |
1026 | } | 1026 | } |
1027 | 1027 | ||
1028 | /* | ||
1029 | * AMD errata checking | ||
1030 | */ | ||
1031 | #ifdef CONFIG_CPU_SUP_AMD | ||
1032 | extern bool cpu_has_amd_erratum(const int *); | ||
1033 | |||
1034 | #define AMD_LEGACY_ERRATUM(...) { -1, __VA_ARGS__, 0 } | ||
1035 | #define AMD_OSVW_ERRATUM(osvw_id, ...) { osvw_id, __VA_ARGS__, 0 } | ||
1036 | #define AMD_MODEL_RANGE(f, m_start, s_start, m_end, s_end) \ | ||
1037 | ((f << 24) | (m_start << 16) | (s_start << 12) | (m_end << 4) | (s_end)) | ||
1038 | #define AMD_MODEL_RANGE_FAMILY(range) (((range) >> 24) & 0xff) | ||
1039 | #define AMD_MODEL_RANGE_START(range) (((range) >> 12) & 0xfff) | ||
1040 | #define AMD_MODEL_RANGE_END(range) ((range) & 0xfff) | ||
1041 | |||
1042 | #else | ||
1043 | #define cpu_has_amd_erratum(x) (false) | ||
1044 | #endif /* CONFIG_CPU_SUP_AMD */ | ||
1045 | |||
1028 | #endif /* _ASM_X86_PROCESSOR_H */ | 1046 | #endif /* _ASM_X86_PROCESSOR_H */ |
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 12b9cff047c1..80665410b064 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c | |||
@@ -609,3 +609,63 @@ static const struct cpu_dev __cpuinitconst amd_cpu_dev = { | |||
609 | }; | 609 | }; |
610 | 610 | ||
611 | cpu_dev_register(amd_cpu_dev); | 611 | cpu_dev_register(amd_cpu_dev); |
612 | |||
613 | /* | ||
614 | * AMD errata checking | ||
615 | * | ||
616 | * Errata are defined as arrays of ints using the AMD_LEGACY_ERRATUM() or | ||
617 | * AMD_OSVW_ERRATUM() macros. The latter is intended for newer errata that | ||
618 | * have an OSVW id assigned, which it takes as first argument. Both take a | ||
619 | * variable number of family-specific model-stepping ranges created by | ||
620 | * AMD_MODEL_RANGE(). Each erratum also has to be declared as extern const | ||
621 | * int[] in arch/x86/include/asm/processor.h. | ||
622 | * | ||
623 | * Example: | ||
624 | * | ||
625 | * const int amd_erratum_319[] = | ||
626 | * AMD_LEGACY_ERRATUM(AMD_MODEL_RANGE(0x10, 0x2, 0x1, 0x4, 0x2), | ||
627 | * AMD_MODEL_RANGE(0x10, 0x8, 0x0, 0x8, 0x0), | ||
628 | * AMD_MODEL_RANGE(0x10, 0x9, 0x0, 0x9, 0x0)); | ||
629 | */ | ||
630 | |||
631 | bool cpu_has_amd_erratum(const int *erratum) | ||
632 | { | ||
633 | struct cpuinfo_x86 *cpu = ¤t_cpu_data; | ||
634 | int osvw_id = *erratum++; | ||
635 | u32 range; | ||
636 | u32 ms; | ||
637 | |||
638 | /* | ||
639 | * If called early enough that current_cpu_data hasn't been initialized | ||
640 | * yet, fall back to boot_cpu_data. | ||
641 | */ | ||
642 | if (cpu->x86 == 0) | ||
643 | cpu = &boot_cpu_data; | ||
644 | |||
645 | if (cpu->x86_vendor != X86_VENDOR_AMD) | ||
646 | return false; | ||
647 | |||
648 | if (osvw_id >= 0 && osvw_id < 65536 && | ||
649 | cpu_has(cpu, X86_FEATURE_OSVW)) { | ||
650 | u64 osvw_len; | ||
651 | |||
652 | rdmsrl(MSR_AMD64_OSVW_ID_LENGTH, osvw_len); | ||
653 | if (osvw_id < osvw_len) { | ||
654 | u64 osvw_bits; | ||
655 | |||
656 | rdmsrl(MSR_AMD64_OSVW_STATUS + (osvw_id >> 6), | ||
657 | osvw_bits); | ||
658 | return osvw_bits & (1ULL << (osvw_id & 0x3f)); | ||
659 | } | ||
660 | } | ||
661 | |||
662 | /* OSVW unavailable or ID unknown, match family-model-stepping range */ | ||
663 | ms = (cpu->x86_model << 8) | cpu->x86_mask; | ||
664 | while ((range = *erratum++)) | ||
665 | if ((cpu->x86 == AMD_MODEL_RANGE_FAMILY(range)) && | ||
666 | (ms >= AMD_MODEL_RANGE_START(range)) && | ||
667 | (ms <= AMD_MODEL_RANGE_END(range))) | ||
668 | return true; | ||
669 | |||
670 | return false; | ||
671 | } | ||