hw: use enum class for PCI config registers#2369
Conversation
Replace PCI config register #defines with typed enum classes (config_reg, command, cap_id). Updates in pci_device.cpp and pci_msi.cpp. Related: includeos#2333
|
All the integration checks seem to be working too :) I wonder if it's reasonable to keep using shrtnd forms for the enum fields, or if we should use full words. Are these acronyms standard in other places, or is it just to save a few keystrokes here? Personally I like when names are expressive on their own right, even if someone is unfamiliar with the context. For instance, I have no idea what |
|
You can add |
There was a problem hiding this comment.
Casting things back to raw numbers for all operations defeats the purpose of using enum classes for type safety, and just adds noise with every cast.
Perhaps it'd be necessary to remove a generic write16 and read16, and make these methods for the enum-class instead for proper type safety?
If these functions are used by other parts of IncludeOS, I would approach it by first making one commit to refactor it internally, and exposing a temporary stub function, and a secondary commit to clean up legacy interface users (easy to find by just deleting the stub).
If these interfaces are required by an external protocol, I don't think there's too much we can do except hope that people prefer using the type-safe versions.
See also inline comments.
| uint16_t data = inpw(PCI::CONFIG_DATA + (reg & 2)); | ||
| return data; | ||
| } | ||
| void PCI_Device::write16(const uint8_t reg, const uint16_t value) noexcept { |
There was a problem hiding this comment.
Can we change the signature here? Is there a reason to keep this uint8_t?
void PCI_Device::write16(const PCI_Device::cap_id reg, const uint16_t value) noexcept {
PCI::msg req;
req.data = 0x80000000;
req.addr = pci_addr_;
req.reg = reg;
outpd(PCI::CONFIG_ADDR, 0x80000000 | req.data);
outpw(PCI::CONFIG_DATA + (reg & 2), value);
}
// idem for PCI_Device::config_reg etc
// fallback if this needs to be exposed, otherwise for type safety better not
void PCI_Device::write16(const uint8_t reg, const uint16_t value) noexcept {
write16(static_cast<PCI_Device::cap_id>(reg), value);
}There was a problem hiding this comment.
Addressed in e47ca6071:
uint16_t PCI_Device::read16(PCI::config_reg reg) noexcept {
return read16(static_cast<uint8_t>(reg));
}
void PCI_Device::write16(PCI::config_reg reg, const uint16_t value) noexcept {
write16(static_cast<uint8_t>(reg), value);
}Same pattern for read32, write_dword, and static read_dword. The uint8_t overloads remain for dynamic offsets (capability chain, BAR indices, etc.) and delegate inward where appropriate.
I used PCI::config_reg (not nested under PCI_Device) since the enums live in the PCI namespace — same as your sketch but with the actual type names from the header.
| cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEM | PCI_COMMAND_IO; | ||
| write_dword(PCI_CMD_REG, cmd); | ||
| uint32_t cmd = read32(static_cast<uint8_t>(PCI::config_reg::CMD)); | ||
| cmd |= static_cast<uint32_t>(PCI::command::MASTER) |
There was a problem hiding this comment.
See api/mem/alloc/buddy.hpp for an example of api/util/bitops.hpp::enable_bitmask_ops. Casting this to uint32_t isn't necessary.
That said, can all combinations of enum fields be combined? I'd like for proper bitmask support to check for valid combinations.
There was a problem hiding this comment.
Also addressed in e47ca6071 — added enable_bitmask_ops<PCI::command> (same pattern as buddy.hpp) and the constructor now does:
cmd |= static_cast<uint32_t>(PCI::command::MASTER
| PCI::command::MEM
| PCI::command::IO);Re combination validity: per PCI spec / Linux pci_regs.h, PCI_COMMAND_IO, PCI_COMMAND_MEMORY, and PCI_COMMAND_MASTER are independent enable bits (0x01, 0x02, 0x04), so OR'ing them is standard. I also added command::INTX_DISABLE (0x400) for the bit used in init_msix / intx_enable instead of a raw (1 << 10).
- Add read/write overloads taking PCI::config_reg; uint8_t overloads delegate - Enable enable_bitmask_ops<PCI::command> for IO | MEM | MASTER - Document enum names against Linux pci_regs.h / PCI Local Bus Spec - Use command::INTX_DISABLE instead of raw bit 10 in intx/msix paths
|
Good question. The short names are not arbitrary — they match what IncludeOS already used in For example:
I've added Doxygen on each enum member mapping to the Linux/spec name, plus a block comment linking https://www.pcisig.com/specifications and |
|
Done — added |
|
Agree on the casts — the latest commit ( I kept the See inline replies for bitmask ops and naming/docs. |
Replace PCI config register
#defines with typedenum classes (config_reg,command,cap_id) for type safety.Changes:
api/hw/pci_device.hpp— enums with Doxygen mapping to Linuxpci_regs.h/ PCI Local Bus Spec;enable_bitmask_ops<PCI::command>read16/write16/read32/write_dwordoverloads takingPCI::config_reg(cast to integer only inside HW accessors)uint8_toverloads retained for dynamic offsets (BARs, capability walk, drivers usingCONFIG_INTR)src/hw/pci_device.cpp,src/hw/pci_msi.cpp— use typed APIs at standard config offsets;command::INTX_DISABLEfor INTx disable bitTested:
nix-build unittests.nix— 85/85 passed (Nix 2.34.7, Linux).Split from #2367 per review feedback.
Closes: #2333