|
@@ -64,15 +64,22 @@ static u64 ibs_op_ctl;
|
|
* IBS cpuid feature detection
|
|
* IBS cpuid feature detection
|
|
*/
|
|
*/
|
|
|
|
|
|
-#define IBS_CPUID_FEATURES 0x8000001b
|
|
|
|
|
|
+#define IBS_CPUID_FEATURES 0x8000001b
|
|
|
|
|
|
/*
|
|
/*
|
|
* Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but
|
|
* Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but
|
|
* bit 0 is used to indicate the existence of IBS.
|
|
* bit 0 is used to indicate the existence of IBS.
|
|
*/
|
|
*/
|
|
-#define IBS_CAPS_AVAIL (1LL<<0)
|
|
|
|
-#define IBS_CAPS_RDWROPCNT (1LL<<3)
|
|
|
|
-#define IBS_CAPS_OPCNT (1LL<<4)
|
|
|
|
|
|
+#define IBS_CAPS_AVAIL (1U<<0)
|
|
|
|
+#define IBS_CAPS_RDWROPCNT (1U<<3)
|
|
|
|
+#define IBS_CAPS_OPCNT (1U<<4)
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * IBS APIC setup
|
|
|
|
+ */
|
|
|
|
+#define IBSCTL 0x1cc
|
|
|
|
+#define IBSCTL_LVT_OFFSET_VALID (1ULL<<8)
|
|
|
|
+#define IBSCTL_LVT_OFFSET_MASK 0x0F
|
|
|
|
|
|
/*
|
|
/*
|
|
* IBS randomization macros
|
|
* IBS randomization macros
|
|
@@ -266,6 +273,74 @@ static void op_amd_stop_ibs(void)
|
|
wrmsrl(MSR_AMD64_IBSOPCTL, 0);
|
|
wrmsrl(MSR_AMD64_IBSOPCTL, 0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static inline int eilvt_is_available(int offset)
|
|
|
|
+{
|
|
|
|
+ /* check if we may assign a vector */
|
|
|
|
+ return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline int ibs_eilvt_valid(void)
|
|
|
|
+{
|
|
|
|
+ u64 val;
|
|
|
|
+ int offset;
|
|
|
|
+
|
|
|
|
+ rdmsrl(MSR_AMD64_IBSCTL, val);
|
|
|
|
+ if (!(val & IBSCTL_LVT_OFFSET_VALID)) {
|
|
|
|
+ pr_err(FW_BUG "cpu %d, invalid IBS "
|
|
|
|
+ "interrupt offset %d (MSR%08X=0x%016llx)",
|
|
|
|
+ smp_processor_id(), offset,
|
|
|
|
+ MSR_AMD64_IBSCTL, val);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ offset = val & IBSCTL_LVT_OFFSET_MASK;
|
|
|
|
+
|
|
|
|
+ if (eilvt_is_available(offset))
|
|
|
|
+ return !0;
|
|
|
|
+
|
|
|
|
+ pr_err(FW_BUG "cpu %d, IBS interrupt offset %d "
|
|
|
|
+ "not available (MSR%08X=0x%016llx)",
|
|
|
|
+ smp_processor_id(), offset,
|
|
|
|
+ MSR_AMD64_IBSCTL, val);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline int get_ibs_offset(void)
|
|
|
|
+{
|
|
|
|
+ u64 val;
|
|
|
|
+
|
|
|
|
+ rdmsrl(MSR_AMD64_IBSCTL, val);
|
|
|
|
+ if (!(val & IBSCTL_LVT_OFFSET_VALID))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ return val & IBSCTL_LVT_OFFSET_MASK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void setup_APIC_ibs(void)
|
|
|
|
+{
|
|
|
|
+ int offset;
|
|
|
|
+
|
|
|
|
+ offset = get_ibs_offset();
|
|
|
|
+ if (offset < 0)
|
|
|
|
+ goto failed;
|
|
|
|
+
|
|
|
|
+ if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0))
|
|
|
|
+ return;
|
|
|
|
+failed:
|
|
|
|
+ pr_warn("oprofile: IBS APIC setup failed on cpu #%d\n",
|
|
|
|
+ smp_processor_id());
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void clear_APIC_ibs(void)
|
|
|
|
+{
|
|
|
|
+ int offset;
|
|
|
|
+
|
|
|
|
+ offset = get_ibs_offset();
|
|
|
|
+ if (offset >= 0)
|
|
|
|
+ setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1);
|
|
|
|
+}
|
|
|
|
+
|
|
#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
|
|
#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
|
|
|
|
|
|
static void op_mux_switch_ctrl(struct op_x86_model_spec const *model,
|
|
static void op_mux_switch_ctrl(struct op_x86_model_spec const *model,
|
|
@@ -376,13 +451,13 @@ static void op_amd_setup_ctrs(struct op_x86_model_spec const *model,
|
|
}
|
|
}
|
|
|
|
|
|
if (ibs_caps)
|
|
if (ibs_caps)
|
|
- setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0);
|
|
|
|
|
|
+ setup_APIC_ibs();
|
|
}
|
|
}
|
|
|
|
|
|
static void op_amd_cpu_shutdown(void)
|
|
static void op_amd_cpu_shutdown(void)
|
|
{
|
|
{
|
|
if (ibs_caps)
|
|
if (ibs_caps)
|
|
- setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1);
|
|
|
|
|
|
+ clear_APIC_ibs();
|
|
}
|
|
}
|
|
|
|
|
|
static int op_amd_check_ctrs(struct pt_regs * const regs,
|
|
static int op_amd_check_ctrs(struct pt_regs * const regs,
|
|
@@ -445,16 +520,11 @@ static void op_amd_stop(struct op_msrs const * const msrs)
|
|
op_amd_stop_ibs();
|
|
op_amd_stop_ibs();
|
|
}
|
|
}
|
|
|
|
|
|
-static int __init_ibs_nmi(void)
|
|
|
|
|
|
+static int setup_ibs_ctl(int ibs_eilvt_off)
|
|
{
|
|
{
|
|
-#define IBSCTL_LVTOFFSETVAL (1 << 8)
|
|
|
|
-#define IBSCTL 0x1cc
|
|
|
|
struct pci_dev *cpu_cfg;
|
|
struct pci_dev *cpu_cfg;
|
|
int nodes;
|
|
int nodes;
|
|
u32 value = 0;
|
|
u32 value = 0;
|
|
- u8 ibs_eilvt_off;
|
|
|
|
-
|
|
|
|
- ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1);
|
|
|
|
|
|
|
|
nodes = 0;
|
|
nodes = 0;
|
|
cpu_cfg = NULL;
|
|
cpu_cfg = NULL;
|
|
@@ -466,21 +536,60 @@ static int __init_ibs_nmi(void)
|
|
break;
|
|
break;
|
|
++nodes;
|
|
++nodes;
|
|
pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off
|
|
pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off
|
|
- | IBSCTL_LVTOFFSETVAL);
|
|
|
|
|
|
+ | IBSCTL_LVT_OFFSET_VALID);
|
|
pci_read_config_dword(cpu_cfg, IBSCTL, &value);
|
|
pci_read_config_dword(cpu_cfg, IBSCTL, &value);
|
|
- if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) {
|
|
|
|
|
|
+ if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) {
|
|
pci_dev_put(cpu_cfg);
|
|
pci_dev_put(cpu_cfg);
|
|
printk(KERN_DEBUG "Failed to setup IBS LVT offset, "
|
|
printk(KERN_DEBUG "Failed to setup IBS LVT offset, "
|
|
- "IBSCTL = 0x%08x", value);
|
|
|
|
- return 1;
|
|
|
|
|
|
+ "IBSCTL = 0x%08x\n", value);
|
|
|
|
+ return -EINVAL;
|
|
}
|
|
}
|
|
} while (1);
|
|
} while (1);
|
|
|
|
|
|
if (!nodes) {
|
|
if (!nodes) {
|
|
- printk(KERN_DEBUG "No CPU node configured for IBS");
|
|
|
|
- return 1;
|
|
|
|
|
|
+ printk(KERN_DEBUG "No CPU node configured for IBS\n");
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int force_ibs_eilvt_setup(void)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /* find the next free available EILVT entry */
|
|
|
|
+ for (i = 1; i < 4; i++) {
|
|
|
|
+ if (!eilvt_is_available(i))
|
|
|
|
+ continue;
|
|
|
|
+ ret = setup_ibs_ctl(i);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ printk(KERN_DEBUG "No EILVT entry available\n");
|
|
|
|
+
|
|
|
|
+ return -EBUSY;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int __init_ibs_nmi(void)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (ibs_eilvt_valid())
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ ret = force_ibs_eilvt_setup();
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ if (!ibs_eilvt_valid())
|
|
|
|
+ return -EFAULT;
|
|
|
|
+
|
|
|
|
+ pr_err(FW_BUG "workaround enabled for IBS LVT offset\n");
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|