290 lines
9.0 KiB
C
290 lines
9.0 KiB
C
//
|
|
// Created by rick on 02-02-21.
|
|
//
|
|
// https://wiki.osdev.org/PCI
|
|
|
|
#include "pci.h"
|
|
|
|
#include <types.h>
|
|
#include <drivers/ports.h>
|
|
#include <libc/libc.h>
|
|
#include <kprint.h>
|
|
#include <libk.h>
|
|
|
|
#define PCI_CONFIG_ENABLE (1 << 31)
|
|
|
|
#define PCI_CONFIG_SHIFT_BUS_NUMBER 16
|
|
#define PCI_CONFIG_SHIFT_DEV_NUMBER 11
|
|
#define PCI_CONFIG_SHIFT_FUNC_NUMBER 8
|
|
|
|
#define PCI_CONFIG_LINE_0 0x00
|
|
#define PCI_CONFIG_LINE_1 0x01
|
|
#define PCI_CONFIG_LINE_2 0x08
|
|
#define PCI_CONFIG_LINE_3 0x0C
|
|
|
|
|
|
#define MAX_PCI_DRIVERS 64
|
|
#define MAX_PCI_DEVICES 64
|
|
|
|
#define MASK_BAR_IOSPACE 0xFFFFFFFC
|
|
#define MASK_BAR_MEMSPACE 0xFFFFFFF0
|
|
|
|
const pci_driver *pci_drivers[MAX_PCI_DRIVERS];
|
|
int last_pci_device_index = 0;
|
|
pci_device pci_devices[MAX_PCI_DEVICES];
|
|
|
|
u32 pci_register_driver(const pci_driver *pci_driver) {
|
|
for (int i = 0; i < MAX_PCI_DRIVERS; ++i) {
|
|
if (pci_drivers[i] != NULL) {
|
|
continue;
|
|
}
|
|
pci_drivers[i] = pci_driver;
|
|
return PCI_REGISTER_OK;
|
|
}
|
|
return PCI_REGISTER_ERR_FULL;
|
|
}
|
|
|
|
u32 pci_config_address(u8 bus, u8 slot, u8 func, u8 offset) {
|
|
return PCI_CONFIG_ENABLE
|
|
| ((u32) bus << PCI_CONFIG_SHIFT_BUS_NUMBER)
|
|
| ((u32) slot << PCI_CONFIG_SHIFT_DEV_NUMBER)
|
|
| ((u32) func << PCI_CONFIG_SHIFT_FUNC_NUMBER)
|
|
| (offset & 0xFC);
|
|
}
|
|
|
|
u32 pci_config_read_double_word(u8 bus, u8 slot, u8 func, u8 offset) {
|
|
u32 address = pci_config_address(bus, slot, func, offset);
|
|
port_double_word_out(PORT_PCI_CONFIG_ADDRESS, address);
|
|
return port_double_word_in(PORT_PCI_CONFIG_DATA);
|
|
}
|
|
|
|
u16 pci_config_read_word(u8 bus, u8 slot, u8 func, u8 offset) {
|
|
u32 address = pci_config_address(bus, slot, func, offset);
|
|
port_double_word_out(PORT_PCI_CONFIG_ADDRESS, address);
|
|
return port_double_word_in(PORT_PCI_CONFIG_DATA) >> ((offset & 2) * 8) & 0xFFFF;
|
|
}
|
|
|
|
u8 pci_config_read_byte(u8 bus, u8 slot, u8 func, u8 offset) {
|
|
u32 address = pci_config_address(bus, slot, func, offset);
|
|
port_double_word_out(PORT_PCI_CONFIG_ADDRESS, address);
|
|
return port_double_word_in(PORT_PCI_CONFIG_DATA) >> ((offset & 0b11) * 8) & 0xFF;
|
|
}
|
|
|
|
void pci_config_write_double_word(u8 bus, u8 slot, u8 func, u8 offset, u32 value) {
|
|
u32 address = pci_config_address(bus, slot, func, offset);
|
|
port_double_word_out(PORT_PCI_CONFIG_ADDRESS, address);
|
|
port_double_word_out(PORT_PCI_CONFIG_DATA, value);
|
|
}
|
|
|
|
void pci_config_write_word(u8 bus, u8 slot, u8 func, u8 offset, u16 value) {
|
|
u32 address = pci_config_address(bus, slot, func, offset);
|
|
port_double_word_out(PORT_PCI_CONFIG_ADDRESS, address);
|
|
port_word_out(PORT_PCI_CONFIG_DATA, value);
|
|
}
|
|
|
|
void pci_config_write_byte(u8 bus, u8 slot, u8 func, u8 offset, u8 value) {
|
|
u32 address = pci_config_address(bus, slot, func, offset);
|
|
port_double_word_out(PORT_PCI_CONFIG_ADDRESS, address);
|
|
port_byte_out(PORT_PCI_CONFIG_DATA, value);
|
|
}
|
|
|
|
u16 pci_get_vendor_id(u8 bus, u8 slot, u8 func) {
|
|
return pci_config_read_word(bus, slot, func, PCI_CONFIG_VENDOR_ID);
|
|
}
|
|
|
|
u8 pci_get_header_type(u8 bus, u8 slot, u8 func) {
|
|
return pci_config_read_byte(bus, slot, func, PCI_CONFIG_HEADER_TYPE);
|
|
}
|
|
|
|
void print_u8(u8 val) {
|
|
char buf[3];
|
|
itoa(val, buf, 16);
|
|
kprint(buf);
|
|
}
|
|
|
|
void print_u16(u16 val) {
|
|
char buf[5];
|
|
itoa(val, buf, 16);
|
|
kprint(buf);
|
|
}
|
|
|
|
void pci_pick_driver(pci_device *device) {
|
|
for (int i = 0; i < MAX_PCI_DRIVERS; ++i) {
|
|
if (pci_drivers[i] == NULL) {
|
|
continue;
|
|
}
|
|
if (device->class != pci_drivers[i]->pci_class) {
|
|
continue;
|
|
}
|
|
if (pci_drivers[i]->pci_use_subclass && device->subclass != pci_drivers[i]->pci_subclass) {
|
|
continue;
|
|
}
|
|
if (pci_drivers[i]->validate(device) != PCI_VALIDATE_OK) {
|
|
continue;
|
|
}
|
|
device->pci_driver = pci_drivers[i];
|
|
}
|
|
}
|
|
|
|
void pci_check_function(u8 bus, u8 slot, u8 func) {
|
|
pci_devices[last_pci_device_index].bus = bus;
|
|
pci_devices[last_pci_device_index].slot = slot;
|
|
pci_devices[last_pci_device_index].func = func;
|
|
pci_devices[last_pci_device_index].config_line_0 = pci_config_read_double_word(bus, slot, func, PCI_CONFIG_LINE_0);
|
|
pci_devices[last_pci_device_index].config_line_2 = pci_config_read_double_word(bus, slot, func, PCI_CONFIG_LINE_2);
|
|
pci_devices[last_pci_device_index].config_line_3 = pci_config_read_double_word(bus, slot, func, PCI_CONFIG_LINE_3);
|
|
pci_devices[last_pci_device_index].device_state.present = 1;
|
|
pci_pick_driver(&pci_devices[last_pci_device_index]);
|
|
last_pci_device_index++;
|
|
|
|
// todo do something with the function
|
|
}
|
|
|
|
void pci_check_device(u8 bus, u8 device) {
|
|
u8 function = 0;
|
|
u16 vendor_id = pci_get_vendor_id(bus, device, function);
|
|
if (vendor_id == 0xFFFF) return;
|
|
pci_check_function(bus, device, function);
|
|
u8 header_type = pci_get_header_type(bus, device, function);
|
|
if (header_type & PCI_HEADER_TYPE_MULTI_FUNC) {
|
|
for (function = 1; function < 8; ++function) {
|
|
if (pci_get_vendor_id(bus, device, function) != 0xFFFF) {
|
|
pci_check_function(bus, device, function);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void pci_check_bus(u8 bus) {
|
|
for (int device = 0; device < 32; ++device) {
|
|
pci_check_device(bus, device);
|
|
}
|
|
}
|
|
|
|
void pci_sort_drivers() {
|
|
// todo
|
|
}
|
|
|
|
void pci_scan() {
|
|
if (last_pci_device_index != 0) {
|
|
k_panics("Can only scan once!");
|
|
}
|
|
u8 header_type = pci_get_header_type(0, 0, 0);
|
|
if (header_type & PCI_HEADER_TYPE_MULTI_FUNC) {
|
|
// multiple pci host controllers
|
|
for (int function = 0; function < 8; ++function) {
|
|
pci_check_bus(function);
|
|
}
|
|
} else {
|
|
pci_check_bus(0);
|
|
}
|
|
}
|
|
|
|
void pci_init_drivers() {
|
|
for (int device_index = 0; device_index < MAX_PCI_DEVICES; ++device_index) {
|
|
if (!pci_devices[device_index].device_state.present) {
|
|
continue;
|
|
}
|
|
if (pci_devices[device_index].driver_state.initialized) {
|
|
continue; // already done
|
|
}
|
|
if (pci_devices[device_index].pci_driver == NULL) {
|
|
continue; // no driver found
|
|
}
|
|
pci_devices[device_index].pci_driver->initialize(&pci_devices[device_index]);
|
|
pci_devices[device_index].driver_state.initialized = 1;
|
|
}
|
|
}
|
|
|
|
void pci_print_info() {
|
|
for (int i = 0; i < last_pci_device_index; ++i) {
|
|
kprint("PCI BSF: ");
|
|
print_u8(pci_devices[i].bus);
|
|
kprint("/");
|
|
print_u8(pci_devices[i].slot);
|
|
kprint("/");
|
|
print_u8(pci_devices[i].func);
|
|
kprint(", Class/Sub/If: ");
|
|
print_u8(pci_devices[i].class);
|
|
kprint("/");
|
|
print_u8(pci_devices[i].subclass);
|
|
kprint("/");
|
|
print_u8(pci_devices[i].programInterface);
|
|
|
|
kprint(", V/D: ");
|
|
print_u16(pci_devices[i].vendorId);
|
|
kprint("/");
|
|
print_u16(pci_devices[i].deviceId);
|
|
kprint(" driver_info: ");
|
|
|
|
if (pci_devices[i].pci_driver == NULL) {
|
|
kprint("NULL");
|
|
} else {
|
|
kprint(pci_devices[i].pci_driver->name);
|
|
}
|
|
|
|
kprint("\n");
|
|
}
|
|
}
|
|
|
|
void pci_init_bar(pci_device *device, u8 bar_index) {
|
|
if (device->headerType != 0x00) {
|
|
k_panics("Only header 0x00 supported for now");
|
|
return;
|
|
}
|
|
u8 offset = 0;
|
|
bar_info *bar;
|
|
switch (bar_index) {
|
|
case 0:
|
|
offset = PCI_CONFIG_BAR0;
|
|
bar = &device->bar0;
|
|
break;
|
|
case 1:
|
|
offset = PCI_CONFIG_BAR1;
|
|
bar = &device->bar1;
|
|
break;
|
|
case 2:
|
|
offset = PCI_CONFIG_BAR2;
|
|
bar = &device->bar2;
|
|
break;
|
|
case 3:
|
|
offset = PCI_CONFIG_BAR3;
|
|
bar = &device->bar3;
|
|
break;
|
|
case 4:
|
|
offset = PCI_CONFIG_BAR4;
|
|
bar = &device->bar4;
|
|
break;
|
|
case 5:
|
|
offset = PCI_CONFIG_BAR5;
|
|
bar = &device->bar5;
|
|
break;
|
|
default:
|
|
k_panics("Bar index too high");
|
|
return;
|
|
}
|
|
|
|
|
|
u32 original_address = pci_config_read_double_word(device->bus, device->slot, device->func, offset);
|
|
pci_config_write_double_word(device->bus, device->slot, device->func, offset, 0xFFFFFFFF);
|
|
u32 masked_size = pci_config_read_double_word(device->bus, device->slot, device->func, offset);
|
|
|
|
if (original_address & 0x1) {
|
|
// IO Space
|
|
bar->size = ~(masked_size & MASK_BAR_IOSPACE) + 1;
|
|
bar->address = original_address & MASK_BAR_IOSPACE;
|
|
bar->is_io_space = 1;
|
|
} else {
|
|
// Memory space
|
|
bar->size = ~(masked_size & MASK_BAR_MEMSPACE) + 1;
|
|
bar->address = original_address & 0xFFFFFFF0;
|
|
bar->is_io_space = 0;
|
|
bar->type = (original_address & 0b110) >> 1;
|
|
bar->prefetchable = (original_address & 0b111000) >> 3;
|
|
}
|
|
// todo identify conflicts, move to different address
|
|
pci_config_write_double_word(device->bus, device->slot, device->func, offset, original_address);
|
|
bar->present = 1;
|
|
}
|
|
|
|
// todo https://wiki.osdev.org/Universal_Serial_Bus if i dare
|