diff options
author | Marvin Borner | 2021-07-05 16:48:07 +0200 |
---|---|---|
committer | Marvin Borner | 2021-07-05 16:51:23 +0200 |
commit | 46ce42e9b13ce209c8c971084cfc849a83848270 (patch) | |
tree | a85283f85c970bb26df6690a4ba08b017f1d4e25 | |
parent | 981342ef807ebcefba8eeba74511cdcedf9c36bd (diff) |
Added PCI, MBR, bugs and fixes
-rw-r--r-- | .github/workflows/build.yml | 2 | ||||
-rwxr-xr-x | run | 5 | ||||
-rw-r--r-- | src/entry/bootsector.asm | 4 | ||||
-rw-r--r-- | src/loader/cpu.c | 19 | ||||
-rw-r--r-- | src/loader/dev.c | 17 | ||||
-rw-r--r-- | src/loader/fs.c | 10 | ||||
-rw-r--r-- | src/loader/ide.c | 2 | ||||
-rw-r--r-- | src/loader/inc/cpu.h | 3 | ||||
-rw-r--r-- | src/loader/inc/dev.h | 10 | ||||
-rw-r--r-- | src/loader/inc/fs.h | 15 | ||||
-rw-r--r-- | src/loader/inc/mbr.h | 28 | ||||
-rw-r--r-- | src/loader/inc/pci.h | 271 | ||||
-rw-r--r-- | src/loader/main.c | 4 | ||||
-rw-r--r-- | src/loader/mbr.c | 58 | ||||
-rw-r--r-- | src/loader/pci.c | 119 |
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 @@ -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); + } +} |