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
Could someone familiar with the AGESA codebase please take over or rework and resubmit what I attempted to do in https://review.coreboot.org/22843? There are two problems with my version:
A) Microcode blob should go in 3rdparty/blobs and CBFS- completely agree and understand the reasoning. I found some code in AGESA that appeared to search for microcode_amd_fam15h in CBFS but when I added the module manually it still didn't seem to find it. Attempted to debug but my coding-fu was too weak. If I spent more time on it I could probably figure it out, but:
B) Real name policy- I didn't realize these were required but see now it's called out in https://www.coreboot.org/Development_Guidelines#How_to_contribute. It's my fault for missing that, but I'm unwilling to compromise my anonymity after seeing what they did to Appelbaum. Not looking to start a debate either way. I plan to continue contributing to Coreboot through email.
The key part of my proposed commit is the addition of those two lines to F15TnEquivalenceTable.c. This permits AGESA's logic to match CPU models when searching for applicable microcode updates. From there, there should be a way to fix the code so microcode_amd_fam15h is properly added and searched in CBFS, which would remove the need to update F15TnMicrocodePatch0600110F_Enc.c.
On a related note, my next commit was going to be to update 3rdparty/blobs/.../microcode_amd_fam15h.bin. Looks like the microcode in there for the 6020 processors is outdated (07/23/2014 vs. Debian distro's 01/25/2016). The updated microcode addresses a Piledriver virtualization vulnerability ( https://www.theregister.co.uk/2016/03/06/amd_microcode_6000836_fix/?page=2).
Please let me know here or via direct email if you have any questions!