Skip to content

Commit

Permalink
pci: Switch to pure MMIO configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
marv7000 committed Dec 26, 2024
1 parent ff83599 commit 321b634
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 176 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "kernel/system/acpi/uACPI"]
path = kernel/system/acpi/uacpi
url = https://github.com/UltraOS/uACPI
[submodule "kernel/system/uapi/uapi"]
path = kernel/system/uapi/uapi
url = https://github.com/uDrivers/uAPI
15 changes: 2 additions & 13 deletions include/menix/system/acpi/mcfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,5 @@
// Do PCI configuration using ACPI "MCFG". This is the preferred way.
void mcfg_init();

// Reads 8 bits from a PCI device using the ACPI MCFG table info.
u8 mcfg_read8(u16 seg, u8 bus, u8 slot, u8 func, u16 offset);
// Reads 16 bits from a PCI device using the ACPI MCFG table info.
u16 mcfg_read16(u16 seg, u8 bus, u8 slot, u8 func, u16 offset);
// Reads 32 bits from a PCI device using the ACPI MCFG table info.
u32 mcfg_read32(u16 seg, u8 bus, u8 slot, u8 func, u16 offset);

// Writes 8 bits to a PCI device using the ACPI MCFG table info.
void mcfg_write8(u16 seg, u8 bus, u8 slot, u8 func, u16 offset, u8 value);
// Writes 16 bits to a PCI device using the ACPI MCFG table info.
void mcfg_write16(u16 seg, u8 bus, u8 slot, u8 func, u16 offset, u16 value);
// Writes 32 bits to a PCI device using the ACPI MCFG table info.
void mcfg_write32(u16 seg, u8 bus, u8 slot, u8 func, u16 offset, u32 value);
// Returns the MMIO address of the configuration space of the given card.
PhysAddr mcfg_get_cfg_addr(u16 segment, u16 bus, u8 slot, u8 function);
50 changes: 21 additions & 29 deletions include/menix/system/pci/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@

