summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarvin Borner2021-07-05 16:48:07 +0200
committerMarvin Borner2021-07-05 16:51:23 +0200
commit46ce42e9b13ce209c8c971084cfc849a83848270 (patch)
treea85283f85c970bb26df6690a4ba08b017f1d4e25
parent981342ef807ebcefba8eeba74511cdcedf9c36bd (diff)
Added PCI, MBR, bugs and fixes
-rw-r--r--.github/workflows/build.yml2
-rwxr-xr-xrun5
-rw-r--r--src/entry/bootsector.asm4
-rw-r--r--src/loader/cpu.c19
-rw-r--r--src/loader/dev.c17
-rw-r--r--src/loader/fs.c10
-rw-r--r--src/loader/ide.c2
-rw-r--r--src/loader/inc/cpu.h3
-rw-r--r--src/loader/inc/dev.h10
-rw-r--r--src/loader/inc/fs.h15
-rw-r--r--src/loader/inc/mbr.h28
-rw-r--r--src/loader/inc/pci.h271
-rw-r--r--src/loader/main.c4
-rw-r--r--src/loader/mbr.c58
-rw-r--r--src/loader/pci.c119
15 files changed, 555 insertions, 12 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 13c3221..b44af06 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -10,7 +10,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- name: Install
- run: sudo apt-get update && sudo apt-get install -y build-essential bison flex libgmp3-dev libmpc-dev libmpfr-dev texinfo curl nasm grub-common qemu qemu-kvm mtools
+ run: sudo apt-get update && sudo apt-get install -y build-essential bison flex libgmp3-dev libmpc-dev libmpfr-dev texinfo curl nasm grub-common qemu qemu-kvm mtools ctags
- name: Get cross compiler
id: cache-cross
uses: actions/cache@v1
diff --git a/run b/run
index feaee4a..4142232 100755
--- a/run
+++ b/run
@@ -87,7 +87,10 @@ build() {
PART="p1"
$SUDO parted -s "$DEV" mklabel msdos mkpart primary ext2 32k 100% -a minimal set 1 boot on
$SUDO mke2fs -b 1024 -q "$DEV$PART"
- $SUDO dd if=build/boot.bin of="$DEV" conv=notrunc status=none
+
+ # Write bootsector but jump over partition table (at 440 to 510)
+ $SUDO dd if=build/boot.bin of="$DEV" bs=1 count=440 conv=notrunc status=none
+ $SUDO dd if=build/boot.bin of="$DEV" bs=1 skip=510 seek=510 conv=notrunc status=none
# Mount disk and copy files
#mkdir -p mnt/
diff --git a/src/entry/bootsector.asm b/src/entry/bootsector.asm
index c3532ee..a98c22b 100644
--- a/src/entry/bootsector.asm
+++ b/src/entry/bootsector.asm
@@ -9,7 +9,6 @@ org LOCATION ; Bootsector location
global _start
_start:
jmp .skip_bpb ; Some BIOSes override the BPB area
- dd "START"
nop
times 87 db 0 ; Fill BPB area with 0
.skip_bpb:
@@ -162,6 +161,9 @@ gdt:
.end:
.size: equ .end - .start
+; The partition table gets inserted here (0x1b8 -> @440B)
+times 0x1b8 - ($ - $$) db 0
+
times SECTOR_SIZE - 2 - ($ - $$) db 0 ; Fill until 512 (SECTOR_SIZE) - 2 bytes; -2 because of 2B signature
dw SECTOR_END_SIG ; Bootsector end signature
diff --git a/src/loader/cpu.c b/src/loader/cpu.c
index a12d2fa..a0694ed 100644
--- a/src/loader/cpu.c
+++ b/src/loader/cpu.c
@@ -3,7 +3,7 @@
#include <cpu.h>
/**
- * CPU IO
+ * CPU serial I/O
*/
u8 inb(u16 port)
@@ -20,7 +20,24 @@ u16 inw(u16 port)
return value;
}
+u32 inl(u16 port)
+{
+ u32 value;
+ __asm__ volatile("inl %1, %0" : "=a"(value) : "Nd"(port));
+ return value;
+}
+
void outb(u16 port, u8 data)
{
__asm__ volatile("outb %0, %1" ::"a"(data), "Nd"(port));
}
+
+void outw(u16 port, u16 data)
+{
+ __asm__ volatile("outw %0, %1" ::"a"(data), "Nd"(port));
+}
+
+void outl(u16 port, u32 data)
+{
+ __asm__ volatile("outl %0, %1" ::"a"(data), "Nd"(port));
+}
diff --git a/src/loader/dev.c b/src/loader/dev.c
index 441aaed..be15083 100644
--- a/src/loader/dev.c
+++ b/src/loader/dev.c
@@ -2,6 +2,7 @@
// Device manager
#include <dev.h>
+#include <fs.h>
#include <lib.h>
#include <pnc.h>
@@ -19,11 +20,18 @@ static const char *dev_resolve_type(enum dev_type type)
}
}
-u32 dev_register(enum dev_type type, char *name, u32 data,
- s32 (*read)(void *, u32, u32, struct dev *),
- s32 (*write)(const void *, u32, u32, struct dev *))
+struct dev *dev_get(u8 id)
+{
+ assert(id < COUNT(devices));
+ return &devices[id];
+}
+
+u8 dev_register(enum dev_type type, char *name, u32 data,
+ s32 (*read)(void *, u32, u32, struct dev *),
+ s32 (*write)(const void *, u32, u32, struct dev *))
{
static u8 id = 0;
+ assert(id + 1 < 0xff);
struct dev *dev = &devices[id];
dev->id = id;
@@ -36,6 +44,9 @@ u32 dev_register(enum dev_type type, char *name, u32 data,
assert(strlen(name) < sizeof(dev->name));
memcpy(dev->name, name, sizeof(dev->name));
+ if (type == DEV_DISK)
+ fs_detect(dev);
+
return id++;
}
diff --git a/src/loader/fs.c b/src/loader/fs.c
new file mode 100644
index 0000000..8e0dc01
--- /dev/null
+++ b/src/loader/fs.c
@@ -0,0 +1,10 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+
+#include <fs.h>
+#include <mbr.h>
+
+void fs_detect(struct dev *dev)
+{
+ if (mbr_detect(dev))
+ return;
+}
diff --git a/src/loader/ide.c b/src/loader/ide.c
index 5cd67c7..c5461d0 100644
--- a/src/loader/ide.c
+++ b/src/loader/ide.c
@@ -111,6 +111,8 @@ static void ata_probe(void)
char name[4] = { 0 };
strlcpy(name, "hd", sizeof(name));
name[2] = 'a' + i;
+
+ // Register without write support
dev_register(DEV_DISK, name, data, ata_read, NULL);
}
}
diff --git a/src/loader/inc/cpu.h b/src/loader/inc/cpu.h
index 88db34b..0236002 100644
--- a/src/loader/inc/cpu.h
+++ b/src/loader/inc/cpu.h
@@ -7,7 +7,10 @@
u8 inb(u16 port);
u16 inw(u16 port);
+u32 inl(u16 port);
void outb(u16 port, u8 data);
+void outw(u16 port, u16 data);
+void outl(u16 port, u32 data);
#endif
diff --git a/src/loader/inc/dev.h b/src/loader/inc/dev.h
index 930b35c..7ea3147 100644
--- a/src/loader/inc/dev.h
+++ b/src/loader/inc/dev.h
@@ -4,6 +4,7 @@
#define DEV_H
#include <def.h>
+#include <fs.h>
enum dev_type {
DEV_DISK,
@@ -18,13 +19,16 @@ struct dev {
s32 (*read)(void *, u32, u32, struct dev *);
s32 (*write)(const void *, u32, u32, struct dev *);
+ struct fs fs;
+
u32 data; // Optional (device-specific) data/information
u8 exists : 1;
};
-u32 dev_register(enum dev_type type, char *name, u32 data,
- s32 (*read)(void *, u32, u32, struct dev *),
- s32 (*write)(const void *, u32, u32, struct dev *));
+struct dev *dev_get(u8 id);
+u8 dev_register(enum dev_type type, char *name, u32 data,
+ s32 (*read)(void *, u32, u32, struct dev *),
+ s32 (*write)(const void *, u32, u32, struct dev *));
void dev_print(void);
#endif
diff --git a/src/loader/inc/fs.h b/src/loader/inc/fs.h
new file mode 100644
index 0000000..84cd2d7
--- /dev/null
+++ b/src/loader/inc/fs.h
@@ -0,0 +1,15 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+
+#ifndef FS_H
+#define FS_H
+
+#include <def.h>
+struct dev;
+
+struct fs {
+ s32 (*read)(void *, u32, u32, struct dev *);
+};
+
+void fs_detect(struct dev *dev);
+
+#endif
diff --git a/src/loader/inc/mbr.h b/src/loader/inc/mbr.h
new file mode 100644
index 0000000..a9befe5
--- /dev/null
+++ b/src/loader/inc/mbr.h
@@ -0,0 +1,28 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+
+#ifndef MBR_H
+#define MBR_H
+
+#include <def.h>
+#include <dev.h>
+
+struct mbr_entry {
+ u8 attributes;
+ u8 chs_start[3];
+ u8 type;
+ u8 chs_end[3];
+ u32 start;
+ u32 size;
+} PACKED;
+
+struct mbr {
+ u8 bootstrap[440];
+ u32 signature;
+ u16 reserved;
+ struct mbr_entry entries[4];
+ u16 magic;
+} PACKED;
+
+u8 mbr_detect(struct dev *dev);
+
+#endif
diff --git a/src/loader/inc/pci.h b/src/loader/inc/pci.h
new file mode 100644
index 0000000..2a62cc2
--- /dev/null
+++ b/src/loader/inc/pci.h
@@ -0,0 +1,271 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+
+#ifndef PCI_H
+#define PCI_H
+
+#include <def.h>
+
+// From PCI spec
+#define PCI_VENDOR_ID 0x00 // 2
+#define PCI_DEVICE_ID 0x02 // 2
+#define PCI_COMMAND 0x04 // 2
+#define PCI_STATUS 0x06 // 2
+#define PCI_REVISION_ID 0x08 // 1
+
+#define PCI_PROG_IF 0x09 // 1
+#define PCI_SUBCLASS 0x0a // 1
+#define PCI_CLASS 0x0b // 1
+#define PCI_CACHE_LINE_SIZE 0x0c // 1
+#define PCI_LATENCY_TIMER 0x0d // 1
+#define PCI_HEADER_TYPE 0x0e // 1
+#define PCI_BIST 0x0f // 1
+#define PCI_BAR0 0x10 // 4
+#define PCI_BAR1 0x14 // 4
+#define PCI_BAR2 0x18 // 4
+#define PCI_BAR3 0x1C // 4
+#define PCI_BAR4 0x20 // 4
+#define PCI_BAR5 0x24 // 4
+
+#define PCI_INTERRUPT_LINE 0x3C // 1
+
+#define PCI_SECONDARY_BUS 0x19 // 1
+
+#define PCI_HEADER_TYPE_DEVICE 0
+#define PCI_HEADER_TYPE_BRIDGE 1
+#define PCI_HEADER_TYPE_CARDBUS 2
+
+#define PCI_TYPE_BRIDGE 0x060400
+#define PCI_TYPE_SATA 0x010600
+
+#define PCI_ADDR_PORT 0xcf8
+#define PCI_VALUE_PORT 0xcfc
+
+#define PCI_NONE 0xffff
+
+void pci_probe(void);
+
+/**
+ * Some common PCI IDs
+ */
+
+#define PCI_IDE(vendor_id, device_id) \
+ (vendor_id == 0x8086 && (device_id == 0x7010 || device_id == 0x7111))
+
+static const struct {
+ u16 id;
+ const char *name;
+} pci_vendors[] = {
+ { 0x1022, "AMD" }, { 0x106b, "Apple, Inc." }, { 0x1234, "Bochs/QEMU" },
+ { 0x1274, "Ensoniq" }, { 0x15ad, "VMWare" }, { 0x8086, "Intel Corporation" },
+ { 0x80EE, "VirtualBox" },
+};
+
+static const struct {
+ u16 vendor_id;
+ u16 device_id;
+ const char *name;
+} pci_devices[] = {
+ { 0x1022, 0x2000, "PCNet Ethernet Controller (pcnet)" },
+ { 0x106b, 0x003f, "OHCI Controller" },
+ { 0x1234, 0x1111, "VGA BIOS Graphics Extensions" },
+ { 0x1274, 0x1371, "Creative Labs CT2518 (ensoniq audio)" },
+ { 0x15ad, 0x0740, "VM Communication Interface" },
+ { 0x15ad, 0x0405, "SVGA II Adapter" },
+ { 0x15ad, 0x0790, "PCI bridge" },
+ { 0x15ad, 0x07a0, "PCI Express Root Port" },
+ { 0x8086, 0x100e, "Gigabit Ethernet Controller (e1000)" },
+ { 0x8086, 0x100f, "Gigabit Ethernet Controller (e1000)" },
+ { 0x8086, 0x1237, "PCI & Memory" },
+ { 0x8086, 0x2415, "AC'97 Audio Chipset" },
+ { 0x8086, 0x7000, "PCI-to-ISA Bridge" },
+ { 0x8086, 0x7010, "PIIX3 IDE" },
+ { 0x8086, 0x7110, "PIIX4 ISA" },
+ { 0x8086, 0x7111, "PIIX4 IDE" },
+ { 0x8086, 0x7113, "Power Management Controller" },
+ { 0x8086, 0x7190, "Host Bridge" },
+ { 0x8086, 0x7191, "AGP Bridge" },
+ { 0x80EE, 0xBEEF, "Bochs/QEMU-compatible Graphics Adapter" },
+ { 0x80EE, 0xCAFE, "Guest Additions Device" },
+};
+
+static const struct {
+ u32 id : 24; // Only 24 Bit
+ const char *name;
+} pci_types[] = {
+ { 0x000000, "Legacy Device" },
+ { 0x000100, "VGA-Compatible Device" },
+
+ { 0x010000, "SCSI bus controller" },
+ { 0x010100, "ISA Compatibility mode-only controller" },
+ { 0x010105, "PCI native mode-only controller" },
+ { 0x01010a, "ISA Compatibility mode controller, supports both channels "
+ "switched to PCI native mode" },
+ { 0x01010f, "PCI native mode controller, supports both channels switched "
+ "to ISA compatibility mode" },
+ { 0x010180, "ISA Compatibility mode-only controller, supports bus mastering" },
+ { 0x010185, "PCI native mode-only controller, supports bus mastering" },
+ { 0x01018a, "ISA Compatibility mode controller, supports both channels "
+ "switched to PCI native mode, supports bus mastering" },
+ { 0x01018f, "PCI native mode controller, supports both channels switched "
+ "\to ISA compatibility mode, supports bus mastering" },
+
+ { 0x010200, "Floppy disk controller" },
+ { 0x010300, "IPI bus controller" },
+ { 0x010400, "RAID controller" },
+ { 0x010520, "ATA controller, single stepping" },
+ { 0x010530, "ATA controller, continuous" },
+ { 0x010600, "Serial ATA controller - vendor specific interface" },
+ { 0x010601, "Serial ATA controller - AHCI 1.0 interface" },
+ { 0x010700, "Serial Attached SCSI controller" },
+ { 0x018000, "Mass Storage controller" },
+
+ { 0x020000, "Ethernet controller" },
+ { 0x020100, "Token Ring controller" },
+ { 0x020200, "FDDI controller" },
+ { 0x020300, "ATM controller" },
+ { 0x020400, "ISDN controller" },
+ { 0x020500, "WorldFip controller" },
+ // { 0x0206xx , "PICMG 2.14 Multi Computing" },
+ { 0x028000, "Network controller" },
+
+ { 0x030000, "VGA Display controller" },
+ { 0x030001, "8514-compatible Display controller" },
+ { 0x030100, "XGA Display controller" },
+ { 0x030200, "3D Display controller" },
+ { 0x038000, "Display controller" },
+
+ { 0x040000, "Video device" },
+ { 0x040100, "Audio device" },
+ { 0x040200, "Computer Telephony device" },
+ { 0x048000, "Multimedia device" },
+
+ { 0x050000, "RAM memory controller" },
+ { 0x050100, "Flash memory controller" },
+ { 0x058000, "Memory controller" },
+
+ { 0x060000, "Host bridge" },
+ { 0x060100, "ISA bridge" },
+ { 0x060200, "EISA bridge" },
+ { 0x060300, "MCA bridge" },
+ { 0x060400, "PCI-to-PCI bridge" },
+ { 0x060401, "PCI-to-PCI bridge (subtractive decoding)" },
+ { 0x060500, "PCMCIA bridge" },
+ { 0x060600, "NuBus bridge" },
+ { 0x060700, "CardBus bridge" },
+ // { 0x0608xx , "RACEway bridge" },
+ { 0x060940, "PCI-to-PCI bridge, Semi-transparent, primary facing Host" },
+ { 0x060980, "PCI-to-PCI bridge, Semi-transparent, secondary facing Host" },
+ { 0x060A00, "InfiniBand-to-PCI host bridge" },
+ { 0x068000, "Bridge device" },
+
+ { 0x070000, "Generic XT-compatible serial controller" },
+ { 0x070001, "16450-compatible serial controller" },
+ { 0x070002, "16550-compatible serial controller" },
+ { 0x070003, "16650-compatible serial controller" },
+ { 0x070004, "16750-compatible serial controller" },
+ { 0x070005, "16850-compatible serial controller" },
+ { 0x070006, "16950-compatible serial controller" },
+
+ { 0x070100, "Parallel port" },
+ { 0x070101, "Bi-directional parallel port" },
+ { 0x070102, "ECP 1.X compliant parallel port" },
+ { 0x070103, "IEEE1284 controller" },
+ { 0x0701FE, "IEEE1284 target device" },
+ { 0x070200, "Multiport serial controller" },
+
+ { 0x070300, "Generic modem" },
+ { 0x070301, "Hayes 16450-compatible modem" },
+ { 0x070302, "Hayes 16550-compatible modem" },
+ { 0x070303, "Hayes 16650-compatible modem" },
+ { 0x070304, "Hayes 16750-compatible modem" },
+ { 0x070400, "GPIB (IEEE 488.1/2) controller" },
+ { 0x070500, "Smart Card" },
+ { 0x078000, "Communications device" },
+
+ { 0x080000, "Generic 8259 PIC" },
+ { 0x080001, "ISA PIC" },
+ { 0x080002, "EISA PIC" },
+ { 0x080010, "I/O APIC interrupt controller" },
+ { 0x080020, "I/O(x) APIC interrupt controller" },
+
+ { 0x080100, "Generic 8237 DMA controller" },
+ { 0x080101, "ISA DMA controller" },
+ { 0x080102, "EISA DMA controller" },
+
+ { 0x080200, "Generic 8254 system timer" },
+ { 0x080201, "ISA system timer" },
+ { 0x080202, "EISA system timer-pair" },
+
+ { 0x080300, "Generic RTC controller" },
+ { 0x080301, "ISA RTC controller" },
+
+ { 0x080400, "Generic PCI Hot-Plug controller" },
+ { 0x080500, "SD Host controller" },
+ { 0x088000, "System peripheral" },
+
+ { 0x090000, "Keyboard controller" },
+ { 0x090100, "Digitizer (pen)" },
+ { 0x090200, "Mouse controller" },
+ { 0x090300, "Scanner controller" },
+ { 0x090400, "Generic Gameport controller" },
+ { 0x090410, "Legacy Gameport controller" },
+ { 0x098000, "Input controller" },
+
+ { 0x0a0000, "Generic docking station" },
+ { 0x0a8000, "Docking station" },
+
+ { 0x0b0000, "386 Processor" },
+ { 0x0b0100, "486 Processor" },
+ { 0x0b0200, "Pentium Processor" },
+ { 0x0b1000, "Alpha Processor" },
+ { 0x0b2000, "PowerPC Processor" },
+ { 0x0b3000, "MIPS Processor" },
+ { 0x0b4000, "Co-processor" },
+
+ { 0x0c0000, "IEEE 1394 (FireWire)" },
+ { 0x0c0010, "IEEE 1394 -- OpenHCI spec" },
+ { 0x0c0100, "ACCESS.bus" },
+ { 0x0c0200, "SSA" },
+ { 0x0c0300, "Universal Serial Bus (UHC spec)" },
+ { 0x0c0310, "Universal Serial Bus (Open Host spec)" },
+ { 0x0c0320, "USB2 Host controller (Intel Enhanced HCI spec)" },
+ { 0x0c0380, "Universal Serial Bus (no PI spec)" },
+ { 0x0c03FE, "USB Target Device" },
+ { 0x0c0400, "Fibre Channel" },
+ { 0x0c0500, "System Management Bus" },
+ { 0x0c0600, "InfiniBand" },
+ { 0x0c0700, "IPMI SMIC Interface" },
+ { 0x0c0701, "IPMI Kybd Controller Style Interface" },
+ { 0x0c0702, "IPMI Block Transfer Interface" },
+ // { 0x0c08xx , "SERCOS Interface" },
+ { 0x0c0900, "CANbus" },
+
+ { 0x0d100, "iRDA compatible controller" },
+ { 0x0d100, "Consumer IR controller" },
+ { 0x0d100, "RF controller" },
+ { 0x0d100, "Bluetooth controller" },
+ { 0x0d100, "Broadband controller" },
+ { 0x0d100, "Ethernet (802.11a 5 GHz) controller" },
+ { 0x0d100, "Ethernet (802.11b 2.4 GHz) controller" },
+ { 0x0d100, "Wireless controller" },
+
+ // { 0x0e00xx , "I2O Intelligent I/O, spec 1.0" },
+ { 0x0e0000, "Message FIFO at offset 040h" },
+
+ { 0x0f0100, "TV satellite comm. controller" },
+ { 0x0f0200, "Audio satellite comm. controller" },
+ { 0x0f0300, "Voice satellite comm. controller" },
+ { 0x0f0400, "Data satellite comm. controller" },
+
+ { 0x100000, "Network and computing en/decryption" },
+ { 0x101000, "Entertainment en/decryption" },
+ { 0x108000, "En/Decryption" },
+
+ { 0x110000, "DPIO modules" },
+ { 0x110100, "Perf. counters" },
+ { 0x111000, "Comm. synch., time and freq. test" },
+ { 0x112000, "Management card" },
+ { 0x118000, "Data acq./Signal proc." },
+};
+
+#endif
diff --git a/src/loader/main.c b/src/loader/main.c
index 6fedcba..517e120 100644
--- a/src/loader/main.c
+++ b/src/loader/main.c
@@ -4,6 +4,7 @@
#include <dev.h>
#include <ide.h>
#include <log.h>
+#include <pci.h>
/**
* Entry
@@ -18,8 +19,7 @@ int start(void)
log("Log initiated\n");
- ata_install();
-
+ pci_probe();
dev_print();
while (1)
diff --git a/src/loader/mbr.c b/src/loader/mbr.c
new file mode 100644
index 0000000..1ee298b
--- /dev/null
+++ b/src/loader/mbr.c
@@ -0,0 +1,58 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+
+#include <lib.h>
+#include <log.h>
+#include <mbr.h>
+#include <pnc.h>
+
+static struct mbr_entry entries[16] = { 0 };
+
+static s32 mbr_read(void *buf, u32 lba, u32 sector_count, struct dev *part)
+{
+ u8 dev_id = (part->data & 0xff00) >> 8;
+ struct dev *dev = dev_get(dev_id);
+ assert(dev && dev->type == DEV_DISK && dev->read);
+
+ u8 mbr_id = part->data & 0xff;
+ assert(mbr_id < COUNT(entries));
+ struct mbr_entry *entry = &entries[mbr_id];
+
+ return dev->read(buf, entry->start + lba, sector_count, dev);
+}
+
+static u8 mbr_add_entry(struct mbr_entry *entry)
+{
+ static u8 i = 0;
+ assert(i + 1 < (u8)COUNT(entries));
+ entries[i] = *entry;
+ return i++;
+}
+
+u8 mbr_detect(struct dev *dev)
+{
+ assert(dev->type == DEV_DISK);
+
+ struct mbr mbr = { 0 };
+ dev->read(&mbr, 0, 1, dev); // Read first sector (MBR)
+
+ if (mbr.magic != 0xaa55)
+ return 0;
+
+ for (u8 i = 0; i < 4; i++) {
+ struct mbr_entry *entry = &mbr.entries[i];
+ if (!entry->type || !entry->size)
+ continue;
+
+ char name[5] = { 0 };
+ strlcpy(name, dev->name, sizeof(name));
+ name[3] = '0' + i;
+
+ // Saving space and everything
+ u16 data = mbr_add_entry(entry) | (dev->id << 8);
+
+ dev_register(DEV_DISK, name, data, mbr_read, NULL);
+ log("Found part %s%c\n", dev->name, '0' + i);
+ }
+
+ return 1;
+}
diff --git a/src/loader/pci.c b/src/loader/pci.c
new file mode 100644
index 0000000..e2c485d
--- /dev/null
+++ b/src/loader/pci.c
@@ -0,0 +1,119 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+
+#include <cpu.h>
+#include <ide.h>
+#include <log.h>
+#include <pci.h>
+
+// Generate device ID
+#define PCI_DEVICE(bus, slot, func) ((u32)((bus << 16) | (slot << 8) | func))
+
+// Extract data from device ID
+#define PCI_BUS(device) ((u8)(device >> 16))
+#define PCI_SLOT(device) ((u8)(device >> 8))
+#define PCI_FUNC(device) ((u8)(device))
+
+// Calculate address
+#define PCI_ADDR(device, field) \
+ (0x80000000 | (u32)(PCI_BUS(device) << 16) | (u32)(PCI_SLOT(device) << 11) | \
+ (u32)(PCI_FUNC(device) << 8) | ((field)&0xfc))
+
+// Read pci device type
+#define PCI_TYPE(device) \
+ ((u32)((pci_read(device, PCI_CLASS, 1) << 16) | (pci_read(device, PCI_SUBCLASS, 1) << 8) | \
+ pci_read(device, PCI_PROG_IF, 1)))
+
+static u32 pci_read(u32 device, u8 field, u32 size)
+{
+ outl(PCI_ADDR_PORT, PCI_ADDR(device, field));
+
+ if (size == 4) {
+ u32 t = inl(PCI_VALUE_PORT);
+ return t;
+ } else if (size == 2) {
+ u16 t = inw((u16)(PCI_VALUE_PORT + (field & 2)));
+ return t;
+ } else if (size == 1) {
+ u8 t = inb((u16)(PCI_VALUE_PORT + (field & 3)));
+ return t;
+ }
+
+ return PCI_NONE;
+}
+
+/**
+ * PCI scanning
+ */
+
+static void pci_print(u16 vendor_id, u16 device_id, u32 type_id)
+{
+ const char *vendor = "unknown";
+ const char *device = "unknown";
+ const char *class = "unknown";
+
+ for (u16 i = 0; i < COUNT(pci_vendors); i++)
+ if (pci_vendors[i].id == vendor_id)
+ vendor = pci_vendors[i].name;
+
+ for (u16 i = 0; i < COUNT(pci_devices); i++)
+ if (pci_devices[i].vendor_id == vendor_id && pci_devices[i].device_id == device_id)
+ device = pci_devices[i].name;
+
+ for (u16 i = 0; i < COUNT(pci_types); i++)
+ if (pci_types[i].id == type_id)
+ class = pci_types[i].name;
+
+ log("[PCI] %s: %s - %s\n", vendor, device, class);
+}
+
+static void pci_probe_bus(u8 bus);
+static void pci_probe_func(u8 bus, u8 slot, u8 func)
+{
+ u32 device = PCI_DEVICE(bus, slot, func);
+
+ u16 vendor_id = pci_read(device, PCI_VENDOR_ID, 2);
+ u16 device_id = pci_read(device, PCI_DEVICE_ID, 2);
+ u32 type_id = PCI_TYPE(device);
+ pci_print(vendor_id, device_id, type_id);
+
+ if (PCI_TYPE(device) == PCI_TYPE_BRIDGE)
+ pci_probe_bus(pci_read(device, PCI_SECONDARY_BUS, 1));
+
+ if (PCI_IDE(vendor_id, device_id))
+ ata_install();
+}
+
+static void pci_probe_slot(u8 bus, u8 slot)
+{
+ u32 device = PCI_DEVICE(bus, slot, 0);
+ if (pci_read(device, PCI_VENDOR_ID, 2) == PCI_NONE)
+ return;
+
+ // Scan first func
+ pci_probe_func(bus, slot, 0);
+
+ if (!pci_read(device, PCI_HEADER_TYPE, 1))
+ return;
+
+ // Scan rest
+ for (u8 func = 1; func < 8; func++) {
+ device = PCI_DEVICE(bus, slot, func);
+ if (pci_read(device, PCI_VENDOR_ID, 2) != PCI_NONE)
+ pci_probe_func(bus, slot, func);
+ }
+}
+
+static void pci_probe_bus(u8 bus)
+{
+ for (u8 slot = 0; slot < 32; slot++)
+ pci_probe_slot(bus, slot);
+}
+
+void pci_probe(void)
+{
+ for (u8 func = 0; func < 8; func++) {
+ u32 device = PCI_DEVICE(0, 0, func);
+ if (pci_read(device, PCI_VENDOR_ID, 2) != PCI_NONE)
+ pci_probe_bus(func);
+ }
+}