diff --git a/api/hw/pci_device.hpp b/api/hw/pci_device.hpp index 40921708c..b4b6b472d 100644 --- a/api/hw/pci_device.hpp +++ b/api/hw/pci_device.hpp @@ -21,35 +21,42 @@ #include #include #include - -/* PCI Register Config Space */ -#define PCI_DEV_VEND_REG 0x00 /* for the 32 bit read of dev/vend */ -#define PCI_VENDID_REG 0x00 -#define PCI_DEVID_REG 0x02 -#define PCI_CMD_REG 0x04 -#define PCI_STATUS_REG 0x06 -#define PCI_REVID_REG 0x08 -#define PCI_PROGIF_REG 0x09 -#define PCI_SUBCLASS_REG 0x0a -#define PCI_CLASS_REG 0x0b -#define PCI_CLSZ_REG 0x0c -#define PCI_LATTIM_REG 0x0d -#define PCI_HEADER_REG 0x0e -#define PCI_BIST_REG 0x0f -#define PCI_CAPABILITY_REG 0x34 - -#define PCI_COMMAND_IO 0x01 -#define PCI_COMMAND_MEM 0x02 -#define PCI_COMMAND_MASTER 0x04 - -#define PCI_CAP_ID_VNDR 0x09 -#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */ -#define PCI_CAP_ID_MAX PCI_CAP_ID_AF -#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */ -#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PASID +#include namespace PCI { + // Names/values match Linux include/linux/pci_regs.h (PCI Local Bus Spec). + enum class config_reg : uint8_t { + DEV_VEND = 0x00, + DEVID = 0x02, + CMD = 0x04, + STATUS = 0x06, + REVID = 0x08, + PROGIF = 0x09, + SUBCLASS = 0x0a, + CLASS = 0x0b, + CLSZ = 0x0c, + LATTIM = 0x0d, + HEADER = 0x0e, + BIST = 0x0f, + CAPABILITY = 0x34 + }; + + enum class command : uint16_t { + IO = 0x01, + MEM = 0x02, + MASTER = 0x04, + INTX_DISABLE = 0x400, + }; + + enum class cap_id : uint8_t { + MSI = 0x05, + VNDR = 0x09, + MSIX = 0x11, + AF = 0x13, + MAX = AF + }; + static const uint16_t CONFIG_ADDR {0xCF8U}; static const uint16_t CONFIG_DATA {0xCFCU}; static const uint8_t CONFIG_INTR {0x3CU}; @@ -169,16 +176,22 @@ struct msix_t; */ explicit PCI_Device(const uint16_t pci_addr, const uint32_t, const uint32_t); + // config_reg overloads own the HW path; uint8_t is for dynamic offsets. //! @brief Read from device with implicit pci_address (e.g. used by Nic) + uint32_t read32(PCI::config_reg reg) noexcept; uint32_t read32(const uint8_t reg) noexcept; //! @brief Read from device with explicit pci_addr + static uint32_t read_dword(const uint16_t pci_addr, PCI::config_reg reg) noexcept; static uint32_t read_dword(const uint16_t pci_addr, const uint8_t reg) noexcept; //! @brief Write to device with implicit pci_address (e.g. used by Nic) + void write_dword(PCI::config_reg reg, const uint32_t value) noexcept; void write_dword(const uint8_t reg, const uint32_t value) noexcept; + uint16_t read16(PCI::config_reg reg) noexcept; uint16_t read16(const uint8_t reg) noexcept; + void write16(PCI::config_reg reg, const uint16_t value) noexcept; void write16(const uint8_t reg, const uint16_t value) noexcept; /** A descriptive name */ @@ -308,7 +321,7 @@ struct msix_t; int m_iobase = -1; std::array m_resources; - std::array caps; + std::array(PCI::cap_id::MAX)+1> caps; // has msix support if not null msix_t* msix = nullptr; @@ -361,4 +374,15 @@ static const char* PCI::vendor_str(uint16_t code){ return it == classcodes.end() ? "Unknown vendor" : it->second; } +/** Enable bitmask operators for PCI command register flags */ +namespace util { +inline namespace bitops { +template<> +struct enable_bitmask_ops { + using type = std::underlying_type::type; + static constexpr bool enable = true; +}; +} +} + #endif //< HW_PCI_DEVICE_HPP diff --git a/src/hw/pci_device.cpp b/src/hw/pci_device.cpp index 508ffea81..3220f89ea 100644 --- a/src/hw/pci_device.cpp +++ b/src/hw/pci_device.cpp @@ -20,9 +20,12 @@ #include #include #include +#include namespace hw { + using namespace util::bitops; + static constexpr std::array bridge_subclasses { "Host", "ISA", @@ -89,66 +92,91 @@ namespace hw { const uint32_t devclass) : pci_addr_{pci_addr}, device_id_{device_id} { - // set master, mem and io flags - uint32_t cmd = read32(PCI_CMD_REG); - cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEM | PCI_COMMAND_IO; - write_dword(PCI_CMD_REG, cmd); + // set master, mem and io flags (16-bit command register only) + auto cmd = static_cast(read16(PCI::config_reg::CMD)); + cmd |= PCI::command::MASTER | PCI::command::MEM | PCI::command::IO; + write16(PCI::config_reg::CMD, static_cast(cmd)); // device class info is coming from pci manager to save a PCI read this->devtype_.reg = devclass; } - uint32_t PCI_Device::read_dword(const uint16_t pci_addr, const uint8_t reg) noexcept { + // Typed config-space accessors own the HW path; cast only at the wire boundary. + uint32_t PCI_Device::read32(PCI::config_reg reg) noexcept { PCI::msg req; req.data = 0x80000000; - req.addr = pci_addr; - req.reg = reg; + req.addr = pci_addr_; + req.reg = static_cast(reg); outpd(PCI::CONFIG_ADDR, 0x80000000 | req.data); return inpd(PCI::CONFIG_DATA); } - void PCI_Device::write_dword(const uint8_t reg, const uint32_t value) noexcept { + + uint32_t PCI_Device::read_dword(const uint16_t pci_addr, PCI::config_reg reg) noexcept { PCI::msg req; req.data = 0x80000000; - req.addr = pci_addr_; - req.reg = reg; + req.addr = pci_addr; + req.reg = static_cast(reg); outpd(PCI::CONFIG_ADDR, 0x80000000 | req.data); - outpd(PCI::CONFIG_DATA, value); + return inpd(PCI::CONFIG_DATA); } - uint32_t PCI_Device::read32(const uint8_t reg) noexcept { + void PCI_Device::write_dword(PCI::config_reg reg, const uint32_t value) noexcept { PCI::msg req; req.data = 0x80000000; req.addr = pci_addr_; - req.reg = reg; + req.reg = static_cast(reg); outpd(PCI::CONFIG_ADDR, 0x80000000 | req.data); - return inpd(PCI::CONFIG_DATA); + outpd(PCI::CONFIG_DATA, value); } __attribute__((noinline)) - uint16_t PCI_Device::read16(const uint8_t reg) noexcept { + uint16_t PCI_Device::read16(PCI::config_reg reg) noexcept { + const auto off = static_cast(reg); PCI::msg req; req.data = 0x80000000; req.addr = pci_addr_; - req.reg = reg; + req.reg = off; outpd(PCI::CONFIG_ADDR, 0x80000000 | req.data); - uint16_t data = inpw(PCI::CONFIG_DATA + (reg & 2)); - return data; + return inpw(PCI::CONFIG_DATA + (off & 2)); } - void PCI_Device::write16(const uint8_t reg, const uint16_t value) noexcept { + + void PCI_Device::write16(PCI::config_reg reg, const uint16_t value) noexcept { + const auto off = static_cast(reg); PCI::msg req; req.data = 0x80000000; req.addr = pci_addr_; - req.reg = reg; + req.reg = off; outpd(PCI::CONFIG_ADDR, 0x80000000 | req.data); - outpw(PCI::CONFIG_DATA + (reg & 2), value); + outpw(PCI::CONFIG_DATA + (off & 2), value); + } + + // Raw-offset fallbacks for dynamic addresses (BARs, capability chain, CONFIG_INTR). + uint32_t PCI_Device::read_dword(const uint16_t pci_addr, const uint8_t reg) noexcept { + return read_dword(pci_addr, static_cast(reg)); + } + + void PCI_Device::write_dword(const uint8_t reg, const uint32_t value) noexcept { + write_dword(static_cast(reg), value); + } + + uint32_t PCI_Device::read32(const uint8_t reg) noexcept { + return read32(static_cast(reg)); + } + + uint16_t PCI_Device::read16(const uint8_t reg) noexcept { + return read16(static_cast(reg)); + } + + void PCI_Device::write16(const uint8_t reg, const uint16_t value) noexcept { + write16(static_cast(reg), value); } union capability_t @@ -167,13 +195,10 @@ namespace hw { caps = {}; // the capability list is only available if bit 4 // in the status register is set - uint16_t status = read16(PCI_STATUS_REG); - //printf("read16 %#x status %#x\n", PCI_STATUS_REG, status); + uint16_t status = read16(PCI::config_reg::STATUS); if ((status & 0x10) == 0) return; // this offset works for non-cardbus bridges - uint32_t offset = PCI_CAPABILITY_REG; - // read first capability - offset = read16(offset) & 0xff; + uint32_t offset = read16(PCI::config_reg::CAPABILITY) & 0xff; offset &= ~0x3; // lower 2 bits reserved while (offset) { @@ -189,19 +214,20 @@ namespace hw { void PCI_Device::deactivate() { // disables device (except for configuration) - write_dword(PCI_CMD_REG, 0); + write_dword(PCI::config_reg::CMD, 0); } void PCI_Device::intx_enable() { - auto cmd = read16(PCI_CMD_REG); - write16(PCI_CMD_REG, cmd & ~(1 << 10)); + auto cmd = static_cast(read16(PCI::config_reg::CMD)); + cmd &= ~PCI::command::INTX_DISABLE; + write16(PCI::config_reg::CMD, static_cast(cmd)); // delete msi-x if (this->msix) delete this->msix; } bool PCI_Device::intx_status() { - auto stat = read16(PCI_STATUS_REG); + auto stat = read16(PCI::config_reg::STATUS); return stat & (1 << 3); } diff --git a/src/hw/pci_msi.cpp b/src/hw/pci_msi.cpp index 358aa3e25..1ff8ecd22 100644 --- a/src/hw/pci_msi.cpp +++ b/src/hw/pci_msi.cpp @@ -2,12 +2,6 @@ #include #include -#define PCI_CMD_REG 0x04 - -// MSI and MSI-X capability registers -#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ -#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ - // Message Signalled Interrupts registers #define PCI_MSI_FLAGS_ENABLE 0x0001 /* MSI feature enabled */ #define PCI_MSI_FLAGS_QMASK 0x000e /* Maximum queue size available */ @@ -30,20 +24,22 @@ namespace hw { int PCI_Device::msi_cap() { - return caps[PCI_CAP_ID_MSI]; + return caps[static_cast(PCI::cap_id::MSI)]; } int PCI_Device::msix_cap() { - return caps[PCI_CAP_ID_MSIX]; + return caps[static_cast(PCI::cap_id::MSIX)]; } void PCI_Device::init_msix() { assert(this->msix == nullptr); // disable intx - auto cmd = read16(PCI_CMD_REG); - write16(PCI_CMD_REG, cmd | (1 << 10)); + using namespace util::bitops; + auto cmd = static_cast(read16(PCI::config_reg::CMD)); + cmd |= PCI::command::INTX_DISABLE; + write16(PCI::config_reg::CMD, static_cast(cmd)); // enable MSI-X this->msix = new msix_t(*this, msix_cap()); // deallocate if it failed