-
Notifications
You must be signed in to change notification settings - Fork 163
add igvmfile read/write to vmgstool #2566
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
base: main
Are you sure you want to change the base?
Conversation
|
This PR modifies files containing For more on why we check whole files, instead of just diffs, check out the Rustonomicon |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds functionality to vmgstool for reading IGVM firmware files from Windows DLLs and writing them to VMGS file ID 8 (GUEST_FIRMWARE). The implementation uses Windows API calls to extract resources from DLLs and supports multiple resource codes (nonconfidential, snp, snp_no_hcl, tdx, tdx_no_hcl) for different VM configurations.
Key changes:
- New
copy-igvmfilecommand that extracts IGVM files from DLLs using Windows resource APIs - Support for encrypted and unencrypted VMGS files when writing IGVM data
- Platform-specific implementation for Windows x86_64 only
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| vm/vmgs/vmgstool/src/main.rs | Implements the copy-igvmfile command with Windows API resource loading, adds error handling, command-line parsing, and test cases |
| vm/vmgs/vmgstool/build.rs | Adds rustc-check-cfg directive for guest_arch configuration |
| vm/vmgs/vmgstool/Cargo.toml | Adds winapi dependency with required Windows API features |
| Guide/src/dev_guide/dev_tools/vmgstool.md | Documents the new copy-igvmfile command usage |
86213db to
a684b8d
Compare
vm/vmgs/vmgstool/src/main.rs
Outdated
| GspUnknown, | ||
| #[error("VMGS file is using an unknown encryption algorithm")] | ||
| EncryptionUnknown, | ||
| #[cfg(all(windows, guest_arch = "x86_64"))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What makes any of this x86_64 specific?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file this command is being used to parse (vmfirmwarehcl.dll) doesn't exist in ARM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the code is capable of running on ARM we should let it, even if it'll never be used. cfgs like this should only be used when the code can't run on a given platform due to actual hardware differences.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good, thanks for the feedback. I'll take out the cfgs and gate the couple of tests that try to access vmfirmwarehcl.dll, so they don't fail in the ARM tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you do what I suggested below and use include_bytes, the test should work fine on arm.
smalis-msft
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-vmgs portions look good to me, I'll let @tjones60 do the final signoff
vm/vmgs/vmgstool/src/main.rs
Outdated
| Ok(()) | ||
| } | ||
|
|
||
| async fn write_igvmfile( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this function is necessary, just put the calls to read_igvmfile and vmgs_write in vmgs_file_copy_igvmfile.
vm/vmgs/vmgstool/src/main.rs
Outdated
|
|
||
| let encrypt = key_path.is_some(); | ||
| let mut vmgs = vmgs_file_open(file_path, key_path, OpenMode::ReadWrite).await?; | ||
| eprintln!("Reading IGVMfile from DLL"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, as written we log "Reading IGVMfile..." three times; we should do that only once.
vm/vmgs/vmgstool/src/main.rs
Outdated
| allow_overwrite: bool, | ||
| resource_code: ResourceCode, | ||
| ) -> Result<(), Error> { | ||
| eprintln!("Writing IGVMfile to VMGS"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is redundant, vmgs_write logs the file ID
vm/vmgs/vmgstool/src/main.rs
Outdated
| use std::io::{Read, Seek, SeekFrom}; | ||
|
|
||
| eprintln!("Reading IGVMfile from DLL"); | ||
| // Convert the wide string back to a PathBuf |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this comment stale?
vm/vmgs/vmgstool/src/main.rs
Outdated
| #[error("VMGS file is using an unknown encryption algorithm")] | ||
| EncryptionUnknown, | ||
| #[error("Unable to read IGVM file with Error: {0}")] | ||
| UnableToReadIgvmFile(String), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably just be
#[error("Unable to read IGVM file")]
IgvmFile(#[source] std::io::Error)
That way you don't have to manually format the string every time. Also, we could probably just use Error::DataFile for this, since for this operation it is obvious that the data file is the igvm file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, since the error from try_find_resource_from_dll is anyhow, how about we use this for the anyhow errors and use DataFile for the io Errors
#[error("Unable to parse IGVM file")]
IgvmFile(#[source] anyhow::Error)
vm/vmgs/vmgstool/src/main.rs
Outdated
| async fn read_igvmfile(dll_path: PathBuf, resource_code: ResourceCode) -> Result<Vec<u8>, Error> { | ||
| use std::io::{Read, Seek, SeekFrom}; | ||
|
|
||
| eprintln!("Reading IGVMfile from DLL"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Is this the correct stylization? Seems like "IGVM file" would look better than "IGVMfile" here and elsewhere.
vm/vmgs/vmgstool/src/main.rs
Outdated
| file.read_exact(&mut bytes) | ||
| .map_err(|e| Error::UnableToReadIgvmFile(format!("Failed to read resource data: {}", e)))?; | ||
|
|
||
| eprintln!("Successfully loaded IGVMfile from DLL"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this seems redundant
vm/vmgs/vmgstool/src/main.rs
Outdated
| /// `keypath` and `encryptionalgorithm` must both be specified if encrypted | ||
| /// guest state is required. | ||
| Create { | ||
| /// VMGS file path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this added? FilePathArg should contain the appropriate doc comment used in the help message.
| ### Read DLL File to Write IGVMfile to VMGS | ||
|
|
||
| Additionally, the VmgsTool contains a tool to read the IGVMfile from a DLL (passed in as a data file) | ||
| and write it to VMGS FileId 8 (GUEST_FIRMWARE). To do this pass one of three resource codes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...five resource codes... ?
vm/vmgs/vmgstool/src/main.rs
Outdated
|
|
||
| fn parse_resource_code(resource_code: &str) -> Result<ResourceCode, &'static str> { | ||
| match resource_code { | ||
| "nonconfidential" => Ok(ResourceCode::NonConfidential), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: All the other values use all caps, would it be ok to be consistent here and use all caps for the resource codes as well? We should probably just make it case insensitive, but I'd rather be consistent for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, since this isn't an OpenEnum used elsewhere, we can probably just have clap auto generate the help message and parsing function (#[derive(clap::ValueEnum)]). It wouldn't be consistent with the other ones (which are used in data structures that rely on their format), but would prevent us from having extra boilerplate.
vm/vmgs/vmgstool/src/main.rs
Outdated
| } | ||
|
|
||
| #[derive(Debug, Clone, Copy)] | ||
| #[repr(i32)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DllResourceDescriptor::new accepts a u32, but here you are saying it is an i32. Shouldn't they be the same?
vm/vmgs/vmgstool/src/main.rs
Outdated
| ) -> Result<(), Error> { | ||
| eprintln!("Writing IGVMfile to VMGS"); | ||
|
|
||
| let encrypt = key_path.is_some(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would we ever want to encrypt the IGVM file? Would it ever contain user data?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was a holdover, I changed it in the Host Agent version, but I guess Heather didn't get a chance to change it here. I'll update
Add tool to VmgsTool to read the IGVMfile from a DLL (passed in as a data file) and write it to VMGS FileId 8 (GUEST_FIRMWARE). To do this pass one of three resource codes (nonconfidential, snp, tdx) into the cmdline tool:
vmgstool.exe copy-igvmfile --filepath --keypath --datapath --resource-code