Skip to content

APIs to write CPUID leaves#206

Draft
iximeow wants to merge 15 commits intogz:masterfrom
oxidecomputer:cpuid-write
Draft

APIs to write CPUID leaves#206
iximeow wants to merge 15 commits intogz:masterfrom
oxidecomputer:cpuid-write

Conversation

@iximeow
Copy link
Contributor

@iximeow iximeow commented Sep 4, 2025

hi! In oxidecomputer/omicron#8728 I've wanted to assemble synthetic CPUID profiles; an initial run at that involved a fairly impenetrable array of 33 leaves/subleaves. It's much better to be able to write out all the bits and get a table out, so this patch takes a swing at doing that. We're using this in our fork, but it seems useful enough I wonder if you might want this in rust-cpuid itself?

If you're in favor of pulling this in, I'll pick #205, #204, and #202 back out of this PR and clean up the history some. Even so, this is a draft because there are a few wrinkles here that need sorting out - I'll leave comments here about what stands out to me.

I've included an approximation of the CPUID profile I'm assembling there as an examples/synthetic.rs here to live as a demonstration of using the new APIs. I was also pleasantly surprised that this lets you write reasonable tests like "this CPUID profile should be different from that profile only in these ways", or "these CPUID profiles are wholly incompatible if they vary on these fields" (such as for CPUID fields that describe architectural behavior, like the XSAVE region layout - it'd be quite bad to run a VM that wants one XSAVE layout on a processor that does another...)

@gz
Copy link
Owner

gz commented Sep 4, 2025

Hi, thanks -- in general this seems pretty useful so I'd be happy to merge it if you want to clean it up.

I'll have to review it a bit closer in a bit!

Comment on lines +10 to +15
// TODO: Clone is necessary because CpuIdReader wants it (for leaves with more complex subleaf
// structures, like the extended topology info leaf)
//
// This implies that there's a full clone of the dump held on for those leaf-specific views, which
// is unfortunate! It's also not yet really clear how to assemble those more complex leaves for
// writer purposes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ExtendedTopologyIter and related are challenging. I think they could take a borrow of the reader, which could work for reading and writing as well as for synthetic sources and CpuIdReaderNative, but I haven't taken a really close look. But mostly I assume that having a core::alloc::Vec<CpuIdResult> of subleaves in the ExtendedTopologyIter-like types would be not be okay!

};

impl CpuIdWriter for CpuIdDump {
fn set_leaf(&mut self, leaf: u32, mut bits: Option<CpuIdResult>) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

CpuIdWriter requiring implementers to maintain internal consistency of the CPUID profile is not great, but I can imagine it being useful for the library to be able to represent any bit patterns in a CPUID profile. What's your preference on internally-inconsistent CPUID profiles?

Either way, the "maintain internal consistency" logic here could be pulled out into a function in the library to support implementations, so external implementations are more like

fn set_leaf(...) {
    // update storage so that `.cpuid1()` and `.cpuid2()` queries reflect the new leaf
    self.update_state(leaf, bits);
    // update any other leaves as necessary with knowledge that `leaf` was set to `bits`?
    self.write_hook(leaf, bits);
}

}

impl CpuIdDump {
fn update_max_leaves(&mut self) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

updating the max leaves on every write is naive, and this implementation is really naive. If you want this change we could lean into "max leaf indicated by leaf 0, 4000_0000, or 8000_0000 is accurate" and check if they need changes on write.

Some(LeafOrSubleaves::Leaf(res)) => *res,
Some(LeafOrSubleaves::Subleaf(subleaves)) => {
*subleaves.get(&0).unwrap_or_else(|| {
// TODO: vendor-specific fallback behavior
Copy link
Contributor Author

Choose a reason for hiding this comment

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

AMD and Intel have different behavior when querying leaves/subleaves that aren't actually defined and don't have behavior otherwise defined. I've fully punted here but would fill this out if you want the patch.

Comment on lines +494 to +509
/*
* TODO:
* set_cache_info (leaf 2h)
* set_processor_serial (leaf 3h)
* set_cache_parameters (leaf 4h)
* set_extended_topology_info_v2 (leaf 1Fh)
* set_rdt_monitoring_info (leaf Fh)
* set_rdt_allocation_info (leaf 10H)
* set_sgx_info (leaf 12h)
* set_processor_trace_info (leaf 14h)
* set_tsc_info (leaf 15h)
* set_processor_frequency_info (leaf 16h)
* set_soc_vendor_info (leaf 17h)
* set_deterministic_address_translation_info (leaf 18h)
* set_hypervisor_info
*/
Copy link
Contributor Author

Choose a reason for hiding this comment

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

my very AMD-specific use case meant I punted on all the Intel-only leaves but adding some setters here is straightforward if the change seems interesting.

@gz
Copy link
Owner

gz commented Sep 4, 2025

like the XSAVE region layout - it'd be quite bad to run a VM that wants one XSAVE layout on a processor that does another

I hear you on that one... it's a mystery how this one made it into the spec in the end XD

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.

3 participants