|
1 | 1 | // SPDX-License-Identifier: Apache-2.0 |
2 | | -// This file contains code related to Hyper-V integration (Hypervisor). It provides a flag (`hyperv::present`) indicating whether the SNP Guest is running within a Hyper-V guest environment. |
3 | 2 |
|
4 | | -use super::*; |
5 | | - |
6 | | -use std::arch::x86_64::__cpuid; |
7 | | -use std::mem::size_of; |
8 | | - |
9 | | -const CPUID_GET_HIGHEST_FUNCTION: u32 = 0x80000000; |
10 | | -const CPUID_PROCESSOR_INFO_AND_FEATURE_BITS: u32 = 0x1; |
11 | | - |
12 | | -const CPUID_FEATURE_HYPERVISOR: u32 = 1 << 31; |
13 | | - |
14 | | -const CPUID_HYPERV_SIG: &str = "Microsoft Hv"; |
15 | | -const CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS: u32 = 0x40000000; |
16 | | -const CPUID_HYPERV_FEATURES: u32 = 0x40000003; |
17 | | -const CPUID_HYPERV_MIN: u32 = 0x40000005; |
18 | | -const CPUID_HYPERV_MAX: u32 = 0x4000ffff; |
19 | | -const CPUID_HYPERV_ISOLATION: u32 = 1 << 22; |
20 | | -const CPUID_HYPERV_CPU_MANAGEMENT: u32 = 1 << 12; |
21 | | -const CPUID_HYPERV_ISOLATION_CONFIG: u32 = 0x4000000C; |
22 | | -const CPUID_HYPERV_ISOLATION_TYPE_MASK: u32 = 0xf; |
23 | | -const CPUID_HYPERV_ISOLATION_TYPE_SNP: u32 = 2; |
24 | | - |
25 | | -const RSV1_SIZE: usize = size_of::<u32>() * 8; |
26 | | -const REPORT_SIZE: usize = 1184; |
27 | | -const RSV2_SIZE: usize = size_of::<u32>() * 5; |
28 | | -const TOTAL_SIZE: usize = RSV1_SIZE + REPORT_SIZE + RSV2_SIZE; |
29 | | -const REPORT_RANGE: std::ops::Range<usize> = RSV1_SIZE..(RSV1_SIZE + REPORT_SIZE); |
30 | | - |
31 | | -pub fn present() -> bool { |
32 | | - let mut cpuid = unsafe { __cpuid(CPUID_PROCESSOR_INFO_AND_FEATURE_BITS) }; |
33 | | - if (cpuid.ecx & CPUID_FEATURE_HYPERVISOR) == 0 { |
34 | | - return false; |
35 | | - } |
36 | | - |
37 | | - cpuid = unsafe { __cpuid(CPUID_GET_HIGHEST_FUNCTION) }; |
38 | | - if cpuid.eax < CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS { |
39 | | - return false; |
40 | | - } |
41 | | - |
42 | | - cpuid = unsafe { __cpuid(CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS) }; |
43 | | - if cpuid.eax < CPUID_HYPERV_MIN || cpuid.eax > CPUID_HYPERV_MAX { |
44 | | - return false; |
45 | | - } |
46 | | - |
47 | | - let mut sig: Vec<u8> = vec![]; |
48 | | - sig.append(&mut cpuid.ebx.to_le_bytes().to_vec()); |
49 | | - sig.append(&mut cpuid.ecx.to_le_bytes().to_vec()); |
50 | | - sig.append(&mut cpuid.edx.to_le_bytes().to_vec()); |
51 | | - |
52 | | - if sig != CPUID_HYPERV_SIG.as_bytes() { |
53 | | - return false; |
54 | | - } |
55 | | - |
56 | | - cpuid = unsafe { __cpuid(CPUID_HYPERV_FEATURES) }; |
57 | | - |
58 | | - let isolated: bool = (cpuid.ebx & CPUID_HYPERV_ISOLATION) != 0; |
59 | | - let managed: bool = (cpuid.ebx & CPUID_HYPERV_CPU_MANAGEMENT) != 0; |
60 | | - |
61 | | - if !isolated || managed { |
62 | | - return false; |
63 | | - } |
64 | | - |
65 | | - cpuid = unsafe { __cpuid(CPUID_HYPERV_ISOLATION_CONFIG) }; |
66 | | - let mask = cpuid.ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK; |
67 | | - let snp = CPUID_HYPERV_ISOLATION_TYPE_SNP; |
68 | | - |
69 | | - if mask != snp { |
70 | | - return false; |
71 | | - } |
72 | | - |
73 | | - true |
74 | | -} |
75 | | - |
76 | | -pub mod report { |
77 | | - use super::*; |
78 | | - |
79 | | - use anyhow::{anyhow, Context}; |
80 | | - use serde::{Deserialize, Serialize}; |
81 | | - use sev::firmware::guest::AttestationReport; |
82 | | - use tss_esapi::{ |
83 | | - abstraction::nv, |
84 | | - handles::NvIndexTpmHandle, |
85 | | - interface_types::{resource_handles::NvAuth, session_handles::AuthSession}, |
86 | | - tcti_ldr::{DeviceConfig, TctiNameConf}, |
87 | | - }; |
88 | | - |
89 | | - const VTPM_HCL_REPORT_NV_INDEX: u32 = 0x01400001; |
90 | | - |
91 | | - #[repr(C)] |
92 | | - #[derive(Deserialize, Serialize, Debug, Clone, Copy)] |
93 | | - struct Hcl { |
94 | | - rsv1: [u32; 8], |
95 | | - report: AttestationReport, |
96 | | - rsv2: [u32; 5], |
97 | | - } |
98 | | - |
99 | | - pub fn get(vmpl: u32) -> Result<AttestationReport> { |
100 | | - if vmpl > 0 { |
101 | | - eprintln!("Warning: --vmpl argument was ignored because attestation report is pre-fetched at VMPL 0 and stored in vTPM."); |
102 | | - } |
103 | | - let bytes = tpm2_read().context("unable to read attestation report bytes from vTPM")?; |
104 | | - |
105 | | - hcl_report(&bytes) |
106 | | - } |
107 | | - |
108 | | - fn tpm2_read() -> Result<Vec<u8>> { |
109 | | - let handle = NvIndexTpmHandle::new(VTPM_HCL_REPORT_NV_INDEX) |
110 | | - .context("unable to initialize TPM handle")?; |
111 | | - let mut ctx = tss_esapi::Context::new(TctiNameConf::Device(DeviceConfig::default()))?; |
112 | | - ctx.set_sessions((Some(AuthSession::Password), None, None)); |
113 | | - |
114 | | - nv::read_full(&mut ctx, NvAuth::Owner, handle) |
115 | | - .context("unable to read non-volatile vTPM data") |
116 | | - } |
117 | | - |
118 | | - fn hcl_report(bytes: &[u8]) -> Result<AttestationReport> { |
119 | | - if bytes.len() < TOTAL_SIZE { |
120 | | - return Err(anyhow!( |
121 | | - "HCL report size mismatch: expected at least {}, got {}", |
122 | | - TOTAL_SIZE, |
123 | | - bytes.len() |
124 | | - )); |
125 | | - } |
126 | | - |
127 | | - let report_bytes = &bytes[REPORT_RANGE]; |
128 | | - |
129 | | - AttestationReport::from_bytes(report_bytes) |
130 | | - .context("Unable to convert HCL report bytes to AttestationReport") |
131 | | - } |
132 | | -} |
| 3 | +pub mod check; |
| 4 | +pub mod report; |
| 5 | +pub(crate) mod tpm; |
0 commit comments