Some basic framework for PCI(e) enumeration
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "sys/amd64/hw/pci/pci.h"
|
||||
|
||||
struct ahci_registers;
|
||||
struct pci_device;
|
||||
|
||||
struct ahci_fis_reg_h2d {
|
||||
uint8_t type;
|
||||
@@ -26,7 +26,7 @@ struct ahci_fis_reg_h2d {
|
||||
};
|
||||
|
||||
struct ahci_controller {
|
||||
// pci_addr_t addr;
|
||||
struct pci_device *pci_dev;
|
||||
uintptr_t abar_phys;
|
||||
struct ahci_registers *regs;
|
||||
};
|
||||
|
||||
@@ -10,14 +10,22 @@
|
||||
#define PCI_CONFIG_INFO 0x0C
|
||||
#define PCI_CONFIG_BAR(n) (0x10 + (n) * 4)
|
||||
#define PCI_CONFIG_SUBSYSTEM 0x2C
|
||||
#define PCI_CONFIG_CAPABILITIES 0x34
|
||||
#define PCI_CONFIG_IRQ 0x3C
|
||||
|
||||
#define PCI_CONFIG_BRIDGE 0x18
|
||||
|
||||
#define PCI_ID(vnd, dev) (((uint32_t) (vnd)) | ((uint32_t) (dev) << 16))
|
||||
|
||||
struct pci_device;
|
||||
typedef void (*pci_driver_func_t)(struct pci_device *dev);
|
||||
|
||||
void pci_init(void);
|
||||
void pci_add_root_bus(uint8_t n);
|
||||
|
||||
uint32_t pci_config_read_dword(struct pci_device *dev, uint16_t off);
|
||||
|
||||
void pci_add_class_driver(uint32_t full_class, pci_driver_func_t func);
|
||||
|
||||
// pcidb.c
|
||||
const char *pci_class_string(uint16_t full_class);
|
||||
|
||||
@@ -136,8 +136,12 @@ int irq_has_handler(uint8_t gsi) {
|
||||
return !!handler_list[0].func;
|
||||
}
|
||||
|
||||
extern void amd64_msi_handler();
|
||||
|
||||
void irq_init(int cpu) {
|
||||
// Special entry
|
||||
amd64_idt_set(cpu, 32, (uintptr_t) amd64_irq0, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
|
||||
|
||||
amd64_idt_set(cpu, 33, (uintptr_t) amd64_irq1, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
|
||||
amd64_idt_set(cpu, 34, (uintptr_t) amd64_irq2, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
|
||||
amd64_idt_set(cpu, 35, (uintptr_t) amd64_irq3, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
|
||||
@@ -154,6 +158,30 @@ void irq_init(int cpu) {
|
||||
amd64_idt_set(cpu, 46, (uintptr_t) amd64_irq14, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
|
||||
amd64_idt_set(cpu, 47, (uintptr_t) amd64_irq15, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
|
||||
|
||||
// Message signaled interrupt support
|
||||
// Message address register format:
|
||||
// 0 .. 1 Unused
|
||||
// 2 Destination mode
|
||||
// 3 Redirection hint
|
||||
// 4 .. 11 Reserved
|
||||
// 12 .. 19 Destination APIC ID
|
||||
// 31 .. 20 0xFEE
|
||||
//
|
||||
// DM RH Behavior
|
||||
// 0 0 Destination ID field specifies target CPU
|
||||
// 1 0 Destination ID must point to a valid cpu
|
||||
// 0 1 Only matching CPU receives interrupt (without redirection)
|
||||
// 1 1 Redirection is limited to only the Destination ID's logical group
|
||||
//
|
||||
// Message data register format:
|
||||
// 0 .. 7 Interrupt vector (0x80)
|
||||
// 8 .. 10 Delivery mode
|
||||
// 11 .. 13 Reserved
|
||||
// 14 Trigger level (don't care, all are edge-trigggered)
|
||||
// 15 Level/Edge trigger
|
||||
// 16 .. 63 Reserved
|
||||
amd64_idt_set(cpu, 0x80, (uintptr_t) amd64_msi_handler, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
|
||||
|
||||
#if defined(AMD64_MAX_SMP)
|
||||
// Common for all CPUs
|
||||
amd64_idt_set(cpu, IPI_VECTOR_GENERIC, (uintptr_t) amd64_irq_ipi, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
|
||||
|
||||
@@ -46,3 +46,14 @@ _msg0:
|
||||
amd64_idt_load:
|
||||
lidt (%rdi)
|
||||
ret
|
||||
|
||||
.global amd64_msi_handler
|
||||
amd64_msi_handler:
|
||||
cli
|
||||
leaq _msg1(%rip), %rsi
|
||||
movq $1, %rdi
|
||||
call debugs
|
||||
hlt
|
||||
|
||||
_msg1:
|
||||
.string "MSI handler\n"
|
||||
|
||||
+21
-25
@@ -553,7 +553,7 @@ static void ahci_port_init(struct ahci_controller *ahci, struct ahci_port *port,
|
||||
ahci_port_add(port);
|
||||
}
|
||||
|
||||
/* static */ void ahci_controller_init(struct ahci_controller *ahci) {
|
||||
static void ahci_controller_init(struct ahci_controller *ahci) {
|
||||
// Check controller version
|
||||
kinfo("AHCI controller version is %02x.%02x\n", (ahci->regs->vs >> 16), (ahci->regs->vs & 0xFFFF));
|
||||
|
||||
@@ -582,27 +582,23 @@ static void ahci_port_init(struct ahci_controller *ahci, struct ahci_port *port,
|
||||
|
||||
//// PCI-specific
|
||||
|
||||
//static void pci_ahci_init(pci_addr_t addr) {
|
||||
// //uint32_t info = pci_config_read_dword(addr, PCI_CONFIG_CLASS);
|
||||
//
|
||||
// //if (((info >> 8) & 0xFF) == 0x01) {
|
||||
// // // AHCI 1.0 controller
|
||||
// // uint32_t abar_phys = pci_config_read_dword(addr, PCI_CONFIG_BAR(5));
|
||||
// // if (abar_phys & 1) {
|
||||
// // kwarn("AHCI controller " PCI_FMTADDR " has ABAR in I/O space\n", PCI_VAADDR(addr));
|
||||
// // return;
|
||||
// // }
|
||||
//
|
||||
// // struct ahci_controller *obj = kmalloc(sizeof(struct ahci_controller));
|
||||
//
|
||||
// // obj->addr = addr;
|
||||
// // obj->abar_phys = abar_phys;
|
||||
// // obj->regs = (void *) MM_VIRTUALIZE(abar_phys);
|
||||
//
|
||||
// // ahci_controller_init(obj);
|
||||
// //}
|
||||
//}
|
||||
//
|
||||
//static __init void ahci_register_class(void) {
|
||||
// pci_add_class_driver(0x0106, pci_ahci_init);
|
||||
//}
|
||||
static void pci_ahci_init(struct pci_device *pci_dev) {
|
||||
// TODO: change pcie -> pci
|
||||
uint32_t abar_phys = pci_config_read_dword(pci_dev, PCI_CONFIG_BAR(5));
|
||||
if (abar_phys & 1) {
|
||||
kwarn("AHCI controller has ABAR in I/O space\n");
|
||||
return;
|
||||
}
|
||||
|
||||
struct ahci_controller *obj = kmalloc(sizeof(struct ahci_controller));
|
||||
|
||||
obj->pci_dev = pci_dev;
|
||||
obj->abar_phys = abar_phys;
|
||||
obj->regs = (void *) MM_VIRTUALIZE(abar_phys);
|
||||
|
||||
ahci_controller_init(obj);
|
||||
}
|
||||
|
||||
static __init void ahci_register_class(void) {
|
||||
pci_add_class_driver(0x010601, pci_ahci_init);
|
||||
}
|
||||
|
||||
+244
-2
@@ -2,13 +2,255 @@
|
||||
#include "sys/amd64/hw/ioapic.h"
|
||||
#include "sys/amd64/hw/acpi.h"
|
||||
#include "sys/amd64/hw/io.h"
|
||||
#include "sys/panic.h"
|
||||
#include "sys/assert.h"
|
||||
#include "sys/panic.h"
|
||||
#include "sys/debug.h"
|
||||
#include "sys/heap.h"
|
||||
#include "sys/mm.h"
|
||||
|
||||
#define PCI_MAX_DRIVERS 64
|
||||
|
||||
#define pcie_config_read_dword(dev, off) \
|
||||
(*(uint32_t *) ((dev)->pcie_config + (off)))
|
||||
|
||||
#define PCI_CAP_MSI_64 (1 << 7)
|
||||
#define PCI_CAP_MSI_EN (1 << 0)
|
||||
struct pci_cap_msi {
|
||||
uint8_t cap_id;
|
||||
uint8_t cap_link;
|
||||
uint16_t message_control;
|
||||
union {
|
||||
struct {
|
||||
uint32_t message_address;
|
||||
uint16_t message_data;
|
||||
} __attribute__((packed)) msi32;
|
||||
struct {
|
||||
uint64_t message_address;
|
||||
uint16_t message_data;
|
||||
} __attribute__((packed)) msi64;
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pci_device {
|
||||
// PCI address
|
||||
uint8_t bus;
|
||||
uint8_t dev;
|
||||
uint8_t func;
|
||||
|
||||
// PCIe addressing: segment group number and configuration space pointer
|
||||
uint16_t pcie_segment_group;
|
||||
void *pcie_config;
|
||||
|
||||
// Interrupt resources
|
||||
struct pci_cap_msi *msi;
|
||||
int irq_pin;
|
||||
|
||||
// For list
|
||||
struct pci_device *next;
|
||||
};
|
||||
|
||||
struct pci_driver {
|
||||
pci_driver_func_t init_func;
|
||||
uint32_t type;
|
||||
uint32_t match;
|
||||
};
|
||||
|
||||
static struct pci_device *g_pci_devices = NULL;
|
||||
static struct pci_driver g_pci_drivers[PCI_MAX_DRIVERS] = {0};
|
||||
static size_t g_pci_driver_count;
|
||||
|
||||
#define PCI_DRIVER_CLASS 1
|
||||
#define PCI_DRIVER_DEV 2
|
||||
|
||||
uint32_t pci_config_read_dword(struct pci_device *dev, uint16_t off) {
|
||||
// TODO: check if device is really PCIe
|
||||
return pcie_config_read_dword(dev, off);
|
||||
}
|
||||
|
||||
void pci_add_class_driver(uint32_t full_class, pci_driver_func_t func) {
|
||||
if (g_pci_driver_count == PCI_MAX_DRIVERS) {
|
||||
panic("Too many PCI drivers loaded\n");
|
||||
}
|
||||
|
||||
struct pci_driver *driver = &g_pci_drivers[g_pci_driver_count++];
|
||||
|
||||
driver->init_func = func;
|
||||
driver->type = PCI_DRIVER_CLASS;
|
||||
driver->match = full_class & ~0xFF000000;
|
||||
}
|
||||
|
||||
void pci_add_root_bus(uint8_t n) {
|
||||
kwarn("%s: %02x\n", __func__, n);
|
||||
}
|
||||
|
||||
void pci_init(void) {
|
||||
static void pci_pick_driver(uint32_t device_id, uint32_t class_id, pci_driver_func_t *class_driver, pci_driver_func_t *dev_driver) {
|
||||
pci_driver_func_t rclass = NULL, rdev = NULL;
|
||||
for (size_t i = 0; i < g_pci_driver_count; ++i) {
|
||||
if (rclass && rdev) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (g_pci_drivers[i].type == PCI_DRIVER_CLASS && !rclass) {
|
||||
uint32_t match = g_pci_drivers[i].match;
|
||||
// Check if prog_if has to be matched
|
||||
// 0xFF means (match all prog. IF)
|
||||
if ((match & 0xFF) == 0xFF) {
|
||||
match &= ~0xFF;
|
||||
class_id &= ~0xFF;
|
||||
}
|
||||
|
||||
if (match == class_id) {
|
||||
rclass = g_pci_drivers[i].init_func;
|
||||
continue;
|
||||
}
|
||||
} else if (g_pci_drivers[i].type == PCI_DRIVER_DEV && !rdev) {
|
||||
if (device_id == g_pci_drivers[i].match) {
|
||||
rdev = g_pci_drivers[i].init_func;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*class_driver = rclass;
|
||||
*dev_driver = rdev;
|
||||
}
|
||||
|
||||
static void pci_device_add(struct pci_device *dev) {
|
||||
dev->next = g_pci_devices;
|
||||
g_pci_devices = dev;
|
||||
}
|
||||
|
||||
static int pcie_device_setup(struct pci_device *dev) {
|
||||
uint32_t class, caps_offset, irq_info;
|
||||
uint32_t id;
|
||||
uint8_t irq_pin;
|
||||
|
||||
class = pcie_config_read_dword(dev, PCI_CONFIG_CLASS);
|
||||
caps_offset = pcie_config_read_dword(dev, PCI_CONFIG_CAPABILITIES) & 0xFF;
|
||||
irq_info = pcie_config_read_dword(dev, PCI_CONFIG_IRQ);
|
||||
id = pcie_config_read_dword(dev, PCI_CONFIG_ID);
|
||||
|
||||
kinfo("%02x:%02x:%02x:\n", dev->bus, dev->dev, dev->func);
|
||||
kinfo(" Class %02x:%02x:%02x\n", (class >> 24), (class >> 16) & 0xFF, (class >> 8) & 0xFF);
|
||||
kinfo(" Device %04x:%04x\n", id & 0xFFFF, (id >> 16) & 0xFFFF);
|
||||
|
||||
irq_pin = (irq_info >> 8) & 0xFF;
|
||||
if (irq_pin) {
|
||||
dev->irq_pin = irq_pin - 1;
|
||||
kinfo(" IRQ pin INT%c#\n", dev->irq_pin + 'A');
|
||||
}
|
||||
|
||||
while (caps_offset) {
|
||||
uint8_t *link = (uint8_t *) (dev->pcie_config + caps_offset);
|
||||
|
||||
switch (link[0]) {
|
||||
case 0x05:
|
||||
kinfo(" * MSI capability\n");
|
||||
dev->msi = (struct pci_cap_msi *) link;
|
||||
break;
|
||||
case 0x10:
|
||||
kinfo(" * PCIe capability\n");
|
||||
break;
|
||||
default:
|
||||
// Unknown capability
|
||||
kinfo(" * Device capability: %02x\n", link[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
caps_offset = link[1];
|
||||
}
|
||||
|
||||
pci_driver_func_t driver_class, driver_dev;
|
||||
pci_pick_driver(id, class >> 8, &driver_class, &driver_dev);
|
||||
|
||||
if (driver_dev) {
|
||||
driver_dev(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (driver_class) {
|
||||
driver_class(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void pcie_enumerate_device(uintptr_t base_address, uint16_t seg, uint8_t start_bus, uint8_t bus, uint8_t dev_no) {
|
||||
void *cfg;
|
||||
uint32_t id;
|
||||
|
||||
for (uint8_t func = 0; func < 8; ++func) {
|
||||
uint32_t d = ((uint32_t) (bus - start_bus) << 20) | ((uint32_t) dev_no << 15) | ((uint32_t) func << 12);
|
||||
cfg = (void *) MM_VIRTUALIZE(base_address + d);
|
||||
id = *(uint32_t *) cfg;
|
||||
if ((id & 0xFFFF) == 0xFFFF) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct pci_device *dev = kmalloc(sizeof(struct pci_device));
|
||||
_assert(dev);
|
||||
dev->bus = bus;
|
||||
dev->dev = dev_no;
|
||||
dev->func = func;
|
||||
|
||||
dev->pcie_segment_group = seg;
|
||||
dev->pcie_config = cfg;
|
||||
dev->msi = NULL;
|
||||
dev->irq_pin = -1;
|
||||
|
||||
pcie_device_setup(dev);
|
||||
pci_device_add(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void pcie_enumerate_bus(uintptr_t base_address, uint16_t seg, uint8_t start_bus, uint8_t bus) {
|
||||
for (uint8_t dev = 0; dev < 32; ++dev) {
|
||||
// Check if function 0 is present
|
||||
void *cfg = (void *) MM_VIRTUALIZE(base_address + (((bus - start_bus) << 20) | (dev << 15)));
|
||||
|
||||
if (((*(uint32_t *) cfg) & 0xFFFF) != 0xFFFF) {
|
||||
uint32_t header = *(uint32_t *) (cfg + 0x0C);
|
||||
header >>= 16;
|
||||
header &= 0x7F;
|
||||
|
||||
switch (header) {
|
||||
case 0x00:
|
||||
pcie_enumerate_device(base_address, seg, start_bus, bus, dev);
|
||||
break;
|
||||
default:
|
||||
kwarn("Skipping unsupported header type: %02x\n", header);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pcie_enumerate_segment(uintptr_t base_address, uint16_t seg, uint8_t start_bus, uint8_t end_bus) {
|
||||
for (uint16_t bus = start_bus; bus < end_bus; ++bus) {
|
||||
pcie_enumerate_bus(base_address, seg, start_bus, bus);
|
||||
}
|
||||
}
|
||||
|
||||
void pci_init(void) {
|
||||
if (!acpi_mcfg) {
|
||||
panic("TODO: legacy PCI\n");
|
||||
}
|
||||
|
||||
uint32_t mcfg_entry_count = (acpi_mcfg->hdr.length - sizeof(struct acpi_header) - 8) / sizeof(struct acpi_mcfg_entry);
|
||||
kinfo("MCFG has %u entries:\n", mcfg_entry_count);
|
||||
for (uint32_t i = 0; i < mcfg_entry_count; ++i) {
|
||||
kinfo("%u:\n", i);
|
||||
kinfo(" Base address: %p\n", acpi_mcfg->entry[i].base_address);
|
||||
kinfo(" Segment group #%u\n", acpi_mcfg->entry[i].pci_segment_group);
|
||||
kinfo(" PCI buses: %02x-%02x\n", acpi_mcfg->entry[i].start_pci_bus, acpi_mcfg->entry[i].end_pci_bus);
|
||||
}
|
||||
|
||||
// Start enumerating buses specified in MCFG
|
||||
for (uint32_t i = 0; i < mcfg_entry_count; ++i) {
|
||||
pcie_enumerate_segment(acpi_mcfg->entry[i].base_address,
|
||||
acpi_mcfg->entry[i].pci_segment_group,
|
||||
acpi_mcfg->entry[i].start_pci_bus,
|
||||
acpi_mcfg->entry[i].end_pci_bus);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user