#define pci_log_dev(dev, fmt, ...) \
print_log("pci: %02hhx:%02hhx.%hhx (%hhu,%hhu,%hhu): " fmt, (dev)->slot->bus->id, (dev)->slot->id, \
(dev)->function, (dev)->class, (dev)->sub_class, (dev)->prog_if, ##__VA_ARGS__)
(dev)->function, (dev)->config_space->class, (dev)->config_space->sub_class, \
(dev)->config_space->prog_if, ##__VA_ARGS__)
#define pci_error_dev(dev, fmt, ...) \
print_error("pci: %02hhx:%02hhx.%hhx (%hhu,%hhu,%hhu): " fmt, (dev)->slot->bus->id, (dev)->slot->id, \
(dev)->function, (dev)->class, (dev)->sub_class, (dev)->prog_if, ##__VA_ARGS__)
(dev)->function, (dev)->config_space->class, (dev)->config_space->sub_class, \
(dev)->config_space->prog_if, ##__VA_ARGS__)

#define PCI_ANY_ID (~0U)
#define PCI_DEVICE(ven, dev) \
Expand All @@ -32,21 +34,19 @@ typedef struct PciDriver PciDriver;
typedef struct PciSlot PciSlot;
typedef struct PciBus PciBus;
typedef struct PciDevice PciDevice;
typedef struct PciConfigSpace PciConfigSpace;

// Represents a PCI(e) device.
struct PciDevice
struct ATTR(packed) PciConfigSpace
{
u8 function; // Function index on the current slot.
u16 vendor, device; // Primary IDs of this device.
u16 vendor, device;
u16 command, status;
u8 revision, class, sub_class, prog_if;
u8 revision, prog_if, sub_class, class;
u8 cache_line_size, latency_timer, header_type, bist;

// Fields depending on the header type.
//! CardBus is not supported.
union
{
struct
struct ATTR(packed)
{
u32 bar[6]; // Base addresses.
u32 cardbus_cis; // CardBus CIS pointer.
Expand All @@ -58,7 +58,7 @@ struct PciDevice
u8 min_grant; // Burst period length (in 0.25 µs units).
u8 max_latency; // How often the device needs to access the PCI bus (in 0.25 µs units).
} generic; // Generic device (0)
struct
struct ATTR(packed)
{
u32 bar[2]; // Base addresses.
u8 bus_primary, bus_secondary; // Bus numbers.
Expand All @@ -77,11 +77,17 @@ struct PciDevice
u16 bridge_control; // Bridge control number.
} pci_bridge; // PCI-to-PCI bridge (1)
};
};

Device* dev; // Underlying device.
PciDriver* driver; // The driver managing this device.
usize variant_idx; // Index into a driver-defined structure array.
PciSlot* slot; // The slot this device is on.
// Represents a PCI(e) device.
struct PciDevice
{
volatile PciConfigSpace* config_space; // Configuration space address.
u8 function; // Function index of this device.
Device* dev; // Underlying device.
PciDriver* driver; // The driver managing this device.
usize variant_idx; // Index into a driver-defined structure array.
PciSlot* slot; // The slot this device is on.
};

struct PciSlot
Expand Down Expand Up @@ -132,24 +138,10 @@ typedef struct PciDriver
// Abstraction for PCI mechanisms. Can be e.g. x86 port IO or ACPI.
typedef struct
{
u8 (*pci_read8)(u16 seg, u8 bus, u8 slot, u8 func, u16 offset);
u16 (*pci_read16)(u16 seg, u8 bus, u8 slot, u8 func, u16 offset);
u32 (*pci_read32)(u16 seg, u8 bus, u8 slot, u8 func, u16 offset);
void (*pci_write8)(u16 seg, u8 bus, u8 slot, u8 func, u16 offset, u8 value);
void (*pci_write16)(u16 seg, u8 bus, u8 slot, u8 func, u16 offset, u16 value);
void (*pci_write32)(u16 seg, u8 bus, u8 slot, u8 func, u16 offset, u32 value);

PhysAddr (*get_cfg_addr)(u16 segment, u16 bus, u8 slot, u8 function);
List(PciBus*) buses;
} PciPlatform;

#define PCI_READ8(seg, bus, slot, func, offset) pci_platform.pci_read8(seg, bus, slot, func, offset)
#define PCI_READ16(seg, bus, slot, func, offset) pci_platform.pci_read16(seg, bus, slot, func, offset)
#define PCI_READ32(seg, bus, slot, func, offset) pci_platform.pci_read32(seg, bus, slot, func, offset)

#define PCI_WRITE8(seg, bus, slot, func, offset, value) pci_platform.pci_read8(seg, bus, slot, func, offset, value)
#define PCI_WRITE16(seg, bus, slot, func, offset, value) pci_platform.pci_read16(seg, bus, slot, func, offset, value)
#define PCI_WRITE32(seg, bus, slot, func, offset, value) pci_platform.pci_read32(seg, bus, slot, func, offset, value)

extern PciPlatform pci_platform;

typedef List(PciDriver*) PciDriverList;
Expand Down
1 change: 1 addition & 0 deletions kernel/system/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ add_subdirectory(dtb)
add_subdirectory(net)
add_subdirectory(sch)
add_subdirectory(time)
add_subdirectory(uapi)
add_subdirectory(video)
62 changes: 14 additions & 48 deletions kernel/system/acpi/mcfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,7 @@ void mcfg_init()
}

mcfg = mcfg_table.ptr;

pci_platform.pci_read8 = mcfg_read8;
pci_platform.pci_read16 = mcfg_read16;
pci_platform.pci_read32 = mcfg_read32;
pci_platform.pci_write8 = mcfg_write8;
pci_platform.pci_write16 = mcfg_write16;
pci_platform.pci_write32 = mcfg_write32;
pci_platform.get_cfg_addr = mcfg_get_cfg_addr;

const usize num_entries = (mcfg->hdr.length - sizeof(struct acpi_mcfg)) / sizeof(struct acpi_mcfg_allocation);
list_new(pci_platform.buses, num_entries);
Expand All @@ -52,45 +46,17 @@ void mcfg_init()
pci_init();
}

#define implement_read(type, name, fn) \
type name(u16 seg, u8 bus, u8 slot, u8 func, u16 offset) \
{ \
const usize num_entries = (mcfg->hdr.length - sizeof(struct acpi_mcfg)) / sizeof(struct acpi_mcfg_allocation); \
for (usize i = 0; i < num_entries; i++) \
{ \
struct acpi_mcfg_allocation* entry = &mcfg->entries[i]; \
if (entry->segment != seg) \
continue; \
if (bus < entry->start_bus && bus > entry->end_bus) \
continue; \
void* addr = (void*)ACPI_ADDR( \
((entry->address + (((bus - entry->start_bus) << 20) | (slot << 15) | (func << 12))) | offset)); \
return fn(addr); \
} \
return 0; \
}

