aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mubios/core/post.c1
-rw-r--r--mubios/drivers/pci.c114
-rw-r--r--mubios/include/bios.h1
3 files changed, 116 insertions, 0 deletions
diff --git a/mubios/core/post.c b/mubios/core/post.c
index 8d12369..9a54dd6 100644
--- a/mubios/core/post.c
+++ b/mubios/core/post.c
@@ -37,6 +37,7 @@ post_main (void)
pic_init ();
pit_init ();
ioapic_init ();
+ pci_irq_init ();
ata_init ();
volatile u8 *mbr;
diff --git a/mubios/drivers/pci.c b/mubios/drivers/pci.c
new file mode 100644
index 0000000..b9c955c
--- /dev/null
+++ b/mubios/drivers/pci.c
@@ -0,0 +1,114 @@
+/*
+ RCC-Project
+
+ Copyright (C) 2026 Richard Qin
+
+ This file is part of RCC-Project.
+
+ RCC-Project is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your option) any
+ later version.
+
+ RCC-Project is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ RCC-Project; see the file COPYING. If not see
+ <http://www.gnu.org/licenses/>.
+ */
+
+#include "bios.h"
+#include "io.h"
+
+#define PCI_CONFIG_ADDRESS 0x0CF8u
+#define PCI_CONFIG_DATA 0x0CFCu
+#define PCI_VENDOR_DEVICE 0x00u
+#define PCI_INTERRUPT_LINE 0x3Cu
+#define PCI_INTERRUPT_PIN 0x3Du
+#define PCI_VENDOR_ABSENT 0xFFFFu
+#define PIIX3_ISA_ID 0x70008086u
+#define PIIX4_PM_ID 0x71138086u
+#define PIIX_PIRQ_ROUTE 0x60u
+#define PIIX_SCI_IRQ 9u
+#define ELCR_MASTER 0x04D0u
+#define ELCR_SLAVE 0x04D1u
+
+/* Match SeaBIOS's PIRQ A-D assignment for the QEMU pc machine. */
+static const u8 pirq_irqs[4] = { 10u, 10u, 11u, 11u };
+
+static u32
+pci_read (u16 devfn, u8 offset)
+{
+ io_out32 (PCI_CONFIG_ADDRESS,
+ 0x80000000u | ((u32)devfn << 8) | (offset & 0xFCu));
+ return io_in32 (PCI_CONFIG_DATA);
+}
+
+static void
+pci_write8 (u16 devfn, u8 offset, u8 value)
+{
+ io_out32 (PCI_CONFIG_ADDRESS,
+ 0x80000000u | ((u32)devfn << 8) | (offset & 0xFCu));
+ io_out8 ((u16)(PCI_CONFIG_DATA + (offset & 3u)), value);
+}
+
+void
+pci_irq_init (void)
+{
+ u16 bridge;
+ u16 devfn;
+ u16 level_irqs = 0;
+ u32 id;
+ u8 pin;
+ u8 pirq;
+ u8 irq;
+
+ /* PIRQ routing registers below belong to the PIIX3 ISA bridge. */
+ for (bridge = 0; bridge < 256u; ++bridge)
+ if (pci_read (bridge, PCI_VENDOR_DEVICE) == PIIX3_ISA_ID)
+ break;
+
+ if (bridge == 256u)
+ return;
+
+ for (pirq = 0; pirq < 4u; ++pirq)
+ {
+ irq = pirq_irqs[pirq];
+ pci_write8 (bridge, (u8)(PIIX_PIRQ_ROUTE + pirq), irq);
+ level_irqs |= (u16)(1u << irq);
+ }
+
+ for (devfn = 0; devfn < 256u; ++devfn)
+ {
+ id = pci_read (devfn, PCI_VENDOR_DEVICE);
+ /* PCI returns an all-ones vendor ID for an absent function. */
+ if ((u16)id == PCI_VENDOR_ABSENT)
+ continue;
+
+ pin = (u8)(pci_read (devfn, PCI_INTERRUPT_PIN) >> 8);
+ /* Interrupt Pin zero denotes a function without a PCI INTx pin. */
+ if (pin == 0u)
+ continue;
+
+ /* The PIIX PM SCI is wired directly to IRQ9, not through PIRQ. */
+ if (id == PIIX4_PM_ID)
+ {
+ irq = PIIX_SCI_IRQ;
+ level_irqs |= (u16)(1u << irq);
+ }
+ else
+ {
+ /* QEMU pc: PIRQ = (slot - 1 + zero-based INTx pin) & 3. */
+ pirq = (u8)(((devfn >> 3) - 1u + pin - 1u) & 3u);
+ irq = pirq_irqs[pirq];
+ }
+
+ pci_write8 (devfn, PCI_INTERRUPT_LINE, irq);
+ }
+
+ /* PCI INTx and SCI are level-triggered; preserve other IRQ settings. */
+ io_out8 (ELCR_MASTER, (u8)(io_in8 (ELCR_MASTER) | (u8)level_irqs));
+ io_out8 (ELCR_SLAVE, (u8)(io_in8 (ELCR_SLAVE) | (level_irqs >> 8)));
+}
diff --git a/mubios/include/bios.h b/mubios/include/bios.h
index c52af80..3bd13dc 100644
--- a/mubios/include/bios.h
+++ b/mubios/include/bios.h
@@ -56,6 +56,7 @@ void kbd_init (void);
void pic_init (void);
void pit_init (void);
void ioapic_init (void);
+void pci_irq_init (void);
void vga_text_mode_init (void);
void ivt_init (void);