summaryrefslogtreecommitdiff
path: root/usr/src/uts/i86pc/os
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/i86pc/os')
-rw-r--r--usr/src/uts/i86pc/os/cpuid.c189
-rw-r--r--usr/src/uts/i86pc/os/cpupm/pwrnow.c45
-rw-r--r--usr/src/uts/i86pc/os/cpupm/speedstep.c204
-rw-r--r--usr/src/uts/i86pc/os/cpupm/turbo.c208
-rw-r--r--usr/src/uts/i86pc/os/microcode.c31
-rw-r--r--usr/src/uts/i86pc/os/mlsetup.c5
-rw-r--r--usr/src/uts/i86pc/os/mp_machdep.c13
-rw-r--r--usr/src/uts/i86pc/os/mp_startup.c18
8 files changed, 441 insertions, 272 deletions
diff --git a/usr/src/uts/i86pc/os/cpuid.c b/usr/src/uts/i86pc/os/cpuid.c
index 0a389e0063..10946474dd 100644
--- a/usr/src/uts/i86pc/os/cpuid.c
+++ b/usr/src/uts/i86pc/os/cpuid.c
@@ -30,7 +30,7 @@
* Portions Copyright 2009 Advanced Micro Devices, Inc.
*/
/*
- * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
/*
* Various routines to handle identification
@@ -160,7 +160,8 @@ static char *x86_feature_names[NUM_X86_FEATURES] = {
"xsave",
"avx",
"vmx",
- "svm"
+ "svm",
+ "topoext"
};
boolean_t
@@ -269,7 +270,7 @@ struct xsave_info {
*/
#define NMAX_CPI_STD 6 /* eax = 0 .. 5 */
-#define NMAX_CPI_EXTD 0x1c /* eax = 0x80000000 .. 0x8000001b */
+#define NMAX_CPI_EXTD 0x1f /* eax = 0x80000000 .. 0x8000001e */
/*
* Some terminology needs to be explained:
@@ -283,6 +284,8 @@ struct xsave_info {
* memory controllers, PCI configuration spaces. They are connected
* inside the package with Hypertransport links. On single-node
* processors, processor node is equivalent to chip/socket/package.
+ * - Compute Unit: Some AMD processors pair cores in "compute units" that
+ * share the FPU and the I$ and L2 caches.
*/
struct cpuid_info {
@@ -343,6 +346,8 @@ struct cpuid_info {
uint_t cpi_procnodeid; /* AMD: nodeID on HT, Intel: chipid */
uint_t cpi_procnodes_per_pkg; /* AMD: # of nodes in the package */
/* Intel: 1 */
+ uint_t cpi_compunitid; /* AMD: ComputeUnit ID, Intel: coreid */
+ uint_t cpi_cores_per_compunit; /* AMD: # of cores in the ComputeUnit */
struct xsave_info cpi_xsave; /* fn D: xsave/xrestor info */
};
@@ -727,6 +732,7 @@ cpuid_intel_getids(cpu_t *cpu, void *feature)
cpi->cpi_pkgcoreid = 0;
}
cpi->cpi_procnodeid = cpi->cpi_chipid;
+ cpi->cpi_compunitid = cpi->cpi_coreid;
}
static void
@@ -736,6 +742,7 @@ cpuid_amd_getids(cpu_t *cpu)
uint32_t nb_caps_reg;
uint_t node2_1;
struct cpuid_info *cpi = cpu->cpu_m.mcpu_cpi;
+ struct cpuid_regs *cp;
/*
* AMD CMP chips currently have a single thread per core.
@@ -753,9 +760,15 @@ cpuid_amd_getids(cpu_t *cpu)
* from 0 regardless of how many or which are disabled, and there
* is no way for operating system to discover the real core id when some
* are disabled.
+ *
+ * In family 0x15, the cores come in pairs called compute units. They
+ * share I$ and L2 caches and the FPU. Enumeration of this feature is
+ * simplified by the new topology extensions CPUID leaf, indicated by
+ * the X86 feature X86FSET_TOPOEXT.
*/
cpi->cpi_coreid = cpu->cpu_id;
+ cpi->cpi_compunitid = cpu->cpu_id;
if (cpi->cpi_xmaxeax >= 0x80000008) {
@@ -784,10 +797,21 @@ cpuid_amd_getids(cpu_t *cpu)
cpi->cpi_apicid & ((1<<coreidsz) - 1);
cpi->cpi_ncpu_per_chip = cpi->cpi_ncore_per_chip;
- /* Get nodeID */
- if (cpi->cpi_family == 0xf) {
+ /* Get node ID, compute unit ID */
+ if (is_x86_feature(x86_featureset, X86FSET_TOPOEXT) &&
+ cpi->cpi_xmaxeax >= 0x8000001e) {
+ cp = &cpi->cpi_extd[0x1e];
+ cp->cp_eax = 0x8000001e;
+ (void) __cpuid_insn(cp);
+
+ cpi->cpi_procnodes_per_pkg = BITX(cp->cp_ecx, 10, 8) + 1;
+ cpi->cpi_procnodeid = BITX(cp->cp_ecx, 7, 0);
+ cpi->cpi_cores_per_compunit = BITX(cp->cp_ebx, 15, 8) + 1;
+ cpi->cpi_compunitid = BITX(cp->cp_ebx, 7, 0)
+ + (cpi->cpi_ncore_per_chip / cpi->cpi_cores_per_compunit)
+ * (cpi->cpi_procnodeid / cpi->cpi_procnodes_per_pkg);
+ } else if (cpi->cpi_family == 0xf || cpi->cpi_family >= 0x11) {
cpi->cpi_procnodeid = (cpi->cpi_apicid >> coreidsz) & 7;
- cpi->cpi_chipid = cpi->cpi_procnodeid;
} else if (cpi->cpi_family == 0x10) {
/*
* See if we are a multi-node processor.
@@ -798,7 +822,6 @@ cpuid_amd_getids(cpu_t *cpu)
/* Single-node */
cpi->cpi_procnodeid = BITX(cpi->cpi_apicid, 5,
coreidsz);
- cpi->cpi_chipid = cpi->cpi_procnodeid;
} else {
/*
@@ -813,7 +836,6 @@ cpuid_amd_getids(cpu_t *cpu)
if (cpi->cpi_apicid == cpi->cpi_pkgcoreid) {
/* We are BSP */
cpi->cpi_procnodeid = (first_half ? 0 : 1);
- cpi->cpi_chipid = cpi->cpi_procnodeid >> 1;
} else {
/* We are AP */
@@ -833,17 +855,14 @@ cpuid_amd_getids(cpu_t *cpu)
else
cpi->cpi_procnodeid = node2_1 +
first_half;
-
- cpi->cpi_chipid = cpi->cpi_procnodeid >> 1;
}
}
- } else if (cpi->cpi_family >= 0x11) {
- cpi->cpi_procnodeid = (cpi->cpi_apicid >> coreidsz) & 7;
- cpi->cpi_chipid = cpi->cpi_procnodeid;
} else {
cpi->cpi_procnodeid = 0;
- cpi->cpi_chipid = cpi->cpi_procnodeid;
}
+
+ cpi->cpi_chipid =
+ cpi->cpi_procnodeid / cpi->cpi_procnodes_per_pkg;
}
/*
@@ -1218,30 +1237,28 @@ cpuid_pass1(cpu_t *cpu, uchar_t *featureset)
if (cp->cp_ecx & CPUID_INTC_ECX_SSE3) {
add_x86_feature(featureset, X86FSET_SSE3);
}
- if (cpi->cpi_vendor == X86_VENDOR_Intel) {
- if (cp->cp_ecx & CPUID_INTC_ECX_SSSE3) {
- add_x86_feature(featureset, X86FSET_SSSE3);
- }
- if (cp->cp_ecx & CPUID_INTC_ECX_SSE4_1) {
- add_x86_feature(featureset, X86FSET_SSE4_1);
- }
- if (cp->cp_ecx & CPUID_INTC_ECX_SSE4_2) {
- add_x86_feature(featureset, X86FSET_SSE4_2);
- }
- if (cp->cp_ecx & CPUID_INTC_ECX_AES) {
- add_x86_feature(featureset, X86FSET_AES);
- }
- if (cp->cp_ecx & CPUID_INTC_ECX_PCLMULQDQ) {
- add_x86_feature(featureset, X86FSET_PCLMULQDQ);
- }
+ if (cp->cp_ecx & CPUID_INTC_ECX_SSSE3) {
+ add_x86_feature(featureset, X86FSET_SSSE3);
+ }
+ if (cp->cp_ecx & CPUID_INTC_ECX_SSE4_1) {
+ add_x86_feature(featureset, X86FSET_SSE4_1);
+ }
+ if (cp->cp_ecx & CPUID_INTC_ECX_SSE4_2) {
+ add_x86_feature(featureset, X86FSET_SSE4_2);
+ }
+ if (cp->cp_ecx & CPUID_INTC_ECX_AES) {
+ add_x86_feature(featureset, X86FSET_AES);
+ }
+ if (cp->cp_ecx & CPUID_INTC_ECX_PCLMULQDQ) {
+ add_x86_feature(featureset, X86FSET_PCLMULQDQ);
+ }
- if (cp->cp_ecx & CPUID_INTC_ECX_XSAVE) {
- add_x86_feature(featureset, X86FSET_XSAVE);
- /* We only test AVX when there is XSAVE */
- if (cp->cp_ecx & CPUID_INTC_ECX_AVX) {
- add_x86_feature(featureset,
- X86FSET_AVX);
- }
+ if (cp->cp_ecx & CPUID_INTC_ECX_XSAVE) {
+ add_x86_feature(featureset, X86FSET_XSAVE);
+ /* We only test AVX when there is XSAVE */
+ if (cp->cp_ecx & CPUID_INTC_ECX_AVX) {
+ add_x86_feature(featureset,
+ X86FSET_AVX);
}
}
}
@@ -1439,6 +1456,10 @@ cpuid_pass1(cpu_t *cpu, uchar_t *featureset)
if (cp->cp_ecx & CPUID_AMD_ECX_SVM) {
add_x86_feature(featureset, X86FSET_SVM);
}
+
+ if (cp->cp_ecx & CPUID_AMD_ECX_TOPOEXT) {
+ add_x86_feature(featureset, X86FSET_TOPOEXT);
+ }
break;
default:
break;
@@ -1547,6 +1568,7 @@ cpuid_pass1(cpu_t *cpu, uchar_t *featureset)
cpi->cpi_apicid = CPI_APIC_ID(cpi);
cpi->cpi_procnodes_per_pkg = 1;
+ cpi->cpi_cores_per_compunit = 1;
if (is_x86_feature(featureset, X86FSET_HTT) == B_FALSE &&
is_x86_feature(featureset, X86FSET_CMP) == B_FALSE) {
/*
@@ -1573,6 +1595,7 @@ cpuid_pass1(cpu_t *cpu, uchar_t *featureset)
cpi->cpi_coreid = cpi->cpi_chipid;
cpi->cpi_pkgcoreid = 0;
cpi->cpi_procnodeid = cpi->cpi_chipid;
+ cpi->cpi_compunitid = cpi->cpi_chipid;
}
}
@@ -1797,7 +1820,7 @@ cpuid_pass2(cpu_t *cpu)
/*
* XSAVE enumeration
*/
- if (cpi->cpi_maxeax >= 0xD && cpi->cpi_vendor == X86_VENDOR_Intel) {
+ if (cpi->cpi_maxeax >= 0xD) {
struct cpuid_regs regs;
boolean_t cpuid_d_valid = B_TRUE;
@@ -2531,23 +2554,21 @@ cpuid_pass4(cpu_t *cpu)
if (!is_x86_feature(x86_featureset, X86FSET_SSE3))
*ecx &= ~CPUID_INTC_ECX_SSE3;
- if (cpi->cpi_vendor == X86_VENDOR_Intel) {
- if (!is_x86_feature(x86_featureset, X86FSET_SSSE3))
- *ecx &= ~CPUID_INTC_ECX_SSSE3;
- if (!is_x86_feature(x86_featureset, X86FSET_SSE4_1))
- *ecx &= ~CPUID_INTC_ECX_SSE4_1;
- if (!is_x86_feature(x86_featureset, X86FSET_SSE4_2))
- *ecx &= ~CPUID_INTC_ECX_SSE4_2;
- if (!is_x86_feature(x86_featureset, X86FSET_AES))
- *ecx &= ~CPUID_INTC_ECX_AES;
- if (!is_x86_feature(x86_featureset, X86FSET_PCLMULQDQ))
- *ecx &= ~CPUID_INTC_ECX_PCLMULQDQ;
- if (!is_x86_feature(x86_featureset, X86FSET_XSAVE))
- *ecx &= ~(CPUID_INTC_ECX_XSAVE |
- CPUID_INTC_ECX_OSXSAVE);
- if (!is_x86_feature(x86_featureset, X86FSET_AVX))
- *ecx &= ~CPUID_INTC_ECX_AVX;
- }
+ if (!is_x86_feature(x86_featureset, X86FSET_SSSE3))
+ *ecx &= ~CPUID_INTC_ECX_SSSE3;
+ if (!is_x86_feature(x86_featureset, X86FSET_SSE4_1))
+ *ecx &= ~CPUID_INTC_ECX_SSE4_1;
+ if (!is_x86_feature(x86_featureset, X86FSET_SSE4_2))
+ *ecx &= ~CPUID_INTC_ECX_SSE4_2;
+ if (!is_x86_feature(x86_featureset, X86FSET_AES))
+ *ecx &= ~CPUID_INTC_ECX_AES;
+ if (!is_x86_feature(x86_featureset, X86FSET_PCLMULQDQ))
+ *ecx &= ~CPUID_INTC_ECX_PCLMULQDQ;
+ if (!is_x86_feature(x86_featureset, X86FSET_XSAVE))
+ *ecx &= ~(CPUID_INTC_ECX_XSAVE |
+ CPUID_INTC_ECX_OSXSAVE);
+ if (!is_x86_feature(x86_featureset, X86FSET_AVX))
+ *ecx &= ~CPUID_INTC_ECX_AVX;
/*
* [no explicit support required beyond x87 fp context]
@@ -2567,22 +2588,24 @@ cpuid_pass4(cpu_t *cpu)
hwcap_flags |= AV_386_SSE2;
if (*ecx & CPUID_INTC_ECX_SSE3)
hwcap_flags |= AV_386_SSE3;
- if (cpi->cpi_vendor == X86_VENDOR_Intel) {
- if (*ecx & CPUID_INTC_ECX_SSSE3)
- hwcap_flags |= AV_386_SSSE3;
- if (*ecx & CPUID_INTC_ECX_SSE4_1)
- hwcap_flags |= AV_386_SSE4_1;
- if (*ecx & CPUID_INTC_ECX_SSE4_2)
- hwcap_flags |= AV_386_SSE4_2;
- if (*ecx & CPUID_INTC_ECX_MOVBE)
- hwcap_flags |= AV_386_MOVBE;
- if (*ecx & CPUID_INTC_ECX_AES)
- hwcap_flags |= AV_386_AES;
- if (*ecx & CPUID_INTC_ECX_PCLMULQDQ)
- hwcap_flags |= AV_386_PCLMULQDQ;
- if ((*ecx & CPUID_INTC_ECX_XSAVE) &&
- (*ecx & CPUID_INTC_ECX_OSXSAVE))
- hwcap_flags |= AV_386_XSAVE;
+ if (*ecx & CPUID_INTC_ECX_SSSE3)
+ hwcap_flags |= AV_386_SSSE3;
+ if (*ecx & CPUID_INTC_ECX_SSE4_1)
+ hwcap_flags |= AV_386_SSE4_1;
+ if (*ecx & CPUID_INTC_ECX_SSE4_2)
+ hwcap_flags |= AV_386_SSE4_2;
+ if (*ecx & CPUID_INTC_ECX_MOVBE)
+ hwcap_flags |= AV_386_MOVBE;
+ if (*ecx & CPUID_INTC_ECX_AES)
+ hwcap_flags |= AV_386_AES;
+ if (*ecx & CPUID_INTC_ECX_PCLMULQDQ)
+ hwcap_flags |= AV_386_PCLMULQDQ;
+ if ((*ecx & CPUID_INTC_ECX_XSAVE) &&
+ (*ecx & CPUID_INTC_ECX_OSXSAVE)) {
+ hwcap_flags |= AV_386_XSAVE;
+
+ if (*ecx & CPUID_INTC_ECX_AVX)
+ hwcap_flags |= AV_386_AVX;
}
if (*ecx & CPUID_INTC_ECX_VMX)
hwcap_flags |= AV_386_VMX;
@@ -3006,6 +3029,20 @@ cpuid_get_procnodes_per_pkg(cpu_t *cpu)
return (cpu->cpu_m.mcpu_cpi->cpi_procnodes_per_pkg);
}
+uint_t
+cpuid_get_compunitid(cpu_t *cpu)
+{
+ ASSERT(cpuid_checkpass(cpu, 1));
+ return (cpu->cpu_m.mcpu_cpi->cpi_compunitid);
+}
+
+uint_t
+cpuid_get_cores_per_compunit(cpu_t *cpu)
+{
+ ASSERT(cpuid_checkpass(cpu, 1));
+ return (cpu->cpu_m.mcpu_cpi->cpi_cores_per_compunit);
+}
+
/*ARGSUSED*/
int
cpuid_have_cr8access(cpu_t *cpu)
@@ -3336,6 +3373,13 @@ cpuid_opteron_erratum(cpu_t *cpu, uint_t erratum)
return (DR_AX(eax) || DR_B0(eax) || DR_B1(eax) || DR_BA(eax) ||
DR_B2(eax) || RB_C0(eax));
+ case 721:
+#if defined(__amd64)
+ return (cpi->cpi_family == 0x10 || cpi->cpi_family == 0x12);
+#else
+ return (0);
+#endif
+
default:
return (-1);
@@ -4135,6 +4179,9 @@ cpuid_set_cpu_properties(void *dip, processorid_t cpu_id,
case X86_VENDOR_Intel:
create = IS_NEW_F6(cpi) || cpi->cpi_family >= 0xf;
break;
+ case X86_VENDOR_AMD:
+ create = cpi->cpi_family >= 0xf;
+ break;
default:
create = 0;
break;
diff --git a/usr/src/uts/i86pc/os/cpupm/pwrnow.c b/usr/src/uts/i86pc/os/cpupm/pwrnow.c
index d403c69e34..0116fe9157 100644
--- a/usr/src/uts/i86pc/os/cpupm/pwrnow.c
+++ b/usr/src/uts/i86pc/os/cpupm/pwrnow.c
@@ -38,6 +38,8 @@ static void pwrnow_fini(cpu_t *);
static void pwrnow_power(cpuset_t, uint32_t);
static void pwrnow_stop(cpu_t *);
+static boolean_t pwrnow_cpb_supported(void);
+
/*
* Interfaces for modules implementing AMD's PowerNow!.
*/
@@ -67,6 +69,7 @@ cpupm_state_ops_t pwrnow_ops = {
#define AMD_CPUID_PSTATE_HARDWARE (1<<7)
#define AMD_CPUID_TSC_CONSTANT (1<<8)
+#define AMD_CPUID_CPB (1<<9)
/*
* Debugging support
@@ -128,6 +131,10 @@ pwrnow_pstate_transition(uint32_t req_state)
ctrl = CPU_ACPI_PSTATE_CTRL(req_pstate);
write_ctrl(handle, ctrl);
+ if (mach_state->ms_turbo != NULL)
+ cpupm_record_turbo_info(mach_state->ms_turbo,
+ mach_state->ms_pstate.cma_state.pstate, req_state);
+
mach_state->ms_pstate.cma_state.pstate = req_state;
cpu_set_curr_clock((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000);
}
@@ -192,6 +199,12 @@ pwrnow_init(cpu_t *cp)
cpupm_alloc_domains(cp, CPUPM_P_STATES);
+ /*
+ * Check for Core Performance Boost support
+ */
+ if (pwrnow_cpb_supported())
+ mach_state->ms_turbo = cpupm_turbo_init(cp);
+
PWRNOW_DEBUG(("Processor %d succeeded.\n", cp->cpu_id))
return (PWRNOW_RET_SUCCESS);
}
@@ -208,6 +221,10 @@ pwrnow_fini(cpu_t *cp)
cpupm_free_domains(&cpupm_pstate_domains);
cpu_acpi_free_pstate_data(handle);
+
+ if (mach_state->ms_turbo != NULL)
+ cpupm_turbo_fini(mach_state->ms_turbo);
+ mach_state->ms_turbo = NULL;
}
boolean_t
@@ -249,6 +266,30 @@ pwrnow_supported()
return (B_TRUE);
}
+static boolean_t
+pwrnow_cpb_supported(void)
+{
+ struct cpuid_regs cpu_regs;
+
+ /* Required features */
+ if (!is_x86_feature(x86_featureset, X86FSET_CPUID) ||
+ !is_x86_feature(x86_featureset, X86FSET_MSR)) {
+ PWRNOW_DEBUG(("No CPUID or MSR support."));
+ return (B_FALSE);
+ }
+
+ /*
+ * Get the Advanced Power Management Information.
+ */
+ cpu_regs.cp_eax = 0x80000007;
+ (void) __cpuid_insn(&cpu_regs);
+
+ if (!(cpu_regs.cp_edx & AMD_CPUID_CPB))
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
static void
pwrnow_stop(cpu_t *cp)
{
@@ -258,4 +299,8 @@ pwrnow_stop(cpu_t *cp)
cpupm_remove_domains(cp, CPUPM_P_STATES, &cpupm_pstate_domains);
cpu_acpi_free_pstate_data(handle);
+
+ if (mach_state->ms_turbo != NULL)
+ cpupm_turbo_fini(mach_state->ms_turbo);
+ mach_state->ms_turbo = NULL;
}
diff --git a/usr/src/uts/i86pc/os/cpupm/speedstep.c b/usr/src/uts/i86pc/os/cpupm/speedstep.c
index 2be4529a83..27df5c022d 100644
--- a/usr/src/uts/i86pc/os/cpupm/speedstep.c
+++ b/usr/src/uts/i86pc/os/cpupm/speedstep.c
@@ -38,33 +38,11 @@
#include <sys/dtrace.h>
#include <sys/sdt.h>
-/*
- * turbo related structure definitions
- */
-typedef struct cpupm_turbo_info {
- kstat_t *turbo_ksp; /* turbo kstat */
- int in_turbo; /* in turbo? */
- int turbo_supported; /* turbo flag */
- uint64_t t_mcnt; /* turbo mcnt */
- uint64_t t_acnt; /* turbo acnt */
-} cpupm_turbo_info_t;
-
-typedef struct turbo_kstat_s {
- struct kstat_named turbo_supported; /* turbo flag */
- struct kstat_named t_mcnt; /* IA32_MPERF_MSR */
- struct kstat_named t_acnt; /* IA32_APERF_MSR */
-} turbo_kstat_t;
-
static int speedstep_init(cpu_t *);
static void speedstep_fini(cpu_t *);
static void speedstep_power(cpuset_t, uint32_t);
static void speedstep_stop(cpu_t *);
-static boolean_t turbo_supported(void);
-static int turbo_kstat_update(kstat_t *, int);
-static void get_turbo_info(cpupm_turbo_info_t *);
-static void reset_turbo_info(void);
-static void record_turbo_info(cpupm_turbo_info_t *, uint32_t, uint32_t);
-static void update_turbo_info(cpupm_turbo_info_t *);
+static boolean_t speedstep_turbo_supported(void);
/*
* Interfaces for modules implementing Intel's Enhanced SpeedStep.
@@ -96,16 +74,6 @@ cpupm_state_ops_t speedstep_ops = {
#define IA32_MISC_ENABLE_CXE (1<<25)
#define CPUID_TURBO_SUPPORT (1 << 1)
-#define CPU_ACPI_P0 0
-#define CPU_IN_TURBO 1
-
-/*
- * MSR for hardware coordination feedback mechanism
- * - IA32_MPERF: increments in proportion to a fixed frequency
- * - IA32_APERF: increments in proportion to actual performance
- */
-#define IA32_MPERF_MSR 0xE7
-#define IA32_APERF_MSR 0xE8
/*
* Debugging support
@@ -117,116 +85,6 @@ volatile int ess_debug = 0;
#define ESSDEBUG(arglist)
#endif
-static kmutex_t turbo_mutex;
-
-turbo_kstat_t turbo_kstat = {
- { "turbo_supported", KSTAT_DATA_UINT32 },
- { "turbo_mcnt", KSTAT_DATA_UINT64 },
- { "turbo_acnt", KSTAT_DATA_UINT64 },
-};
-
-/*
- * kstat update function of the turbo mode info
- */
-static int
-turbo_kstat_update(kstat_t *ksp, int flag)
-{
- cpupm_turbo_info_t *turbo_info = ksp->ks_private;
-
- if (flag == KSTAT_WRITE) {
- return (EACCES);
- }
-
- /*
- * update the count in case CPU is in the turbo
- * mode for a long time
- */
- if (turbo_info->in_turbo == CPU_IN_TURBO)
- update_turbo_info(turbo_info);
-
- turbo_kstat.turbo_supported.value.ui32 =
- turbo_info->turbo_supported;
- turbo_kstat.t_mcnt.value.ui64 = turbo_info->t_mcnt;
- turbo_kstat.t_acnt.value.ui64 = turbo_info->t_acnt;
-
- return (0);
-}
-
-/*
- * Get count of MPERF/APERF MSR
- */
-static void
-get_turbo_info(cpupm_turbo_info_t *turbo_info)
-{
- ulong_t iflag;
- uint64_t mcnt, acnt;
-
- iflag = intr_clear();
- mcnt = rdmsr(IA32_MPERF_MSR);
- acnt = rdmsr(IA32_APERF_MSR);
- turbo_info->t_mcnt += mcnt;
- turbo_info->t_acnt += acnt;
- intr_restore(iflag);
-}
-
-/*
- * Clear MPERF/APERF MSR
- */
-static void
-reset_turbo_info(void)
-{
- ulong_t iflag;
-
- iflag = intr_clear();
- wrmsr(IA32_MPERF_MSR, 0);
- wrmsr(IA32_APERF_MSR, 0);
- intr_restore(iflag);
-}
-
-/*
- * sum up the count of one CPU_ACPI_P0 transition
- */
-static void
-record_turbo_info(cpupm_turbo_info_t *turbo_info,
- uint32_t cur_state, uint32_t req_state)
-{
- if (!turbo_info->turbo_supported)
- return;
- /*
- * enter P0 state
- */
- if (req_state == CPU_ACPI_P0) {
- reset_turbo_info();
- turbo_info->in_turbo = CPU_IN_TURBO;
- }
- /*
- * Leave P0 state
- */
- else if (cur_state == CPU_ACPI_P0) {
- turbo_info->in_turbo = 0;
- get_turbo_info(turbo_info);
- }
-}
-
-/*
- * update the sum of counts and clear MSRs
- */
-static void
-update_turbo_info(cpupm_turbo_info_t *turbo_info)
-{
- ulong_t iflag;
- uint64_t mcnt, acnt;
-
- iflag = intr_clear();
- mcnt = rdmsr(IA32_MPERF_MSR);
- acnt = rdmsr(IA32_APERF_MSR);
- wrmsr(IA32_MPERF_MSR, 0);
- wrmsr(IA32_APERF_MSR, 0);
- turbo_info->t_mcnt += mcnt;
- turbo_info->t_acnt += acnt;
- intr_restore(iflag);
-}
-
/*
* Write the ctrl register. How it is written, depends upon the _PCT
* APCI object value.
@@ -276,8 +134,6 @@ speedstep_pstate_transition(uint32_t req_state)
cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
cpu_acpi_pstate_t *req_pstate;
uint32_t ctrl;
- cpupm_turbo_info_t *turbo_info =
- (cpupm_turbo_info_t *)(mach_state->ms_vendor);
req_pstate = (cpu_acpi_pstate_t *)CPU_ACPI_PSTATES(handle);
req_pstate += req_state;
@@ -290,11 +146,10 @@ speedstep_pstate_transition(uint32_t req_state)
ctrl = CPU_ACPI_PSTATE_CTRL(req_pstate);
write_ctrl(handle, ctrl);
- if (turbo_info)
- record_turbo_info(turbo_info,
+ if (mach_state->ms_turbo != NULL)
+ cpupm_record_turbo_info(mach_state->ms_turbo,
mach_state->ms_pstate.cma_state.pstate, req_state);
-
mach_state->ms_pstate.cma_state.pstate = req_state;
cpu_set_curr_clock(((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000));
}
@@ -330,7 +185,6 @@ speedstep_init(cpu_t *cp)
(cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
cpu_acpi_pct_t *pct_stat;
- cpupm_turbo_info_t *turbo_info;
ESSDEBUG(("speedstep_init: processor %d\n", cp->cpu_id));
@@ -363,34 +217,8 @@ speedstep_init(cpu_t *cp)
cpupm_alloc_domains(cp, CPUPM_P_STATES);
- if (!turbo_supported()) {
- mach_state->ms_vendor = NULL;
- goto ess_ret_success;
- }
- /*
- * turbo mode supported
- */
- turbo_info = mach_state->ms_vendor =
- kmem_zalloc(sizeof (cpupm_turbo_info_t), KM_SLEEP);
- turbo_info->turbo_supported = 1;
- turbo_info->turbo_ksp = kstat_create("turbo", cp->cpu_id,
- "turbo", "misc", KSTAT_TYPE_NAMED,
- sizeof (turbo_kstat) / sizeof (kstat_named_t),
- KSTAT_FLAG_VIRTUAL);
-
- if (turbo_info->turbo_ksp == NULL) {
- cmn_err(CE_NOTE, "kstat_create(turbo) fail");
- } else {
- turbo_info->turbo_ksp->ks_data = &turbo_kstat;
- turbo_info->turbo_ksp->ks_lock = &turbo_mutex;
- turbo_info->turbo_ksp->ks_update = turbo_kstat_update;
- turbo_info->turbo_ksp->ks_data_size += MAXNAMELEN;
- turbo_info->turbo_ksp->ks_private = turbo_info;
-
- kstat_install(turbo_info->turbo_ksp);
- }
-
-ess_ret_success:
+ if (speedstep_turbo_supported())
+ mach_state->ms_turbo = cpupm_turbo_init(cp);
ESSDEBUG(("Processor %d succeeded.\n", cp->cpu_id))
return (ESS_RET_SUCCESS);
@@ -405,17 +233,13 @@ speedstep_fini(cpu_t *cp)
cpupm_mach_state_t *mach_state =
(cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
- cpupm_turbo_info_t *turbo_info =
- (cpupm_turbo_info_t *)(mach_state->ms_vendor);
cpupm_free_domains(&cpupm_pstate_domains);
cpu_acpi_free_pstate_data(handle);
- if (turbo_info) {
- if (turbo_info->turbo_ksp != NULL)
- kstat_delete(turbo_info->turbo_ksp);
- kmem_free(turbo_info, sizeof (cpupm_turbo_info_t));
- }
+ if (mach_state->ms_turbo != NULL)
+ cpupm_turbo_fini(mach_state->ms_turbo);
+ mach_state->ms_turbo = NULL;
}
static void
@@ -424,17 +248,13 @@ speedstep_stop(cpu_t *cp)
cpupm_mach_state_t *mach_state =
(cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
- cpupm_turbo_info_t *turbo_info =
- (cpupm_turbo_info_t *)(mach_state->ms_vendor);
cpupm_remove_domains(cp, CPUPM_P_STATES, &cpupm_pstate_domains);
cpu_acpi_free_pstate_data(handle);
- if (turbo_info) {
- if (turbo_info->turbo_ksp != NULL)
- kstat_delete(turbo_info->turbo_ksp);
- kmem_free(turbo_info, sizeof (cpupm_turbo_info_t));
- }
+ if (mach_state->ms_turbo != NULL)
+ cpupm_turbo_fini(mach_state->ms_turbo);
+ mach_state->ms_turbo = NULL;
}
boolean_t
@@ -470,7 +290,7 @@ speedstep_supported(uint_t family, uint_t model)
}
boolean_t
-turbo_supported(void)
+speedstep_turbo_supported(void)
{
struct cpuid_regs cpu_regs;
diff --git a/usr/src/uts/i86pc/os/cpupm/turbo.c b/usr/src/uts/i86pc/os/cpupm/turbo.c
new file mode 100644
index 0000000000..7844ce2247
--- /dev/null
+++ b/usr/src/uts/i86pc/os/cpupm/turbo.c
@@ -0,0 +1,208 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+/*
+ * Copyright (c) 2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <sys/x86_archext.h>
+#include <sys/machsystm.h>
+#include <sys/archsystm.h>
+#include <sys/x_call.h>
+#include <sys/acpi/acpi.h>
+#include <sys/acpica.h>
+#include <sys/speedstep.h>
+#include <sys/cpu_acpi.h>
+#include <sys/cpupm.h>
+#include <sys/dtrace.h>
+#include <sys/sdt.h>
+
+typedef struct turbo_kstat_s {
+ struct kstat_named turbo_supported; /* turbo flag */
+ struct kstat_named t_mcnt; /* IA32_MPERF_MSR */
+ struct kstat_named t_acnt; /* IA32_APERF_MSR */
+} turbo_kstat_t;
+
+static int turbo_kstat_update(kstat_t *, int);
+static void get_turbo_info(cpupm_mach_turbo_info_t *);
+static void reset_turbo_info(void);
+static void record_turbo_info(cpupm_mach_turbo_info_t *, uint32_t, uint32_t);
+static void update_turbo_info(cpupm_mach_turbo_info_t *);
+
+static kmutex_t turbo_mutex;
+
+turbo_kstat_t turbo_kstat = {
+ { "turbo_supported", KSTAT_DATA_UINT32 },
+ { "turbo_mcnt", KSTAT_DATA_UINT64 },
+ { "turbo_acnt", KSTAT_DATA_UINT64 },
+};
+
+#define CPU_ACPI_P0 0
+#define CPU_IN_TURBO 1
+
+/*
+ * MSR for hardware coordination feedback mechanism
+ * - IA32_MPERF: increments in proportion to a fixed frequency
+ * - IA32_APERF: increments in proportion to actual performance
+ */
+#define IA32_MPERF_MSR 0xE7
+#define IA32_APERF_MSR 0xE8
+
+/*
+ * kstat update function of the turbo mode info
+ */
+static int
+turbo_kstat_update(kstat_t *ksp, int flag)
+{
+ cpupm_mach_turbo_info_t *turbo_info = ksp->ks_private;
+
+ if (flag == KSTAT_WRITE) {
+ return (EACCES);
+ }
+
+ /*
+ * update the count in case CPU is in the turbo
+ * mode for a long time
+ */
+ if (turbo_info->in_turbo == CPU_IN_TURBO)
+ update_turbo_info(turbo_info);
+
+ turbo_kstat.turbo_supported.value.ui32 =
+ turbo_info->turbo_supported;
+ turbo_kstat.t_mcnt.value.ui64 = turbo_info->t_mcnt;
+ turbo_kstat.t_acnt.value.ui64 = turbo_info->t_acnt;
+
+ return (0);
+}
+
+/*
+ * update the sum of counts and clear MSRs
+ */
+static void
+update_turbo_info(cpupm_mach_turbo_info_t *turbo_info)
+{
+ ulong_t iflag;
+ uint64_t mcnt, acnt;
+
+ iflag = intr_clear();
+ mcnt = rdmsr(IA32_MPERF_MSR);
+ acnt = rdmsr(IA32_APERF_MSR);
+ wrmsr(IA32_MPERF_MSR, 0);
+ wrmsr(IA32_APERF_MSR, 0);
+ turbo_info->t_mcnt += mcnt;
+ turbo_info->t_acnt += acnt;
+ intr_restore(iflag);
+}
+
+/*
+ * Get count of MPERF/APERF MSR
+ */
+static void
+get_turbo_info(cpupm_mach_turbo_info_t *turbo_info)
+{
+ ulong_t iflag;
+ uint64_t mcnt, acnt;
+
+ iflag = intr_clear();
+ mcnt = rdmsr(IA32_MPERF_MSR);
+ acnt = rdmsr(IA32_APERF_MSR);
+ turbo_info->t_mcnt += mcnt;
+ turbo_info->t_acnt += acnt;
+ intr_restore(iflag);
+}
+
+/*
+ * Clear MPERF/APERF MSR
+ */
+static void
+reset_turbo_info(void)
+{
+ ulong_t iflag;
+
+ iflag = intr_clear();
+ wrmsr(IA32_MPERF_MSR, 0);
+ wrmsr(IA32_APERF_MSR, 0);
+ intr_restore(iflag);
+}
+
+/*
+ * sum up the count of one CPU_ACPI_P0 transition
+ */
+void
+cpupm_record_turbo_info(cpupm_mach_turbo_info_t *turbo_info,
+ uint32_t cur_state, uint32_t req_state)
+{
+ if (!turbo_info->turbo_supported)
+ return;
+ /*
+ * enter P0 state
+ */
+ if (req_state == CPU_ACPI_P0) {
+ reset_turbo_info();
+ turbo_info->in_turbo = CPU_IN_TURBO;
+ }
+ /*
+ * Leave P0 state
+ */
+ else if (cur_state == CPU_ACPI_P0) {
+ turbo_info->in_turbo = 0;
+ get_turbo_info(turbo_info);
+ }
+}
+
+cpupm_mach_turbo_info_t *
+cpupm_turbo_init(cpu_t *cp)
+{
+ cpupm_mach_turbo_info_t *turbo_info;
+
+ turbo_info = kmem_zalloc(sizeof (cpupm_mach_turbo_info_t), KM_SLEEP);
+
+ turbo_info->turbo_supported = 1;
+ turbo_info->turbo_ksp = kstat_create("turbo", cp->cpu_id,
+ "turbo", "misc", KSTAT_TYPE_NAMED,
+ sizeof (turbo_kstat) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL);
+
+ if (turbo_info->turbo_ksp == NULL) {
+ cmn_err(CE_NOTE, "kstat_create(turbo) fail");
+ } else {
+ turbo_info->turbo_ksp->ks_data = &turbo_kstat;
+ turbo_info->turbo_ksp->ks_lock = &turbo_mutex;
+ turbo_info->turbo_ksp->ks_update = turbo_kstat_update;
+ turbo_info->turbo_ksp->ks_data_size += MAXNAMELEN;
+ turbo_info->turbo_ksp->ks_private = turbo_info;
+
+ kstat_install(turbo_info->turbo_ksp);
+ }
+
+ return (turbo_info);
+}
+
+void
+cpupm_turbo_fini(cpupm_mach_turbo_info_t *turbo_info)
+{
+ if (turbo_info->turbo_ksp != NULL)
+ kstat_delete(turbo_info->turbo_ksp);
+ kmem_free(turbo_info, sizeof (cpupm_mach_turbo_info_t));
+}
diff --git a/usr/src/uts/i86pc/os/microcode.c b/usr/src/uts/i86pc/os/microcode.c
index 90d9585640..c5c6a16790 100644
--- a/usr/src/uts/i86pc/os/microcode.c
+++ b/usr/src/uts/i86pc/os/microcode.c
@@ -33,6 +33,7 @@
#include <sys/kobj.h>
#include <sys/kobj_impl.h>
#include <sys/machsystm.h>
+#include <sys/ontrap.h>
#include <sys/param.h>
#include <sys/machparam.h>
#include <sys/promif.h>
@@ -646,16 +647,17 @@ ucode_match_amd(uint16_t eq_sig, cpu_ucode_info_t *uinfop,
if (ucodefp == NULL || size < sizeof (ucode_header_amd_t))
return (EM_NOMATCH);
+ uh = &ucodefp->uf_header;
+
/*
* Don't even think about loading patches that would require code
- * execution.
+ * execution. Does not apply to patches for family 0x14 and beyond.
*/
- if (size > offsetof(ucode_file_amd_t, uf_code_present) &&
+ if (uh->uh_cpu_rev < 0x5000 &&
+ size > offsetof(ucode_file_amd_t, uf_code_present) &&
ucodefp->uf_code_present)
return (EM_NOMATCH);
- uh = &ucodefp->uf_header;
-
if (eq_sig != uh->uh_cpu_rev)
return (EM_NOMATCH);
@@ -671,7 +673,7 @@ ucode_match_amd(uint16_t eq_sig, cpu_ucode_info_t *uinfop,
return (EM_NOMATCH);
}
- if (uh->uh_patch_id <= uinfop->cui_rev)
+ if (uh->uh_patch_id <= uinfop->cui_rev && !ucode_force_update)
return (EM_HIGHERREV);
return (EM_OK);
@@ -726,6 +728,9 @@ ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
{
ucode_update_t *uusp = (ucode_update_t *)arg1;
cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info;
+#ifndef __xpv
+ on_trap_data_t otd;
+#endif
ASSERT(ucode);
ASSERT(uusp->ucodep);
@@ -743,7 +748,10 @@ ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
return (0);
}
- wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep);
+ if (!on_trap(&otd, OT_DATA_ACCESS))
+ wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep);
+
+ no_trap();
#endif
ucode->read_rev(uinfop);
uusp->new_rev = uinfop->cui_rev;
@@ -758,6 +766,8 @@ ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
ucode_file_amd_t *ucodefp = ufp->amd;
#ifdef __xpv
ucode_update_t uus;
+#else
+ on_trap_data_t otd;
#endif
ASSERT(ucode);
@@ -765,7 +775,13 @@ ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp)
#ifndef __xpv
kpreempt_disable();
+ if (on_trap(&otd, OT_DATA_ACCESS)) {
+ no_trap();
+ kpreempt_enable();
+ return (0);
+ }
wrmsr(ucode->write_msr, (uintptr_t)ucodefp);
+ no_trap();
ucode->read_rev(uinfop);
kpreempt_enable();
@@ -1093,7 +1109,8 @@ ucode_update(uint8_t *ucodep, int size)
kpreempt_enable();
CPUSET_DEL(cpuset, id);
- if (uusp->new_rev != 0 && uusp->info.cui_rev == uusp->new_rev) {
+ if (uusp->new_rev != 0 && uusp->info.cui_rev == uusp->new_rev &&
+ !ucode_force_update) {
rc = EM_HIGHERREV;
} else if ((uusp->new_rev == 0) || (uusp->expected_rev != 0 &&
uusp->expected_rev != uusp->new_rev)) {
diff --git a/usr/src/uts/i86pc/os/mlsetup.c b/usr/src/uts/i86pc/os/mlsetup.c
index 63b858f978..dc79a04af2 100644
--- a/usr/src/uts/i86pc/os/mlsetup.c
+++ b/usr/src/uts/i86pc/os/mlsetup.c
@@ -19,6 +19,8 @@
* CDDL HEADER END
*/
/*
+ * Copyright (c) 2012 Gary Mills
+ *
* Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 by Delphix. All rights reserved.
*/
@@ -110,7 +112,6 @@ mlsetup(struct regs *rp)
extern disp_t cpu0_disp;
extern char t0stack[];
extern int post_fastreboot;
- extern int console;
extern uint64_t plat_dr_options;
ASSERT_STACK_ALIGNED();
@@ -348,7 +349,7 @@ mlsetup(struct regs *rp)
* Explicitly set console to text mode (0x3) if this is a boot
* post Fast Reboot, and the console is set to CONS_SCREEN_TEXT.
*/
- if (post_fastreboot && console == CONS_SCREEN_TEXT)
+ if (post_fastreboot && boot_console_type(NULL) == CONS_SCREEN_TEXT)
set_console_mode(0x3);
/*
diff --git a/usr/src/uts/i86pc/os/mp_machdep.c b/usr/src/uts/i86pc/os/mp_machdep.c
index d7aab080a6..7062df90ff 100644
--- a/usr/src/uts/i86pc/os/mp_machdep.c
+++ b/usr/src/uts/i86pc/os/mp_machdep.c
@@ -245,6 +245,11 @@ pg_plat_hw_shared(cpu_t *cp, pghw_type_t hw)
} else {
return (0);
}
+ case PGHW_FPU:
+ if (cpuid_get_cores_per_compunit(cp) > 1)
+ return (1);
+ else
+ return (0);
case PGHW_PROCNODE:
if (cpuid_get_procnodes_per_pkg(cp) > 1)
return (1);
@@ -306,6 +311,8 @@ pg_plat_hw_instance_id(cpu_t *cpu, pghw_type_t hw)
return (cpuid_get_coreid(cpu));
case PGHW_CACHE:
return (cpuid_get_last_lvl_cacheid(cpu));
+ case PGHW_FPU:
+ return (cpuid_get_compunitid(cpu));
case PGHW_PROCNODE:
return (cpuid_get_procnodeid(cpu));
case PGHW_CHIP:
@@ -331,6 +338,7 @@ pg_plat_hw_rank(pghw_type_t hw1, pghw_type_t hw2)
static pghw_type_t hw_hier[] = {
PGHW_IPIPE,
PGHW_CACHE,
+ PGHW_FPU,
PGHW_PROCNODE,
PGHW_CHIP,
PGHW_POW_IDLE,
@@ -361,8 +369,13 @@ pg_plat_cmt_policy(pghw_type_t hw)
/*
* For shared caches, also load balance across them to
* maximize aggregate cache capacity
+ *
+ * On AMD family 0x15 CPUs, cores come in pairs called
+ * compute units, sharing the FPU and the I$ and L2
+ * caches. Use balancing and cache affinity.
*/
switch (hw) {
+ case PGHW_FPU:
case PGHW_CACHE:
return (CMT_BALANCE|CMT_AFFINITY);
default:
diff --git a/usr/src/uts/i86pc/os/mp_startup.c b/usr/src/uts/i86pc/os/mp_startup.c
index 843c5ae73a..588d38bb7a 100644
--- a/usr/src/uts/i86pc/os/mp_startup.c
+++ b/usr/src/uts/i86pc/os/mp_startup.c
@@ -649,6 +649,10 @@ int opteron_workaround_6323525; /* if non-zero -> at least one cpu has it */
int opteron_erratum_298;
#endif
+#if defined(OPTERON_ERRATUM_721)
+int opteron_erratum_721;
+#endif
+
static void
workaround_warning(cpu_t *cp, uint_t erratum)
{
@@ -1177,6 +1181,16 @@ workaround_errata(struct cpu *cpu)
missing += do_erratum_298(cpu);
+ if (cpuid_opteron_erratum(cpu, 721) > 0) {
+#if defined(OPTERON_ERRATUM_721)
+ wrmsr(MSR_AMD_DE_CFG, rdmsr(MSR_AMD_DE_CFG) | AMD_DE_CFG_E721);
+ opteron_erratum_721++;
+#else
+ workaround_warning(cpu, 721);
+ missing++;
+#endif
+ }
+
#ifdef __xpv
return (0);
#else
@@ -1267,6 +1281,10 @@ workaround_errata_end()
" system\noperation may occur.\n");
}
#endif
+#if defined(OPTERON_ERRATUM_721)
+ if (opteron_erratum_721)
+ workaround_applied(721);
+#endif
}
/*