[init] Move PCIe probing to srv.init

This was kept in the kernel as a way to keep exercising the code, but it
doesn't belong there. This moves it to init, which doesn't do anything
but probe for devices currently - but at least it's executing the code
in userspace now.
This commit is contained in:
Justin C. Miller
2023-02-20 11:23:49 -08:00
parent 15e2f8abf3
commit 3a7a18011c
16 changed files with 294 additions and 235 deletions

View File

@@ -1,210 +0,0 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <util/enum_bitfields.h>
#include <util/misc.h> // for byteswap32
struct acpi_table_header
{
uint32_t type;
uint32_t length;
uint8_t revision;
uint8_t checksum;
char oem_id[6];
char oem_table[8];
uint32_t oem_revision;
uint32_t creator_id;
uint32_t creator_revision;
bool validate(uint32_t expected_type = 0) const;
} __attribute__ ((packed));
#define TABLE_HEADER(signature) \
static constexpr uint32_t type_id = util::byteswap32(signature); \
acpi_table_header header;
template <typename T>
bool acpi_validate(const T *t) { return t->header.validate(T::type_id); }
template <typename T>
size_t acpi_table_entries(const T *t, size_t size)
{
return (t->header.length - sizeof(T)) / size;
}
enum class acpi_gas_type : uint8_t
{
system_memory,
system_io,
pci_config,
embedded,
smbus,
platform_channel = 0x0a,
functional_fixed = 0x7f
};
struct acpi_gas
{
acpi_gas_type type;
uint8_t reg_bits;
uint8_t reg_offset;
uint8_t access_size;
uint64_t address;
} __attribute__ ((packed));
enum class acpi_fadt_flags : uint32_t
{
wbinvd = 0x00000001,
wbinvd_flush = 0x00000002,
proc_c1 = 0x00000004,
p_lvl2_up = 0x00000008,
pwr_button = 0x00000010,
slp_button = 0x00000020,
fix_rtc = 0x00000040,
rtc_s4 = 0x00000080,
tmr_val_ext = 0x00000100,
dck_cap = 0x00000200,
reset_reg_sup = 0x00000400,
sealed_case = 0x00000800,
headless = 0x00001000,
cpu_sw_slp = 0x00002000,
pci_exp_wak = 0x00004000,
use_plat_clock = 0x00008000,
s4_rtc_sts_val = 0x00010000,
remote_pwr_cap = 0x00020000,
apic_cluster = 0x00040000,
apic_physical = 0x00080000,
hw_reduced_acpi = 0x00100000,
low_pwr_s0_idle = 0x00200000
};
is_bitfield(acpi_fadt_flags);
struct acpi_fadt
{
TABLE_HEADER('FACP');
uint32_t facs;
uint32_t dsdt;
uint8_t reserved0;
uint8_t preferred_pm_profile;
uint16_t sci_interrupt;
uint32_t smi_port;
uint8_t acpi_enable;
uint8_t acpi_disable;
uint8_t s4bios_req;
uint8_t pstate_control;
uint32_t pm1a_event_block;
uint32_t pm1b_event_block;
uint32_t pm1a_control_block;
uint32_t pm1b_control_block;
uint32_t pm2_control_block;
uint32_t pm_timer_block;
uint32_t gpe0_block;
uint32_t gpe1_block;
uint8_t pm1_event_length;
uint8_t pm1_control_length;
uint8_t pm2_control_length;
uint8_t pm_timer_length;
uint8_t gpe0_block_length;
uint8_t gpe1_block_length;
uint8_t gpe1_base;
uint8_t cstate_control;
uint16_t cstate2_latency;
uint16_t cstate3_latency;
uint16_t flush_size;
uint16_t flush_stride;
uint8_t duty_offset;
uint8_t duty_width;
uint8_t day_alarm;
uint8_t month_alarm;
uint8_t century;
uint16_t iapc_boot_arch;
uint8_t reserved1;
acpi_fadt_flags flags;
acpi_gas reset_reg;
uint8_t reset_value;
uint16_t arm_boot_arch;
uint8_t fadt_minor_version;
uint64_t x_facs;
uint64_t x_dsdt;
acpi_gas x_pm1a_event_block;
acpi_gas x_pm1b_event_block;
acpi_gas x_pm1a_control_block;
acpi_gas x_pm1b_control_block;
acpi_gas x_pm2_control_block;
acpi_gas x_pm_timer_block;
acpi_gas x_gpe0_block;
acpi_gas x_gpe1_block;
acpi_gas sleep_control_reg;
acpi_gas sleep_status_reg;
uint64_t hypervisor_vendor_id;
} __attribute__ ((packed));
struct acpi_xsdt
{
TABLE_HEADER('XSDT');
acpi_table_header *headers[0];
} __attribute__ ((packed));
struct acpi_apic
{
TABLE_HEADER('APIC');
uint32_t local_address;
uint32_t flags;
uint8_t controller_data[0];
} __attribute__ ((packed));
struct acpi_mcfg_entry
{
uint64_t base;
uint16_t group;
uint8_t bus_start;
uint8_t bus_end;
uint32_t reserved;
} __attribute__ ((packed));
struct acpi_mcfg
{
TABLE_HEADER('MCFG');
uint64_t reserved;
acpi_mcfg_entry entries[0];
} __attribute__ ((packed));
struct acpi_hpet
{
TABLE_HEADER('HPET');
uint32_t hardware_id;
acpi_gas base_address;
uint8_t index;
uint16_t periodic_min;
uint8_t attributes;
} __attribute__ ((packed));
struct acpi_bgrt
{
TABLE_HEADER('BGRT');
uint16_t version;
uint8_t status;
uint8_t type;
uintptr_t address;
uint32_t offset_x;
uint32_t offset_y;
} __attribute__ ((packed));

