aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/apic/apic.c
diff options
context:
space:
mode:
authorRobert Richter <robert.richter@amd.com>2010-10-06 06:27:53 -0400
committerIngo Molnar <mingo@elte.hu>2010-10-19 22:42:13 -0400
commita68c439b1966c91f0ef474e2bf275d6792312726 (patch)
tree9a49f060ee7d20a23fa8d3990e7f733aef60b15c /arch/x86/kernel/apic/apic.c
parent14d4962dc863ab42e898d66d4837aa6c3afedc3b (diff)
apic, x86: Check if EILVT APIC registers are available (AMD only)
This patch implements checks for the availability of LVT entries (APIC500-530) and reserves it if used. The check becomes necessary since we want to let the BIOS provide the LVT offsets. The offsets should be determined by the subsystems using it like those for MCE threshold or IBS. On K8 only offset 0 (APIC500) and MCE interrupts are supported. Beginning with family 10h at least 4 offsets are available. Since offsets must be consistent for all cores, we keep track of the LVT offsets in software and reserve the offset for the same vector also to be used on other cores. An offset is freed by setting the entry to APIC_EILVT_MASKED. If the BIOS is right, there should be no conflicts. Otherwise a "[Firmware Bug]: ..." error message is generated. However, if software does not properly determines the offsets, it is not necessarily a BIOS bug. Signed-off-by: Robert Richter <robert.richter@amd.com> LKML-Reference: <1286360874-1471-2-git-send-email-robert.richter@amd.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/apic/apic.c')
-rw-r--r--arch/x86/kernel/apic/apic.c83
1 files changed, 74 insertions, 9 deletions
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 8cf86fb3b4e3..2bfeafd24f5c 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -52,6 +52,7 @@
52#include <asm/mce.h> 52#include <asm/mce.h>
53#include <asm/kvm_para.h> 53#include <asm/kvm_para.h>
54#include <asm/tsc.h> 54#include <asm/tsc.h>
55#include <asm/atomic.h>
55 56
56unsigned int num_processors; 57unsigned int num_processors;
57 58
@@ -370,24 +371,88 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
370} 371}
371 372
372/* 373/*
373 * Setup extended LVT, AMD specific (K8, family 10h) 374 * Setup extended LVT, AMD specific
374 * 375 *
375 * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and 376 * Software should use the LVT offsets the BIOS provides. The offsets
376 * MCE interrupts are supported. Thus MCE offset must be set to 0. 377 * are determined by the subsystems using it like those for MCE
378 * threshold or IBS. On K8 only offset 0 (APIC500) and MCE interrupts
379 * are supported. Beginning with family 10h at least 4 offsets are
380 * available.
377 * 381 *
378 * If mask=1, the LVT entry does not generate interrupts while mask=0 382 * Since the offsets must be consistent for all cores, we keep track
379 * enables the vector. See also the BKDGs. 383 * of the LVT offsets in software and reserve the offset for the same
384 * vector also to be used on other cores. An offset is freed by
385 * setting the entry to APIC_EILVT_MASKED.
386 *
387 * If the BIOS is right, there should be no conflicts. Otherwise a
388 * "[Firmware Bug]: ..." error message is generated. However, if
389 * software does not properly determines the offsets, it is not
390 * necessarily a BIOS bug.
380 */ 391 */
381 392
382#define APIC_EILVT_LVTOFF_MCE 0 393#define APIC_EILVT_LVTOFF_MCE 0
383#define APIC_EILVT_LVTOFF_IBS 1 394#define APIC_EILVT_LVTOFF_IBS 1
384 395
385static void setup_APIC_eilvt(u8 lvt_off, u8 vector, u8 msg_type, u8 mask) 396static atomic_t eilvt_offsets[APIC_EILVT_NR_MAX];
397
398static inline int eilvt_entry_is_changeable(unsigned int old, unsigned int new)
399{
400 return (old & APIC_EILVT_MASKED)
401 || (new == APIC_EILVT_MASKED)
402 || ((new & ~APIC_EILVT_MASKED) == old);
403}
404
405static unsigned int reserve_eilvt_offset(int offset, unsigned int new)
406{
407 unsigned int rsvd; /* 0: uninitialized */
408
409 if (offset >= APIC_EILVT_NR_MAX)
410 return ~0;
411
412 rsvd = atomic_read(&eilvt_offsets[offset]) & ~APIC_EILVT_MASKED;
413 do {
414 if (rsvd &&
415 !eilvt_entry_is_changeable(rsvd, new))
416 /* may not change if vectors are different */
417 return rsvd;
418 rsvd = atomic_cmpxchg(&eilvt_offsets[offset], rsvd, new);
419 } while (rsvd != new);
420
421 return new;
422}
423
424/*
425 * If mask=1, the LVT entry does not generate interrupts while mask=0
426 * enables the vector. See also the BKDGs.
427 */
428
429static int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask)
386{ 430{
387 unsigned long reg = (lvt_off << 4) + APIC_EILVTn(0); 431 unsigned long reg = APIC_EILVTn(offset);
388 unsigned int v = (mask << 16) | (msg_type << 8) | vector; 432 unsigned int new, old, reserved;
433
434 new = (mask << 16) | (msg_type << 8) | vector;
435 old = apic_read(reg);
436 reserved = reserve_eilvt_offset(offset, new);
389 437
390 apic_write(reg, v); 438 if (reserved != new) {
439 pr_err(FW_BUG "cpu %d, try to setup vector 0x%x, but "
440 "vector 0x%x was already reserved by another core, "
441 "APIC%lX=0x%x\n",
442 smp_processor_id(), new, reserved, reg, old);
443 return -EINVAL;
444 }
445
446 if (!eilvt_entry_is_changeable(old, new)) {
447 pr_err(FW_BUG "cpu %d, try to setup vector 0x%x but "
448 "register already in use, APIC%lX=0x%x\n",
449 smp_processor_id(), new, reg, old);
450 return -EBUSY;
451 }
452
453 apic_write(reg, new);
454
455 return 0;
391} 456}
392 457
393u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask) 458u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask)