[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:
Justin C. Miller
2024-08-18 19:44:47 -07:00
parent 81b331e5da
commit d1ddbd2cf1
22 changed files with 755 additions and 343 deletions

View File

@@ -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"

View File

@@ -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 = [

View 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 &region = 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

View 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",
])

View 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

View File

@@ -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));

View File

@@ -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

View 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

View 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

View 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

View 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

View 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

View 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",
])

View File

@@ -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];

View File

@@ -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

View File

@@ -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 &region = 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;
}
}
}

View File

@@ -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);

View 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;
}

View 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;
};

View File

@@ -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",
])

View File

@@ -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;
}

View File

@@ -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);
};