mirror of
https://github.com/encounter/osdev.git
synced 2026-07-10 12:18:43 -07:00
131 lines
4.1 KiB
C
131 lines
4.1 KiB
C
#include "pci.h"
|
|
#include "ports.h"
|
|
#include "../console.h"
|
|
|
|
#include <vector.h>
|
|
|
|
#define PCI_VENDOR_NONE 0xFFFF
|
|
|
|
static vc_vector *pci_devices;
|
|
|
|
uint32_t pci_config_read_long(uint32_t id, uint8_t offset) {
|
|
port_long_out(0xCF8, (uint32_t) 0x80000000 | id | (offset & 0xFC));
|
|
return port_long_in(0xCFC);
|
|
}
|
|
|
|
uint16_t pci_config_read_word(uint32_t id, uint8_t offset) {
|
|
return (uint16_t) (pci_config_read_long(id, offset) >> (offset & 2) * 8 & 0xFFFF);
|
|
}
|
|
|
|
uint8_t pci_config_read_byte(uint32_t id, uint8_t offset) {
|
|
return (uint8_t) (pci_config_read_word(id, offset) & 0xFF);
|
|
}
|
|
|
|
void pci_config_write_byte(uint32_t id, uint8_t offset, uint8_t val) {
|
|
port_long_out(0xCF8, (uint32_t) 0x80000000 | id | (offset & 0xFC));
|
|
port_byte_out(0xCFC, val);
|
|
}
|
|
|
|
uint16_t pci_read_vendor(uint32_t id) {
|
|
return pci_config_read_word(id, PCI_HEADER_VENDOR_ID);
|
|
}
|
|
|
|
uint8_t pci_read_header_type(uint32_t id) {
|
|
return (uint8_t) (pci_config_read_word(id, PCI_HEADER_TYPE_ID) & 0xFF);
|
|
}
|
|
|
|
// lower half = subclass, upper half = base class
|
|
uint16_t pci_read_class(uint32_t id) {
|
|
return pci_config_read_word(id, PCI_HEADER_CLASS);
|
|
}
|
|
|
|
// lower half = primary, upper half = secondary
|
|
uint16_t pci_read_bus_number(uint32_t id) {
|
|
return pci_config_read_word(id, PCI_HEADER_BUS_NUM);
|
|
}
|
|
|
|
// lower half = revision, upper half = prog IF
|
|
uint16_t pci_read_revision(uint32_t id) {
|
|
return pci_config_read_word(id, PCI_HEADER_REVISION);
|
|
}
|
|
|
|
void pci_check_bus(uint8_t bus);
|
|
|
|
bool pci_check_function(uint32_t id) {
|
|
uint32_t val = pci_config_read_long(id, PCI_HEADER_VENDOR_ID);
|
|
uint16_t vendor_id = (uint16_t) (val & 0xFFFF);
|
|
uint16_t device_id = (uint16_t) (val >> 16 & 0xFFFF);
|
|
if (vendor_id == PCI_VENDOR_NONE) return false;
|
|
|
|
uint16_t class = pci_read_class(id);
|
|
uint16_t revision = pci_read_revision(id);
|
|
uint8_t header_type = pci_read_header_type(id);
|
|
vc_vector_push_back(pci_devices, &(pci_device_t) {
|
|
.loc = {
|
|
.bus = PCI_ID_BUS(id),
|
|
.device = PCI_ID_DEV(id),
|
|
.function = PCI_ID_FUNC(id)
|
|
},
|
|
.class = class,
|
|
.vendor_id = vendor_id,
|
|
.device_id = device_id,
|
|
.revision_id = (uint8_t) (revision & 0xFF),
|
|
.prog_if = (uint8_t) (revision >> 8 & 0xFF),
|
|
.bar0 = pci_config_read_long(id, PCI_HEADER_BAR0_ADDR),
|
|
.bar1 = pci_config_read_long(id, PCI_HEADER_BAR1_ADDR),
|
|
.bar2 = header_type == 0x00 ? pci_config_read_long(id, PCI_HEADER_BAR2_ADDR) : 0,
|
|
.bar3 = header_type == 0x00 ? pci_config_read_long(id, PCI_HEADER_BAR3_ADDR) : 0,
|
|
.bar4 = header_type == 0x00 ? pci_config_read_long(id, PCI_HEADER_BAR4_ADDR) : 0,
|
|
.bar5 = header_type == 0x00 ? pci_config_read_long(id, PCI_HEADER_BAR5_ADDR) : 0,
|
|
});
|
|
|
|
// PCI-to-PCI bridge
|
|
if (class == 0x0604) {
|
|
// FIXME breaks VMWare
|
|
// pci_check_bus(pci_config_read_byte(id, PCI_HEADER_SEC_BUS));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void pci_check_device(uint8_t bus, uint8_t device) {
|
|
uint32_t pci_id = PCI_ID(bus, device, 0);
|
|
if (!pci_check_function(pci_id)) return;
|
|
|
|
uint8_t header_type = pci_read_header_type(pci_id);
|
|
if ((header_type & 0x80) != 0) {
|
|
for (uint8_t function = 1; function < 8; function++) {
|
|
pci_check_function(PCI_ID(bus, device, function));
|
|
}
|
|
}
|
|
}
|
|
|
|
void pci_check_bus(uint8_t bus) {
|
|
uint8_t device;
|
|
for (device = 0; device < 32; device++) {
|
|
pci_check_device(bus, device);
|
|
}
|
|
}
|
|
|
|
void pci_check_all_buses() {
|
|
uint8_t header_type = pci_read_header_type(PCI_ID(0, 0, 0));
|
|
if ((header_type & 0x80) == 0) {
|
|
// Single PCI host controller
|
|
pci_check_bus(0);
|
|
} else {
|
|
// Multiple PCI host controllers
|
|
for (uint8_t function = 0; function < 8; function++) {
|
|
if (pci_read_vendor(PCI_ID(0, 0, function)) != PCI_VENDOR_NONE) break;
|
|
pci_check_bus(function);
|
|
}
|
|
}
|
|
}
|
|
|
|
void pci_init() {
|
|
pci_devices = vc_vector_create(8, sizeof(pci_device_t), NULL);
|
|
pci_check_all_buses();
|
|
vc_vector_reserve_count(pci_devices, 0); // Shrink
|
|
}
|
|
|
|
vc_vector *pci_get_devices() {
|
|
return pci_devices;
|
|
} |