Files
net/platform/pcidetect.c
2026-01-06 15:51:03 -08:00

202 lines
7.5 KiB
C

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <exec/hal.h>
#include <exec/debcon.h>
#include <exec/mem.h>
#include <exec/detect.h>
#include <platform/pcidetect.h>
static enum HalAddressSpace space;
static uintptr_t addr_port, data_port;
static const char *DETECT_STRING_TEMPLATE = "pci()vendor(0000)device(0000)subvendor(0000)subdevice(0000)class(00)subclass(00)progif(00)revision(00)bus(00)dev(00)fun(00)";
static uint32_t PCI_ReadConfig(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) {
uint32_t address = (1U << 31) | ((uint32_t)bus << 16) | ((uint32_t)device << 11) | ((uint32_t)function << 8) | (offset & 0xFC);
HalWriteIO32(space, addr_port, address);
return HalReadIO32(space, data_port);
}
static void PCI_WriteConfig(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value) {
uint32_t address = (1U << 31) | ((uint32_t)bus << 16) | ((uint32_t)device << 11) | ((uint32_t)function << 8) | (offset & 0xFC);
HalWriteIO32(space, addr_port, address);
HalWriteIO32(space, data_port, value);
}
static uint16_t GetVendorID(uint8_t bus, uint8_t device, uint8_t function) {
return (uint16_t)(PCI_ReadConfig(bus, device, function, 0x00) & 0xFFFF);
}
static uint16_t GetDeviceID(uint8_t bus, uint8_t device, uint8_t function) {
return (uint16_t)((PCI_ReadConfig(bus, device, function, 0x00) >> 16) & 0xFFFF);
}
static uint8_t GetHeaderType(uint8_t bus, uint8_t device, uint8_t function) {
return (uint8_t)((PCI_ReadConfig(bus, device, function, 0x0C) >> 16) & 0xFF);
}
static uint8_t GetRevisionID(uint8_t bus, uint8_t device, uint8_t function) {
return (uint8_t)(PCI_ReadConfig(bus, device, function, 0x08) & 0xFF);
}
static uint8_t GetProgIF(uint8_t bus, uint8_t device, uint8_t function) {
return (uint8_t)((PCI_ReadConfig(bus, device, function, 0x08) >> 8) & 0xFF);
}
static uint8_t GetSubclass(uint8_t bus, uint8_t device, uint8_t function) {
return (uint8_t)((PCI_ReadConfig(bus, device, function, 0x08) >> 16) & 0xFF);
}
static uint8_t GetClassCode(uint8_t bus, uint8_t device, uint8_t function) {
return (uint8_t)((PCI_ReadConfig(bus, device, function, 0x08) >> 16) & 0xFF);
}
static uint16_t GetSubsystemVendorID(uint8_t bus, uint8_t device, uint8_t function) {
return (uint16_t)(PCI_ReadConfig(bus, device, function, 0x2C) & 0xFFFF);
}
static uint16_t GetSubsystemID(uint8_t bus, uint8_t device, uint8_t function) {
return (uint16_t)((PCI_ReadConfig(bus, device, function, 0x2C) >> 16) & 0xFFFF);
}
static uint8_t GetInterruptLine(uint8_t bus, uint8_t device, uint8_t function) {
return (uint8_t)(PCI_ReadConfig(bus, device, function, 0x3C) & 0xFF);
}
static void EnumerateDevice(uint8_t bus, uint8_t device) {
for (uint8_t function = 0; function < 8; function++) {
uint16_t vendor_id = GetVendorID(bus, device, function);
/* check if function present */
if (vendor_id == 0xFFFF) {
if (function) continue;
else break;
}
/* get device info */
uint16_t device_id = GetDeviceID(bus, device, function);
uint8_t header_type = GetHeaderType(bus, device, function);
uint8_t revision_id = GetRevisionID(bus, device, function);
uint8_t prog_if = GetProgIF(bus, device, function);
uint8_t subclass = GetSubclass(bus, device, function);
uint8_t class_code = GetClassCode(bus, device, function);
uint16_t subvendor = GetSubsystemVendorID(bus, device, function);
uint16_t subdevice = GetSubsystemID(bus, device, function);
uint8_t interrupt_line = GetInterruptLine(bus, device, function);
/* build compat string */
char *compat = AllocMem(strlen(DETECT_STRING_TEMPLATE) + 1);
strncpy(compat, DETECT_STRING_TEMPLATE, strlen(DETECT_STRING_TEMPLATE));
compat[strlen(compat)] = 0;
BuildCompat(compat,
"vendor", vendor_id, 4,
"device", device_id, 4,
"class", class_code, 2,
"subvendor", subvendor, 4,
"subdevice", subdevice, 4,
"subclass", subclass, 2,
"progif", prog_if, 2,
"revision", revision_id, 2,
"bus", bus, 2,
"dev", device, 2,
"fun", function, 2,
NULL
);
bool has_irq = (interrupt_line && interrupt_line != 0xFF);
/* determine number of BARs */
int region = 0;
for (int i = 0; i < 6; i++) {
uint32_t bar = PCI_ReadConfig(bus, device, function, 0x10 + i * 4);
if (bar == 0) continue;
region++;
}
/* create detect structure */
struct Detect *detect = CreateDetect(compat, region, has_irq);
/* set IRQ if present */
if (has_irq) {
SetDetectIRQ(detect, 0, interrupt_line);
}
/* set regions (BARs) */
region = 0;
for (int i = 0; i < 6; i++) {
int bar_addr = 0x10 + i * 4;
uint32_t bar = PCI_ReadConfig(bus, device, function, bar_addr);
if (bar == 0) continue;
enum HalAddressSpace region_space = (bar & 1) ? AS_IO : AS_MEM;
uint64_t base;
size_t limit;
if (region_space == AS_IO) {
base = (uintptr_t)(bar & ~0x3);
/* determine size */
PCI_WriteConfig(bus, device, function, bar_addr, 0xFFFFFFFF);
uint32_t size_mask = PCI_ReadConfig(bus, device, function, bar_addr);
PCI_WriteConfig(bus, device, function, bar_addr, bar);
if (size_mask == 0) continue;
limit = (size_t)(~(size_mask & ~0x3) + 1);
} else {
if ((bar & 0x6) == 0x4) {
/* 64-bit address */
uint32_t bar_high = PCI_ReadConfig(bus, device, function, bar_addr + 4);
base = ((uint64_t)bar_high << 32) | (uint64_t)(bar & ~0xF);
i++; /* skip next BAR */
} else {
base = (uint64_t)(bar & ~0xF);
}
/* determine size */
PCI_WriteConfig(bus, device, function, bar_addr, 0xFFFFFFFF);
uint32_t size_mask = PCI_ReadConfig(bus, device, function, bar_addr);
PCI_WriteConfig(bus, device, function, bar_addr, bar);
if (size_mask == 0) continue;
limit = (size_t)(~(size_mask & ~0xF) + 1);
}
if (base < 0xFFFFFFFFULL) {
/* only support 32-bit addresses for now */
SetDetectRegion(detect, region++, region_space, base, limit);
}
}
AddDetect(detect);
if (!function && !(header_type & 0x80)) {
/* no more functions */
break;
}
}
}
static void EnumerateBus(uint8_t bus) {
for (uint8_t device = 0; device < 32; device++) {
EnumerateDevice(bus, device);
}
}
static void EnumerateBusses(void) {
uint8_t function;
uint8_t header_type = GetHeaderType(0, 0, 0);
if ((header_type & 0x80) == 0) {
/* single function device */
EnumerateBus(0);
} else {
/* multi-function device */
for (function = 0; function < 8; function++) {
if (GetVendorID(0, 0, function) != 0xFFFF) {
EnumerateBus(function);
}
}
}
}
void PCI_Detect(enum HalAddressSpace _space, uintptr_t _addr_port, uintptr_t _data_port) {
space = _space;
addr_port = _addr_port;
data_port = _data_port;
EnumerateBusses();
}