[PATCH] x86_64: Memorize location of i8259 for reboots.

Currently we attempt to restore virtual wire mode on reboot, which only
works if we can figure out where the i8259 is connected. This is very
useful when we are kexec another kernel and likely helpful to an peculiar
BIOS that make assumptions about how the system is setup.

Since the acpi MADT table does not provide the location where the i8259 is
connected we have to look at the hardware to figure it out.

Most systems have the i8259 connected the local apic of the cpu so won't be
affected but people running Opteron and some serverworks chipsets should be
able to use kexec now.

In addition this patch removes the hard coded assumption that the io_apic
that delivers isa interrups is always known to the kernel as io_apic 0.
There does not appear to be anything to guarantee that assumption is true.

And From: Vivek Goyal <vgoyal@in.ibm.com>

A minor fix to the patch which remembers the location of where i8259 is
connected. Now counter i has been replaced by apic. counter i is having
some junk value which was leading to non-detection of i8259 connected to
IOAPIC.

Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: Vivek Goyal <vgoyal@in.ibm.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Eric W. Biederman and committed by
Linus Torvalds
1008fddc 60923df3

+109 -37
+1 -1
arch/i386/kernel/io_apic.c
··· 1649 1649 for(apic = 0; apic < nr_ioapics; apic++) { 1650 1650 int pin; 1651 1651 /* See if any of the pins is in ExtINT mode */ 1652 - for(pin = 0; pin < nr_ioapic_registers[i]; pin++) { 1652 + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { 1653 1653 struct IO_APIC_route_entry entry; 1654 1654 spin_lock_irqsave(&ioapic_lock, flags); 1655 1655 *(((int *)&entry) + 0) = io_apic_read(apic, 0x10 + 2 * pin);
+108 -36
arch/x86_64/kernel/io_apic.c
··· 47 47 48 48 int disable_timer_pin_1 __initdata; 49 49 50 + /* Where if anywhere is the i8259 connect in external int mode */ 51 + static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; 52 + 50 53 static DEFINE_SPINLOCK(ioapic_lock); 51 54 52 55 /* ··· 364 361 /* 365 362 * Find the pin to which IRQ[irq] (ISA) is connected 366 363 */ 367 - static int find_isa_irq_pin(int irq, int type) 364 + static int __init find_isa_irq_pin(int irq, int type) 368 365 { 369 366 int i; 370 367 ··· 379 376 380 377 return mp_irqs[i].mpc_dstirq; 381 378 } 379 + return -1; 380 + } 381 + 382 + static int __init find_isa_irq_apic(int irq, int type) 383 + { 384 + int i; 385 + 386 + for (i = 0; i < mp_irq_entries; i++) { 387 + int lbus = mp_irqs[i].mpc_srcbus; 388 + 389 + if ((mp_bus_id_to_type[lbus] == MP_BUS_ISA || 390 + mp_bus_id_to_type[lbus] == MP_BUS_EISA || 391 + mp_bus_id_to_type[lbus] == MP_BUS_MCA) && 392 + (mp_irqs[i].mpc_irqtype == type) && 393 + (mp_irqs[i].mpc_srcbusirq == irq)) 394 + break; 395 + } 396 + if (i < mp_irq_entries) { 397 + int apic; 398 + for(apic = 0; apic < nr_ioapics; apic++) { 399 + if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic) 400 + return apic; 401 + } 402 + } 403 + 382 404 return -1; 383 405 } 384 406 ··· 900 872 * Set up the 8259A-master output pin as broadcast to all 901 873 * CPUs. 902 874 */ 903 - static void __init setup_ExtINT_IRQ0_pin(unsigned int pin, int vector) 875 + static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, int vector) 904 876 { 905 877 struct IO_APIC_route_entry entry; 906 878 unsigned long flags; ··· 934 906 * Add it to the IO-APIC irq-routing table: 935 907 */ 936 908 spin_lock_irqsave(&ioapic_lock, flags); 937 - io_apic_write(0, 0x11+2*pin, *(((int *)&entry)+1)); 938 - io_apic_write(0, 0x10+2*pin, *(((int *)&entry)+0)); 909 + io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1)); 910 + io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0)); 939 911 spin_unlock_irqrestore(&ioapic_lock, flags); 940 912 941 913 enable_8259A_irq(0); ··· 1214 1186 static void __init enable_IO_APIC(void) 1215 1187 { 1216 1188 union IO_APIC_reg_01 reg_01; 1217 - int i; 1189 + int i8259_apic, i8259_pin; 1190 + int i, apic; 1218 1191 unsigned long flags; 1219 1192 1220 1193 for (i = 0; i < PIN_MAP_SIZE; i++) { ··· 1229 1200 /* 1230 1201 * The number of IO-APIC IRQ registers (== #pins): 1231 1202 */ 1232 - for (i = 0; i < nr_ioapics; i++) { 1203 + for (apic = 0; apic < nr_ioapics; apic++) { 1233 1204 spin_lock_irqsave(&ioapic_lock, flags); 1234 - reg_01.raw = io_apic_read(i, 1); 1205 + reg_01.raw = io_apic_read(apic, 1); 1235 1206 spin_unlock_irqrestore(&ioapic_lock, flags); 1236 - nr_ioapic_registers[i] = reg_01.bits.entries+1; 1207 + nr_ioapic_registers[apic] = reg_01.bits.entries+1; 1208 + } 1209 + for(apic = 0; apic < nr_ioapics; apic++) { 1210 + int pin; 1211 + /* See if any of the pins is in ExtINT mode */ 1212 + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { 1213 + struct IO_APIC_route_entry entry; 1214 + spin_lock_irqsave(&ioapic_lock, flags); 1215 + *(((int *)&entry) + 0) = io_apic_read(apic, 0x10 + 2 * pin); 1216 + *(((int *)&entry) + 1) = io_apic_read(apic, 0x11 + 2 * pin); 1217 + spin_unlock_irqrestore(&ioapic_lock, flags); 1218 + 1219 + 1220 + /* If the interrupt line is enabled and in ExtInt mode 1221 + * I have found the pin where the i8259 is connected. 1222 + */ 1223 + if ((entry.mask == 0) && (entry.delivery_mode == dest_ExtINT)) { 1224 + ioapic_i8259.apic = apic; 1225 + ioapic_i8259.pin = pin; 1226 + goto found_i8259; 1227 + } 1228 + } 1229 + } 1230 + found_i8259: 1231 + /* Look to see what if the MP table has reported the ExtINT */ 1232 + i8259_pin = find_isa_irq_pin(0, mp_ExtINT); 1233 + i8259_apic = find_isa_irq_apic(0, mp_ExtINT); 1234 + /* Trust the MP table if nothing is setup in the hardware */ 1235 + if ((ioapic_i8259.pin == -1) && (i8259_pin >= 0)) { 1236 + printk(KERN_WARNING "ExtINT not setup in hardware but reported by MP table\n"); 1237 + ioapic_i8259.pin = i8259_pin; 1238 + ioapic_i8259.apic = i8259_apic; 1239 + } 1240 + /* Complain if the MP table and the hardware disagree */ 1241 + if (((ioapic_i8259.apic != i8259_apic) || (ioapic_i8259.pin != i8259_pin)) && 1242 + (i8259_pin >= 0) && (ioapic_i8259.pin >= 0)) 1243 + { 1244 + printk(KERN_WARNING "ExtINT in hardware and MP table differ\n"); 1237 1245 } 1238 1246 1239 1247 /* ··· 1284 1218 */ 1285 1219 void disable_IO_APIC(void) 1286 1220 { 1287 - int pin; 1288 1221 /* 1289 1222 * Clear the IO-APIC before rebooting: 1290 1223 */ ··· 1294 1229 * Put that IOAPIC in virtual wire mode 1295 1230 * so legacy interrupts can be delivered. 1296 1231 */ 1297 - pin = find_isa_irq_pin(0, mp_ExtINT); 1298 - if (pin != -1) { 1232 + if (ioapic_i8259.pin != -1) { 1299 1233 struct IO_APIC_route_entry entry; 1300 1234 unsigned long flags; 1301 1235 ··· 1305 1241 entry.polarity = 0; /* High */ 1306 1242 entry.delivery_status = 0; 1307 1243 entry.dest_mode = 0; /* Physical */ 1308 - entry.delivery_mode = 7; /* ExtInt */ 1244 + entry.delivery_mode = dest_ExtINT; /* ExtInt */ 1309 1245 entry.vector = 0; 1310 1246 entry.dest.physical.physical_dest = 0; 1311 - 1312 1247 1313 1248 /* 1314 1249 * Add it to the IO-APIC irq-routing table: 1315 1250 */ 1316 1251 spin_lock_irqsave(&ioapic_lock, flags); 1317 - io_apic_write(0, 0x11+2*pin, *(((int *)&entry)+1)); 1318 - io_apic_write(0, 0x10+2*pin, *(((int *)&entry)+0)); 1252 + io_apic_write(ioapic_i8259.apic, 0x11+2*ioapic_i8259.pin, 1253 + *(((int *)&entry)+1)); 1254 + io_apic_write(ioapic_i8259.apic, 0x10+2*ioapic_i8259.pin, 1255 + *(((int *)&entry)+0)); 1319 1256 spin_unlock_irqrestore(&ioapic_lock, flags); 1320 1257 } 1321 1258 1322 - disconnect_bsp_APIC(pin != -1); 1259 + disconnect_bsp_APIC(ioapic_i8259.pin != -1); 1323 1260 } 1324 1261 1325 1262 /* ··· 1689 1624 */ 1690 1625 static inline void unlock_ExtINT_logic(void) 1691 1626 { 1692 - int pin, i; 1627 + int apic, pin, i; 1693 1628 struct IO_APIC_route_entry entry0, entry1; 1694 1629 unsigned char save_control, save_freq_select; 1695 1630 unsigned long flags; 1696 1631 1697 - pin = find_isa_irq_pin(8, mp_INT); 1632 + pin = find_isa_irq_pin(8, mp_INT); 1633 + apic = find_isa_irq_apic(8, mp_INT); 1698 1634 if (pin == -1) 1699 1635 return; 1700 1636 1701 1637 spin_lock_irqsave(&ioapic_lock, flags); 1702 - *(((int *)&entry0) + 1) = io_apic_read(0, 0x11 + 2 * pin); 1703 - *(((int *)&entry0) + 0) = io_apic_read(0, 0x10 + 2 * pin); 1638 + *(((int *)&entry0) + 1) = io_apic_read(apic, 0x11 + 2 * pin); 1639 + *(((int *)&entry0) + 0) = io_apic_read(apic, 0x10 + 2 * pin); 1704 1640 spin_unlock_irqrestore(&ioapic_lock, flags); 1705 - clear_IO_APIC_pin(0, pin); 1641 + clear_IO_APIC_pin(apic, pin); 1706 1642 1707 1643 memset(&entry1, 0, sizeof(entry1)); 1708 1644 ··· 1716 1650 entry1.vector = 0; 1717 1651 1718 1652 spin_lock_irqsave(&ioapic_lock, flags); 1719 - io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry1) + 1)); 1720 - io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry1) + 0)); 1653 + io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry1) + 1)); 1654 + io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry1) + 0)); 1721 1655 spin_unlock_irqrestore(&ioapic_lock, flags); 1722 1656 1723 1657 save_control = CMOS_READ(RTC_CONTROL); ··· 1735 1669 1736 1670 CMOS_WRITE(save_control, RTC_CONTROL); 1737 1671 CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); 1738 - clear_IO_APIC_pin(0, pin); 1672 + clear_IO_APIC_pin(apic, pin); 1739 1673 1740 1674 spin_lock_irqsave(&ioapic_lock, flags); 1741 - io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry0) + 1)); 1742 - io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry0) + 0)); 1675 + io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry0) + 1)); 1676 + io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry0) + 0)); 1743 1677 spin_unlock_irqrestore(&ioapic_lock, flags); 1744 1678 } 1745 1679 ··· 1751 1685 */ 1752 1686 static inline void check_timer(void) 1753 1687 { 1754 - int pin1, pin2; 1688 + int apic1, pin1, apic2, pin2; 1755 1689 int vector; 1756 1690 1757 1691 /* ··· 1772 1706 init_8259A(1); 1773 1707 enable_8259A_irq(0); 1774 1708 1775 - pin1 = find_isa_irq_pin(0, mp_INT); 1776 - pin2 = find_isa_irq_pin(0, mp_ExtINT); 1709 + pin1 = find_isa_irq_pin(0, mp_INT); 1710 + apic1 = find_isa_irq_apic(0, mp_INT); 1711 + pin2 = ioapic_i8259.pin; 1712 + apic2 = ioapic_i8259.apic; 1777 1713 1778 - apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X pin1=%d pin2=%d\n", vector, pin1, pin2); 1714 + apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", 1715 + vector, apic1, pin1, apic2, pin2); 1779 1716 1780 1717 if (pin1 != -1) { 1781 1718 /* ··· 1796 1727 clear_IO_APIC_pin(0, pin1); 1797 1728 return; 1798 1729 } 1799 - clear_IO_APIC_pin(0, pin1); 1800 - apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n"); 1730 + clear_IO_APIC_pin(apic1, pin1); 1731 + apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not " 1732 + "connected to IO-APIC\n"); 1801 1733 } 1802 1734 1803 - apic_printk(APIC_VERBOSE,KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... "); 1735 + apic_printk(APIC_VERBOSE,KERN_INFO "...trying to set up timer (IRQ0) " 1736 + "through the 8259A ... "); 1804 1737 if (pin2 != -1) { 1805 - apic_printk(APIC_VERBOSE,"\n..... (found pin %d) ...", pin2); 1738 + apic_printk(APIC_VERBOSE,"\n..... (found apic %d pin %d) ...", 1739 + apic2, pin2); 1806 1740 /* 1807 1741 * legacy devices should be connected to IO APIC #0 1808 1742 */ 1809 - setup_ExtINT_IRQ0_pin(pin2, vector); 1743 + setup_ExtINT_IRQ0_pin(apic2, pin2, vector); 1810 1744 if (timer_irq_works()) { 1811 1745 printk("works.\n"); 1812 1746 nmi_watchdog_default(); ··· 1821 1749 /* 1822 1750 * Cleanup, just in case ... 1823 1751 */ 1824 - clear_IO_APIC_pin(0, pin2); 1752 + clear_IO_APIC_pin(apic2, pin2); 1825 1753 } 1826 1754 printk(" failed.\n"); 1827 1755