Document the Resolver system
Summary
The resolver system is a core architectural pattern in OpenVMM, but it does not have dedicated documentation yet. This issue covers what should be documented, where it should live, and how to structure it so a new contributor can understand and use the resolver pattern without reverse-engineering existing implementations.
What the resolver system is
The resolver system lives in vm/vmcore/vm_resource/ and is a type-erased, composable resource dependency injection framework. It solves a specific architectural problem: devices need backing resources (disks, network endpoints, serial backends, etc.), but the device code should not be statically coupled to every possible backend implementation.
The key insight is that resource descriptions are serialized via mesh as opaque messages, then resolved at runtime by a registry of resolvers. That breaks the compile-time coupling: a device like NVMe declares that it needs a Resource<DiskHandleKind>, and the resolver system figures out at VM construction time whether that is a file-backed disk, a VHD, a layered disk, a blob-backed disk, or anything else.
Today there are ~40 registered resolvers in openvmm_resources, covering chipset devices, disks, disk layers, network backends, serial backends, PCI devices, SCSI devices, virtio devices, and VMBus devices. About 13 resource kinds are defined in vm_resource/src/kind.rs. The pattern is actively growing.
What needs to be documented
Guide content (architecture + tutorials)
These belong in the Guide because they are conceptual and workflow-oriented. A reader should be able to understand the resolver architecture and know how to use it without reading the source.
1. Architecture overview — what the resolver system is, why it exists, how it fits into VM construction. This is the "explain it to a smart colleague who is new to the project" page. Should cover:
- the problem: why not just use an enum of all possible backends?
- the solution: type-erased resources, resource kinds, resource IDs, and resolvers
- the three-step flow: declare a resource handle → implement a resolver → register it
- the registration model:
declare_static_resolver! / declare_static_async_resolver! + register_static_resolvers! + linkme
- sub-resource resolution: how NVMe resolves its namespace disks recursively
- input context injection: how
ResolvePciDeviceHandleParams, ResolveDiskParameters, etc. flow into resolvers
- a mermaid diagram showing the resolution flow from user config → Resource → Resolver → constructed device
2. "How to add a new device backend" — a step-by-step tutorial. Walk through adding a hypothetical new disk backend:
- define a resource handle struct with
#[derive(MeshPayload)] + impl ResourceId<DiskHandleKind>
- implement
ResolveResource or AsyncResolveResource
- use
declare_static_resolver! or declare_static_async_resolver!
- add the resolver to
openvmm_resources/src/lib.rs (and openvmm_hcl_resources if needed for OpenHCL)
- test it
3. "How to add a new resource kind" — shorter page or section. When you need a wholly new kind (not just a new backend for an existing kind):
- define the kind tag type +
impl ResourceKind
- implement
CanResolveTo with the output type and input context
- where to put it:
vm_resource/src/kind.rs for shared kinds, or a device-specific resource crate for scoped kinds
4. Pattern catalog — a reference page with the notable resolver patterns, each with a one-paragraph explanation and a link to the canonical in-tree example:
| Pattern |
Example |
Key File |
| Simple sync resolver |
FileDiskResolver |
disk_file/src/lib.rs |
| Async resolver with sub-resources |
NvmeControllerResolver |
nvme/src/resolver.rs |
| Parallel sub-resource resolution |
LayeredDiskResolver |
disk_layered/src/resolver.rs |
| Virtio-over-PCI wrapping |
VirtioPciResolver |
virtio/src/resolver.rs |
| VMBus device with network backend |
NetvspResolver |
netvsp/src/resolver.rs |
| Platform resource (use-default) |
HaltResolver |
vmm_core/src/platform_resolvers.rs |
| Chipset device resolver |
BatteryResolver |
chipset/src/battery/resolver.rs |
5. Decision tree — when should you use the resolver pattern versus direct construction? Short page or callout box:
- if the device has interchangeable backends → resolver
- if the device is a singleton with no alternatives (CMOS RTC, PIC, PIT, DMA) → direct construction is fine
- if the resource is runtime-configurable from user input → resolver
- if the device is firmware → currently direct, could go either way
Rustdoc content (API reference)
These belong as rustdoc because they are type-level, crate-level, or trait-level reference material that should live next to the code.
1. vm_resource crate-level docs — the existing module doc is good but terse. It should be expanded with:
- a worked example showing the full declare → implement → register → resolve cycle
- doc links to the key types and traits
- a "see also" pointing to the Guide architecture page
2. ResourceKind trait docs — already decent, but should link to kind.rs and explain the "tag type" pattern more explicitly
3. CanResolveTo docs — explain why this is a separate trait (coherence/orphan rules), and show where the input context type comes from
4. Resource<K> docs — explain the type erasure: what id and message contain, how IntoResource works, and why this is a mesh message
5. ResourceResolver docs — explain the resolution lookup flow: static resolvers (linkme) → dynamic resolvers → error
6. Macro docs — declare_static_resolver!, declare_static_async_resolver!, register_static_resolvers! — each should have a usage example showing the typical declaration pattern
7. Per-resolver rustdoc — each resolver.rs should have a module-level doc comment explaining what resource kind it resolves, what it produces, and any notable sub-resource resolution it performs. Many already have inline comments; those should be promoted to doc comments.
Devices and subsystems not yet on the resolver framework
These are areas that currently use direct construction or manual dependency injection and could potentially benefit from moving to the resolver pattern. I'm listing them for awareness, not as a mandate to migrate everything immediately.
Chipset infrastructure (singletons)
CMOS RTC, DMA, IO APIC, PIC, PIT, power management — these are constructed directly in the chipset builder. They are singletons with no alternative implementations, so the resolver pattern may not add value. Document them as an explicit "we chose not to resolve these" decision.
Firmware
UEFI (firmware_uefi) and PCAT BIOS (firmware_pcat) are constructed directly. These could potentially benefit from a resolver if firmware selection becomes more dynamic, but today it is effectively a compile-time choice. Worth documenting the current state.
Framebuffer / video
framebuffer, video_core, vga_proxy — direct construction. Low priority for resolver migration unless the backend story becomes more pluggable.
VPC relay / VPC client
vpci_relay, vpci_client — infrastructure that wires PCI device pass-through. Not clear that these benefit from the resolver pattern.
What I do NOT want
- I do not want to document every resolver implementation in detail. The pattern catalog and per-resolver rustdoc should be sufficient.
- I do not want to mandate migration for devices that are fine with direct construction today. The decision tree should make it clear when the resolver is and is not the right tool.
- I do not want the Guide pages to duplicate the rustdoc. The Guide should explain concepts and workflows; the rustdoc should be the API reference. Cross-link between them.
Summary: where each piece lives
| Content |
Location |
Format |
| Architecture overview |
Guide/src/dev_guide/ (contributor-oriented) |
Guide page |
| "Add a new device backend" tutorial |
Same page or sub-page |
Guide page |
| "Add a new resource kind" |
Same page or sub-page |
Guide page |
| Pattern catalog |
Same page (table with links to source and rustdoc) |
Guide page |
| Decision tree (resolver vs. direct) |
Same page (callout or short section) |
Guide page |
vm_resource crate docs |
vm/vmcore/vm_resource/src/lib.rs |
Rustdoc |
Key trait docs (ResourceKind, CanResolveTo, etc.) |
Same file, per-trait |
Rustdoc |
| Macro docs |
Same file, per-macro |
Rustdoc |
| Per-resolver module docs |
Each resolver.rs |
Rustdoc |
Goals
- A new contributor can understand the resolver pattern from the Guide without reading source code
- A contributor adding a new device backend can follow the tutorial and get it right on the first try
- The rustdoc for
vm_resource is comprehensive enough that someone reading the API docs understands the type relationships
- The pattern catalog gives experienced contributors a quick reference for "which existing resolver should I model mine after?"
- The decision tree prevents unnecessary resolver adoption for devices that don't benefit from it
Non-goals
- Migrating all devices to the resolver framework
- Documenting every individual resolver's implementation logic
- Redesigning the resolver system itself
- Covering the mesh serialization layer in depth (that is a separate documentation effort)
Rough implementation plan
- Write the Guide architecture page: overview, three-step flow, mermaid diagram, registration model, sub-resource resolution
- Add the "how to add a backend" tutorial section with a worked example
- Add the pattern catalog table with links to canonical examples
- Add the decision tree section
- Expand rustdoc on
vm_resource crate: crate-level docs, key traits, macros
- Add module-level doc comments to the highest-traffic resolver implementations
- Update
Guide/src/SUMMARY.md to include the new page(s)
Resolved questions
- Guide placement: This is contributor-oriented content, so it belongs under
dev_guide/, not reference/architecture/.
- Pattern catalog links: Link to both GitHub source and rustdoc pages for each resolver.
- Troubleshooting /
ResolveError patterns: Better left to rustdoc on ResolveError and ResourceResolver rather than a separate Guide section.
Document the Resolver system
Summary
The resolver system is a core architectural pattern in OpenVMM, but it does not have dedicated documentation yet. This issue covers what should be documented, where it should live, and how to structure it so a new contributor can understand and use the resolver pattern without reverse-engineering existing implementations.
What the resolver system is
The resolver system lives in
vm/vmcore/vm_resource/and is a type-erased, composable resource dependency injection framework. It solves a specific architectural problem: devices need backing resources (disks, network endpoints, serial backends, etc.), but the device code should not be statically coupled to every possible backend implementation.The key insight is that resource descriptions are serialized via mesh as opaque messages, then resolved at runtime by a registry of resolvers. That breaks the compile-time coupling: a device like NVMe declares that it needs a
Resource<DiskHandleKind>, and the resolver system figures out at VM construction time whether that is a file-backed disk, a VHD, a layered disk, a blob-backed disk, or anything else.Today there are ~40 registered resolvers in
openvmm_resources, covering chipset devices, disks, disk layers, network backends, serial backends, PCI devices, SCSI devices, virtio devices, and VMBus devices. About 13 resource kinds are defined invm_resource/src/kind.rs. The pattern is actively growing.What needs to be documented
Guide content (architecture + tutorials)
These belong in the Guide because they are conceptual and workflow-oriented. A reader should be able to understand the resolver architecture and know how to use it without reading the source.
1. Architecture overview — what the resolver system is, why it exists, how it fits into VM construction. This is the "explain it to a smart colleague who is new to the project" page. Should cover:
declare_static_resolver!/declare_static_async_resolver!+register_static_resolvers!+ linkmeResolvePciDeviceHandleParams,ResolveDiskParameters, etc. flow into resolvers2. "How to add a new device backend" — a step-by-step tutorial. Walk through adding a hypothetical new disk backend:
#[derive(MeshPayload)]+impl ResourceId<DiskHandleKind>ResolveResourceorAsyncResolveResourcedeclare_static_resolver!ordeclare_static_async_resolver!openvmm_resources/src/lib.rs(andopenvmm_hcl_resourcesif needed for OpenHCL)3. "How to add a new resource kind" — shorter page or section. When you need a wholly new kind (not just a new backend for an existing kind):
impl ResourceKindCanResolveTowith the output type and input contextvm_resource/src/kind.rsfor shared kinds, or a device-specific resource crate for scoped kinds4. Pattern catalog — a reference page with the notable resolver patterns, each with a one-paragraph explanation and a link to the canonical in-tree example:
FileDiskResolverdisk_file/src/lib.rsNvmeControllerResolvernvme/src/resolver.rsLayeredDiskResolverdisk_layered/src/resolver.rsVirtioPciResolvervirtio/src/resolver.rsNetvspResolvernetvsp/src/resolver.rsHaltResolvervmm_core/src/platform_resolvers.rsBatteryResolverchipset/src/battery/resolver.rs5. Decision tree — when should you use the resolver pattern versus direct construction? Short page or callout box:
Rustdoc content (API reference)
These belong as rustdoc because they are type-level, crate-level, or trait-level reference material that should live next to the code.
1.
vm_resourcecrate-level docs — the existing module doc is good but terse. It should be expanded with:2.
ResourceKindtrait docs — already decent, but should link tokind.rsand explain the "tag type" pattern more explicitly3.
CanResolveTodocs — explain why this is a separate trait (coherence/orphan rules), and show where the input context type comes from4.
Resource<K>docs — explain the type erasure: whatidandmessagecontain, howIntoResourceworks, and why this is a mesh message5.
ResourceResolverdocs — explain the resolution lookup flow: static resolvers (linkme) → dynamic resolvers → error6. Macro docs —
declare_static_resolver!,declare_static_async_resolver!,register_static_resolvers!— each should have a usage example showing the typical declaration pattern7. Per-resolver rustdoc — each
resolver.rsshould have a module-level doc comment explaining what resource kind it resolves, what it produces, and any notable sub-resource resolution it performs. Many already have inline comments; those should be promoted to doc comments.Devices and subsystems not yet on the resolver framework
These are areas that currently use direct construction or manual dependency injection and could potentially benefit from moving to the resolver pattern. I'm listing them for awareness, not as a mandate to migrate everything immediately.
Chipset infrastructure (singletons)
CMOS RTC, DMA, IO APIC, PIC, PIT, power management — these are constructed directly in the chipset builder. They are singletons with no alternative implementations, so the resolver pattern may not add value. Document them as an explicit "we chose not to resolve these" decision.
Firmware
UEFI (
firmware_uefi) and PCAT BIOS (firmware_pcat) are constructed directly. These could potentially benefit from a resolver if firmware selection becomes more dynamic, but today it is effectively a compile-time choice. Worth documenting the current state.Framebuffer / video
framebuffer,video_core,vga_proxy— direct construction. Low priority for resolver migration unless the backend story becomes more pluggable.VPC relay / VPC client
vpci_relay,vpci_client— infrastructure that wires PCI device pass-through. Not clear that these benefit from the resolver pattern.What I do NOT want
Summary: where each piece lives
Guide/src/dev_guide/(contributor-oriented)vm_resourcecrate docsvm/vmcore/vm_resource/src/lib.rsResourceKind,CanResolveTo, etc.)resolver.rsGoals
vm_resourceis comprehensive enough that someone reading the API docs understands the type relationshipsNon-goals
Rough implementation plan
vm_resourcecrate: crate-level docs, key traits, macrosGuide/src/SUMMARY.mdto include the new page(s)Resolved questions
dev_guide/, notreference/architecture/.ResolveErrorpatterns: Better left to rustdoc onResolveErrorandResourceResolverrather than a separate Guide section.