View File

@@ -3,9 +3,9 @@
#include <stdint.h>
#include <util/misc.h> // for checksum
#include <util/pointers.h>
#include <arch/acpi/tables.h>
#include "assert.h"
#include "acpi_tables.h"
#include "apic.h"
#include "clock.h"
#include "device_manager.h"
@@ -21,37 +21,6 @@ static const char expected_signature[] = "RSD PTR ";
device_manager device_manager::s_instance;
struct acpi1_rsdp
{
char signature[8];
uint8_t checksum;
char oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
} __attribute__ ((packed));
struct acpi2_rsdp
{
char signature[8];
uint8_t checksum10;
char oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
uint32_t length;
acpi_table_header *xsdt_address;
uint8_t checksum20;
uint8_t reserved[3];
} __attribute__ ((packed));
bool
acpi_table_header::validate(uint32_t expected_type) const
{
if (util::checksum(this, length) != 0) return false;
return !expected_type || (expected_type == type);
}
device_manager::device_manager() :
m_lapic_base(0)
{
@@ -63,37 +32,32 @@ device_manager::device_manager() :
m_irqs[2] = {ignore_event, 0};
}
template <typename T> static const T *
check_get_table(const acpi_table_header *header)
{
kassert(header && header->validate(T::type_id), "Invalid ACPI table.");
return reinterpret_cast<const T *>(header);
}
void
device_manager::parse_acpi(const void *root_table)
{
kassert(root_table != 0, "ACPI root table pointer is null.");
const acpi1_rsdp *acpi1 = mem::to_virtual(
reinterpret_cast<const acpi1_rsdp *>(root_table));
const acpi::rsdp1 *acpi1 = mem::to_virtual(
reinterpret_cast<const acpi::rsdp1 *>(root_table));
for (int i = 0; i < sizeof(acpi1->signature); ++i)
kassert(acpi1->signature[i] == expected_signature[i],
"ACPI RSDP table signature mismatch");
uint8_t sum = util::checksum(acpi1, sizeof(acpi1_rsdp), 0);
uint8_t sum = util::checksum(acpi1, sizeof(acpi::rsdp1), 0);
kassert(sum == 0, "ACPI 1.0 RSDP checksum mismatch.");
kassert(acpi1->revision > 1, "ACPI 1.0 not supported.");
const acpi2_rsdp *acpi2 =
reinterpret_cast<const acpi2_rsdp *>(acpi1);
const acpi::rsdp2 *acpi2 =
reinterpret_cast<const acpi::rsdp2 *>(acpi1);
sum = util::checksum(acpi2, sizeof(acpi2_rsdp), sizeof(acpi1_rsdp));
sum = util::checksum(acpi2, sizeof(acpi::rsdp2), sizeof(acpi::rsdp1));
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
load_xsdt(mem::to_virtual(acpi2->xsdt_address));
const acpi::table_header *xsdt = mem::to_virtual(acpi2->xsdt_address);
kassert(xsdt->validate(), "Bad XSDT table");
load_xsdt(xsdt);
}
const device_manager::apic_nmi *
@@ -129,53 +93,50 @@ put_sig(char *into, uint32_t type)
}
void
device_manager::load_xsdt(const acpi_table_header *header)
device_manager::load_xsdt(const acpi::table_header *header)
{
const auto *xsdt = check_get_table<acpi_xsdt>(header);
const auto *xsdt = acpi::check_get_table<acpi::xsdt>(header);
char sig[5] = {0,0,0,0,0};
log::info(logs::device, "ACPI 2.0+ tables loading");
put_sig(sig, xsdt->header.type);
log::verbose(logs::device, " Found table %s", sig);
log::verbose(logs::device, " Loading table %s", sig);
size_t num_tables = acpi_table_entries(xsdt, sizeof(void*));
size_t num_tables = acpi::table_entries(xsdt, sizeof(void*));
for (size_t i = 0; i < num_tables; ++i) {
const acpi_table_header *header =
const acpi::table_header *header =
mem::to_virtual(xsdt->headers[i]);
put_sig(sig, header->type);
log::verbose(logs::device, " Found table %s", sig);
kassert(header->validate(), "Table failed validation.");
put_sig(sig, header->type);
switch (header->type) {
case acpi_apic::type_id:
case acpi::apic::type_id:
log::verbose(logs::device, " Loading table %s", sig);
load_apic(header);
break;
case acpi_mcfg::type_id:
load_mcfg(header);
break;
case acpi_hpet::type_id:
case acpi::hpet::type_id:
log::verbose(logs::device, " Loading table %s", sig);
load_hpet(header);
break;
default:
log::verbose(logs::device, " Skipping table %s", sig);
break;
}
}
}
void
device_manager::load_apic(const acpi_table_header *header)
device_manager::load_apic(const acpi::table_header *header)
{
const auto *apic = check_get_table<acpi_apic>(header);
const auto *apic = acpi::check_get_table<acpi::apic>(header);
m_lapic_base = apic->local_address;
size_t count = acpi_table_entries(apic, 1);
size_t count = acpi::table_entries(apic, 1);
uint8_t const *p = apic->controller_data;
uint8_t const *end = p + count;
@@ -265,33 +226,9 @@ device_manager::load_apic(const acpi_table_header *header)
}
void
device_manager::load_mcfg(const acpi_table_header *header)
device_manager::load_hpet(const acpi::table_header *header)
{
const auto *mcfg = check_get_table<acpi_mcfg>(header);
size_t count = acpi_table_entries(mcfg, sizeof(acpi_mcfg_entry));
m_pci.set_size(count);
m_devices.set_capacity(16);
for (unsigned i = 0; i < count; ++i) {
const acpi_mcfg_entry &mcfge = mcfg->entries[i];
m_pci[i].group = mcfge.group;
m_pci[i].bus_start = mcfge.bus_start;
m_pci[i].bus_end = mcfge.bus_end;
m_pci[i].base = mem::to_virtual<uint32_t>(mcfge.base);
log::spam(logs::device, " Found MCFG entry: base %lx group %d bus %d-%d",
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
}
probe_pci();
}
void
device_manager::load_hpet(const acpi_table_header *header)
{
const auto *hpet = check_get_table<acpi_hpet>(header);
const auto *hpet = acpi::check_get_table<acpi::hpet>(header);
log::verbose(logs::device, " Found HPET device #%3d: base %016lx pmin %d attr %02x",
hpet->index, hpet->base_address.address, hpet->periodic_min, hpet->attributes);
@@ -311,28 +248,6 @@ device_manager::load_hpet(const acpi_table_header *header)
reinterpret_cast<uint64_t*>(hpet->base_address.address + mem::linear_offset));
}
void
device_manager::probe_pci()
{
for (auto &pci : m_pci) {
log::verbose(logs::device, "Probing PCI group at base %016lx", pci.base);
for (int bus = pci.bus_start; bus <= pci.bus_end; ++bus) {
for (int dev = 0; dev < 32; ++dev) {
if (!pci.has_device(bus, dev, 0)) continue;
auto &d0 = m_devices.emplace(pci, bus, dev, 0);
if (!d0.multi()) continue;
for (int i = 1; i < 8; ++i) {
if (pci.has_device(bus, dev, i))
m_devices.emplace(pci, bus, dev, i);
}
}
}
}
}
static uint64_t
tsc_clock_source(void*)
{
@@ -366,7 +281,7 @@ device_manager::init_drivers()
// becomes the singleton
master_clock = new clock(h.rate(), hpet_clock_source, &h);
log::info(logs::clock, "Created master clock using HPET 0: Rate %d", h.rate());
log::info(logs::timer, "Created master clock using HPET 0: Rate %d", h.rate());
} else {
//TODO: Other clocks, APIC clock?
master_clock = new clock(5000, tsc_clock_source, nullptr);
@@ -410,10 +325,10 @@ device_manager::unbind_irqs(obj::event *target)
}
}
/*
bool
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
{
/*
// TODO: find gaps to fill
uint8_t irq = m_irqs.count();
isr vector = isr::irq00 + irq;
@@ -424,12 +339,6 @@ device_manager::allocate_msi(const char *name, pci_device &device, irq_callback
device.write_msi_regs(
0xFEE00000,
static_cast<uint16_t>(vector));
*/
return true;
}
void
device_manager::register_block_device(block_device *blockdev)
{
m_blockdevs.append(blockdev);
}
*/

