I know that today nobody is really interested in support of legacy boot options like 'acpi=off', 'noapic', 'nolapic', irq tables like MP table and $PIR, but sometimes booting with these legacy modes can help you to debug BIOS issues. So I've wanted to implement support of these options for my board (Haswell CPU + LynxpointLP PCH).
I've implemented MP table and $PIR and came to fact, that linux can't boot with 'acpi=off nolapic' options combination. Through some investigation of Linux boot process I've found out that the problem can be in wrong PCI IRQ routing. I've enabled debug information in file "arch/x86/pci/irq.c" ("dyndbg='file arch/x86/pci/irq.c +pmf;'") and got this:
[ 0.080004] irq:pcibios_fixup_irqs: pci 0000:00:02.0: ignoring bogus IRQ 139 [ 0.080008] irq:pcibios_fixup_irqs: pci 0000:00:03.0: ignoring bogus IRQ 139 [ 0.080012] irq:pcibios_fixup_irqs: pci 0000:00:16.0: ignoring bogus IRQ 139 [ 0.080017] irq:pcibios_fixup_irqs: pci 0000:00:1b.0: ignoring bogus IRQ 139 [ 0.080021] irq:pcibios_fixup_irqs: pci 0000:00:1c.0: ignoring bogus IRQ 139 [ 0.080025] irq:pcibios_fixup_irqs: pci 0000:00:1c.1: ignoring bogus IRQ 139 [ 0.080029] irq:pcibios_fixup_irqs: pci 0000:00:1c.2: ignoring bogus IRQ 138 [ 0.080033] irq:pcibios_fixup_irqs: pci 0000:00:1c.3: ignoring bogus IRQ 138 [ 0.080037] irq:pcibios_fixup_irqs: pci 0000:00:1c.4: ignoring bogus IRQ 139 [ 0.080041] irq:pcibios_fixup_irqs: pci 0000:00:1c.5: ignoring bogus IRQ 139 [ 0.080046] irq:pcibios_fixup_irqs: pci 0000:00:1d.0: ignoring bogus IRQ 139 [ 0.080050] irq:pcibios_fixup_irqs: pci 0000:00:1f.3: ignoring bogus IRQ 138 [ 0.080055] irq:pcibios_fixup_irqs: pci 0000:05:00.0: ignoring bogus IRQ 139 [ 0.080059] irq:pcibios_fixup_irqs: pci 0000:05:00.1: ignoring bogus IRQ 139 [ 0.080069] irq:pcibios_lookup_irq: pci 0000:00:02.0: PCI INT A -> PIRQ 60, mask dc78, excl 0000 [ 0.080073] irq:pcibios_lookup_irq: pci 0000:00:02.0: PCI INT A -> newirq 0 [ 0.080079] irq:pcibios_lookup_irq: pci 0000:00:02.0: can't route interrupt [ 0.080086] irq:pcibios_lookup_irq: pci 0000:00:03.0: PCI INT A -> PIRQ 60, mask dc78, excl 0000 [ 0.080090] irq:pcibios_lookup_irq: pci 0000:00:03.0: PCI INT A -> newirq 0 [ 0.080095] irq:pcibios_lookup_irq: pci 0000:00:03.0: can't route interrupt [ 0.080111] irq:pcibios_lookup_irq: pci 0000:00:16.0: PCI INT A -> PIRQ 60, mask dc78, excl 0000 [ 0.080115] irq:pcibios_lookup_irq: pci 0000:00:16.0: PCI INT A -> newirq 0 [ 0.080120] irq:pcibios_lookup_irq: pci 0000:00:16.0: can't route interrupt [ 0.080128] irq:pcibios_lookup_irq: pci 0000:00:1b.0: PCI INT A -> PIRQ 6a, mask dc78, excl 0000 [ 0.080131] irq:pcibios_lookup_irq: pci 0000:00:1b.0: PCI INT A -> newirq 0 [ 0.080137] irq:pcibios_lookup_irq: pci 0000:00:1b.0: can't route interrupt [ 0.080144] irq:pcibios_lookup_irq: pci 0000:00:1c.0: PCI INT A -> PIRQ 60, mask dc78, excl 0000 [ 0.080148] irq:pcibios_lookup_irq: pci 0000:00:1c.0: PCI INT A -> newirq 0 [ 0.080154] irq:pcibios_lookup_irq: pci 0000:00:1c.0: can't route interrupt [ 0.080161] irq:pcibios_lookup_irq: pci 0000:00:1c.1: PCI INT B -> PIRQ 61, mask dc78, excl 0000 [ 0.080165] irq:pcibios_lookup_irq: pci 0000:00:1c.1: PCI INT B -> newirq 0 [ 0.080171] irq:pcibios_lookup_irq: pci 0000:00:1c.1: can't route interrupt [ 0.080178] irq:pcibios_lookup_irq: pci 0000:00:1c.2: PCI INT C -> PIRQ 62, mask dc78, excl 0000 [ 0.080182] irq:pcibios_lookup_irq: pci 0000:00:1c.2: PCI INT C -> newirq 0 [ 0.080188] irq:pcibios_lookup_irq: pci 0000:00:1c.2: can't route interrupt [ 0.080195] irq:pcibios_lookup_irq: pci 0000:00:1c.3: PCI INT D -> PIRQ 63, mask dc78, excl 0000 [ 0.080199] irq:pcibios_lookup_irq: pci 0000:00:1c.3: PCI INT D -> newirq 0 [ 0.080204] irq:pcibios_lookup_irq: pci 0000:00:1c.3: can't route interrupt [ 0.080212] irq:pcibios_lookup_irq: pci 0000:00:1c.4: PCI INT A -> PIRQ 60, mask dc78, excl 0000 [ 0.080216] irq:pcibios_lookup_irq: pci 0000:00:1c.4: PCI INT A -> newirq 0 [ 0.080221] irq:pcibios_lookup_irq: pci 0000:00:1c.4: can't route interrupt [ 0.080228] irq:pcibios_lookup_irq: pci 0000:00:1c.5: PCI INT B -> PIRQ 61, mask dc78, excl 0000 [ 0.080232] irq:pcibios_lookup_irq: pci 0000:00:1c.5: PCI INT B -> newirq 0 [ 0.080238] irq:pcibios_lookup_irq: pci 0000:00:1c.5: can't route interrupt [ 0.080245] irq:pcibios_lookup_irq: pci 0000:00:1d.0: PCI INT A -> PIRQ 6b, mask dc78, excl 0000 [ 0.080249] irq:pcibios_lookup_irq: pci 0000:00:1d.0: PCI INT A -> newirq 0 [ 0.080255] irq:pcibios_lookup_irq: pci 0000:00:1d.0: can't route interrupt [ 0.080266] irq:pcibios_lookup_irq: pci 0000:00:1f.3: PCI INT C -> PIRQ 62, mask dc78, excl 0000 [ 0.080270] irq:pcibios_lookup_irq: pci 0000:00:1f.3: PCI INT C -> newirq 0 [ 0.080275] irq:pcibios_lookup_irq: pci 0000:00:1f.3: can't route interrupt [ 0.080320] irq:pcibios_lookup_irq: pci 0000:05:00.0: PCI INT A not found in routing table [ 0.080365] irq:pcibios_lookup_irq: pci 0000:05:00.1: PCI INT B not found in routing table
So the problem is in strings: irq:pcibios_fixup_irqs: pci 0000:00:XX.0: ignoring bogus IRQ 139
139 (=0x8b) is value from my board devicetree.cb:
register "pirqa_routing" = "0x8b" register "pirqb_routing" = "0x8b" register "pirqc_routing" = "0x8a" register "pirqd_routing" = "0x8a" register "pirqe_routing" = "0x85" register "pirqf_routing" = "0x80" register "pirqg_routing" = "0x8b" register "pirqh_routing" = "0x85"
0x8b means IRQ 0xb (=IRQ11) and Interrupt Routing Enable bit = 1.
This value is written to PCH in file "southbridge\intel\lynxpoint\lpc.c"
static void pch_pirq_init(device_t dev) { device_t irq_dev; /* Get the chip configuration */ config_t *config = dev->chip_info;
pci_write_config8(dev, PIRQA_ROUT, config->pirqa_routing); pci_write_config8(dev, PIRQB_ROUT, config->pirqb_routing); pci_write_config8(dev, PIRQC_ROUT, config->pirqc_routing); pci_write_config8(dev, PIRQD_ROUT, config->pirqd_routing);
pci_write_config8(dev, PIRQE_ROUT, config->pirqe_routing); pci_write_config8(dev, PIRQF_ROUT, config->pirqf_routing); pci_write_config8(dev, PIRQG_ROUT, config->pirqg_routing); pci_write_config8(dev, PIRQH_ROUT, config->pirqh_routing);
/* Eric Biederman once said we should let the OS do this. * I am not so sure anymore he was right. */
for(irq_dev = all_devices; irq_dev; irq_dev = irq_dev->next) { u8 int_pin=0, int_line=0;
if (!irq_dev->enabled || irq_dev->path.type != DEVICE_PATH_PCI) continue;
int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN);
switch (int_pin) { case 1: /* INTA# */ int_line = config->pirqa_routing; break; case 2: /* INTB# */ int_line = config->pirqb_routing; break; case 3: /* INTC# */ int_line = config->pirqc_routing; break; case 4: /* INTD# */ int_line = config->pirqd_routing; break; }
if (!int_line) continue;
pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, int_line); } }
It is correct and right to write values like 0x8b to PIRQX_ROUT registers but in PCI_INTERRUPT_LINE registers Linux Kernel seems to expect values <16 (http://elixir.free-electrons.com/linux/v3.13/source/arch/x86/pci/irq.c#L102 6). So maybe the correct way to write these registers is:
pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, int_line & 0xf);
After this change Linux boots correctly with 'acpi=off nolapic' options combination.
PCI specification states: "For x86 based PCs, the values in this register correspond to IRQ numbers (0-15) of the standard dual 8259 configuration. The value 255 is defined as meaning "unknown" or "no connection" to the interrupt controller. Values between 15 and 254 are reserved." (https://www.xilinx.com/Attachment/PCI_SPEV_V3_0.pdf - page 223)
I'm not an expert with IRQs and also I see that many other southbridges in coreboot fill this registers like Lynxpoint with values >=16, so before creating a patch I've wanted to ask you if I'm wrong with this?
Best regards, Aladyshev Konstantin