|
@@ -609,3 +609,63 @@ static const struct cpu_dev __cpuinitconst amd_cpu_dev = {
|
|
|
};
|
|
|
|
|
|
cpu_dev_register(amd_cpu_dev);
|
|
|
+
|
|
|
+/*
|
|
|
+ * AMD errata checking
|
|
|
+ *
|
|
|
+ * Errata are defined as arrays of ints using the AMD_LEGACY_ERRATUM() or
|
|
|
+ * AMD_OSVW_ERRATUM() macros. The latter is intended for newer errata that
|
|
|
+ * have an OSVW id assigned, which it takes as first argument. Both take a
|
|
|
+ * variable number of family-specific model-stepping ranges created by
|
|
|
+ * AMD_MODEL_RANGE(). Each erratum also has to be declared as extern const
|
|
|
+ * int[] in arch/x86/include/asm/processor.h.
|
|
|
+ *
|
|
|
+ * Example:
|
|
|
+ *
|
|
|
+ * const int amd_erratum_319[] =
|
|
|
+ * AMD_LEGACY_ERRATUM(AMD_MODEL_RANGE(0x10, 0x2, 0x1, 0x4, 0x2),
|
|
|
+ * AMD_MODEL_RANGE(0x10, 0x8, 0x0, 0x8, 0x0),
|
|
|
+ * AMD_MODEL_RANGE(0x10, 0x9, 0x0, 0x9, 0x0));
|
|
|
+ */
|
|
|
+
|
|
|
+bool cpu_has_amd_erratum(const int *erratum)
|
|
|
+{
|
|
|
+ struct cpuinfo_x86 *cpu = ¤t_cpu_data;
|
|
|
+ int osvw_id = *erratum++;
|
|
|
+ u32 range;
|
|
|
+ u32 ms;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If called early enough that current_cpu_data hasn't been initialized
|
|
|
+ * yet, fall back to boot_cpu_data.
|
|
|
+ */
|
|
|
+ if (cpu->x86 == 0)
|
|
|
+ cpu = &boot_cpu_data;
|
|
|
+
|
|
|
+ if (cpu->x86_vendor != X86_VENDOR_AMD)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (osvw_id >= 0 && osvw_id < 65536 &&
|
|
|
+ cpu_has(cpu, X86_FEATURE_OSVW)) {
|
|
|
+ u64 osvw_len;
|
|
|
+
|
|
|
+ rdmsrl(MSR_AMD64_OSVW_ID_LENGTH, osvw_len);
|
|
|
+ if (osvw_id < osvw_len) {
|
|
|
+ u64 osvw_bits;
|
|
|
+
|
|
|
+ rdmsrl(MSR_AMD64_OSVW_STATUS + (osvw_id >> 6),
|
|
|
+ osvw_bits);
|
|
|
+ return osvw_bits & (1ULL << (osvw_id & 0x3f));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* OSVW unavailable or ID unknown, match family-model-stepping range */
|
|
|
+ ms = (cpu->x86_model << 8) | cpu->x86_mask;
|
|
|
+ while ((range = *erratum++))
|
|
|
+ if ((cpu->x86 == AMD_MODEL_RANGE_FAMILY(range)) &&
|
|
|
+ (ms >= AMD_MODEL_RANGE_START(range)) &&
|
|
|
+ (ms <= AMD_MODEL_RANGE_END(range)))
|
|
|
+ return true;
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|