[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:
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
141
src/kernel/pci.h
141
src/kernel/pci.h
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user