#define implement_write(type, name, fn) \
void name(u16 seg, u8 bus, u8 slot, u8 func, u16 offset, type value) \
{ \
const usize num_entries = (mcfg->hdr.length - sizeof(struct acpi_mcfg)) / sizeof(struct acpi_mcfg_allocation); \
for (usize i = 0; i < num_entries; i++) \
{ \
struct acpi_mcfg_allocation* entry = &mcfg->entries[i]; \
if (entry->segment != seg) \
continue; \
if (bus < entry->start_bus && bus > entry->end_bus) \
continue; \
void* addr = (void*)ACPI_ADDR( \
((entry->address + (((bus - entry->start_bus) << 20) | (slot << 15) | (func << 12))) | offset)); \
fn(addr, value); \
} \
PhysAddr mcfg_get_cfg_addr(u16 segment, u16 bus, u8 slot, u8 function)
{
const usize num_entries = (mcfg->hdr.length - sizeof(struct acpi_mcfg)) / sizeof(struct acpi_mcfg_allocation);
for (usize i = 0; i < num_entries; i++)
{
struct acpi_mcfg_allocation* entry = &mcfg->entries[i];
if (entry->segment != segment)
continue;
if (bus < entry->start_bus && bus > entry->end_bus)
continue;
return (PhysAddr)(entry->address + (((bus - entry->start_bus) << 20) | (slot << 15) | (function << 12)));
}

implement_read(u8, mcfg_read8, mmio_read8);
implement_read(u16, mcfg_read16, mmio_read16);
implement_read(u32, mcfg_read32, mmio_read32);

implement_write(u8, mcfg_write8, mmio_write8);
implement_write(u16, mcfg_write16, mmio_write16);
implement_write(u32, mcfg_write32, mmio_write32);
return 0;
}
11 changes: 9 additions & 2 deletions kernel/system/acpi/uacpi_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
#include <uacpi/types.h>
#include <uacpi/uacpi.h>

#ifdef CONFIG_pci
#include <menix/system/acpi/mcfg.h>
#endif

#ifdef CONFIG_arch_x86_64
#include <hpet.h>
#endif
Expand All @@ -32,6 +36,10 @@ void acpi_init(PhysAddr rsdp)
madt_init();
#endif

#ifdef CONFIG_pci
mcfg_init();
#endif

uacpi_initialize(0);
kfree(temp_buffer);

Expand Down Expand Up @@ -121,8 +129,7 @@ uacpi_status uacpi_kernel_raw_io_write(uacpi_io_addr address, uacpi_u8 byte_widt

uacpi_status uacpi_kernel_pci_read(uacpi_pci_address* address, uacpi_size offset, uacpi_u8 byte_width, uacpi_u64* value)
{
PCI_READ8(address->segment, address->bus, address->device, address->function, offset);
return UACPI_STATUS_OK;
return UACPI_STATUS_UNIMPLEMENTED;
}

uacpi_status uacpi_kernel_pci_write(uacpi_pci_address* address, uacpi_size offset, uacpi_u8 byte_width, uacpi_u64 value)
Expand Down
90 changes: 12 additions & 78 deletions kernel/system/bus/pci/device.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <menix/abi/errno.h>
#include <menix/common.h>
#include <menix/memory/pm.h>
#include <menix/system/pci/pci.h>

PciDeviceList pci_devices;
Expand All @@ -9,7 +10,7 @@ i32 pci_register_device(PciDevice* device)
if (device == NULL)
return -ENOENT;
list_push(&pci_devices, device);
pci_log_dev(device, "%s\n", pci_get_class_name(device->class));
pci_log_dev(device, "%s\n", pci_get_class_name(device->config_space->class));

return 0;
}
Expand All @@ -22,100 +23,32 @@ static void pci_scan_device(PciSlot* slot, u8 fn)
const u8 slot_id = slot->id;
const u8 bus_id = slot->bus->id;

const u16 vendor_id = PCI_READ16(0, bus_id, slot_id, fn, 0x0);
// The MMIO config space address of this PCI device.
PhysAddr config_space_phys = pci_platform.get_cfg_addr(0, bus_id, slot_id, fn);
volatile PciConfigSpace* config_space = pm_get_phys_base() + config_space_phys;

// If no device is present, return null.
if (vendor_id == 0xFFFF)
if (config_space->vendor == 0xFFFF)
return;

// Otherwise allocate memory.
PciDevice* device = kmalloc(sizeof(PciDevice));

// Read common header.
device->vendor = vendor_id;
device->device = PCI_READ16(0, bus_id, slot_id, fn, 0x2);
device->command = PCI_READ16(0, bus_id, slot_id, fn, 0x4);
device->status = PCI_READ16(0, bus_id, slot_id, fn, 0x6);
device->revision = PCI_READ8(0, bus_id, slot_id, fn, 0x8);
device->prog_if = PCI_READ8(0, bus_id, slot_id, fn, 0x9);
device->sub_class = PCI_READ8(0, bus_id, slot_id, fn, 0xA);
device->class = PCI_READ8(0, bus_id, slot_id, fn, 0xB);
device->cache_line_size = PCI_READ8(0, bus_id, slot_id, fn, 0xC);
device->latency_timer = PCI_READ8(0, bus_id, slot_id, fn, 0xD);
device->header_type = PCI_READ8(0, bus_id, slot_id, fn, 0xE);
device->bist = PCI_READ8(0, bus_id, slot_id, fn, 0xF);

// Read header based on the type.
switch (device->header_type & PCI_TYPE_MASK)
{
case PCI_TYPE_GENERIC:
{
device->generic.bar[0] = PCI_READ32(0, bus_id, slot_id, fn, 0x10);
device->generic.bar[1] = PCI_READ32(0, bus_id, slot_id, fn, 0x14);
device->generic.bar[2] = PCI_READ32(0, bus_id, slot_id, fn, 0x18);
device->generic.bar[3] = PCI_READ32(0, bus_id, slot_id, fn, 0x1C);
device->generic.bar[4] = PCI_READ32(0, bus_id, slot_id, fn, 0x20);
device->generic.bar[5] = PCI_READ32(0, bus_id, slot_id, fn, 0x24);
device->generic.cardbus_cis = PCI_READ32(0, bus_id, slot_id, fn, 0x28);
device->generic.sub_vendor = PCI_READ16(0, bus_id, slot_id, fn, 0x2C);
device->generic.sub_device = PCI_READ16(0, bus_id, slot_id, fn, 0x2E);
device->generic.expansion_rom = PCI_READ32(0, bus_id, slot_id, fn, 0x30);
device->generic.capabilities = PCI_READ8(0, bus_id, slot_id, fn, 0x34);
device->generic.int_line = PCI_READ8(0, bus_id, slot_id, fn, 0x3C);
device->generic.int_pin = PCI_READ8(0, bus_id, slot_id, fn, 0x3D);
device->generic.min_grant = PCI_READ8(0, bus_id, slot_id, fn, 0x3E);
device->generic.max_latency = PCI_READ8(0, bus_id, slot_id, fn, 0x3F);
break;
}
case PCI_TYPE_PCI_BRIDGE:
{
device->pci_bridge.bar[0] = PCI_READ32(0, bus_id, slot_id, fn, 0x10);
device->pci_bridge.bar[1] = PCI_READ32(0, bus_id, slot_id, fn, 0x14);
device->pci_bridge.bus_primary = PCI_READ8(0, bus_id, slot_id, fn, 0x18);
device->pci_bridge.bus_secondary = PCI_READ8(0, bus_id, slot_id, fn, 0x19);
device->pci_bridge.bus_subordinate = PCI_READ8(0, bus_id, slot_id, fn, 0x1A);
device->pci_bridge.latency_timer2 = PCI_READ8(0, bus_id, slot_id, fn, 0x1B);
device->pci_bridge.io_base = PCI_READ8(0, bus_id, slot_id, fn, 0x1C);
device->pci_bridge.io_limit = PCI_READ8(0, bus_id, slot_id, fn, 0x1D);
device->pci_bridge.status2 = PCI_READ16(0, bus_id, slot_id, fn, 0x1E);
device->pci_bridge.mem_base = PCI_READ16(0, bus_id, slot_id, fn, 0x20);
device->pci_bridge.mem_limit = PCI_READ16(0, bus_id, slot_id, fn, 0x22);
device->pci_bridge.pre_base = PCI_READ16(0, bus_id, slot_id, fn, 0x24);
device->pci_bridge.pre_limit = PCI_READ16(0, bus_id, slot_id, fn, 0x26);
device->pci_bridge.pre_base_upper = PCI_READ32(0, bus_id, slot_id, fn, 0x28);
device->pci_bridge.pre_limit_upper = PCI_READ32(0, bus_id, slot_id, fn, 0x2C);
device->pci_bridge.io_base_upper = PCI_READ16(0, bus_id, slot_id, fn, 0x30);
device->pci_bridge.io_limit_upper = PCI_READ16(0, bus_id, slot_id, fn, 0x32);
device->pci_bridge.capabilities = PCI_READ8(0, bus_id, slot_id, fn, 0x34);
device->pci_bridge.expansion_rom = PCI_READ32(0, bus_id, slot_id, fn, 0x38);
device->pci_bridge.int_line = PCI_READ8(0, bus_id, slot_id, fn, 0x3C);
device->pci_bridge.int_pin = PCI_READ8(0, bus_id, slot_id, fn, 0x3D);
device->pci_bridge.bridge_control = PCI_READ16(0, bus_id, slot_id, fn, 0x3E);
break;
}
default:
{
pci_log_dev(device, "Unsupported header type %hhu, skipping!\n", device->header_type);
return;
}
}

device->slot = slot;
device->function = fn;
device->config_space = config_space;

// Scan all other functions if multi-function bit is set.
if (fn == 0 && device->header_type & PCI_TYPE_MF_MASK)
if (fn == 0 && config_space->header_type & PCI_TYPE_MF_MASK)
{
for (usize f = 1; f < 8; f++)
pci_scan_device(slot, f);
}

// Handle PCI bridges.
if ((device->header_type & PCI_TYPE_MASK) == PCI_TYPE_PCI_BRIDGE)
if ((config_space->header_type & PCI_TYPE_MASK) == PCI_TYPE_PCI_BRIDGE)
{
pci_log_dev(device, "PCI-to-PCI bridge: Primary = %hhx, Secondary = %hhx, Subordinate = %hhx\n",
device->pci_bridge.bus_primary, device->pci_bridge.bus_secondary,
device->pci_bridge.bus_subordinate);
config_space->pci_bridge.bus_primary, config_space->pci_bridge.bus_secondary,
config_space->pci_bridge.bus_subordinate);
}