View File

@@ -6,11 +6,13 @@
#include "apic.h"
#include "hpet.h"
#include "pci.h"
struct acpi_table_header;
class block_device;
namespace acpi {
struct table_header;
}
namespace obj {
class event;
}
@@ -53,18 +55,6 @@ public:
/// \arg target The endpoint to remove
void unbind_irqs(obj::event *target);
/// Allocate an MSI IRQ for a device
/// \arg name Name of the interrupt, for display to user
/// \arg device Device this MSI is being allocated for
/// \arg cb Callback to call when the interrupt is received
/// \arg data Data to pass to the callback
/// \returns True if an interrupt was allocated successfully
bool allocate_msi(
const char *name,
pci_device &device,
irq_callback cb,
void *data);
/// Dispatch an IRQ interrupt
/// \arg irq The irq number of the interrupt
/// \returns True if the interrupt was handled
@@ -103,23 +93,6 @@ public:
/// configuration, or null if no configuration was provided
const irq_override * get_irq_override(uint8_t irq) const;
/// Register the existance of a block device.
/// \arg blockdev Pointer to the block device
void register_block_device(block_device *blockdev);
/// Get the number of block devices in the system
/// \returns A count of devices
inline unsigned get_num_block_devices() const { return m_blockdevs.count(); }
/// Get a block device
/// \arg i Index of the device to get
/// \returns A pointer to the requested device, or nullptr
inline block_device * get_block_device(unsigned i)
{
return i < m_blockdevs.count() ?
m_blockdevs[i] : nullptr;
}
/// Get an HPET device
/// \arg i Index of the device to get
/// \returns A pointer to the requested device, or nullptr
@@ -132,23 +105,15 @@ public:
private:
/// Parse the ACPI XSDT and load relevant sub-tables.
/// \arg xsdt Pointer to the XSDT from the firmware
void load_xsdt(const acpi_table_header *xsdt);
void load_xsdt(const acpi::table_header *xsdt);
/// Parse the ACPI MADT and initialize APICs from it.
/// \arg apic Pointer to the MADT from the XSDT
void load_apic(const acpi_table_header *apic);
/// Parse the ACPI MCFG and initialize PCIe from it.
/// \arg mcfg Pointer to the MCFG from the XSDT
void load_mcfg(const acpi_table_header *mcfg);
void load_apic(const acpi::table_header *apic);
/// Parse the ACPI HPET and initialize an HPET from it.
/// \arg hpet Pointer to the HPET from the XSDT
void load_hpet(const acpi_table_header *hpet);
/// Probe the PCIe busses and add found devices to our
/// device list. The device list is destroyed and rebuilt.
void probe_pci();
void load_hpet(const acpi::table_header *hpet);
/// Handle a bad IRQ. Called when an interrupt is dispatched
/// that has no callback.
@@ -162,9 +127,6 @@ private:
util::vector<apic_nmi> m_nmis;
util::vector<irq_override> m_overrides;
util::vector<pci_group> m_pci;
util::vector<pci_device> m_devices;
struct irq_binding
{
obj::event *target = nullptr;
@@ -172,8 +134,6 @@ private:
};
util::vector<irq_binding> m_irqs;
util::vector<block_device *> m_blockdevs;
static device_manager s_instance;
device_manager(const device_manager &) = delete;

