Skip to content

pci-bus: support I/O port BARs and per-function Subsystem IDs - #225

Merged
LekKit merged 1 commit into
LekKit:stagingfrom
pufit:feat/pci-bus-io-bars
Jun 18, 2026
Merged

pci-bus: support I/O port BARs and per-function Subsystem IDs#225
LekKit merged 1 commit into
LekKit:stagingfrom
pufit:feat/pci-bus-io-bars

Conversation

@SolAstrius

Copy link
Copy Markdown
Contributor

Two related extensions needed by Linux drivers that match on strict subsystem IDs and/or probe via inb/outb on the BAR. Both are purely additive — defaults preserve the existing behaviour for every device already in the tree.

Per-function Subsystem Vendor / Subsystem Device ID

The PCI configuration Subsystem IDs at config-space offset 0x2C (§6.2.4) were hardcoded to 0x510010DC (CERN/ECP/EDU) for every emulated function. Most Linux drivers ignore subsys IDs and match on vendor:device, so this was invisible — until parport_pc, whose PCI table for the NetMos 9900 entry strictly requires 0xA000:0x2000:

{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
  0xA000, 0x2000, 0, 0, netmos_9900 },

Without the match, parport_pc skips the device, no parport0 / lp0 nodes appear, and userspace can't reach the emulated port even though lspci correctly shows the MosChip MCS9900 ID.

Add subsys_vendor_id / subsys_device_id to pci_func_desc_t and pci_func_t. Default zero — the read handler falls back to the original 0x510010DC placeholder so existing devices that don't set these (sound-hda, nvme, rtl8169, usb-xhci, ata, bochs-display, pci-vfio) round-trip the same value they always have.

I/O port BARs

The bus only emitted Memory-type BARs — every BAR allocation went through pci_assign_mmio_addr (0x40000000+ range), and the BAR config read never set PCI_BAR_IO_SPACE. This worked for every existing device because all of them use memory-mapped registers.

Fails for any Linux driver whose PCI probe path does:

int io = pci_resource_start(dev, n);
inb(io); outb(val, io);

On RISC-V (and other non-x86 platforms where Linux maps PCI I/O space to MMIO) inb(addr) resolves to a memory access at PCI_IOBASE + addr. With a Memory BAR, pci_resource_start returns e.g. 0x40009000inb() then reads at PCI_IOBASE + 0x40009000, way outside the legal I/O window, returns 0xFF, the driver sees no data and skips the device.

parport_pc is the canonical example. Linux's __parport_pc_probe_port takes raw I/O addresses and feeds them through inb/outb. There is no memory-mapped probe path. The same applies to many vintage / legacy PCI drivers (some serial chips, ISA-style PCI cards, GPIB controllers).

Add I/O BAR support:

  • pci_func_desc_t.bar_io_mask: bitmap of BAR indices that should be declared I/O type. Default 0 = all Memory.
  • pci_assign_io_addr: allocates from bus->io_addr range (PCI_IO_ADDR_DEFAULT = 0x03000000, length 0x10000) instead of the memory pool.
  • pci_attach_bar takes is_io, threads to the right allocator.
  • BAR config-space read: I/O BARs return the I/O port number (offset within bus->io_addr) ORed with PCI_BAR_IO_SPACE (bit 0).
  • BAR config-space write: I/O BARs use 4-byte alignment per PCI spec §6.2.5.1, preserving type bits and relocating to bus->io_addr + (val & ~0x3).
  • MSI-X BAR allocation always uses the memory pool — those are architecturally Memory BARs per spec §6.8.

Signed-off-by: Sol Astrius <sol@astrius.ink>
@LekKit
LekKit merged commit e6d6919 into LekKit:staging Jun 18, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants