From d1ddbd2cf1bdc8ae2f60fa7366d3a36350b2d918 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sun, 18 Aug 2024 19:44:47 -0700 Subject: [PATCH] [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. --- src/kernel/device_manager.cpp | 2 +- src/kernel/kernel.module | 2 +- src/libraries/acpi/acpi.cpp | 79 ++++++++++ src/libraries/acpi/acpi.module | 12 ++ src/libraries/acpi/include/acpi/acpi.h | 53 +++++++ .../acpi/include}/acpi/tables.h | 1 + .../pci.cpp => libraries/pci/device.cpp} | 147 +++++++++--------- src/libraries/pci/group.cpp | 55 +++++++ src/libraries/pci/include/pci/bus_addr.h | 28 ++++ src/libraries/pci/include/pci/config.h | 131 ++++++++++++++++ src/libraries/pci/include/pci/device.h | 88 +++++++++++ src/libraries/pci/include/pci/group.h | 64 ++++++++ src/libraries/pci/pci.module | 14 ++ src/libraries/util/include/util/misc.h | 7 +- src/libraries/util/include/util/pointers.h | 9 ++ src/user/srv.init/acpi.cpp | 104 ------------- src/user/srv.init/acpi.h | 12 -- src/user/srv.init/device_manager.cpp | 96 ++++++++++++ src/user/srv.init/device_manager.h | 29 ++++ src/user/srv.init/init.module | 7 +- src/user/srv.init/main.cpp | 19 ++- src/user/srv.init/pci.h | 139 ----------------- 22 files changed, 755 insertions(+), 343 deletions(-) create mode 100644 src/libraries/acpi/acpi.cpp create mode 100644 src/libraries/acpi/acpi.module create mode 100644 src/libraries/acpi/include/acpi/acpi.h rename src/{include/arch => libraries/acpi/include}/acpi/tables.h (98%) rename src/{user/srv.init/pci.cpp => libraries/pci/device.cpp} (61%) create mode 100644 src/libraries/pci/group.cpp create mode 100644 src/libraries/pci/include/pci/bus_addr.h create mode 100644 src/libraries/pci/include/pci/config.h create mode 100644 src/libraries/pci/include/pci/device.h create mode 100644 src/libraries/pci/include/pci/group.h create mode 100644 src/libraries/pci/pci.module delete mode 100644 src/user/srv.init/acpi.cpp delete mode 100644 src/user/srv.init/acpi.h create mode 100644 src/user/srv.init/device_manager.cpp create mode 100644 src/user/srv.init/device_manager.h delete mode 100644 src/user/srv.init/pci.h diff --git a/src/kernel/device_manager.cpp b/src/kernel/device_manager.cpp index bbd2f24..659a99d 100644 --- a/src/kernel/device_manager.cpp +++ b/src/kernel/device_manager.cpp @@ -2,7 +2,7 @@ #include #include // for checksum #include -#include +#include #include "kassert.h" #include "apic.h" diff --git a/src/kernel/kernel.module b/src/kernel/kernel.module index 3ee03ec..f7010dc 100644 --- a/src/kernel/kernel.module +++ b/src/kernel/kernel.module @@ -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 = [ diff --git a/src/libraries/acpi/acpi.cpp b/src/libraries/acpi/acpi.cpp new file mode 100644 index 0000000..dbbb0e2 --- /dev/null +++ b/src/libraries/acpi/acpi.cpp @@ -0,0 +1,79 @@ +#include +#include +#include + +namespace acpi { + +system::system(const void* phys, const void *virt) : + m_offset { util::get_offset(phys, virt) }, + m_root { reinterpret_cast(virt) } +{} + +system::iterator +system::begin() const +{ + const xsdt *sdt = + acpi::check_get_table(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(m_root->xsdt_address); + + if (!sdt) + return {nullptr, 0}; + + size_t nheaders = table_entries(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(); + 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(root_table); + + const auto *xsdt = + acpi::check_get_table(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 diff --git a/src/libraries/acpi/acpi.module b/src/libraries/acpi/acpi.module new file mode 100644 index 0000000..f6de7d0 --- /dev/null +++ b/src/libraries/acpi/acpi.module @@ -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", + ]) diff --git a/src/libraries/acpi/include/acpi/acpi.h b/src/libraries/acpi/include/acpi/acpi.h new file mode 100644 index 0000000..e85a9b6 --- /dev/null +++ b/src/libraries/acpi/include/acpi/acpi.h @@ -0,0 +1,53 @@ +#pragma once +/// \file acpi.h +/// Routines for loading and parsing ACPI tables + +#include +#include +#include + +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(util::offset_pointer(m_root->xsdt_address, m_offset)); + } + + ptrdiff_t m_offset; + const rsdp2* m_root; +}; + +} // namespace acpi diff --git a/src/include/arch/acpi/tables.h b/src/libraries/acpi/include/acpi/tables.h similarity index 98% rename from src/include/arch/acpi/tables.h rename to src/libraries/acpi/include/acpi/tables.h index 0889a9e..65433b1 100644 --- a/src/include/arch/acpi/tables.h +++ b/src/libraries/acpi/include/acpi/tables.h @@ -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)); diff --git a/src/user/srv.init/pci.cpp b/src/libraries/pci/device.cpp similarity index 61% rename from src/user/srv.init/pci.cpp rename to src/libraries/pci/device.cpp index 5364d7c..9078052 100644 --- a/src/user/srv.init/pci.cpp +++ b/src/libraries/pci/device.cpp @@ -1,19 +1,20 @@ #include #include +#include -#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(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(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(util::offset_pointer(m_base, next)); - next = cap->next; - //log::verbose(logs::device, " - found PCI cap type %02x", cap->id); + cap *c = reinterpret_cast(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(cap); + if (c->id == cap::type::msi) { + m_msi = c; + cap_msi *mcap = reinterpret_cast(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(m_msi); + if (m_msi->id == cap::type::msi) { + cap_msi *mcap = reinterpret_cast(m_msi); if (mcap->control & 0x0080) { - pci_cap_msi64 *mcap64 = reinterpret_cast(m_msi); + cap_msi64 *mcap64 = reinterpret_cast(m_msi); mcap64->address = address; mcap64->data = data; } else { - pci_cap_msi32 *mcap32 = reinterpret_cast(m_msi); + cap_msi32 *mcap32 = reinterpret_cast(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); + 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); + 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 diff --git a/src/libraries/pci/group.cpp b/src/libraries/pci/group.cpp new file mode 100644 index 0000000..661d8a3 --- /dev/null +++ b/src/libraries/pci/group.cpp @@ -0,0 +1,55 @@ +#include "pci/config.h" +#include +#include +#include +#include + +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 diff --git a/src/libraries/pci/include/pci/bus_addr.h b/src/libraries/pci/include/pci/bus_addr.h new file mode 100644 index 0000000..bb5bb80 --- /dev/null +++ b/src/libraries/pci/include/pci/bus_addr.h @@ -0,0 +1,28 @@ +#pragma once +/// \file bus_addr.h +/// PCIe Bus Address structure + +#include + +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(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 diff --git a/src/libraries/pci/include/pci/config.h b/src/libraries/pci/include/pci/config.h new file mode 100644 index 0000000..25f16b0 --- /dev/null +++ b/src/libraries/pci/include/pci/config.h @@ -0,0 +1,131 @@ +#pragma once +/// \file config.h +/// PCIe device configuration headers + +#include +#include + +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 diff --git a/src/libraries/pci/include/pci/device.h b/src/libraries/pci/include/pci/device.h new file mode 100644 index 0000000..776e21a --- /dev/null +++ b/src/libraries/pci/include/pci/device.h @@ -0,0 +1,88 @@ +#pragma once +/// \file device.h +/// PCIe device + +#include +#include +#include + +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 diff --git a/src/libraries/pci/include/pci/group.h b/src/libraries/pci/include/pci/group.h new file mode 100644 index 0000000..2dd6540 --- /dev/null +++ b/src/libraries/pci/include/pci/group.h @@ -0,0 +1,64 @@ +#pragma once +/// \file pci.h +/// PCIe devices and groups + +#include +#include +#include + +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 diff --git a/src/libraries/pci/pci.module b/src/libraries/pci/pci.module new file mode 100644 index 0000000..3b2e59c --- /dev/null +++ b/src/libraries/pci/pci.module @@ -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", + ]) diff --git a/src/libraries/util/include/util/misc.h b/src/libraries/util/include/util/misc.h index ada1c29..2cccc7a 100644 --- a/src/libraries/util/include/util/misc.h +++ b/src/libraries/util/include/util/misc.h @@ -1,9 +1,12 @@ #pragma once +#include +#include + 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(p); for (size_t i = off; i < len; ++i) sum += c[i]; diff --git a/src/libraries/util/include/util/pointers.h b/src/libraries/util/include/util/pointers.h index abf6432..fb2e695 100644 --- a/src/libraries/util/include/util/pointers.h +++ b/src/libraries/util/include/util/pointers.h @@ -16,6 +16,15 @@ inline T* offset_pointer(T* input, ptrdiff_t offset) { return reinterpret_cast(reinterpret_cast(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 +inline ptrdiff_t get_offset(T* ptr1, U* ptr2) { + return reinterpret_cast(ptr2) - reinterpret_cast(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 diff --git a/src/user/srv.init/acpi.cpp b/src/user/srv.init/acpi.cpp deleted file mode 100644 index d40bf6c..0000000 --- a/src/user/srv.init/acpi.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include -#include -#include -#include - -#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(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(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(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(); - 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(root_table); - - const auto *xsdt = - acpi::check_get_table(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; - } - } -} diff --git a/src/user/srv.init/acpi.h b/src/user/srv.init/acpi.h deleted file mode 100644 index 60b9554..0000000 --- a/src/user/srv.init/acpi.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -/// \file acpi.h -/// Routines for loading and parsing ACPI tables - -#include -#include - -namespace bootproto { - struct module; -} - -void load_acpi(j6_handle_t sys, const bootproto::module *mod); diff --git a/src/user/srv.init/device_manager.cpp b/src/user/srv.init/device_manager.cpp new file mode 100644 index 0000000..d0eee66 --- /dev/null +++ b/src/user/srv.init/device_manager.cpp @@ -0,0 +1,96 @@ +#include +#include +#include + +#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(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(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_manager::find_devices(uint8_t klass, uint8_t subclass, uint8_t subsubclass) +{ + std::vector found; + + for (auto group : m_groups) { + for (unsigned b = group.bus_start; b <= group.bus_end; ++b) { + uint8_t bus = static_cast(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(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(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; +} diff --git a/src/user/srv.init/device_manager.h b/src/user/srv.init/device_manager.h new file mode 100644 index 0000000..1167161 --- /dev/null +++ b/src/user/srv.init/device_manager.h @@ -0,0 +1,29 @@ +#pragma once +/// \file device_manager.h +/// APCI and PCI device lookup + +#include +#include +#include +#include + +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 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 m_groups; +}; diff --git a/src/user/srv.init/init.module b/src/user/srv.init/init.module index 0cb5b17..6ce82a7 100644 --- a/src/user/srv.init/init.module +++ b/src/user/srv.init/init.module @@ -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", - ]) \ No newline at end of file + ]) diff --git a/src/user/srv.init/main.cpp b/src/user/srv.init/main.cpp index 83bb5bb..259cebf 100644 --- a/src/user/srv.init/main.cpp +++ b/src/user/srv.init/main.cpp @@ -10,11 +10,14 @@ #include #include +#include #include #include #include +#include +#include -#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(); + 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; } - diff --git a/src/user/srv.init/pci.h b/src/user/srv.init/pci.h deleted file mode 100644 index f81fa11..0000000 --- a/src/user/srv.init/pci.h +++ /dev/null @@ -1,139 +0,0 @@ -#pragma once -/// \file pci.h -/// PCI devices and groups - -#include -#include - -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); -}; - -