View File

@@ -43,7 +43,6 @@ kernel = module("kernel",
"objects/vm_area.cpp",
"page_table.cpp",
"page_tree.cpp",
"pci.cpp",
"scheduler.cpp",
"smp.cpp",
"smp.s",

View File

@@ -1,183 +0,0 @@
#include "assert.h"
#include "logger.h"
#include "interrupts.h"
#include "pci.h"
struct pci_cap_msi
{
pci_cap::type id;
uint8_t next;
uint16_t control;
} __attribute__ ((packed));
struct pci_cap_msi32
{
pci_cap::type id;
uint8_t next;
uint16_t control;
uint32_t address;
uint16_t data;
uint16_t reserved;
uint32_t mask;
uint32_t pending;
} __attribute__ ((packed));
struct pci_cap_msi64
{
pci_cap::type id;
uint8_t next;
uint16_t control;
uint64_t address;
uint16_t data;
uint16_t reserved;
uint32_t mask;
uint32_t pending;
} __attribute__ ((packed));
/*
void dump_msi(pci_cap_msi *cap)
{
auto cons = console::get();
cons->printf("MSI Cap:\n");
cons->printf(" id: %02x\n", cap->id);
cons->printf(" next: %02x\n", cap->next);
cons->printf("control: %04x\n", cap->control);
if (cap->control & 0x0080) {
pci_cap_msi64 *cap64 = reinterpret_cast<pci_cap_msi64 *>(cap);
cons->printf("address: %016x\n", cap64->address);
cons->printf(" data: %04x\n", cap64->data);
if (cap->control & 0x100) {
cons->printf(" mask: %08x\n", cap64->mask);
cons->printf("pending: %08x\n", cap64->pending);
}
} else {
pci_cap_msi32 *cap32 = reinterpret_cast<pci_cap_msi32 *>(cap);
cons->printf("address: %08x\n", cap32->address);
cons->printf(" data: %04x\n", cap32->data);
if (cap->control & 0x100) {
cons->printf(" mask: %08x\n", cap32->mask);
cons->printf("pending: %08x\n", cap32->pending);
}
}
cons->putc('\n');
};
*/
pci_device::pci_device() :
m_base(nullptr),
m_bus_addr(0),
m_vendor(0),
m_device(0),
m_class(0),
m_subclass(0),
m_progif(0),
m_revision(0),
m_irq(isr::isrIgnore0),
m_header_type(0)
{
}
pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func) :
m_base(group.base_for(bus, device, func)),
m_msi(nullptr),
m_bus_addr(bus_addr(bus, device, func)),
m_irq(isr::isrIgnore0)
{
m_vendor = m_base[0] & 0xffff;
m_device = (m_base[0] >> 16) & 0xffff;
m_revision = m_base[2] & 0xff;
m_progif = (m_base[2] >> 8) & 0xff;
m_subclass = (m_base[2] >> 16) & 0xff;
m_class = (m_base[2] >> 24) & 0xff;
m_header_type = (m_base[3] >> 16) & 0x7f;
m_multi = ((m_base[3] >> 16) & 0x80) == 0x80;
uint16_t *command = reinterpret_cast<uint16_t *>(&m_base[1]);
*command |= 0x400; // Mask old INTx style interrupts
uint16_t *status = command + 1;
log::info(logs::device, "Found PCIe device at %02d:%02d:%d of type %x.%x.%x id %04x:%04x",
bus, device, func, m_class, m_subclass, m_progif, m_vendor, m_device);
log::spam(logs::device, " = BAR0 %016lld", get_bar(0));
log::spam(logs::device, " = BAR1 %016lld", get_bar(1));
if (*status & 0x0010) {
// Walk the extended capabilities list
uint8_t next = m_base[13] & 0xff;
while (next) {
pci_cap *cap = reinterpret_cast<pci_cap *>(util::offset_pointer(m_base, next));
next = cap->next;
log::verbose(logs::device, " - found PCI cap type %02x", cap->id);
if (cap->id == pci_cap::type::msi) {
m_msi = cap;
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(cap);
mcap->control &= ~0x70; // at most 1 vector allocated
mcap->control |= 0x01; // Enable interrupts, at most 1 vector allocated
}
}
}
}
uint32_t
pci_device::get_bar(unsigned i)
{
if (m_header_type == 0) {
kassert(i < 6, "Requested BAR >5 for PCI device");
} else if (m_header_type == 1) {
kassert(i < 2, "Requested BAR >1 for PCI bridge");
} else {
kassert(0, "Requested BAR for other PCI device type");
}
return m_base[4+i];
}
void
pci_device::set_bar(unsigned i, uint32_t val)
{
if (m_header_type == 0) {
kassert(i < 6, "Requested BAR >5 for PCI device");
} else if (m_header_type == 1) {
kassert(i < 2, "Requested BAR >1 for PCI bridge");
} else {
kassert(0, "Requested BAR for other PCI device type");
}
m_base[4+i] = val;
}
void
pci_device::write_msi_regs(uintptr_t address, uint16_t data)
{
kassert(m_msi, "Tried to write MSI for a device without that cap");
if (m_msi->id == pci_cap::type::msi) {
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(m_msi);
if (mcap->control & 0x0080) {
pci_cap_msi64 *mcap64 = reinterpret_cast<pci_cap_msi64 *>(m_msi);
mcap64->address = address;
mcap64->data = data;
} else {
pci_cap_msi32 *mcap32 = reinterpret_cast<pci_cap_msi32 *>(m_msi);
mcap32->address = address;
mcap32->data = data;
}
uint16_t control = mcap->control;
control &= 0xff8f; // We're allocating one vector, clear 6::4
control |= 0x0001; // Enable MSI
mcap->control = control;
} else {
kassert(0, "MIS-X is NYI");
}
}
bool
pci_group::has_device(uint8_t bus, uint8_t device, uint8_t func)
{
return (*base_for(bus, device, func) & 0xffff) != 0xffff;
}