// Register the device.
Expand All @@ -129,6 +62,7 @@ static void pci_scan_device(PciSlot* slot, u8 fn)

void pci_scan_devices()
{
print_log("pci: Scanning devices.\n");
list_iter(&pci_platform.buses, bus)
{
for (usize slot = 0; slot < PCI_MAX_SLOTS; slot++)
Expand Down
11 changes: 6 additions & 5 deletions kernel/system/bus/pci/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,20 @@ i32 pci_register_driver(PciDriver* driver)
for (usize variant = 0; variant <= driver->num_variants; variant++)
{
const PciVariant* const var = &driver->variants[variant];
const volatile PciConfigSpace* cfg = dev->config_space;

// Check if this driver is a generic class driver. If it isn't, check if the IDs match.
if (var->vendor != (u16)PCI_ANY_ID && var->vendor != dev->vendor)
if (var->vendor != (u16)PCI_ANY_ID && var->vendor != cfg->vendor)
continue;
if (var->device != (u16)PCI_ANY_ID && var->device != dev->device)
if (var->device != (u16)PCI_ANY_ID && var->device != cfg->device)
continue;

// If it's generic make sure the class types match.
if (var->has_class && var->class != dev->class)
if (var->has_class && var->class != cfg->class)
continue;
if (var->has_sub_class && var->sub_class != dev->sub_class)
if (var->has_sub_class && var->sub_class != cfg->sub_class)
continue;
if (var->has_prog_if && var->prog_if != dev->prog_if)
if (var->has_prog_if && var->prog_if != cfg->prog_if)
continue;

// Connect the driver to the device.
Expand Down
Loading

0 comments on commit 321b634

Please sign in to comment.