aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Richter <robert.richter@amd.com>2010-10-06 06:27:54 -0400
committerIngo Molnar <mingo@elte.hu>2010-10-19 22:42:13 -0400
commit27afdf2008da0b8878a73e32e4eb12381b84e224 (patch)
tree84aaf8c200f597553a2874b30d9589718d937878
parenta68c439b1966c91f0ef474e2bf275d6792312726 (diff)
apic, x86: Use BIOS settings for IBS and MCE threshold interrupt LVT offsets
We want the BIOS to setup the EILVT APIC registers. The offsets were hardcoded and BIOS settings were overwritten by the OS. Now, the subsystems for MCE threshold and IBS determine the LVT offset from the registers the BIOS has setup. If the BIOS setup is buggy on a family 10h system, a workaround enables IBS. If the OS determines an invalid register setup, a "[Firmware Bug]: " error message is reported. We need this change also for upcomming cpu families. Signed-off-by: Robert Richter <robert.richter@amd.com> LKML-Reference: <1286360874-1471-3-git-send-email-robert.richter@amd.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--arch/x86/include/asm/apic.h4
-rw-r--r--arch/x86/kernel/apic/apic.c19
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce_amd.c27
-rw-r--r--arch/x86/oprofile/op_model_amd.c145
4 files changed, 154 insertions, 41 deletions
diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index 1fa03e04ae44..286de34b0ed6 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -252,9 +252,7 @@ static inline int apic_is_clustered_box(void)
252} 252}
253#endif 253#endif
254 254
255extern u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask); 255extern int setup_APIC_eilvt(u8 lvt_off, u8 vector, u8 msg_type, u8 mask);
256extern u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask);
257
258 256
259#else /* !CONFIG_X86_LOCAL_APIC */ 257#else /* !CONFIG_X86_LOCAL_APIC */
260static inline void lapic_shutdown(void) { } 258static inline void lapic_shutdown(void) { }
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 2bfeafd24f5c..850657d1b0ed 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -390,9 +390,6 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
390 * necessarily a BIOS bug. 390 * necessarily a BIOS bug.
391 */ 391 */
392 392
393#define APIC_EILVT_LVTOFF_MCE 0
394#define APIC_EILVT_LVTOFF_IBS 1
395
396static atomic_t eilvt_offsets[APIC_EILVT_NR_MAX]; 393static atomic_t eilvt_offsets[APIC_EILVT_NR_MAX];
397 394
398static inline int eilvt_entry_is_changeable(unsigned int old, unsigned int new) 395static inline int eilvt_entry_is_changeable(unsigned int old, unsigned int new)
@@ -426,7 +423,7 @@ static unsigned int reserve_eilvt_offset(int offset, unsigned int new)
426 * enables the vector. See also the BKDGs. 423 * enables the vector. See also the BKDGs.
427 */ 424 */
428 425
429static int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask) 426int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask)
430{ 427{
431 unsigned long reg = APIC_EILVTn(offset); 428 unsigned long reg = APIC_EILVTn(offset);
432 unsigned int new, old, reserved; 429 unsigned int new, old, reserved;
@@ -454,19 +451,7 @@ static int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask)
454 451
455 return 0; 452 return 0;
456} 453}
457 454EXPORT_SYMBOL_GPL(setup_APIC_eilvt);
458u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask)
459{
460 setup_APIC_eilvt(APIC_EILVT_LVTOFF_MCE, vector, msg_type, mask);
461 return APIC_EILVT_LVTOFF_MCE;
462}
463
464u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask)
465{
466 setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask);
467 return APIC_EILVT_LVTOFF_IBS;
468}
469EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs);
470 455
471/* 456/*
472 * Program the next event, relative to now 457 * Program the next event, relative to now
diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c
index 39aaee5c1ab2..80c482382d5c 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_amd.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c
@@ -131,7 +131,8 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
131 u32 low = 0, high = 0, address = 0; 131 u32 low = 0, high = 0, address = 0;
132 unsigned int bank, block; 132 unsigned int bank, block;
133 struct thresh_restart tr; 133 struct thresh_restart tr;
134 u8 lvt_off; 134 int lvt_off = -1;
135 u8 offset;
135 136
136 for (bank = 0; bank < NR_BANKS; ++bank) { 137 for (bank = 0; bank < NR_BANKS; ++bank) {
137 for (block = 0; block < NR_BLOCKS; ++block) { 138 for (block = 0; block < NR_BLOCKS; ++block) {
@@ -162,8 +163,28 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
162 if (shared_bank[bank] && c->cpu_core_id) 163 if (shared_bank[bank] && c->cpu_core_id)
163 break; 164 break;
164#endif 165#endif
165 lvt_off = setup_APIC_eilvt_mce(THRESHOLD_APIC_VECTOR, 166 offset = (high & MASK_LVTOFF_HI) >> 20;
166 APIC_EILVT_MSG_FIX, 0); 167 if (lvt_off < 0) {
168 if (setup_APIC_eilvt(offset,
169 THRESHOLD_APIC_VECTOR,
170 APIC_EILVT_MSG_FIX, 0)) {
171 pr_err(FW_BUG "cpu %d, failed to "
172 "setup threshold interrupt "
173 "for bank %d, block %d "
174 "(MSR%08X=0x%x%08x)",
175 smp_processor_id(), bank, block,
176 address, high, low);
177 continue;
178 }
179 lvt_off = offset;
180 } else if (lvt_off != offset) {
181 pr_err(FW_BUG "cpu %d, invalid threshold "
182 "interrupt offset %d for bank %d,"
183 "block %d (MSR%08X=0x%x%08x)",
184 smp_processor_id(), lvt_off, bank,
185 block, address, high, low);
186 continue;
187 }
167 188
168 high &= ~MASK_LVTOFF_HI; 189 high &= ~MASK_LVTOFF_HI;
169 high |= lvt_off << 20; 190 high |= lvt_off << 20;
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c
index b67a6b5aa8d4..42fb46f83883 100644
--- a/arch/x86/oprofile/op_model_amd.c
+++ b/arch/x86/oprofile/op_model_amd.c
@@ -64,15 +64,22 @@ static u64 ibs_op_ctl;
64 * IBS cpuid feature detection 64 * IBS cpuid feature detection
65 */ 65 */
66 66
67#define IBS_CPUID_FEATURES 0x8000001b 67#define IBS_CPUID_FEATURES 0x8000001b
68 68
69/* 69/*
70 * Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but 70 * Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but
71 * bit 0 is used to indicate the existence of IBS. 71 * bit 0 is used to indicate the existence of IBS.
72 */ 72 */
73#define IBS_CAPS_AVAIL (1LL<<0) 73#define IBS_CAPS_AVAIL (1U<<0)
74#define IBS_CAPS_RDWROPCNT (1LL<<3) 74#define IBS_CAPS_RDWROPCNT (1U<<3)
75#define IBS_CAPS_OPCNT (1LL<<4) 75#define IBS_CAPS_OPCNT (1U<<4)
76
77/*
78 * IBS APIC setup
79 */
80#define IBSCTL 0x1cc
81#define IBSCTL_LVT_OFFSET_VALID (1ULL<<8)
82#define IBSCTL_LVT_OFFSET_MASK 0x0F
76 83
77/* 84/*
78 * IBS randomization macros 85 * IBS randomization macros
@@ -266,6 +273,74 @@ static void op_amd_stop_ibs(void)
266 wrmsrl(MSR_AMD64_IBSOPCTL, 0); 273 wrmsrl(MSR_AMD64_IBSOPCTL, 0);
267} 274}
268 275
276static inline int eilvt_is_available(int offset)
277{
278 /* check if we may assign a vector */
279 return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1);
280}
281
282static inline int ibs_eilvt_valid(void)
283{
284 u64 val;
285 int offset;
286
287 rdmsrl(MSR_AMD64_IBSCTL, val);
288 if (!(val & IBSCTL_LVT_OFFSET_VALID)) {
289 pr_err(FW_BUG "cpu %d, invalid IBS "
290 "interrupt offset %d (MSR%08X=0x%016llx)",
291 smp_processor_id(), offset,
292 MSR_AMD64_IBSCTL, val);
293 return 0;
294 }
295
296 offset = val & IBSCTL_LVT_OFFSET_MASK;
297
298 if (eilvt_is_available(offset))
299 return !0;
300
301 pr_err(FW_BUG "cpu %d, IBS interrupt offset %d "
302 "not available (MSR%08X=0x%016llx)",
303 smp_processor_id(), offset,
304 MSR_AMD64_IBSCTL, val);
305
306 return 0;
307}
308
309static inline int get_ibs_offset(void)
310{
311 u64 val;
312
313 rdmsrl(MSR_AMD64_IBSCTL, val);
314 if (!(val & IBSCTL_LVT_OFFSET_VALID))
315 return -EINVAL;
316
317 return val & IBSCTL_LVT_OFFSET_MASK;
318}
319
320static void setup_APIC_ibs(void)
321{
322 int offset;
323
324 offset = get_ibs_offset();
325 if (offset < 0)
326 goto failed;
327
328 if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0))
329 return;
330failed:
331 pr_warn("oprofile: IBS APIC setup failed on cpu #%d\n",
332 smp_processor_id());
333}
334
335static void clear_APIC_ibs(void)
336{
337 int offset;
338
339 offset = get_ibs_offset();
340 if (offset >= 0)
341 setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1);
342}
343
269#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX 344#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
270 345
271static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, 346static 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,
376 } 451 }
377 452
378 if (ibs_caps) 453 if (ibs_caps)
379 setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0); 454 setup_APIC_ibs();
380} 455}
381 456
382static void op_amd_cpu_shutdown(void) 457static void op_amd_cpu_shutdown(void)
383{ 458{
384 if (ibs_caps) 459 if (ibs_caps)
385 setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); 460 clear_APIC_ibs();
386} 461}
387 462
388static int op_amd_check_ctrs(struct pt_regs * const regs, 463static 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)
445 op_amd_stop_ibs(); 520 op_amd_stop_ibs();
446} 521}
447 522
448static int __init_ibs_nmi(void) 523static int setup_ibs_ctl(int ibs_eilvt_off)
449{ 524{
450#define IBSCTL_LVTOFFSETVAL (1 << 8)
451#define IBSCTL 0x1cc
452 struct pci_dev *cpu_cfg; 525 struct pci_dev *cpu_cfg;
453 int nodes; 526 int nodes;
454 u32 value = 0; 527 u32 value = 0;
455 u8 ibs_eilvt_off;
456
457 ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1);
458 528
459 nodes = 0; 529 nodes = 0;
460 cpu_cfg = NULL; 530 cpu_cfg = NULL;
@@ -466,21 +536,60 @@ static int __init_ibs_nmi(void)
466 break; 536 break;
467 ++nodes; 537 ++nodes;
468 pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off 538 pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off
469 | IBSCTL_LVTOFFSETVAL); 539 | IBSCTL_LVT_OFFSET_VALID);
470 pci_read_config_dword(cpu_cfg, IBSCTL, &value); 540 pci_read_config_dword(cpu_cfg, IBSCTL, &value);
471 if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) { 541 if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) {
472 pci_dev_put(cpu_cfg); 542 pci_dev_put(cpu_cfg);
473 printk(KERN_DEBUG "Failed to setup IBS LVT offset, " 543 printk(KERN_DEBUG "Failed to setup IBS LVT offset, "
474 "IBSCTL = 0x%08x", value); 544 "IBSCTL = 0x%08x\n", value);
475 return 1; 545 return -EINVAL;
476 } 546 }
477 } while (1); 547 } while (1);
478 548
479 if (!nodes) { 549 if (!nodes) {
480 printk(KERN_DEBUG "No CPU node configured for IBS"); 550 printk(KERN_DEBUG "No CPU node configured for IBS\n");
481 return 1; 551 return -ENODEV;
552 }
553
554 return 0;
555}
556
557static int force_ibs_eilvt_setup(void)
558{
559 int i;
560 int ret;
561
562 /* find the next free available EILVT entry */
563 for (i = 1; i < 4; i++) {
564 if (!eilvt_is_available(i))
565 continue;
566 ret = setup_ibs_ctl(i);
567 if (ret)
568 return ret;
569 return 0;
482 } 570 }
483 571
572 printk(KERN_DEBUG "No EILVT entry available\n");
573
574 return -EBUSY;
575}
576
577static int __init_ibs_nmi(void)
578{
579 int ret;
580
581 if (ibs_eilvt_valid())
582 return 0;
583
584 ret = force_ibs_eilvt_setup();
585 if (ret)
586 return ret;
587
588 if (!ibs_eilvt_valid())
589 return -EFAULT;
590
591 pr_err(FW_BUG "workaround enabled for IBS LVT offset\n");
592
484 return 0; 593 return 0;
485} 594}
486 595