View File

@@ -1,141 +0,0 @@
#pragma once
/// \file pci.h
/// PCI devices and groups
#include <stdint.h>
#include <util/pointers.h>
struct pci_group;
enum class isr : uint8_t;
struct pci_cap
{
enum class type : uint8_t
{
msi = 0x05,
msix = 0x11
};
type id;
uint8_t next;
} __attribute__ ((packed));
/// Information about a discovered PCIe device
class pci_device
{
public:
/// Default constructor creates an empty object.
pci_device();
/// Constructor
/// \arg group The group of this device's bus
/// \arg bus The bus number this device is on
/// \arg device The device number of this device
/// \arg func The function number of this device
pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func);
/// Check if this device is multi-function.
/// \returns True if this device is multi-function
inline bool multi() const { return m_multi; }
/// Get the bus number this device is on.
/// \returns The bus number
inline uint8_t bus() const { return (m_bus_addr >> 8); }
/// Get the device number of this device on its bus
/// \returns The device number
inline uint8_t device() const { return (m_bus_addr >> 3) & 0x1f; }
/// Get the function number of this device on its device
/// \returns The function number
inline uint8_t function() const { return m_bus_addr & 0x7; }
/// Get the device class
/// \returns The PCI device class
inline uint8_t devclass() const { return m_class; }
/// Get the device subclass
/// \returns The PCI device subclass
inline uint8_t subclass() const { return m_subclass; }
/// Get the device program interface
/// \returns The PCI device program interface
inline uint8_t progif() const { return m_progif; }
/// Read one of the device's Base Address Registers
/// \arg i Which BAR to read (up to 5 for non-bridges)
/// \returns The contents of the BAR
uint32_t get_bar(unsigned i);
/// Write one of the device's Base Address Registers
/// \arg i Which BAR to read (up to 5 for non-bridges)
/// \arg val The value to write
void set_bar(unsigned i, uint32_t val);
/// Write to the MSI registers
/// \arg addr The address to write to the MSI address registers
/// \arg data The value to write to the MSI data register
void write_msi_regs(uintptr_t addr, uint16_t data);
/// Get a bus address, given the bus/device/function numbers.
/// \arg bus Number of the bus
/// \arg device Index of the device on the bus
/// \arg func The function number within the device
/// \returns The computed bus_addr
static inline uint16_t bus_addr(uint8_t bus, uint8_t device, uint8_t func)
{
return bus << 8 | device << 3 | func;
}
private:
uint32_t *m_base;
pci_cap *m_msi;
/// Bus address: 15:8 bus, 7:3 device, 2:0 device
uint16_t m_bus_addr;
uint16_t m_vendor;
uint16_t m_device;
uint8_t m_class;
uint8_t m_subclass;
uint8_t m_progif;
uint8_t m_revision;
bool m_multi;
// Might as well cache these to fill out the struct align
isr m_irq;
uint8_t m_header_type;
};
/// Represents data about a PCI bus group from the ACPI MCFG
struct pci_group
{
uint16_t group;
uint16_t bus_start;
uint16_t bus_end;
uint32_t *base;
/// Get the base address of the MMIO configuration registers for a device
/// \arg bus The bus number of the device (relative to this group)
/// \arg device The device number on the given bus
/// \arg func The function number on the device
/// \returns A pointer to the memory-mapped configuration registers
inline uint32_t * base_for(uint8_t bus, uint8_t device, uint8_t func)
{
return util::offset_pointer(base,
pci_device::bus_addr(bus, device, func) << 12);
}
/// Check if the given device function is present.
/// \arg bus The bus number of the device (relative to this group)
/// \arg device The device number on the given bus
/// \arg func The function number on the device
/// \returns True if the device function is present
bool has_device(uint8_t bus, uint8_t device, uint8_t func);
};