aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--hwsetup.c4
-rw-r--r--include/bios.h1
-rw-r--r--include/pci.h33
-rw-r--r--main.c1
-rw-r--r--pci.c141
6 files changed, 176 insertions, 5 deletions
diff --git a/Makefile b/Makefile
index 968c4c3..64da22a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,6 @@
obj-y = code16.o entry.o main.o string.o printf.o cstart.o fw_cfg.o
obj-y += linuxboot.o malloc.o pflash.o cbfs.o tables.o hwsetup.o
+obj-y += pci.o
all-y = bios.bin
all: $(all-y)
diff --git a/hwsetup.c b/hwsetup.c
index 77c6dec..6c3aef9 100644
--- a/hwsetup.c
+++ b/hwsetup.c
@@ -56,10 +56,6 @@ static void setup_ich9_pm(void)
pci_config_writeb(bdf, ICH9_LPC_ACPI_CTRL, 0x80);
}
-#define PCI_VENDOR_ID_INTEL 0x8086
-#define PCI_DEVICE_ID_INTEL_82441 0x1237
-#define PCI_DEVICE_ID_INTEL_Q35_MCH 0x29c0
-
#define I440FX_PAM0 0x59
#define Q35_HOST_BRIDGE_PAM0 0x90
diff --git a/include/bios.h b/include/bios.h
index ff2064b..eddca6b 100644
--- a/include/bios.h
+++ b/include/bios.h
@@ -38,6 +38,7 @@ extern void bios_irq(void);
extern void bios_int10(void);
extern void bios_int15(void);
+extern void setup_pci(void);
extern void setup_hw(void);
extern void extract_acpi(void);
extern void boot_from_fwcfg(void);
diff --git a/include/pci.h b/include/pci.h
index c12a784..6270ac4 100644
--- a/include/pci.h
+++ b/include/pci.h
@@ -39,6 +39,37 @@ static inline uint8_t pci_config_readb(uint16_t bdf, uint32_t addr)
return inb(0xcfc | (addr & 3));
}
-#define PCI_VENDOR_ID 0
+#define PCI_VENDOR_ID 0x00
+#define PCI_DEVICE_ID 0x02
+#define PCI_COMMAND 0x04
+#define PCI_CLASS_DEVICE 0x0a
+#define PCI_HEADER_TYPE 0x0e
+#define PCI_PRIMARY_BUS 0x18
+#define PCI_SECONDARY_BUS 0x19
+#define PCI_SUBORDINATE_BUS 0x1a
+#define PCI_INTERRUPT_LINE 0x3c
+#define PCI_INTERRUPT_PIN 0x3d
+#define PCI_BRIDGE_CONTROL 0x3e
+
+/* PCI_COMMAND */
+#define PCI_COMMAND_DIS_INTX 0x400
+
+/* PCI_CLASS_DEVICE */
+#define PCI_CLASS_STORAGE_IDE 0x0101
+#define PCI_CLASS_BRIDGE_PCI 0x0604
+
+/* PCI_HEADER_TYPE */
+#define PCI_HEADER_TYPE_BRIDGE 1
+#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80
+
+/* PCI_BRIDGE_CONTROL */
+#define PCI_BRIDGE_CTL_SERR 0x02
+
+/* PCI_VENDOR_ID / PCI_DEVICE_ID */
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define PCI_DEVICE_ID_INTEL_82441 0x1237
+#define PCI_DEVICE_ID_INTEL_Q35_MCH 0x29c0
+#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010
+#define PCI_DEVICE_ID_INTEL_82371AB 0x7111
#endif
diff --git a/main.c b/main.c
index 95c33ce..b593237 100644
--- a/main.c
+++ b/main.c
@@ -86,6 +86,7 @@ int main(void)
// in order to probe CBFS!
asm("ljmp $0x8, $1f; 1:");
+ setup_pci();
setup_idt();
fw_cfg_setup();
extract_acpi();
diff --git a/pci.c b/pci.c
new file mode 100644
index 0000000..3976578
--- /dev/null
+++ b/pci.c
@@ -0,0 +1,141 @@
+#include "bios.h"
+#include "ioport.h"
+#include "pci.h"
+
+static uint16_t addend;
+static uint8_t bus, max_bus;
+static bool use_i440fx_routing;
+
+static void pci_foreach(void(*fn)(uint32_t bdf, uint32_t id, uint8_t type))
+{
+ int d, f;
+ for (d = 0; d < 32; d++) {
+ for (f = 0; f < 8; f++) {
+ uint32_t bdf = (bus * 256) + (d * 8) + f;
+ uint32_t id = pci_config_readl(bdf, PCI_VENDOR_ID);
+ uint16_t vendor;
+ uint8_t type;
+
+ /* 0x0000 or 0xFFFF? Skip. */
+ vendor = id & 0xFFFF;
+ if ((uint16_t)(vendor + 1) <= 1) {
+ if (f == 0)
+ break;
+ else
+ continue;
+ }
+
+ type = pci_config_readb(bdf, PCI_HEADER_TYPE);
+ fn(bdf, id, type);
+
+ if (f == 0 && !(type & PCI_HEADER_TYPE_MULTI_FUNCTION))
+ break;
+ }
+ }
+}
+
+static void do_setup_pci_irq(uint32_t bdf, int pin)
+{
+ int dev = (bdf >> 3) & 0x1f;
+ int lnk, irq;
+
+ irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE);
+ if (irq != 0)
+ return;
+
+ lnk = addend + pin;
+ if (use_i440fx_routing)
+ lnk += dev - 1;
+ else {
+ /* Q35 devices 25-31 all use LNKA. Devices 0-24 have
+ * a slightly different mapping.
+ */
+ if (dev <= 24)
+ lnk += dev;
+ }
+ lnk &= 3;
+
+ irq = lnk & 2 ? 11 : 10;
+ pci_config_writeb(bdf, PCI_INTERRUPT_LINE, irq);
+}
+
+static void do_block_pci_bridges(uint32_t bdf, uint32_t id, uint8_t type)
+{
+ uint16_t class;
+
+ class = pci_config_readw(bdf, PCI_CLASS_DEVICE);
+ switch (class) {
+ case PCI_CLASS_BRIDGE_PCI:
+ /* prevent accidental access to unintended devices */
+ pci_config_writeb(bdf, PCI_SECONDARY_BUS, 255);
+ pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 0);
+ break;
+ }
+}
+
+static void do_setup_pci(uint32_t bdf, uint32_t id, uint8_t type)
+{
+ uint16_t class;
+ uint8_t pin;
+ int save_bus;
+
+ pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN);
+ if (pin != 0)
+ do_setup_pci_irq(bdf, pin);
+
+ if (type & PCI_HEADER_TYPE_BRIDGE) {
+ uint32_t ctl;
+
+ ctl = pci_config_readw(bdf, PCI_BRIDGE_CONTROL);
+ pci_config_writew(bdf, PCI_BRIDGE_CONTROL,
+ ctl | PCI_BRIDGE_CTL_SERR);
+ }
+
+ class = pci_config_readw(bdf, PCI_CLASS_DEVICE);
+ switch (class) {
+ case PCI_CLASS_STORAGE_IDE:
+ pci_config_writel(bdf, 0x10, 0x1f0);
+ pci_config_writel(bdf, 0x14, 0x3f4);
+ pci_config_writel(bdf, 0x18, 0x170);
+ pci_config_writel(bdf, 0x1c, 0x374);
+ if (id == (PCI_VENDOR_ID_INTEL | (PCI_DEVICE_ID_INTEL_82371SB_1 << 16))
+ || id == (PCI_VENDOR_ID_INTEL | (PCI_DEVICE_ID_INTEL_82371AB << 16))) {
+ /* Enable IDE0 and IDE1. */
+ pci_config_writew(bdf, 0x40, 0x8000);
+ pci_config_writew(bdf, 0x42, 0x8000);
+ }
+ break;
+
+ case PCI_CLASS_BRIDGE_PCI:
+ save_bus = bus;
+ pci_config_writeb(bdf, PCI_PRIMARY_BUS, bus);
+ bus = ++max_bus;
+ pci_config_writeb(bdf, PCI_SECONDARY_BUS, bus);
+ pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 255);
+
+ /* Add PCI bridge device id for the recursive call. */
+ addend += (bdf >> 3) & 0x1f;
+ pci_foreach(do_setup_pci);
+ addend -= (bdf >> 3) & 0x1f;
+
+ pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, max_bus);
+ bus = save_bus;
+ break;
+ }
+}
+
+void setup_pci(void)
+{
+ const int bdf = 0;
+
+ uint32_t id = pci_config_readl(bdf, 0);
+ if (id == (PCI_VENDOR_ID_INTEL | (PCI_DEVICE_ID_INTEL_82441 << 16)))
+ use_i440fx_routing = true;
+ else if (id == (PCI_VENDOR_ID_INTEL | (PCI_DEVICE_ID_INTEL_Q35_MCH << 16)))
+ use_i440fx_routing = false;
+ else
+ panic();
+
+ pci_foreach(do_block_pci_bridges);
+ pci_foreach(do_setup_pci);
+}