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