diff options
author | Yinghai Lu <yinghai@kernel.org> | 2011-12-21 20:45:17 -0500 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2011-12-23 14:01:01 -0500 |
commit | fb209bd891645bb87b9618b724f0b4928e0df3de (patch) | |
tree | 3b59766238dfedbe9f2164048008f4bb2c18a54e | |
parent | a35fd28256e7736cc84af8931a16224f0bfaaf6c (diff) |
x86, x2apic: Fallback to xapic when BIOS doesn't setup interrupt-remapping
On some of the recent Intel SNB platforms, by default bios is pre-enabling
x2apic mode in the cpu with out setting up interrupt-remapping.
This case was resulting in the kernel to panic as the cpu is already in
x2apic mode but the OS was not able to enable interrupt-remapping (which
is a pre-req for using x2apic capability).
On these platforms all the apic-ids are < 255 and the kernel can fallback to
xapic mode if the bios has not enabled interrupt-remapping (which is
mostly the case if the bios has not exported interrupt-remapping tables to the
OS).
Reported-by: Berck E. Nash <flyboy@gmail.com>
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Link: http://lkml.kernel.org/r/20111222014632.600418637@sbsiddha-desk.sc.intel.com
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r-- | arch/x86/include/asm/apic.h | 4 | ||||
-rw-r--r-- | arch/x86/include/asm/apicdef.h | 1 | ||||
-rw-r--r-- | arch/x86/kernel/apic/apic.c | 73 | ||||
-rw-r--r-- | arch/x86/kernel/apic/io_apic.c | 4 |
4 files changed, 64 insertions, 18 deletions
diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h index a0f541a30944..a12d57193fef 100644 --- a/arch/x86/include/asm/apic.h +++ b/arch/x86/include/asm/apic.h | |||
@@ -176,6 +176,7 @@ static inline u64 native_x2apic_icr_read(void) | |||
176 | } | 176 | } |
177 | 177 | ||
178 | extern int x2apic_phys; | 178 | extern int x2apic_phys; |
179 | extern int x2apic_preenabled; | ||
179 | extern void check_x2apic(void); | 180 | extern void check_x2apic(void); |
180 | extern void enable_x2apic(void); | 181 | extern void enable_x2apic(void); |
181 | extern void x2apic_icr_write(u32 low, u32 id); | 182 | extern void x2apic_icr_write(u32 low, u32 id); |
@@ -198,6 +199,9 @@ static inline void x2apic_force_phys(void) | |||
198 | x2apic_phys = 1; | 199 | x2apic_phys = 1; |
199 | } | 200 | } |
200 | #else | 201 | #else |
202 | static inline void disable_x2apic(void) | ||
203 | { | ||
204 | } | ||
201 | static inline void check_x2apic(void) | 205 | static inline void check_x2apic(void) |
202 | { | 206 | { |
203 | } | 207 | } |
diff --git a/arch/x86/include/asm/apicdef.h b/arch/x86/include/asm/apicdef.h index 3925d8007864..134bba00df09 100644 --- a/arch/x86/include/asm/apicdef.h +++ b/arch/x86/include/asm/apicdef.h | |||
@@ -144,6 +144,7 @@ | |||
144 | 144 | ||
145 | #define APIC_BASE (fix_to_virt(FIX_APIC_BASE)) | 145 | #define APIC_BASE (fix_to_virt(FIX_APIC_BASE)) |
146 | #define APIC_BASE_MSR 0x800 | 146 | #define APIC_BASE_MSR 0x800 |
147 | #define XAPIC_ENABLE (1UL << 11) | ||
147 | #define X2APIC_ENABLE (1UL << 10) | 148 | #define X2APIC_ENABLE (1UL << 10) |
148 | 149 | ||
149 | #ifdef CONFIG_X86_32 | 150 | #ifdef CONFIG_X86_32 |
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index 07832363b729..2c07aebbb6f2 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c | |||
@@ -146,7 +146,8 @@ __setup("apicpmtimer", setup_apicpmtimer); | |||
146 | int x2apic_mode; | 146 | int x2apic_mode; |
147 | #ifdef CONFIG_X86_X2APIC | 147 | #ifdef CONFIG_X86_X2APIC |
148 | /* x2apic enabled before OS handover */ | 148 | /* x2apic enabled before OS handover */ |
149 | static int x2apic_preenabled; | 149 | int x2apic_preenabled; |
150 | static int x2apic_disabled; | ||
150 | static __init int setup_nox2apic(char *str) | 151 | static __init int setup_nox2apic(char *str) |
151 | { | 152 | { |
152 | if (x2apic_enabled()) { | 153 | if (x2apic_enabled()) { |
@@ -1432,6 +1433,40 @@ void __init bsp_end_local_APIC_setup(void) | |||
1432 | } | 1433 | } |
1433 | 1434 | ||
1434 | #ifdef CONFIG_X86_X2APIC | 1435 | #ifdef CONFIG_X86_X2APIC |
1436 | /* | ||
1437 | * Need to disable xapic and x2apic at the same time and then enable xapic mode | ||
1438 | */ | ||
1439 | static inline void __disable_x2apic(u64 msr) | ||
1440 | { | ||
1441 | wrmsrl(MSR_IA32_APICBASE, | ||
1442 | msr & ~(X2APIC_ENABLE | XAPIC_ENABLE)); | ||
1443 | wrmsrl(MSR_IA32_APICBASE, msr & ~X2APIC_ENABLE); | ||
1444 | } | ||
1445 | |||
1446 | static void disable_x2apic(void) | ||
1447 | { | ||
1448 | u64 msr; | ||
1449 | |||
1450 | if (!cpu_has_x2apic) | ||
1451 | return; | ||
1452 | |||
1453 | rdmsrl(MSR_IA32_APICBASE, msr); | ||
1454 | if (msr & X2APIC_ENABLE) { | ||
1455 | u32 x2apic_id = read_apic_id(); | ||
1456 | |||
1457 | if (x2apic_id >= 255) | ||
1458 | panic("Cannot disable x2apic, id: %08x\n", x2apic_id); | ||
1459 | |||
1460 | pr_info("Disabling x2apic\n"); | ||
1461 | __disable_x2apic(msr); | ||
1462 | |||
1463 | x2apic_disabled = 1; | ||
1464 | x2apic_mode = 0; | ||
1465 | |||
1466 | register_lapic_address(mp_lapic_addr); | ||
1467 | } | ||
1468 | } | ||
1469 | |||
1435 | void check_x2apic(void) | 1470 | void check_x2apic(void) |
1436 | { | 1471 | { |
1437 | if (x2apic_enabled()) { | 1472 | if (x2apic_enabled()) { |
@@ -1442,15 +1477,20 @@ void check_x2apic(void) | |||
1442 | 1477 | ||
1443 | void enable_x2apic(void) | 1478 | void enable_x2apic(void) |
1444 | { | 1479 | { |
1445 | int msr, msr2; | 1480 | u64 msr; |
1481 | |||
1482 | rdmsrl(MSR_IA32_APICBASE, msr); | ||
1483 | if (x2apic_disabled) { | ||
1484 | __disable_x2apic(msr); | ||
1485 | return; | ||
1486 | } | ||
1446 | 1487 | ||
1447 | if (!x2apic_mode) | 1488 | if (!x2apic_mode) |
1448 | return; | 1489 | return; |
1449 | 1490 | ||
1450 | rdmsr(MSR_IA32_APICBASE, msr, msr2); | ||
1451 | if (!(msr & X2APIC_ENABLE)) { | 1491 | if (!(msr & X2APIC_ENABLE)) { |
1452 | printk_once(KERN_INFO "Enabling x2apic\n"); | 1492 | printk_once(KERN_INFO "Enabling x2apic\n"); |
1453 | wrmsr(MSR_IA32_APICBASE, msr | X2APIC_ENABLE, msr2); | 1493 | wrmsrl(MSR_IA32_APICBASE, msr | X2APIC_ENABLE); |
1454 | } | 1494 | } |
1455 | } | 1495 | } |
1456 | #endif /* CONFIG_X86_X2APIC */ | 1496 | #endif /* CONFIG_X86_X2APIC */ |
@@ -1487,7 +1527,7 @@ void __init enable_IR_x2apic(void) | |||
1487 | ret = save_ioapic_entries(); | 1527 | ret = save_ioapic_entries(); |
1488 | if (ret) { | 1528 | if (ret) { |
1489 | pr_info("Saving IO-APIC state failed: %d\n", ret); | 1529 | pr_info("Saving IO-APIC state failed: %d\n", ret); |
1490 | goto out; | 1530 | return; |
1491 | } | 1531 | } |
1492 | 1532 | ||
1493 | local_irq_save(flags); | 1533 | local_irq_save(flags); |
@@ -1499,13 +1539,19 @@ void __init enable_IR_x2apic(void) | |||
1499 | else | 1539 | else |
1500 | ret = enable_IR(); | 1540 | ret = enable_IR(); |
1501 | 1541 | ||
1542 | if (!x2apic_supported()) | ||
1543 | goto nox2apic; | ||
1544 | |||
1502 | if (ret < 0) { | 1545 | if (ret < 0) { |
1503 | /* IR is required if there is APIC ID > 255 even when running | 1546 | /* IR is required if there is APIC ID > 255 even when running |
1504 | * under KVM | 1547 | * under KVM |
1505 | */ | 1548 | */ |
1506 | if (max_physical_apicid > 255 || | 1549 | if (max_physical_apicid > 255 || |
1507 | !hypervisor_x2apic_available()) | 1550 | !hypervisor_x2apic_available()) { |
1551 | if (x2apic_preenabled) | ||
1552 | disable_x2apic(); | ||
1508 | goto nox2apic; | 1553 | goto nox2apic; |
1554 | } | ||
1509 | /* | 1555 | /* |
1510 | * without IR all CPUs can be addressed by IOAPIC/MSI | 1556 | * without IR all CPUs can be addressed by IOAPIC/MSI |
1511 | * only in physical mode | 1557 | * only in physical mode |
@@ -1513,8 +1559,10 @@ void __init enable_IR_x2apic(void) | |||
1513 | x2apic_force_phys(); | 1559 | x2apic_force_phys(); |
1514 | } | 1560 | } |
1515 | 1561 | ||
1516 | if (ret == IRQ_REMAP_XAPIC_MODE) | 1562 | if (ret == IRQ_REMAP_XAPIC_MODE) { |
1563 | pr_info("x2apic not enabled, IRQ remapping is in xapic mode\n"); | ||
1517 | goto nox2apic; | 1564 | goto nox2apic; |
1565 | } | ||
1518 | 1566 | ||
1519 | x2apic_enabled = 1; | 1567 | x2apic_enabled = 1; |
1520 | 1568 | ||
@@ -1529,17 +1577,6 @@ nox2apic: | |||
1529 | restore_ioapic_entries(); | 1577 | restore_ioapic_entries(); |
1530 | legacy_pic->restore_mask(); | 1578 | legacy_pic->restore_mask(); |
1531 | local_irq_restore(flags); | 1579 | local_irq_restore(flags); |
1532 | |||
1533 | out: | ||
1534 | if (x2apic_enabled || !x2apic_supported()) | ||
1535 | return; | ||
1536 | |||
1537 | if (x2apic_preenabled) | ||
1538 | panic("x2apic: enabled by BIOS but kernel init failed."); | ||
1539 | else if (ret == IRQ_REMAP_XAPIC_MODE) | ||
1540 | pr_info("x2apic not enabled, IRQ remapping is in xapic mode\n"); | ||
1541 | else if (ret < 0) | ||
1542 | pr_info("x2apic not enabled, IRQ remapping init failed\n"); | ||
1543 | } | 1580 | } |
1544 | 1581 | ||
1545 | #ifdef CONFIG_X86_64 | 1582 | #ifdef CONFIG_X86_64 |
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index 6d939d7847e2..45b461fdb344 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c | |||
@@ -2948,6 +2948,10 @@ static inline void __init check_timer(void) | |||
2948 | } | 2948 | } |
2949 | local_irq_disable(); | 2949 | local_irq_disable(); |
2950 | apic_printk(APIC_QUIET, KERN_INFO "..... failed :(.\n"); | 2950 | apic_printk(APIC_QUIET, KERN_INFO "..... failed :(.\n"); |
2951 | if (x2apic_preenabled) | ||
2952 | apic_printk(APIC_QUIET, KERN_INFO | ||
2953 | "Perhaps problem with the pre-enabled x2apic mode\n" | ||
2954 | "Try booting with x2apic and interrupt-remapping disabled in the bios.\n"); | ||
2951 | panic("IO-APIC + timer doesn't work! Boot with apic=debug and send a " | 2955 | panic("IO-APIC + timer doesn't work! Boot with apic=debug and send a " |
2952 | "report. Then try booting with the 'noapic' option.\n"); | 2956 | "report. Then try booting with the 'noapic' option.\n"); |
2953 | out: | 2957 | out: |