Skip to content

nvme: allow PCI ID overrides #987

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

mattkur
Copy link
Contributor

@mattkur mattkur commented Mar 9, 2025

I have found it useful to ask the nvme device to pretend to be a different PCI device type. This is useful for testing and prototyping.

Validated via unit tests.

@mattkur mattkur requested review from a team as code owners March 9, 2025 23:09
@@ -38,7 +38,7 @@ pub mod hwid {
}

open_enum::open_enum! {
/// ClassCode identifies the PCI device's type.
/// ClassCode identifies the PCI device's type, the "Base Class".
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for reviewers: these changes are to match the terminology in PCI Code and ID Assignment Specification from the PCI SIG standards body.

@mattkur
Copy link
Contributor Author

mattkur commented Mar 9, 2025

While this primitive is useful for me in the NVMe context, I could see a generalized override make sense. In the current architecture, each device owns the ConfigSpaceType0Emulator emulator for that device, which is where these HardwareIds are set. I didn't immediately come up with an elegant way of making this a general primitive.

@daprilik
Copy link
Contributor

In the current architecture, each device owns the ConfigSpaceType0Emulator emulator for that device, which is where these HardwareIds are set. I didn't immediately come up with an elegant way of making this a general primitive.

I can offer some context on why this is modeled the way it is.

In a nutshell - we wanted to keep the "core" PCI interconnect trait as simple (and as flexible) as possible, so we could decouple our PCI root controller implementations (e.g: vPCI, legacy PCI, and in the future - PCIe) from the various PCI devices that plugged into them (e.g: legacy gen1 PCI devices, virtio-pci, modern pci deviecs, etc...). Pushing the details of config space emulation / layout into the devices themselves (aided by a common shared "helper" emulator that those devices could leverage) enables the core interface to be as simple as "read/write bytes from a device's PCI config space", which significantly simplifies the wiring required to mix-and-match PCI devices / busses / topologies.

Of course, this means there's no centralized place to set up these sorts of overrides... and you are forced to plumb through this config info manually.

That said - I might have some ideas on how we can simplify some of the plumbing here. I'll leave some more comments on the PR

@jstarks
Copy link
Member

jstarks commented Mar 10, 2025

If this is just for testing/prototyping, I wonder if it would be better to instead make a wrapper PCI device that allows for various config space functionality to be tweaked before/after calling through to the real device.

@daprilik
Copy link
Contributor

Hah, and here I was just about to leave a different suggestion (something along the lines of adding a new field to ResolvePciDeviceHandleParams, where you can stuff these overrides). I do like John's a lot more.

The caveat is that you end up paying extra virtual dispatch cost when doing chipset device operations (IO, MMIO, PCI, etc...), which is a bit unfortunate.

My follow-on suggestion is that you could implement overrides as a feature on the PCI "bus" itself. i.e: when attaching a device to the vPCI bus, give it the ability to associate ID overrides with particular devices, and have it do an extra intercept when incoming/outbound pci_{read/write} to devices.

@mattkur
Copy link
Contributor Author

mattkur commented Mar 11, 2025

@jstarks, @daprilik Thank you for the suggestions, and I agree with the course of action. If I'm following correctly, then I'd:

  • Create a wrapper type that impl ChipsetDevice, C
  • Create a wrapper type that impl PciConfigSpace, P
  • C's supports_pci method would return a P, with whatever overrides the creator sets.
  • Everything else gets passed through to the underlying device.
  • Still wire up the override in impl AsyncResolveResource<PciDeviceHandleKind, NvmeControllerHandle> for NvmeControllerResolver (with the actual config space overrides coming from ResolvePciDeviceHandleParams).

Is this what you had in mind?

This raises a separate question for me: where is the machinery to decide how to offer this device (e.g. over virtio, vpci, or on the emulated pci bus)?

I don't think I'll get to them in a timely manner. I'll move this PR back to draft and pick it back up when I am able.

@jstarks
Copy link
Member

jstarks commented Mar 11, 2025

If you take my suggestion (as opposed to doing this in the bus, which is also a reasonable suggestion)...

C and P can be the same struct.

Then, instead of doing something in the NVMe resolver, you'd make a new resource:

struct DisguisedPciDeviceHandle {
    device: Resource<PciDeviceHandleKind>,
    prog_id: Option<Whatever>,
    ...
}

The resolver for this thing would resolve device to a device and instantiate a C/P that wraps it.

So there wouldn't be any NVMe-related changes here.

@mattkur
Copy link
Contributor Author

mattkur commented Mar 11, 2025

If you take my suggestion (as opposed to doing this in the bus, which is also a reasonable suggestion)...

C and P can be the same struct.

Then, instead of doing something in the NVMe resolver, you'd make a new resource:

struct DisguisedPciDeviceHandle {
    device: Resource<PciDeviceHandleKind>,
    prog_id: Option<Whatever>,
    ...
}

The resolver for this thing would resolve device to a device and instantiate a C/P that wraps it.

So there wouldn't be any NVMe-related changes here.

Got it, thanks. This is helpful.

@mattkur mattkur marked this pull request as draft March 11, 2025 16:54
@daprilik
Copy link
Contributor

And if you take my suggestion (pushing this override logic into the vpci bus itself)

I believe the key function to work around is build_vpci_device. If you follow the code leading into / out-of that function, it should be pretty straightforward to add in the right hooks / logic to teach the underlying VpciBus how to do overrides (by updating its impl MmioIntercept for VpciBus implementation).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants