[pci] Move PCIe and ACPI code to their own libs
This pulls them out of srv.init (and src/include where the kernel uses them) and puts them into their own libs for sharing.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
#include <stdint.h>
|
||||
#include <util/misc.h> // for checksum
|
||||
#include <util/pointers.h>
|
||||
#include <arch/acpi/tables.h>
|
||||
#include <acpi/tables.h>
|
||||
|
||||
#include "kassert.h"
|
||||
#include "apic.h"
|
||||
|
||||
@@ -5,7 +5,7 @@ kernel = module("kernel",
|
||||
basename = "jsix",
|
||||
targets = [ "kernel" ],
|
||||
description = "jsix kernel",
|
||||
deps = [ "util", "cpu", "bootproto", "j6" ],
|
||||
deps = [ "util", "cpu", "bootproto", "j6", "acpi" ],
|
||||
static = True,
|
||||
ld_script = "kernel.ld",
|
||||
sources = [
|
||||
|
||||
79
src/libraries/acpi/acpi.cpp
Normal file
79
src/libraries/acpi/acpi.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include <acpi/acpi.h>
|
||||
#include <acpi/tables.h>
|
||||
#include <j6/syslog.hh>
|
||||
|
||||
namespace acpi {
|
||||
|
||||
system::system(const void* phys, const void *virt) :
|
||||
m_offset { util::get_offset(phys, virt) },
|
||||
m_root { reinterpret_cast<const rsdp2*>(virt) }
|
||||
{}
|
||||
|
||||
system::iterator
|
||||
system::begin() const
|
||||
{
|
||||
const xsdt *sdt =
|
||||
acpi::check_get_table<xsdt>(m_root->xsdt_address);
|
||||
|
||||
if (!sdt)
|
||||
return {nullptr, 0};
|
||||
|
||||
return {&sdt->headers[0], m_offset};
|
||||
}
|
||||
|
||||
|
||||
system::iterator
|
||||
system::end() const
|
||||
{
|
||||
const xsdt *sdt =
|
||||
acpi::check_get_table<xsdt>(m_root->xsdt_address);
|
||||
|
||||
if (!sdt)
|
||||
return {nullptr, 0};
|
||||
|
||||
size_t nheaders = table_entries<xsdt>(sdt, sizeof(table_header*));
|
||||
return {&sdt->headers[nheaders], m_offset};
|
||||
}
|
||||
|
||||
#if 0
|
||||
void
|
||||
load_acpi(j6_handle_t sys, const bootproto::module *mod)
|
||||
{
|
||||
const bootproto::acpi *info = mod->data<bootproto::acpi>();
|
||||
const util::const_buffer ®ion = info->region;
|
||||
|
||||
map_phys(sys, region.pointer, region.count);
|
||||
|
||||
const void *root_table = info->root;
|
||||
if (!root_table) {
|
||||
j6::syslog(j6::logs::srv, j6::log_level::error, "null ACPI root table pointer");
|
||||
return;
|
||||
}
|
||||
|
||||
const acpi::rsdp2 *acpi2 =
|
||||
reinterpret_cast<const acpi::rsdp2 *>(root_table);
|
||||
|
||||
const auto *xsdt =
|
||||
acpi::check_get_table<acpi::xsdt>(acpi2->xsdt_address);
|
||||
|
||||
size_t num_tables = acpi::table_entries(xsdt, sizeof(void*));
|
||||
for (size_t i = 0; i < num_tables; ++i) {
|
||||
const acpi::table_header *header = xsdt->headers[i];
|
||||
if (!header->validate()) {
|
||||
j6::syslog(j6::logs::srv, j6::log_level::error, "ACPI table at %lx failed validation", header);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (header->type) {
|
||||
case acpi::mcfg::type_id:
|
||||
load_mcfg(sys, header);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace acpi
|
||||
12
src/libraries/acpi/acpi.module
Normal file
12
src/libraries/acpi/acpi.module
Normal file
@@ -0,0 +1,12 @@
|
||||
# vim: ft=python
|
||||
|
||||
module("acpi",
|
||||
kind = "lib",
|
||||
deps = [ "util", "j6" ],
|
||||
sources = [
|
||||
"acpi.cpp",
|
||||
],
|
||||
public_headers = [
|
||||
"acpi/acpi.h",
|
||||
"acpi/tables.h",
|
||||
])
|
||||
53
src/libraries/acpi/include/acpi/acpi.h
Normal file
53
src/libraries/acpi/include/acpi/acpi.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
/// \file acpi.h
|
||||
/// Routines for loading and parsing ACPI tables
|
||||
|
||||
#include <stddef.h>
|
||||
#include <acpi/tables.h>
|
||||
#include <util/pointers.h>
|
||||
|
||||
namespace acpi {
|
||||
|
||||
struct system
|
||||
{
|
||||
system(const void* phys, const void *virt);
|
||||
|
||||
/// Iterator for all tables in the system
|
||||
struct iterator
|
||||
{
|
||||
iterator(const table_header *const *addr, ptrdiff_t off) : m_addr {addr}, m_off {off} {};
|
||||
|
||||
operator const table_header *() const { return m_addr ? offset(*m_addr) : nullptr; }
|
||||
const table_header * operator*() const { return m_addr ? offset(*m_addr) : nullptr; }
|
||||
const table_header * operator->() const { return m_addr ? offset(*m_addr) : nullptr; }
|
||||
|
||||
iterator & operator++() { increment(); return *this; }
|
||||
iterator operator++(int) { iterator old = *this; increment(); return old; }
|
||||
|
||||
friend bool operator==(const iterator &a, const iterator &b) { return a.m_addr == b.m_addr; }
|
||||
friend bool operator!=(const iterator &a, const iterator &b) { return a.m_addr != b.m_addr; }
|
||||
|
||||
private:
|
||||
inline void increment() { if (m_addr) ++m_addr; }
|
||||
|
||||
table_header const * const * m_addr;
|
||||
ptrdiff_t m_off;
|
||||
|
||||
inline const table_header * offset(const table_header * addr) const {
|
||||
return util::offset_pointer(addr, m_off);
|
||||
}
|
||||
};
|
||||
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
|
||||
private:
|
||||
const xsdt * get_xsdt() {
|
||||
return check_get_table<xsdt>(util::offset_pointer(m_root->xsdt_address, m_offset));
|
||||
}
|
||||
|
||||
ptrdiff_t m_offset;
|
||||
const rsdp2* m_root;
|
||||
};
|
||||
|
||||
} // namespace acpi
|
||||
@@ -21,6 +21,7 @@ struct table_header
|
||||
uint32_t creator_revision;
|
||||
|
||||
bool validate() const { return util::checksum(this, length) == 0; }
|
||||
bool validate(uint32_t sig) const { return type == sig && validate(); }
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include <j6/syslog.hh>
|
||||
#include <pci/device.h>
|
||||
|
||||
#include "pci.h"
|
||||
namespace pci {
|
||||
|
||||
struct pci_cap_msi
|
||||
struct cap_msi
|
||||
{
|
||||
pci_cap::type id;
|
||||
cap::type id;
|
||||
uint8_t next;
|
||||
uint16_t control;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct pci_cap_msi32
|
||||
struct cap_msi32
|
||||
{
|
||||
pci_cap::type id;
|
||||
cap::type id;
|
||||
uint8_t next;
|
||||
uint16_t control;
|
||||
uint32_t address;
|
||||
@@ -23,9 +24,9 @@ struct pci_cap_msi32
|
||||
uint32_t pending;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct pci_cap_msi64
|
||||
struct cap_msi64
|
||||
{
|
||||
pci_cap::type id;
|
||||
cap::type id;
|
||||
uint8_t next;
|
||||
uint16_t control;
|
||||
uint64_t address;
|
||||
@@ -35,53 +36,23 @@ struct pci_cap_msi64
|
||||
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_header_type(0)
|
||||
device::device() :
|
||||
m_base {nullptr},
|
||||
m_bus_addr {0, 0, 0},
|
||||
m_vendor {0},
|
||||
m_device {0},
|
||||
m_class {0},
|
||||
m_subclass {0},
|
||||
m_progif {0},
|
||||
m_revision {0},
|
||||
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))
|
||||
device::device(bus_addr addr, uint32_t *base) :
|
||||
m_base {base},
|
||||
m_msi {nullptr},
|
||||
m_bus_addr {addr}
|
||||
{
|
||||
m_vendor = m_base[0] & 0xffff;
|
||||
m_device = (m_base[0] >> 16) & 0xffff;
|
||||
@@ -99,8 +70,10 @@ pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t fu
|
||||
|
||||
uint16_t *status = command + 1;
|
||||
|
||||
j6::syslog(j6::logs::srv, j6::log_level::info, "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);
|
||||
j6::syslog(j6::logs::srv, j6::log_level::info,
|
||||
"Found PCIe device at %02d:%02d:%d of type %x.%x.%x id %04x:%04x",
|
||||
addr.bus, addr.device, addr.function,
|
||||
m_class, m_subclass, m_progif, m_vendor, m_device);
|
||||
|
||||
j6::syslog(j6::logs::srv, j6::log_level::verbose, " = BAR0 %016lld", get_bar(0));
|
||||
j6::syslog(j6::logs::srv, j6::log_level::verbose, " = BAR1 %016lld", get_bar(1));
|
||||
@@ -109,13 +82,13 @@ pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t fu
|
||||
// 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);
|
||||
cap *c = reinterpret_cast<cap *>(util::offset_pointer(m_base, next));
|
||||
next = c->next;
|
||||
//log::verbose(logs::device, " - found PCI cap type %02x", c->id);
|
||||
|
||||
if (cap->id == pci_cap::type::msi) {
|
||||
m_msi = cap;
|
||||
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(cap);
|
||||
if (c->id == cap::type::msi) {
|
||||
m_msi = c;
|
||||
cap_msi *mcap = reinterpret_cast<cap_msi *>(c);
|
||||
mcap->control &= ~0x70; // at most 1 vector allocated
|
||||
mcap->control |= 0x01; // Enable interrupts, at most 1 vector allocated
|
||||
}
|
||||
@@ -124,20 +97,17 @@ pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t fu
|
||||
}
|
||||
|
||||
uint32_t
|
||||
pci_device::get_bar(unsigned i)
|
||||
device::get_bar(unsigned i)
|
||||
{
|
||||
if (m_header_type == 0) {
|
||||
assert(i < 6); // Device max BAR is 5
|
||||
} else {
|
||||
assert(m_header_type == 1); // Only device or bridge
|
||||
assert(i < 2); // Bridge max BAR is 1
|
||||
}
|
||||
if ((m_header_type == 0 && i > 5) || // Device max BAR is 5
|
||||
(m_header_type == 1 && i > 2)) // Bridge max BAR is 1
|
||||
return 0;
|
||||
|
||||
return m_base[4+i];
|
||||
}
|
||||
|
||||
void
|
||||
pci_device::set_bar(unsigned i, uint32_t val)
|
||||
device::set_bar(unsigned i, uint32_t val)
|
||||
{
|
||||
if (m_header_type == 0) {
|
||||
assert(i < 6); // Device max BAR is 5
|
||||
@@ -150,19 +120,19 @@ pci_device::set_bar(unsigned i, uint32_t val)
|
||||
}
|
||||
|
||||
void
|
||||
pci_device::write_msi_regs(uintptr_t address, uint16_t data)
|
||||
device::write_msi_regs(uintptr_t address, uint16_t data)
|
||||
{
|
||||
if (!m_msi)
|
||||
return;
|
||||
|
||||
if (m_msi->id == pci_cap::type::msi) {
|
||||
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(m_msi);
|
||||
if (m_msi->id == cap::type::msi) {
|
||||
cap_msi *mcap = reinterpret_cast<cap_msi *>(m_msi);
|
||||
if (mcap->control & 0x0080) {
|
||||
pci_cap_msi64 *mcap64 = reinterpret_cast<pci_cap_msi64 *>(m_msi);
|
||||
cap_msi64 *mcap64 = reinterpret_cast<cap_msi64 *>(m_msi);
|
||||
mcap64->address = address;
|
||||
mcap64->data = data;
|
||||
} else {
|
||||
pci_cap_msi32 *mcap32 = reinterpret_cast<pci_cap_msi32 *>(m_msi);
|
||||
cap_msi32 *mcap32 = reinterpret_cast<cap_msi32 *>(m_msi);
|
||||
mcap32->address = address;
|
||||
mcap32->data = data;
|
||||
}
|
||||
@@ -175,8 +145,33 @@ pci_device::write_msi_regs(uintptr_t address, uint16_t data)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
pci_group::has_device(uint8_t bus, uint8_t device, uint8_t func)
|
||||
/*
|
||||
void dump_msi(cap_msi *cap)
|
||||
{
|
||||
return (*base_for(bus, device, func) & 0xffff) != 0xffff;
|
||||
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) {
|
||||
cap_msi64 *cap64 = reinterpret_cast<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 {
|
||||
cap_msi32 *cap32 = reinterpret_cast<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');
|
||||
};
|
||||
*/
|
||||
|
||||
} // namespace pci
|
||||
55
src/libraries/pci/group.cpp
Normal file
55
src/libraries/pci/group.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "pci/config.h"
|
||||
#include <j6/syslog.hh>
|
||||
#include <pci/config.h>
|
||||
#include <pci/device.h>
|
||||
#include <pci/group.h>
|
||||
|
||||
namespace pci {
|
||||
|
||||
//map config space into memory:
|
||||
//inline constexpr j6_vm_flags mmio_flags = (j6_vm_flags)(j6_vm_flag_write | j6_vm_flag_mmio);
|
||||
//map_phys(sys, group.base, pci::group::config_size, mmio_flags);
|
||||
group::iterator::iterator(const group &g, bus_addr a) :
|
||||
m_group {g}, m_addr {a}
|
||||
{
|
||||
increment_until_valid(false);
|
||||
}
|
||||
|
||||
void
|
||||
group::iterator::increment()
|
||||
{
|
||||
// If we're iterating functions, the device must be valid.
|
||||
// Finish iterating all functions.
|
||||
if (m_addr.function && m_addr.function < bus_addr::max_functions) {
|
||||
++m_addr.function;
|
||||
return;
|
||||
}
|
||||
|
||||
m_addr.function = 0;
|
||||
if (m_addr.device < bus_addr::max_devices) {
|
||||
++m_addr.device;
|
||||
return;
|
||||
}
|
||||
|
||||
m_addr.device = 0;
|
||||
++m_addr.bus;
|
||||
}
|
||||
|
||||
void
|
||||
group::iterator::increment_until_valid(bool pre_increment)
|
||||
{
|
||||
if (pre_increment)
|
||||
increment();
|
||||
|
||||
bus_addr end_addr = {0, 0, uint16_t(m_group.bus_end+1)};
|
||||
if (!m_group.has_device(m_addr) && m_addr < end_addr)
|
||||
increment();
|
||||
}
|
||||
|
||||
bool
|
||||
group::has_device(bus_addr addr) const
|
||||
{
|
||||
return device_base(addr)->vendor != 0xffff;
|
||||
}
|
||||
|
||||
} // namespace pci
|
||||
28
src/libraries/pci/include/pci/bus_addr.h
Normal file
28
src/libraries/pci/include/pci/bus_addr.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
/// \file bus_addr.h
|
||||
/// PCIe Bus Address structure
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace pci {
|
||||
|
||||
/// Bus address: 15:8 bus, 7:3 device, 2:0 device
|
||||
struct bus_addr
|
||||
{
|
||||
static constexpr uint16_t max_functions = 8;
|
||||
static constexpr uint16_t max_devices = 32;
|
||||
|
||||
uint16_t function : 3;
|
||||
uint16_t device : 5;
|
||||
uint16_t bus : 8;
|
||||
|
||||
uint16_t as_int() const { return *reinterpret_cast<const uint16_t*>(this); }
|
||||
operator uint16_t() const { return as_int(); }
|
||||
};
|
||||
|
||||
inline bool operator==(const bus_addr &a, const bus_addr &b) { return a.as_int() == b.as_int(); }
|
||||
inline bool operator!=(const bus_addr &a, const bus_addr &b) { return a.as_int() != b.as_int(); }
|
||||
inline bool operator< (const bus_addr &a, const bus_addr &b) { return a.as_int() < b.as_int(); }
|
||||
inline bool operator> (const bus_addr &a, const bus_addr &b) { return a.as_int() > b.as_int(); }
|
||||
|
||||
} // namespace pci
|
||||
131
src/libraries/pci/include/pci/config.h
Normal file
131
src/libraries/pci/include/pci/config.h
Normal file
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
/// \file config.h
|
||||
/// PCIe device configuration headers
|
||||
|
||||
#include <stdint.h>
|
||||
#include <util/bitset.h>
|
||||
|
||||
namespace pci {
|
||||
|
||||
/// Command register bits
|
||||
enum class control
|
||||
{
|
||||
io_space, // Respond to IO space access
|
||||
mem_space, // Respond to memory space access
|
||||
bus_master, // Allow acting as a bus master
|
||||
special, // (Not in PCIe) Enable Special Cycle
|
||||
mwi_enable, // (Not in PCIe) Enable Memory Write and Invalidate
|
||||
vga_snoop, // (Not in PCIe) Enable VGA Palette Snoop
|
||||
parity_err, // Enable PERR# assertion for parity errors
|
||||
reserved7,
|
||||
serr_enable, // Enable SERR# pin
|
||||
fb2b_enable, // (Not in PCIe) Allow fast back-to-back
|
||||
int_disable, // Disable INTx# assertions
|
||||
};
|
||||
|
||||
/// Status register bits
|
||||
enum class status
|
||||
{
|
||||
intr = 3, // Interrupt status
|
||||
caps = 4, // Has capability list
|
||||
mhz66 = 5, // (Not in PCIe) Can run at 66MHz
|
||||
fb2b = 7, // (Not in PCIe) Supports fast back-to-back
|
||||
md_parity = 8, // Master data parity error
|
||||
tgt_abort_s = 11, // Target-Abort sent
|
||||
tgt_abort_r = 12, // Target-Abort received
|
||||
mst_abort_r = 13, // Master-Abort received
|
||||
sys_err_s = 14, // SERR# asserted
|
||||
parity = 15, // Parity error dedected
|
||||
};
|
||||
|
||||
struct header_base
|
||||
{
|
||||
uint16_t vendor; // Vendor ID
|
||||
uint16_t device; // Vendor's device ID
|
||||
|
||||
util::bitset16 command;
|
||||
util::bitset16 status;
|
||||
|
||||
uint8_t revision; // Device revision
|
||||
uint8_t device_class[3];
|
||||
|
||||
uint8_t cache_line; // Not used in PCIe
|
||||
uint8_t master_latency; // Not used in PCIe
|
||||
uint8_t header_type : 7;
|
||||
uint8_t multi_device : 1;
|
||||
uint8_t bist; // Built-in Self Test
|
||||
};
|
||||
|
||||
struct header_unknown :
|
||||
public header_base
|
||||
{
|
||||
uint8_t unknown1[36];
|
||||
|
||||
uint8_t caps;
|
||||
uint8_t unknown2[7];
|
||||
|
||||
uint8_t int_line;
|
||||
uint8_t int_pin;
|
||||
|
||||
uint8_t unknown3[2];
|
||||
};
|
||||
|
||||
/// Device configuration header
|
||||
struct header0 :
|
||||
public header_base
|
||||
{
|
||||
uint32_t bar[6]; // Base address registers
|
||||
|
||||
uint32_t cis;
|
||||
|
||||
uint16_t subsystem_vendor;
|
||||
uint16_t subsystem;
|
||||
|
||||
uint32_t exrom;
|
||||
|
||||
uint8_t caps;
|
||||
uint8_t reserved1[3];
|
||||
uint32_t reserved2;
|
||||
|
||||
uint8_t int_line;
|
||||
uint8_t int_pin;
|
||||
uint8_t min_gnt; // Not used in PCIe
|
||||
uint8_t max_lat; // Not used in PCIe
|
||||
};
|
||||
|
||||
/// Bridge configuration header
|
||||
struct header1 :
|
||||
public header_base
|
||||
{
|
||||
uint32_t bar[2]; // Base address registers
|
||||
|
||||
uint8_t pri_bus; // Not used in PCIe
|
||||
uint8_t sec_bus;
|
||||
uint8_t sub_bus;
|
||||
uint8_t sec_lat; // Not used in PCIe
|
||||
|
||||
uint8_t io_base;
|
||||
uint8_t io_limit;
|
||||
uint16_t sec_status;
|
||||
|
||||
uint16_t mem_base;
|
||||
uint16_t mem_limit;
|
||||
uint16_t prefetch_mem_base;
|
||||
uint16_t prefetch_mem_limit;
|
||||
|
||||
uint32_t prefetch_mem_base_high;
|
||||
uint32_t prefetch_mem_limit_high;
|
||||
uint16_t io_base_high;
|
||||
uint16_t io_limit_high;
|
||||
|
||||
uint8_t caps;
|
||||
uint8_t reserved1[3];
|
||||
|
||||
uint32_t exrom;
|
||||
|
||||
uint8_t int_line;
|
||||
uint8_t int_pin;
|
||||
uint16_t bridge_control;
|
||||
};
|
||||
|
||||
} // namespace pci
|
||||
88
src/libraries/pci/include/pci/device.h
Normal file
88
src/libraries/pci/include/pci/device.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
/// \file device.h
|
||||
/// PCIe device
|
||||
|
||||
#include <stdint.h>
|
||||
#include <pci/bus_addr.h>
|
||||
#include <util/pointers.h>
|
||||
|
||||
namespace pci {
|
||||
|
||||
struct cap
|
||||
{
|
||||
enum class type : uint8_t
|
||||
{
|
||||
msi = 0x05,
|
||||
msix = 0x11
|
||||
};
|
||||
|
||||
type id;
|
||||
uint8_t next;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/// Information about a discovered PCIe device
|
||||
class device
|
||||
{
|
||||
public:
|
||||
/// Default constructor creates an empty object.
|
||||
device();
|
||||
|
||||
/// Constructor
|
||||
/// \arg addr The bus address of this device
|
||||
/// \arg base Base address of this device's config space
|
||||
device(bus_addr addr, uint32_t *base);
|
||||
|
||||
/// 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 address of this device/function
|
||||
inline bus_addr addr() const { return m_bus_addr; }
|
||||
|
||||
/// 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);
|
||||
|
||||
private:
|
||||
uint32_t *m_base;
|
||||
cap *m_msi;
|
||||
|
||||
bus_addr 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;
|
||||
|
||||
uint8_t m_header_type;
|
||||
};
|
||||
|
||||
} // namespace pci
|
||||
64
src/libraries/pci/include/pci/group.h
Normal file
64
src/libraries/pci/include/pci/group.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
/// \file pci.h
|
||||
/// PCIe devices and groups
|
||||
|
||||
#include <stdint.h>
|
||||
#include <pci/bus_addr.h>
|
||||
#include <util/pointers.h>
|
||||
|
||||
namespace pci {
|
||||
|
||||
struct header_unknown;
|
||||
|
||||
/// Represents data about a PCI bus group from the ACPI MCFG
|
||||
struct group
|
||||
{
|
||||
static constexpr size_t config_size = 0x1000'0000;
|
||||
|
||||
uint16_t group_id;
|
||||
uint16_t bus_start;
|
||||
uint16_t bus_end;
|
||||
|
||||
header_unknown *base;
|
||||
|
||||
/// Iterator that returns successive valid bus addresses for a group
|
||||
struct iterator {
|
||||
iterator(const group &g, bus_addr a = {0,0,0});
|
||||
iterator(const iterator &i) : m_group {i.m_group}, m_addr {i.m_addr} {}
|
||||
|
||||
inline const bus_addr & operator*() const { return m_addr; }
|
||||
inline const bus_addr * operator->() const { return &m_addr; }
|
||||
inline bus_addr & operator*() { return m_addr; }
|
||||
inline bus_addr * operator->() { return &m_addr; }
|
||||
|
||||
iterator & operator++() { increment_until_valid(); return *this; }
|
||||
iterator operator++(int) { iterator old = *this; increment_until_valid(); return old; }
|
||||
|
||||
friend bool operator==(const iterator &a, const iterator &b) { return a.m_addr == b.m_addr; }
|
||||
friend bool operator!=(const iterator &a, const iterator &b) { return a.m_addr != b.m_addr; }
|
||||
|
||||
private:
|
||||
void increment();
|
||||
void increment_until_valid(bool pre_increment=true);
|
||||
|
||||
const group &m_group;
|
||||
bus_addr m_addr;
|
||||
};
|
||||
|
||||
iterator begin() const { return iterator {*this, {0, 0, bus_start}}; }
|
||||
iterator end() const { return iterator {*this, {0, 0, uint16_t(bus_end + 1)}}; }
|
||||
|
||||
/// Get the base address of the MMIO configuration registers for a device
|
||||
/// \arg addr The bus address of the device
|
||||
/// \returns A pointer to the MMIO configuration registers
|
||||
inline header_unknown* device_base(bus_addr addr) const {
|
||||
return util::offset_pointer(base, addr.as_int() << 12);
|
||||
}
|
||||
|
||||
/// Check if the given device function is present.
|
||||
/// \arg addr The bus address of the device (relative to this group)
|
||||
/// \returns True if the device function is present
|
||||
bool has_device(bus_addr addr) const;
|
||||
};
|
||||
|
||||
} // namespace pci
|
||||
14
src/libraries/pci/pci.module
Normal file
14
src/libraries/pci/pci.module
Normal file
@@ -0,0 +1,14 @@
|
||||
# vim: ft=python
|
||||
|
||||
module("pci",
|
||||
kind = "lib",
|
||||
deps = [ "libc", "util" ],
|
||||
sources = [
|
||||
"device.cpp",
|
||||
"group.cpp",
|
||||
],
|
||||
public_headers = [
|
||||
"pci/bus_addr.h",
|
||||
"pci/device.h",
|
||||
"pci/group.h",
|
||||
])
|
||||
@@ -1,9 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
/// Reverse the order of bytes in a 32 bit integer
|
||||
constexpr uint32_t
|
||||
constexpr inline uint32_t
|
||||
byteswap32(uint32_t x) {
|
||||
return ((x >> 24) & 0x000000ff) | ((x >> 8) & 0x0000ff00)
|
||||
| ((x << 8) & 0x00ff0000) | ((x << 24) & 0xff000000);
|
||||
@@ -14,7 +17,7 @@ byteswap32(uint32_t x) {
|
||||
/// \arg p The start of the memory region
|
||||
/// \arg len The number of bytes in the region
|
||||
/// \arg off An optional offset into the region
|
||||
uint8_t checksum(const void *p, size_t len, size_t off = 0) {
|
||||
inline uint8_t checksum(const void *p, size_t len, size_t off = 0) {
|
||||
uint8_t sum = 0;
|
||||
const uint8_t *c = reinterpret_cast<const uint8_t *>(p);
|
||||
for (size_t i = off; i < len; ++i) sum += c[i];
|
||||
|
||||
@@ -16,6 +16,15 @@ inline T* offset_pointer(T* input, ptrdiff_t offset) {
|
||||
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(input) + offset);
|
||||
}
|
||||
|
||||
/// Get the offset from ptr1 to ptr2.
|
||||
/// \arg ptr1 The base pointer
|
||||
/// \arg ptr2 The offset pointer
|
||||
/// \returns Offset from pt1 to ptr2
|
||||
template <typename T, typename U>
|
||||
inline ptrdiff_t get_offset(T* ptr1, U* ptr2) {
|
||||
return reinterpret_cast<const char*>(ptr2) - reinterpret_cast<const char*>(ptr1);
|
||||
}
|
||||
|
||||
/// Return a pointer with the given bits masked out
|
||||
/// \arg input The original pointer
|
||||
/// \arg mask A bitmask of bits to clear from p
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
#include <arch/acpi/tables.h>
|
||||
#include <bootproto/acpi.h>
|
||||
#include <bootproto/init.h>
|
||||
#include <j6/syslog.hh>
|
||||
|
||||
#include "acpi.h"
|
||||
#include "loader.h"
|
||||
#include "pci.h"
|
||||
|
||||
inline constexpr size_t bus_mmio_size = 0x1000'0000;
|
||||
inline constexpr j6_vm_flags mmio_flags = (j6_vm_flags)(j6_vm_flag_write | j6_vm_flag_mmio);
|
||||
|
||||
void
|
||||
probe_pci(j6_handle_t sys, pci_group &pci)
|
||||
{
|
||||
j6::syslog(j6::logs::srv, j6::log_level::info, "Probing PCI group at base %016lx", pci.base);
|
||||
map_phys(sys, pci.base, bus_mmio_size, mmio_flags);
|
||||
|
||||
for (unsigned b = pci.bus_start; b <= pci.bus_end; ++b) {
|
||||
uint8_t bus = static_cast<uint8_t>(b);
|
||||
|
||||
for (uint8_t dev = 0; dev < 32; ++dev) {
|
||||
if (!pci.has_device(bus, dev, 0)) continue;
|
||||
|
||||
pci_device d0 {pci, bus, dev, 0};
|
||||
if (!d0.multi()) continue;
|
||||
|
||||
for (uint8_t i = 1; i < 8; ++i) {
|
||||
if (pci.has_device(bus, dev, i))
|
||||
pci_device dn {pci, bus, dev, i};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
load_mcfg(j6_handle_t sys, const acpi::table_header *header)
|
||||
{
|
||||
const auto *mcfg = acpi::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];
|
||||
|
||||
pci_group group = {
|
||||
.group = mcfge.group,
|
||||
.bus_start = mcfge.bus_start,
|
||||
.bus_end = mcfge.bus_end,
|
||||
.base = reinterpret_cast<uint32_t*>(mcfge.base),
|
||||
};
|
||||
|
||||
probe_pci(sys, group);
|
||||
|
||||
j6::syslog(j6::logs::srv, j6::log_level::info, " Found MCFG entry: base %lx group %d bus %d-%d",
|
||||
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
|
||||
}
|
||||
|
||||
//probe_pci();
|
||||
}
|
||||
|
||||
void
|
||||
load_acpi(j6_handle_t sys, const bootproto::module *mod)
|
||||
{
|
||||
const bootproto::acpi *info = mod->data<bootproto::acpi>();
|
||||
const util::const_buffer ®ion = info->region;
|
||||
|
||||
map_phys(sys, region.pointer, region.count);
|
||||
|
||||
const void *root_table = info->root;
|
||||
if (!root_table) {
|
||||
j6::syslog(j6::logs::srv, j6::log_level::error, "null ACPI root table pointer");
|
||||
return;
|
||||
}
|
||||
|
||||
const acpi::rsdp2 *acpi2 =
|
||||
reinterpret_cast<const acpi::rsdp2 *>(root_table);
|
||||
|
||||
const auto *xsdt =
|
||||
acpi::check_get_table<acpi::xsdt>(acpi2->xsdt_address);
|
||||
|
||||
size_t num_tables = acpi::table_entries(xsdt, sizeof(void*));
|
||||
for (size_t i = 0; i < num_tables; ++i) {
|
||||
const acpi::table_header *header = xsdt->headers[i];
|
||||
if (!header->validate()) {
|
||||
j6::syslog(j6::logs::srv, j6::log_level::error, "ACPI table at %lx failed validation", header);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (header->type) {
|
||||
case acpi::mcfg::type_id:
|
||||
load_mcfg(sys, header);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
/// \file acpi.h
|
||||
/// Routines for loading and parsing ACPI tables
|
||||
|
||||
#include <util/counted.h>
|
||||
#include <j6/types.h>
|
||||
|
||||
namespace bootproto {
|
||||
struct module;
|
||||
}
|
||||
|
||||
void load_acpi(j6_handle_t sys, const bootproto::module *mod);
|
||||
96
src/user/srv.init/device_manager.cpp
Normal file
96
src/user/srv.init/device_manager.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include <j6/syslog.hh>
|
||||
#include <j6/flags.h>
|
||||
#include <pci/config.h>
|
||||
|
||||
#include "device_manager.h"
|
||||
#include "loader.h"
|
||||
|
||||
inline constexpr j6_vm_flags mmio_flags = (j6_vm_flags)(j6_vm_flag_write | j6_vm_flag_mmio);
|
||||
|
||||
|
||||
device_manager::device_manager(j6_handle_t sys, const void *root) :
|
||||
m_sys {sys}, m_acpi {root, root}
|
||||
{
|
||||
load_mcfg();
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::load_mcfg()
|
||||
{
|
||||
m_groups.clear();
|
||||
|
||||
for (const auto header : m_acpi) {
|
||||
if (header->type != acpi::mcfg::type_id)
|
||||
continue;
|
||||
|
||||
const auto *mcfg = acpi::check_get_table<acpi::mcfg>(header);
|
||||
if (!mcfg) {
|
||||
j6::syslog(j6::logs::srv, j6::log_level::error, "MCFG table at %lx failed validation", header);
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t count = acpi::table_entries(mcfg, sizeof(acpi::mcfg_entry));
|
||||
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
const acpi::mcfg_entry &mcfge = mcfg->entries[i];
|
||||
|
||||
pci::group group = {
|
||||
.group_id = mcfge.group,
|
||||
.bus_start = mcfge.bus_start,
|
||||
.bus_end = mcfge.bus_end,
|
||||
.base = reinterpret_cast<pci::header_unknown*>(mcfge.base),
|
||||
};
|
||||
|
||||
j6::syslog(j6::logs::srv, j6::log_level::info, "Found MCFG entry: base %lx group %d bus %d-%d",
|
||||
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
|
||||
|
||||
map_phys(m_sys, group.base, pci::group::config_size, mmio_flags);
|
||||
m_groups.push_back(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<device_info>
|
||||
device_manager::find_devices(uint8_t klass, uint8_t subclass, uint8_t subsubclass)
|
||||
{
|
||||
std::vector<device_info> found;
|
||||
|
||||
for (auto group : m_groups) {
|
||||
for (unsigned b = group.bus_start; b <= group.bus_end; ++b) {
|
||||
uint8_t bus = static_cast<uint8_t>(b - group.bus_start);
|
||||
|
||||
for (uint8_t dev = 0; dev < 32; ++dev) {
|
||||
pci::bus_addr a {0, dev, bus};
|
||||
if (!group.has_device(a)) continue;
|
||||
|
||||
pci::header_unknown *h = reinterpret_cast<pci::header_unknown*>(group.device_base(a));
|
||||
j6::syslog(j6::logs::srv, j6::log_level::info,
|
||||
" Found PCI device at %02d:%02d:%d class %x.%x.%x id %04x:%04x",
|
||||
a.bus, a.device, a.function,
|
||||
h->device_class[2], h->device_class[1], h->device_class[0],
|
||||
h->vendor, h->device);
|
||||
|
||||
found.push_back({h, a});
|
||||
|
||||
if (!h->multi_device) continue;
|
||||
for (uint8_t i = 1; i < 8; ++i) {
|
||||
pci::bus_addr fa {i, dev, bus};
|
||||
if (group.has_device(fa)) {
|
||||
pci::header_unknown *fh =
|
||||
reinterpret_cast<pci::header_unknown*>(group.device_base(fa));
|
||||
|
||||
j6::syslog(j6::logs::srv, j6::log_level::info,
|
||||
" found PCI func at %02d:%02d:%d class %x.%x.%x id %04x:%04x",
|
||||
fa.bus, fa.device, fa.function,
|
||||
fh->device_class[2], fh->device_class[1], fh->device_class[0],
|
||||
fh->vendor, fh->device);
|
||||
|
||||
found.push_back({fh, fa});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
29
src/user/srv.init/device_manager.h
Normal file
29
src/user/srv.init/device_manager.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
/// \file device_manager.h
|
||||
/// APCI and PCI device lookup
|
||||
|
||||
#include <vector>
|
||||
#include <j6/types.h>
|
||||
#include <acpi/acpi.h>
|
||||
#include <pci/group.h>
|
||||
|
||||
struct device_info
|
||||
{
|
||||
pci::header_unknown *base;
|
||||
pci::bus_addr addr;
|
||||
};
|
||||
|
||||
class device_manager
|
||||
{
|
||||
public:
|
||||
device_manager(j6_handle_t sys, const void *root);
|
||||
|
||||
std::vector<device_info> find_devices(uint8_t klass, uint8_t subclass, uint8_t subsubclass);
|
||||
|
||||
private:
|
||||
void load_mcfg();
|
||||
|
||||
j6_handle_t m_sys;
|
||||
acpi::system m_acpi;
|
||||
std::vector<pci::group> m_groups;
|
||||
};
|
||||
@@ -2,18 +2,17 @@
|
||||
|
||||
init = module("srv.init",
|
||||
targets = [ "init" ],
|
||||
deps = [ "libc", "elf", "bootproto", "zstd" ],
|
||||
deps = [ "libc", "elf", "bootproto", "zstd", "acpi", "pci" ],
|
||||
static = True,
|
||||
description = "Init server",
|
||||
ld_script = "init.ld",
|
||||
sources = [
|
||||
"acpi.cpp",
|
||||
"device_manager.cpp",
|
||||
"initfs.cpp",
|
||||
"j6romfs.cpp",
|
||||
"loader.cpp",
|
||||
"main.cpp",
|
||||
"modules.cpp",
|
||||
"pci.cpp",
|
||||
"service_locator.cpp",
|
||||
"start.s",
|
||||
])
|
||||
@@ -10,11 +10,14 @@
|
||||
#include <j6/thread.hh>
|
||||
#include <j6/types.h>
|
||||
|
||||
#include <acpi/acpi.h>
|
||||
#include <bootproto/acpi.h>
|
||||
#include <bootproto/init.h>
|
||||
#include <bootproto/devices/framebuffer.h>
|
||||
#include <pci/config.h>
|
||||
#include <pci/group.h>
|
||||
|
||||
#include "acpi.h"
|
||||
#include "device_manager.h"
|
||||
#include "initfs.h"
|
||||
#include "j6romfs.h"
|
||||
#include "loader.h"
|
||||
@@ -24,7 +27,10 @@
|
||||
using bootproto::module;
|
||||
using bootproto::module_type;
|
||||
|
||||
constexpr uintptr_t stack_top = 0xf80000000;
|
||||
inline constexpr uintptr_t stack_top = 0xf80000000;
|
||||
inline constexpr j6_vm_flags mmio_flags = (j6_vm_flags)(j6_vm_flag_write | j6_vm_flag_mmio);
|
||||
|
||||
void load_acpi(j6_handle_t sys, const bootproto::module *mod);
|
||||
|
||||
int
|
||||
main(int argc, const char **argv, const char **env)
|
||||
@@ -141,7 +147,11 @@ main(int argc, const char **argv, const char **env)
|
||||
j6::thread vfs_thread {[=, &initrd](){ initfs_start(initrd, vfs_mb); }, stack_top};
|
||||
j6_status_t result = vfs_thread.start();
|
||||
|
||||
load_acpi(sys, acpi_module);
|
||||
// Load ACPI into device_manager
|
||||
const bootproto::acpi *acpi = acpi_module->data<bootproto::acpi>();
|
||||
map_phys(sys, acpi->region.pointer, acpi->region.count);
|
||||
|
||||
device_manager dm {sys, acpi->root};
|
||||
|
||||
load_program("/jsix/drivers/drv.uart.elf", initrd, sys_child, slp_mb_child, vfs_mb_child);
|
||||
|
||||
@@ -156,6 +166,8 @@ main(int argc, const char **argv, const char **env)
|
||||
}
|
||||
}
|
||||
|
||||
auto ahci_drives = dm.find_devices(1, 6, 1);
|
||||
|
||||
initrd.for_each("/jsix/services",
|
||||
[=](const j6romfs::inode *in, const char *name) {
|
||||
if (in->type != j6romfs::inode_type::file)
|
||||
@@ -169,4 +181,3 @@ main(int argc, const char **argv, const char **env)
|
||||
service_locator_start(slp_mb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,139 +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;
|
||||
|
||||
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