tpm/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Emulated TPM 2.0 device.
5//!
6//! This module implements the hardware TPM interface. This includes
7//! both the MMIO interface for reading/writing TPM command/reply
8//! buffers, as well as the IO Port interface for performing PPI requests and
9//! configuring MMIO request/response regions.
10
11#![cfg(feature = "tpm")]
12#![expect(missing_docs)]
13#![forbid(unsafe_code)]
14
15pub mod ak_cert;
16pub mod logger;
17mod recover;
18pub mod resolver;
19mod tpm20proto;
20mod tpm_helper;
21
22use self::io_port_interface::PpiOperation;
23use self::io_port_interface::TpmIoCommand;
24use crate::ak_cert::TpmAkCertType;
25use chipset_device::ChipsetDevice;
26use chipset_device::io::IoError;
27use chipset_device::io::IoResult;
28use chipset_device::mmio::MmioIntercept;
29use chipset_device::pio::PortIoIntercept;
30use chipset_device::poll_device::PollDevice;
31use cvm_tracing::CVM_ALLOWED;
32use cvm_tracing::CVM_CONFIDENTIAL;
33use guestmem::GuestMemory;
34use inspect::Inspect;
35use inspect::InspectMut;
36use logger::TpmLogEvent;
37use logger::TpmLogger;
38use ms_tpm_20_ref::MsTpm20RefPlatform;
39use parking_lot::Mutex;
40use std::future::Future;
41use std::ops::RangeInclusive;
42use std::pin::Pin;
43use std::sync::Arc;
44use std::task::Poll;
45use std::task::Waker;
46use thiserror::Error;
47use tpm_helper::CommandDebugInfo;
48use tpm_helper::TpmCommandError;
49use tpm_helper::TpmEngineHelper;
50use tpm_helper::TpmHelperError;
51use tpm_resources::TpmRegisterLayout;
52use tpm20proto::CommandCodeEnum;
53use tpm20proto::NV_INDEX_RANGE_BASE_PLATFORM_MANUFACTURER;
54use tpm20proto::NV_INDEX_RANGE_BASE_TCG_ASSIGNED;
55use tpm20proto::ReservedHandle;
56use tpm20proto::TPM20_HT_PERSISTENT;
57use tpm20proto::TPM20_RH_PLATFORM;
58use vmcore::device_state::ChangeDeviceState;
59use vmcore::non_volatile_store::NonVolatileStore;
60use vmcore::non_volatile_store::NonVolatileStoreError;
61use zerocopy::FromBytes;
62use zerocopy::IntoBytes;
63
64pub const TPM_DEVICE_MMIO_REGION_BASE_ADDRESS: u64 = 0xfed40000;
65pub const TPM_DEVICE_MMIO_REGION_SIZE: u64 = 0x70;
66
67pub const TPM_DEVICE_IO_PORT_RANGE_BEGIN: u16 = 0x1040;
68pub const TPM_DEVICE_IO_PORT_RANGE_END: u16 = 0x1048;
69
70pub const TPM_DEVICE_IO_PORT_CONTROL_OFFSET: u16 = 0;
71pub const TPM_DEVICE_IO_PORT_DATA_OFFSET: u16 = 4;
72
73pub const TPM_DEVICE_MMIO_PORT_REGION_BASE_ADDRESS: u64 =
74    TPM_DEVICE_MMIO_REGION_BASE_ADDRESS + 0x80;
75pub const TPM_DEVICE_MMIO_PORT_CONTROL: u64 =
76    TPM_DEVICE_MMIO_PORT_REGION_BASE_ADDRESS + TPM_DEVICE_IO_PORT_CONTROL_OFFSET as u64;
77pub const TPM_DEVICE_MMIO_PORT_DATA: u64 =
78    TPM_DEVICE_MMIO_PORT_REGION_BASE_ADDRESS + TPM_DEVICE_IO_PORT_DATA_OFFSET as u64;
79pub const TPM_DEVICE_MMIO_PORT_REGION_SIZE: u64 = 0x8;
80
81const TPM_PAGE_SIZE: usize = 4096;
82
83const RSA_2K_MODULUS_BITS: u16 = 2048;
84const RSA_2K_MODULUS_SIZE: usize = (RSA_2K_MODULUS_BITS / 8) as usize;
85const RSA_2K_EXPONENT_SIZE: usize = 3;
86
87const TPM_RSA_SRK_HANDLE: ReservedHandle = ReservedHandle::new(TPM20_HT_PERSISTENT, 0x01);
88const TPM_AZURE_AIK_HANDLE: ReservedHandle = ReservedHandle::new(TPM20_HT_PERSISTENT, 0x03);
89const TPM_GUEST_SECRET_HANDLE: ReservedHandle = ReservedHandle::new(TPM20_HT_PERSISTENT, 0x04);
90
91// Reserved handles for Microsoft (Component OEM) ranges from 0x01c101c0 to 0x01c101ff
92const TPM_NV_INDEX_AIK_CERT: u32 = NV_INDEX_RANGE_BASE_TCG_ASSIGNED + 0x000101d0;
93const TPM_NV_INDEX_MITIGATED: u32 = NV_INDEX_RANGE_BASE_TCG_ASSIGNED + 0x000101d2;
94const TPM_NV_INDEX_ATTESTATION_REPORT: u32 = NV_INDEX_RANGE_BASE_PLATFORM_MANUFACTURER + 0x1;
95const TPM_NV_INDEX_GUEST_ATTESTATION_INPUT: u32 = NV_INDEX_RANGE_BASE_PLATFORM_MANUFACTURER + 0x2;
96
97/// Use the SNP and TDX-defined report data size for now.
98// DEVNOTE: This value should be upper bound among all the supported TEE types.
99const ATTESTATION_REPORT_DATA_SIZE: usize = 0x40;
100
101// 24 hours (in seconds)
102const AK_CERT_RENEW_PERIOD: std::time::Duration = std::time::Duration::new(24 * 60 * 60, 0);
103// 2 seconds
104const REPORT_TIMER_PERIOD: std::time::Duration = std::time::Duration::new(2, 0);
105
106// 16kB: vtpmservice provisions a 16kB blob for the vTPM; HCL/OpenHCL provisions a 32k blob
107const LEGACY_VTPM_SIZE: usize = 16384;
108
109#[derive(Debug, Copy, Clone, Inspect)]
110#[repr(C)]
111struct PpiState {
112    pending_ppi_operation: PpiOperation,
113    in_query_ppi_operation: PpiOperation,
114    set_ppi_operation_state: u32,
115    last_ppi_operation: PpiOperation,
116    last_ppi_state: u32,
117    ppi_set_operation_arg3_integer2: u32,
118    tpm_capability_hash_alg_bitmap: u32,
119}
120
121impl PpiState {
122    fn new() -> Self {
123        Self {
124            pending_ppi_operation: PpiOperation::NO_OP,
125            in_query_ppi_operation: PpiOperation::NO_OP,
126            set_ppi_operation_state: 0,
127            last_ppi_operation: PpiOperation::NO_OP,
128            last_ppi_state: 0,
129            ppi_set_operation_arg3_integer2: 0,
130            tpm_capability_hash_alg_bitmap: 0,
131        }
132    }
133}
134
135/// TPM 2.0 Mobile Reference Architecture, Section 3.1
136#[derive(Debug, Copy, Clone, Inspect)]
137struct ControlArea {
138    /// Used to control power state transition.
139    pub request: u32,
140    /// Used to indicate a status.
141    pub status: u32,
142    /// Used to abort command processing.
143    pub cancel: u32,
144    /// Used to indicate that a command is available for processing
145    pub start: u32,
146    /// Size of the Command Buffer.
147    pub command_size: u32,
148    /// Physical address of the Command Buffer.
149    pub command_pa: u64,
150    /// Size of the Response Buffer.
151    pub response_size: u32,
152    /// Physical address of the Response Buffer.
153    pub response_pa: u64,
154}
155
156// TODO: switch this over to open_enum!
157#[expect(dead_code)]
158impl ControlArea {
159    const OFFSET_OF_LOC_STATE: usize = 0x00;
160    const OFFSET_OF_LOC_CTRL: usize = 0x08;
161    const OFFSET_OF_LOC_STS: usize = 0x0C;
162    const OFFSET_OF_CRB_INTF_ID: usize = 0x30;
163    const OFFSET_OF_REQUEST: usize = 0x40;
164    const OFFSET_OF_STATUS: usize = 0x44;
165    const OFFSET_OF_CANCEL: usize = 0x48;
166    const OFFSET_OF_START: usize = 0x4C;
167    const OFFSET_OF_INTERRUPT_CONTROL: usize = 0x50;
168    const OFFSET_OF_COMMAND_SIZE: usize = 0x58;
169    const OFFSET_OF_COMMAND_PHYSICAL_ADDRESS_LO: usize = 0x5C;
170    const OFFSET_OF_COMMAND_PHYSICAL_ADDRESS_HI: usize = 0x60;
171    const OFFSET_OF_RESPONSE_SIZE: usize = 0x64;
172    const OFFSET_OF_RESPONSE_PHYSICAL_ADDRESS_LO: usize = 0x68;
173    const OFFSET_OF_RESPONSE_PHYSICAL_ADDRESS_HI: usize = 0x6C;
174
175    fn new() -> Self {
176        Self {
177            request: 0,
178            status: 0,
179            cancel: 0,
180            start: 0,
181            command_size: 0,
182            command_pa: 0,
183            response_size: 0,
184            response_pa: 0,
185        }
186    }
187}
188
189#[derive(Inspect)]
190#[inspect(skip)]
191struct TpmRuntime {
192    ppi_store: Box<dyn NonVolatileStore>,
193    nvram_store: Box<dyn NonVolatileStore>,
194    mem: GuestMemory,
195}
196
197#[derive(Copy, Clone, Inspect)]
198pub struct TpmKeys {
199    /// Attestation key in RSA public
200    ak_pub: TpmRsa2kPublic,
201    /// Endorsement key in RSA public
202    ek_pub: TpmRsa2kPublic,
203}
204
205#[derive(Copy, Clone, Inspect, Debug, PartialEq)]
206pub struct TpmRsa2kPublic {
207    modulus: [u8; RSA_2K_MODULUS_SIZE],
208    exponent: [u8; RSA_2K_EXPONENT_SIZE],
209}
210
211type AkCertRequestFuture = Box<
212    dyn Send + Future<Output = Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync + 'static>>>,
213>;
214
215/// Implementation of [`ms_tpm_20_ref::PlatformCallbacks::monotonic_timer`]
216pub type MonotonicTimer = Box<dyn Send + FnMut() -> std::time::Duration>;
217
218#[derive(InspectMut)]
219pub struct Tpm {
220    // Static config
221    register_layout: TpmRegisterLayout,
222    refresh_tpm_seeds: bool,
223    #[inspect(skip)]
224    io_region: Option<(&'static str, RangeInclusive<u16>)>, // Valid only on HypervX64
225    #[inspect(skip)]
226    mmio_region: Vec<(&'static str, RangeInclusive<u64>)>,
227
228    // Runtime glue
229    rt: TpmRuntime,
230    #[inspect(skip)]
231    ak_cert_type: TpmAkCertType,
232    #[inspect(skip)]
233    logger: Option<Arc<dyn TpmLogger>>,
234
235    // Sub-emulators
236    #[inspect(skip)]
237    tpm_engine_helper: TpmEngineHelper,
238
239    // Runtime book-keeping
240    command_buffer: [u8; TPM_PAGE_SIZE],
241    #[inspect(rename = "has_pending_nvram", with = "|x| !x.lock().is_empty()")]
242    pending_nvram: Arc<Mutex<Vec<u8>>>,
243    #[inspect(skip)]
244    async_ak_cert_request: Option<Pin<AkCertRequestFuture>>,
245    #[inspect(skip)]
246    waker: Option<Waker>,
247    #[inspect(debug)]
248    ak_cert_renew_time: Option<std::time::SystemTime>,
249    #[inspect(debug)]
250    attestation_report_renew_time: Option<std::time::SystemTime>,
251
252    // Volatile state
253    control_area: ControlArea,
254    current_io_command: Option<TpmIoCommand>,
255    requested_locality: bool,
256    ppi_state: PpiState,
257    // Password authorization for writing to `TPM_NV_INDEX_AIK_CERT`
258    // and `TPM_NV_INDEX_ATTESTATION_REPORT` nv indexes
259    auth_value: Option<u64>,
260    keys: Option<TpmKeys>,
261}
262
263#[derive(Error, Debug)]
264#[error(transparent)]
265pub struct TpmError(#[from] TpmErrorKind);
266
267#[derive(Error, Debug)]
268pub enum TpmErrorKind {
269    #[error("failed to read Ppi state")]
270    ReadPpiState(#[source] NonVolatileStoreError),
271    #[error("failed to persist Ppi state")]
272    PersistPpiState(#[source] NonVolatileStoreError),
273    #[error("failed to read Nvram state")]
274    ReadNvramState(#[source] NonVolatileStoreError),
275    #[error("failed to persist Nvram state")]
276    PersistNvramState(#[source] NonVolatileStoreError),
277    #[error("failed to deserialized Ppi state")]
278    InvalidPpiState,
279    #[error("failed to instantiate TPM")]
280    InstantiateTpm(#[source] ms_tpm_20_ref::Error),
281    #[error("failed to reset TPM without Nvram state")]
282    ResetTpmWithoutState(#[source] ms_tpm_20_ref::Error),
283    #[error("failed to reset TPM with Nvram state")]
284    ResetTpmWithState(#[source] ms_tpm_20_ref::Error),
285    #[error("failed to initialize TPM engine")]
286    InitializeTpmEngine(#[source] TpmHelperError),
287    #[error("failed to clear TPM platform context")]
288    ClearTpmPlatformContext(#[source] TpmHelperError),
289    #[error("failed to refresh TPM seeds")]
290    RefreshTpmSeeds(#[source] TpmHelperError),
291    #[error("failed to create ak public")]
292    CreateAkPublic(#[source] TpmHelperError),
293    #[error("failed to create ek public")]
294    CreateEkPublic(#[source] TpmHelperError),
295    #[error("failed to allocate guest attestation nv indices")]
296    AllocateGuestAttestationNvIndices(#[source] TpmHelperError),
297    #[error("failed to read from nv index")]
298    ReadFromNvIndex(#[source] TpmHelperError),
299    #[error("failed to write to nv index")]
300    WriteToNvIndex(#[source] TpmHelperError),
301    #[error("failed to get an attestation report")]
302    GetAttestationReport(#[source] Box<dyn std::error::Error + Send + Sync>),
303    #[error("failed to clear platform hierarchy")]
304    ClearPlatformHierarchy(#[source] TpmHelperError),
305    #[error("failed to set pcr banks")]
306    SetPcrBanks(#[source] TpmHelperError),
307}
308
309struct TpmPlatformCallbacks {
310    pending_nvram: Arc<Mutex<Vec<u8>>>,
311    monotonic_timer: MonotonicTimer,
312}
313
314impl ms_tpm_20_ref::PlatformCallbacks for TpmPlatformCallbacks {
315    fn commit_nv_state(&mut self, state: &[u8]) -> ms_tpm_20_ref::DynResult<()> {
316        *self.pending_nvram.lock() = state.to_vec();
317        Ok(())
318    }
319
320    fn get_crypt_random(&mut self, buf: &mut [u8]) -> ms_tpm_20_ref::DynResult<usize> {
321        getrandom::fill(buf).expect("rng failure");
322        Ok(buf.len())
323    }
324
325    fn monotonic_timer(&mut self) -> std::time::Duration {
326        (self.monotonic_timer)()
327    }
328
329    fn get_unique_value(&self) -> &'static [u8] {
330        b"hvlite vtpm"
331    }
332}
333
334impl Tpm {
335    pub async fn new(
336        register_layout: TpmRegisterLayout,
337        mem: GuestMemory,
338        ppi_store: Box<dyn NonVolatileStore>,
339        nvram_store: Box<dyn NonVolatileStore>,
340        monotonic_timer: MonotonicTimer,
341        refresh_tpm_seeds: bool,
342        is_restoring: bool,
343        ak_cert_type: TpmAkCertType,
344        guest_secret_key: Option<Vec<u8>>,
345        logger: Option<Arc<dyn TpmLogger>>,
346    ) -> Result<Self, TpmError> {
347        tracing::info!("initializing TPM");
348
349        let pending_nvram = Arc::new(Mutex::new(Vec::new()));
350
351        let tpm_engine_helper = TpmEngineHelper {
352            tpm_engine: {
353                MsTpm20RefPlatform::initialize(
354                    Box::new(TpmPlatformCallbacks {
355                        pending_nvram: pending_nvram.clone(),
356                        monotonic_timer,
357                    }),
358                    ms_tpm_20_ref::InitKind::ColdInit,
359                )
360                .map_err(TpmErrorKind::InstantiateTpm)?
361            },
362            reply_buffer: [0u8; TPM_PAGE_SIZE],
363        };
364
365        let io_region = if register_layout == TpmRegisterLayout::IoPort {
366            Some((
367                "io",
368                TPM_DEVICE_IO_PORT_RANGE_BEGIN..=TPM_DEVICE_IO_PORT_RANGE_END,
369            ))
370        } else {
371            None
372        };
373
374        let mmio_region = {
375            let mut regions = vec![(
376                "control_area",
377                TPM_DEVICE_MMIO_REGION_BASE_ADDRESS
378                    ..=TPM_DEVICE_MMIO_REGION_BASE_ADDRESS + TPM_DEVICE_MMIO_REGION_SIZE - 1,
379            )];
380
381            if register_layout == TpmRegisterLayout::Mmio {
382                regions.push((
383                    "port",
384                    TPM_DEVICE_MMIO_PORT_REGION_BASE_ADDRESS
385                        ..=TPM_DEVICE_MMIO_PORT_REGION_BASE_ADDRESS
386                            + TPM_DEVICE_MMIO_PORT_REGION_SIZE
387                            - 1,
388                ));
389            }
390
391            regions
392        };
393
394        let mut tpm = Tpm {
395            register_layout,
396            refresh_tpm_seeds,
397            io_region,
398            mmio_region,
399
400            rt: TpmRuntime {
401                mem,
402                ppi_store,
403                nvram_store,
404            },
405            ak_cert_type,
406            logger,
407
408            tpm_engine_helper,
409
410            command_buffer: [0; TPM_PAGE_SIZE],
411            pending_nvram,
412            async_ak_cert_request: None,
413            waker: None,
414            ak_cert_renew_time: None,
415            attestation_report_renew_time: None,
416
417            control_area: ControlArea::new(),
418            current_io_command: None,
419            requested_locality: false,
420            ppi_state: PpiState::new(),
421            auth_value: None,
422            keys: None,
423        };
424
425        if !is_restoring {
426            tpm.on_first_boot(guest_secret_key).await?;
427        }
428
429        tracing::info!("TPM initialized");
430        Ok(tpm)
431    }
432
433    async fn flush_pending_nvram(&mut self) -> Result<(), NonVolatileStoreError> {
434        let data = {
435            let mut pending_nvram = self.pending_nvram.lock();
436            if pending_nvram.is_empty() {
437                return Ok(());
438            }
439            std::mem::take(&mut *pending_nvram)
440        };
441
442        (self.rt.nvram_store).persist(data).await?;
443
444        Ok(())
445    }
446
447    async fn on_first_boot(&mut self, guest_secret_key: Option<Vec<u8>>) -> Result<(), TpmError> {
448        use ms_tpm_20_ref::NvError;
449        let fixup_16k_ak_cert;
450
451        // Check whether or not we need to pave-over the blank TPM with our
452        // existing nvmem state.
453        {
454            let existing_nvmem_blob = (self.rt.nvram_store)
455                .restore()
456                .await
457                .map_err(TpmErrorKind::ReadNvramState)?;
458
459            if let Some(mut blob) = existing_nvmem_blob {
460                // Previous versions before this code had a bug where sizes
461                // smaller than 32K would be reported as 32K. Fixup the blob so
462                // that the TPM nvram is consistent - this code can be removed
463                // once the fix for reporting the NVRAM size correctly is
464                // everywhere.
465                recover::recover_blob(&mut blob);
466                if let Err(e) = self.tpm_engine_helper.tpm_engine.reset(Some(&blob)) {
467                    if let ms_tpm_20_ref::Error::NvMem(NvError::MismatchedBlobSize) = e {
468                        self.logger
469                            .log_event_and_flush(TpmLogEvent::InvalidState)
470                            .await;
471                    }
472
473                    return Err(TpmErrorKind::ResetTpmWithState(e).into());
474                }
475
476                // If this is a small vTPM blob, potentially fixup the AK cert.
477                fixup_16k_ak_cert = blob.len() == LEGACY_VTPM_SIZE;
478            } else {
479                // No fixup is required, because there is no existing NVRAM blob.
480                fixup_16k_ak_cert = false;
481            }
482        }
483
484        self.tpm_engine_helper
485            .initialize_tpm_engine()
486            .map_err(TpmErrorKind::InitializeTpmEngine)?;
487
488        // If necessary, recreate EPS & PPS.
489        // The host indicates this when VM identity changes.
490        if self.refresh_tpm_seeds {
491            if let Err(e) = self.tpm_engine_helper.refresh_tpm_seeds() {
492                self.logger
493                    .log_event_and_flush(TpmLogEvent::IdentityChangeFailed)
494                    .await;
495
496                return Err(TpmErrorKind::RefreshTpmSeeds(e).into());
497            }
498
499            tracing::info!("TPM seeds have been refreshed");
500        }
501
502        // Execute any pending PPI requests set prior to reboot
503        {
504            let raw_ppi_state = (self.rt.ppi_store)
505                .restore()
506                .await
507                .map_err(TpmErrorKind::ReadPpiState)?;
508
509            if let Some(buf) = raw_ppi_state {
510                let ppi_state = match persist_restore::deserialize_ppi_state(buf) {
511                    Some(state) => state,
512                    None => {
513                        self.logger
514                            .log_event_and_flush(TpmLogEvent::InvalidState)
515                            .await;
516
517                        return Err(TpmErrorKind::InvalidPpiState.into());
518                    }
519                };
520
521                self.ppi_state = ppi_state;
522                if self.ppi_state.pending_ppi_operation != PpiOperation::NO_OP {
523                    self.execute_pending_ppi()?;
524
525                    (self.rt.ppi_store)
526                        .persist(persist_restore::serialize_ppi_state(self.ppi_state))
527                        .await
528                        .map_err(TpmErrorKind::PersistPpiState)?;
529                }
530            }
531        }
532
533        if matches!(
534            self.ak_cert_type,
535            TpmAkCertType::Trusted(_) | TpmAkCertType::HwAttested(_)
536        ) {
537            // Create auth value for NV index password authorization.
538            // The value needs to be preserved across live servicing.
539            let mut auth_value = 0;
540            getrandom::fill(auth_value.as_mut_bytes()).expect("rng failure");
541            self.auth_value = Some(auth_value);
542
543            // Initialize `TpmKeys`.
544            // The procedure also generates randomized AK based on the TPM seed
545            // and writes the AK into `TPM_AZURE_AIK_HANDLE` NV store.
546            let ak_pub = self
547                .tpm_engine_helper
548                .create_ak_pub(self.refresh_tpm_seeds)
549                .map_err(TpmErrorKind::CreateAkPublic)?;
550            let ek_pub = self
551                .tpm_engine_helper
552                .create_ek_pub()
553                .map_err(TpmErrorKind::CreateEkPublic)?;
554            self.keys = Some(TpmKeys { ak_pub, ek_pub });
555
556            // Conditionally define nv indexes for ak cert and attestation report.
557            // The Nvram size can only be defined with platform hierarchy. Otherwise
558            // `TPM_RC_HIERARCHY` (0c0290285) error code would return.
559            // It means the Nvram index space needs to be allocated before clearing the
560            // tpm hierarchy control. NV index value can be rewritten later.
561            self.tpm_engine_helper
562                .allocate_guest_attestation_nv_indices(
563                    auth_value,
564                    !self.refresh_tpm_seeds, // Preserve AK cert if TPM seeds are not refreshed
565                    matches!(self.ak_cert_type, TpmAkCertType::HwAttested(_)),
566                    fixup_16k_ak_cert,
567                )
568                .map_err(TpmErrorKind::AllocateGuestAttestationNvIndices)?;
569
570            // Initialize `TPM_NV_INDEX_AIK_CERT` and `TPM_NV_INDEX_ATTESTATION_REPORT`
571            //
572            // Only renew AK cert if hardware isolated.
573            if matches!(self.ak_cert_type, TpmAkCertType::HwAttested(_)) {
574                self.renew_ak_cert()?;
575            }
576        }
577
578        // If guest secret key is passed in, import the key into TPM.
579        if let Some(guest_secret_key) = guest_secret_key {
580            tracing::info!("Initializing guest secret key");
581
582            if let Err(e) = self
583                .tpm_engine_helper
584                .initialize_guest_secret_key(&guest_secret_key)
585            {
586                // Failures are non-fatal as the feature is not necessary for booting.
587                tracing::error!(CVM_ALLOWED, "Failed to initialize guest secret key");
588                tracing::error!(
589                    CVM_CONFIDENTIAL,
590                    error = &e as &dyn std::error::Error,
591                    "Failed to initialize guest secret key"
592                );
593            }
594        }
595
596        // clear tpm hierarchy control
597        self.tpm_engine_helper
598            .hierarchy_control(TPM20_RH_PLATFORM, TPM20_RH_PLATFORM, false)
599            .map_err(|error| TpmHelperError::TpmCommandError {
600                command_debug_info: CommandDebugInfo {
601                    command_code: CommandCodeEnum::HierarchyControl,
602                    auth_handle: Some(TPM20_RH_PLATFORM),
603                    nv_index: None,
604                },
605                error,
606            })
607            .map_err(TpmErrorKind::ClearPlatformHierarchy)?;
608
609        self.flush_pending_nvram()
610            .await
611            .map_err(TpmErrorKind::PersistNvramState)?;
612
613        Ok(())
614    }
615
616    fn hyperv_port_read(&mut self, data: &mut [u8]) -> IoResult {
617        let val = {
618            let io_command = match self.current_io_command {
619                Some(cmd) => cmd,
620                None => {
621                    tracelimit::warn_ratelimited!(
622                        CVM_ALLOWED,
623                        "Invalid tpm IO data port read (no command set)"
624                    );
625                    return IoResult::Ok;
626                }
627            };
628
629            match io_command {
630                TpmIoCommand::ESTABLISHED => self.control_area.command_pa as u32,
631                TpmIoCommand::PPI_GET_PENDING_OPERATION => self.ppi_state.pending_ppi_operation.0,
632                TpmIoCommand::PPI_GET_LAST_OPERATION => self.ppi_state.last_ppi_operation.0,
633                TpmIoCommand::PPI_GET_LAST_RESULT => self.ppi_state.last_ppi_state,
634                TpmIoCommand::PPI_SET_OPERATION => self.ppi_state.set_ppi_operation_state,
635                TpmIoCommand::PPI_GET_USER_CONFIRMATION => 4,
636                TpmIoCommand::GET_TCG_PROTOCOL_VERSION => {
637                    io_port_interface::TcgProtocol::Tcg2 as u32
638                }
639                _ => {
640                    tracelimit::warn_ratelimited!(
641                        CVM_ALLOWED,
642                        ?io_command,
643                        "Invalid tpm IO data read"
644                    );
645                    return IoResult::Ok;
646                }
647            }
648        };
649
650        tracing::trace!(
651            ?val,
652            ?self.current_io_command,
653            "TPM IO read",
654        );
655
656        let data = if let Some(data) = data.get_mut(..4) {
657            data
658        } else {
659            return IoResult::Err(IoError::InvalidAccessSize);
660        };
661        data.copy_from_slice(&val.to_le_bytes()[..4]);
662        IoResult::Ok
663    }
664
665    fn hyperv_port_write(&mut self, control_port: bool, data: &[u8]) -> IoResult {
666        let val = if let Ok(data) = data.try_into() {
667            u32::from_le_bytes(data)
668        } else {
669            return IoResult::Err(IoError::InvalidAccessSize);
670        };
671
672        if control_port {
673            self.current_io_command = Some(TpmIoCommand(val));
674        } else {
675            let current_io_command = match self.current_io_command {
676                Some(cmd) => cmd,
677                None => {
678                    tracelimit::warn_ratelimited!(
679                        CVM_ALLOWED,
680                        "Invalid tpm IO data port write (no command set)"
681                    );
682                    return IoResult::Ok;
683                }
684            };
685
686            let mut update_ppi = true;
687            match current_io_command {
688                TpmIoCommand::MAP_SHARED_MEMORY => {
689                    self.control_area.command_size = TPM_PAGE_SIZE as u32;
690                    self.control_area.command_pa = val as u64;
691                    self.control_area.response_size = TPM_PAGE_SIZE as u32;
692                    self.control_area.response_pa = val as u64 + (TPM_PAGE_SIZE as u64);
693                    update_ppi = false;
694                }
695                TpmIoCommand::PPI_SET_OPERATION_ARG3_INTEGER2 => {
696                    self.ppi_state.ppi_set_operation_arg3_integer2 = val;
697                }
698                TpmIoCommand::PPI_SET_OPERATION => {
699                    self.ppi_state.pending_ppi_operation = PpiOperation(val);
700                    self.ppi_state.set_ppi_operation_state = 0;
701                }
702                TpmIoCommand::PPI_GET_USER_CONFIRMATION => {
703                    self.ppi_state.in_query_ppi_operation = PpiOperation(val);
704                }
705                TpmIoCommand::CAPABILITY_HASH_ALG_BITMAP => {
706                    self.ppi_state.tpm_capability_hash_alg_bitmap = val;
707                }
708                other => {
709                    tracelimit::warn_ratelimited!(
710                        CVM_ALLOWED,
711                        ?other,
712                        "unimplemented TpmIoCommand"
713                    );
714                    update_ppi = false;
715                }
716            };
717
718            if update_ppi {
719                let res = pal_async::local::block_on(
720                    (self.rt.ppi_store)
721                        .persist(persist_restore::serialize_ppi_state(self.ppi_state)),
722                );
723                if let Err(e) = res {
724                    tracing::warn!(
725                        CVM_ALLOWED,
726                        "could not persist ppi state to non-volatile store"
727                    );
728                    tracing::warn!(
729                        CVM_CONFIDENTIAL,
730                        error = &e as &dyn std::error::Error,
731                        "could not persist ppi state to non-volatile store"
732                    );
733                }
734            }
735        };
736
737        tracing::trace!(
738            control_port,
739            ?val,
740            ?self.current_io_command,
741            "TPM IO write",
742        );
743        IoResult::Ok
744    }
745
746    fn execute_pending_ppi(&mut self) -> Result<(), TpmError> {
747        self.ppi_state.last_ppi_state = match self.ppi_state.pending_ppi_operation {
748            PpiOperation::CLEAR
749            | PpiOperation::CLEAR_ENABLE_ACTIVATE
750            | PpiOperation::ENABLE_ACTIVATE_CLEAR
751            | PpiOperation::ENABLE_ACTIVATE_CLEAR_ENABLE_ACTIVATE => self
752                .tpm_engine_helper
753                .clear_tpm_platform_context()
754                .map_err(TpmErrorKind::ClearTpmPlatformContext)?,
755            PpiOperation::SET_PCR_BANKS => self.set_tpm_pcr_banks(
756                self.ppi_state.tpm_capability_hash_alg_bitmap,
757                self.ppi_state.ppi_set_operation_arg3_integer2,
758            )?,
759            other => {
760                tracelimit::warn_ratelimited!(CVM_ALLOWED, ?other, "unknown pending PPI operation");
761                0
762            }
763        };
764        self.ppi_state.last_ppi_operation = self.ppi_state.pending_ppi_operation;
765        self.ppi_state.pending_ppi_operation = PpiOperation::NO_OP;
766        Ok(())
767    }
768
769    fn set_tpm_pcr_banks(
770        &mut self,
771        supported_pcr_banks: u32,
772        pcr_banks_to_allocate: u32,
773    ) -> Result<u32, TpmError> {
774        let response_code = match self.tpm_engine_helper.pcr_allocate(
775            TPM20_RH_PLATFORM,
776            supported_pcr_banks,
777            pcr_banks_to_allocate,
778        ) {
779            Err(error) => {
780                if let TpmCommandError::TpmCommandFailed { response_code } = error {
781                    tracelimit::error_ratelimited!(
782                        CVM_ALLOWED,
783                        err = &error as &dyn std::error::Error,
784                        "tpm PcrAllocateCmd failed"
785                    );
786
787                    // Return the error code to be written to `last_ppi_state`
788                    response_code
789                } else {
790                    // Unexpected failure
791                    return Err(TpmErrorKind::SetPcrBanks(TpmHelperError::TpmCommandError {
792                        command_debug_info: CommandDebugInfo {
793                            command_code: CommandCodeEnum::PCR_Allocate,
794                            auth_handle: Some(TPM20_RH_PLATFORM),
795                            nv_index: None,
796                        },
797                        error,
798                    })
799                    .into());
800                }
801            }
802            Ok(response_code) => response_code,
803        };
804
805        // The 1st reboot was triggered by the guest after setActivePcrBank.
806        // It is necessary to put TPM into platform authorization state.
807        // During the first reboot TPM20_CC_PCR_Allocate was executed.
808        //
809        // Below is the 2nd reboot of TPM device so that the new active PCRs take into effect.
810        if response_code == tpm20proto::ResponseCode::Success as u32 {
811            self.tpm_engine_helper
812                .tpm_engine
813                .reset(None)
814                .map_err(TpmErrorKind::ResetTpmWithoutState)?;
815            self.tpm_engine_helper
816                .initialize_tpm_engine()
817                .map_err(TpmErrorKind::InitializeTpmEngine)?;
818            tracelimit::info_ratelimited!(CVM_ALLOWED, "tpm reset after sending PcrAllocateCmd");
819        }
820
821        Ok(response_code)
822    }
823
824    /// Create a new request needed by AK cert request callout.
825    ///
826    /// This function can only be called when `ak_cert_type` is `Trusted` or `HwAttested`.
827    fn create_ak_cert_request(&mut self) -> Result<Vec<u8>, TpmError> {
828        let mut guest_attestation_input = [0u8; ATTESTATION_REPORT_DATA_SIZE];
829        // No need to check the result as long as it's Ok(..) because the output data will
830        // remain unchanged (all 0's) if the NV index is unallocated or uninitialized.
831        self.tpm_engine_helper
832            .read_from_nv_index(
833                TPM_NV_INDEX_GUEST_ATTESTATION_INPUT,
834                &mut guest_attestation_input,
835            )
836            .map_err(TpmErrorKind::ReadFromNvIndex)?;
837
838        let keys = self.keys.as_ref().expect("Tpm keys uninitialized");
839        let request_ak_cert_helper = self
840            .ak_cert_type
841            .get_ak_cert_helper()
842            .expect("`ak_cert_type` should not be `None`");
843        let ak_cert_request = request_ak_cert_helper
844            .create_ak_cert_request(
845                &keys.ak_pub.modulus,
846                &keys.ak_pub.exponent,
847                &keys.ek_pub.modulus,
848                &keys.ek_pub.exponent,
849                &guest_attestation_input,
850            )
851            .map_err(TpmErrorKind::GetAttestationReport)?;
852
853        Ok(ak_cert_request)
854    }
855
856    /// Renew the nv index `TPM_NV_INDEX_ATTESTATION_REPORT` with the input data.
857    ///
858    /// This function is expected to only be called when `ak_cert_type` is `HwAttested`.
859    fn renew_attestation_report(&mut self, data: &[u8]) -> Result<(), TpmError> {
860        let auth_value = self.auth_value.expect("auth value is uninitialized");
861        self.attestation_report_renew_time = Some(std::time::SystemTime::now());
862        self.tpm_engine_helper
863            .write_to_nv_index(auth_value, TPM_NV_INDEX_ATTESTATION_REPORT, data)
864            .map_err(TpmErrorKind::WriteToNvIndex)?;
865
866        Ok(())
867    }
868
869    /// This routine calls (via GET) external server to issue AK cert.
870    /// This function can only be called when `ak_cert_type` is `Trusted` or `HwAttested`.
871    fn renew_ak_cert(&mut self) -> Result<(), TpmError> {
872        // Return if the request is pending
873        if self.async_ak_cert_request.is_some() {
874            return Ok(());
875        }
876
877        tracing::trace!("Request AK cert renewal");
878
879        let ak_cert_request = self.create_ak_cert_request()?;
880        // Store the ak cert request that includes the attestation report if `ak_cert_type` is `HwAttested`.
881        if matches!(self.ak_cert_type, TpmAkCertType::HwAttested(_)) {
882            self.renew_attestation_report(&ak_cert_request)?;
883        }
884
885        let request_ak_cert_helper = self
886            .ak_cert_type
887            .get_ak_cert_helper()
888            .expect("`ak_cert_type` should not be `None`");
889        let fut = {
890            let request_ak_cert_helper = request_ak_cert_helper.clone();
891            async move {
892                request_ak_cert_helper
893                    .request_ak_cert(ak_cert_request)
894                    .await
895            }
896        };
897
898        self.async_ak_cert_request = Some(Box::pin(fut));
899
900        // Ensure poll gets called again.
901        if let Some(waker) = self.waker.take() {
902            waker.wake();
903        }
904
905        Ok(())
906    }
907
908    /// Poll the AK cert request made by `renew_ak_cert`. This function is called by [`PollDevice::poll_device`].
909    fn poll_ak_cert_request(&mut self, cx: &mut std::task::Context<'_>) {
910        if let Some(async_ak_cert_request) = self.async_ak_cert_request.as_mut() {
911            if let Poll::Ready(result) = async_ak_cert_request.as_mut().poll(cx) {
912                // Once the received the response, update the renew time using `SystemTime::now`.
913                // DEVNOTE: The system time may not reflect the real time when suspension and resumption occur.
914                // See more details in `refresh_device_attestation_data_on_nv_read`.
915                let now = std::time::SystemTime::now();
916
917                // Clear `async_ak_cert_request` to allow the next renewal request.
918                self.async_ak_cert_request = None;
919
920                // Parse the response. Empty response indicates that the host agent is unavailable.
921                let response = match result {
922                    Ok(data) if !data.is_empty() => {
923                        // Set the renew time if successfully receiving the data.
924                        // The next renew request will be made after `AK_CERT_RENEW_PERIOD` passes and
925                        // `refresh_device_attestation_data_on_nv_read` is triggered.
926                        self.ak_cert_renew_time = Some(now);
927
928                        data
929                    }
930                    Ok(_data) => {
931                        tracelimit::warn_ratelimited!(
932                            CVM_ALLOWED,
933                            "The requested TPM AK cert is empty - now: {:?}",
934                            now.duration_since(std::time::UNIX_EPOCH),
935                        );
936
937                        // Set the renew time if the ak cert is empty, avoiding retrying on each nv read
938                        // in the case of host agent being unavailable.
939                        // The next renew request will be made after `AK_CERT_RENEW_PERIOD` passes and
940                        // `refresh_device_attestation_data_on_nv_read` is triggered.
941                        self.ak_cert_renew_time = Some(now);
942
943                        return;
944                    }
945                    Err(error) => {
946                        tracelimit::warn_ratelimited!(
947                            CVM_ALLOWED,
948                            error,
949                            "Failed to request new TPM AK cert - now: {:?}",
950                            now.duration_since(std::time::UNIX_EPOCH),
951                        );
952
953                        // Use the non-async version of function to log the event (without flushing).
954                        self.logger.log_event(TpmLogEvent::AkCertRenewalFailed);
955
956                        return;
957                    }
958                };
959
960                let auth_value = self.auth_value.expect("auth value is uninitialized");
961                if let Err(e) = self.tpm_engine_helper.write_to_nv_index(
962                    auth_value,
963                    TPM_NV_INDEX_AIK_CERT,
964                    &response,
965                ) {
966                    tracelimit::error_ratelimited!(
967                        CVM_ALLOWED,
968                        error = &e as &dyn std::error::Error,
969                        "Failed write new TPM AK cert to NV index"
970                    );
971                    return;
972                }
973
974                tracing::info!(
975                    "ak cert renewal is complete - now: {:?}, size: {}",
976                    now.duration_since(std::time::UNIX_EPOCH),
977                    response.len()
978                );
979            }
980        }
981        self.waker = Some(cx.waker().clone());
982    }
983
984    /// Renew device attestation data (i.e., attestation report and AK cert) on NV_Read if needed
985    fn refresh_device_attestation_data_on_nv_read(&mut self) {
986        let Some(nv_read) = tpm20proto::protocol::NvReadCmd::deserialize(&self.command_buffer)
987        else {
988            return;
989        };
990
991        // Only refresh AK cert and attestation report if this is the start of an
992        // NV_Read operation. Otherwise, there could be data coherency issues between
993        // OS read and Underhill refresh.
994        if u16::from(nv_read.offset) != 0 {
995            return;
996        }
997
998        // DEVNOTE: Underhill (VTL2) currently does not have mechanisms to update the
999        // system time when resuming from suspension. This means when suspension and
1000        // resumption occur, the 24hr system time may be longer than the 24h of real time.
1001        // Will revisit the implementation and make it resilient in the future.
1002        let now = std::time::SystemTime::now();
1003        let ak_cert_renew_elapsed = if let Some(renew_time) = self.ak_cert_renew_time {
1004            now.duration_since(renew_time)
1005                .expect("system clock went backwards")
1006        } else {
1007            std::time::Duration::new(0, 0)
1008        };
1009
1010        let attestation_report_renew_elapsed =
1011            if let Some(renew_time) = self.attestation_report_renew_time {
1012                now.duration_since(renew_time)
1013                    .expect("system clock went backwards")
1014            } else {
1015                std::time::Duration::new(0, 0)
1016            };
1017
1018        // On start of read of attestation report index, refresh report when
1019        // attestation report is supported.
1020        if u32::from(nv_read.nv_index) == TPM_NV_INDEX_ATTESTATION_REPORT
1021            && matches!(self.ak_cert_type, TpmAkCertType::HwAttested(_))
1022        {
1023            if attestation_report_renew_elapsed > REPORT_TIMER_PERIOD
1024                || self.attestation_report_renew_time.is_none()
1025            {
1026                // Renew tha attestation report as part of the request creation call.
1027                match self.create_ak_cert_request() {
1028                    Ok(ak_cert_request) => {
1029                        if let Err(e) = self.renew_attestation_report(&ak_cert_request) {
1030                            tracelimit::error_ratelimited!(
1031                                CVM_ALLOWED,
1032                                error = &e as &dyn std::error::Error,
1033                                "Error while renewing the attestation report on NvRead"
1034                            );
1035                        }
1036                    }
1037                    Err(e) => {
1038                        tracelimit::error_ratelimited!(
1039                            CVM_ALLOWED,
1040                            error = &e as &dyn std::error::Error,
1041                            "Error while creating ak cert request for renewing the attestation report"
1042                        );
1043                    }
1044                }
1045            } else {
1046                tracing::warn!("Hardware attestation report generation was rate limited");
1047            }
1048        } else {
1049            // Renew AkCert if exceeds 24 hours since renewal, or not populated,
1050            // and past hardware renewal period.
1051            let renew_cert_needed = (self.ak_cert_renew_time.is_none()
1052                || ak_cert_renew_elapsed > AK_CERT_RENEW_PERIOD)
1053                && (attestation_report_renew_elapsed > REPORT_TIMER_PERIOD
1054                    || self.attestation_report_renew_time.is_none());
1055
1056            tracing::debug!(renew_cert_needed, ak_cert_renew_time =? self.ak_cert_renew_time, "tpm: cert renew check");
1057
1058            if renew_cert_needed {
1059                if let Err(e) = self.renew_ak_cert() {
1060                    tracelimit::error_ratelimited!(
1061                        CVM_ALLOWED,
1062                        error = &e as &dyn std::error::Error,
1063                        "Error while renewing AK cert on NvRead"
1064                    );
1065                }
1066            }
1067        }
1068    }
1069}
1070
1071impl ChangeDeviceState for Tpm {
1072    fn start(&mut self) {}
1073
1074    async fn stop(&mut self) {}
1075
1076    async fn reset(&mut self) {
1077        self.control_area = ControlArea::new();
1078        self.current_io_command = None;
1079        self.requested_locality = false;
1080
1081        self.tpm_engine_helper
1082            .tpm_engine
1083            .reset(None)
1084            .expect("failed to reset TPM");
1085        self.tpm_engine_helper
1086            .initialize_tpm_engine()
1087            .expect("failed to send TPM startup commands");
1088        pal_async::local::block_on(self.flush_pending_nvram())
1089            .expect("failed to flush nvram on reset");
1090    }
1091}
1092
1093impl ChipsetDevice for Tpm {
1094    fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
1095        self.io_region.is_some().then_some(self)
1096    }
1097
1098    fn supports_mmio(&mut self) -> Option<&mut dyn MmioIntercept> {
1099        Some(self)
1100    }
1101
1102    fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
1103        Some(self)
1104    }
1105}
1106
1107impl PollDevice for Tpm {
1108    fn poll_device(&mut self, cx: &mut std::task::Context<'_>) {
1109        self.poll_ak_cert_request(cx)
1110    }
1111}
1112
1113impl PortIoIntercept for Tpm {
1114    fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
1115        let port_offset = io_port - TPM_DEVICE_IO_PORT_RANGE_BEGIN;
1116        if port_offset != TPM_DEVICE_IO_PORT_DATA_OFFSET {
1117            return IoResult::Err(IoError::InvalidRegister);
1118        }
1119
1120        self.hyperv_port_read(data)
1121    }
1122
1123    fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
1124        let port_offset = io_port - TPM_DEVICE_IO_PORT_RANGE_BEGIN;
1125        if port_offset != TPM_DEVICE_IO_PORT_CONTROL_OFFSET
1126            && port_offset != TPM_DEVICE_IO_PORT_DATA_OFFSET
1127        {
1128            return IoResult::Err(IoError::InvalidRegister);
1129        }
1130
1131        self.hyperv_port_write(port_offset == TPM_DEVICE_IO_PORT_CONTROL_OFFSET, data)
1132    }
1133
1134    fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
1135        if let Some(region) = &self.io_region {
1136            std::slice::from_ref(region)
1137        } else {
1138            &[]
1139        }
1140    }
1141}
1142
1143impl MmioIntercept for Tpm {
1144    fn mmio_read(&mut self, address: u64, data: &mut [u8]) -> IoResult {
1145        if self.register_layout == TpmRegisterLayout::Mmio
1146            && address == TPM_DEVICE_MMIO_PORT_DATA
1147            && data.len() == 4
1148        {
1149            return self.hyperv_port_read(data);
1150        }
1151
1152        let offset = (address - TPM_DEVICE_MMIO_REGION_BASE_ADDRESS) as usize;
1153        match data.len() {
1154            1 | 2 | 4 => {}
1155            8 => {
1156                if !matches!(
1157                    offset,
1158                    ControlArea::OFFSET_OF_CRB_INTF_ID
1159                        | ControlArea::OFFSET_OF_COMMAND_PHYSICAL_ADDRESS_LO
1160                        | ControlArea::OFFSET_OF_RESPONSE_PHYSICAL_ADDRESS_LO
1161                ) {
1162                    return IoResult::Err(IoError::InvalidAccessSize);
1163                }
1164            }
1165            _ => {
1166                return IoResult::Err(IoError::InvalidAccessSize);
1167            }
1168        }
1169
1170        // Some Linux guests such as when running under TDX choose to read
1171        // certain fields byte by byte. Floor the offset to the nearest multiple
1172        // of 4.
1173        let floor_offset = offset & !0x3;
1174        let byte_offset = offset - floor_offset;
1175
1176        tracing::trace!(address, offset, floor_offset, byte_offset, "tpm mmio read");
1177
1178        let val: u64 = match floor_offset {
1179            ControlArea::OFFSET_OF_LOC_STATE => {
1180                if self.requested_locality {
1181                    0x83
1182                } else {
1183                    0x81
1184                }
1185            }
1186            ControlArea::OFFSET_OF_LOC_CTRL => 0x0, // write only register, reads return 0
1187            ControlArea::OFFSET_OF_LOC_STS => 0x1,  // locality 0 has been granted access
1188            ControlArea::OFFSET_OF_CRB_INTF_ID => 0x4011, // CRB version 0, locality 0 only, CRB capable only
1189            ControlArea::OFFSET_OF_REQUEST => self.control_area.request.into(),
1190            ControlArea::OFFSET_OF_STATUS => self.control_area.status.into(),
1191            ControlArea::OFFSET_OF_CANCEL => self.control_area.cancel.into(),
1192            ControlArea::OFFSET_OF_START => self.control_area.start.into(),
1193            ControlArea::OFFSET_OF_COMMAND_SIZE => self.control_area.command_size.into(),
1194            ControlArea::OFFSET_OF_COMMAND_PHYSICAL_ADDRESS_LO => self.control_area.command_pa,
1195            ControlArea::OFFSET_OF_COMMAND_PHYSICAL_ADDRESS_HI => {
1196                (self.control_area.command_pa & 0xffff_ffff_0000_0000) >> 32
1197            }
1198            ControlArea::OFFSET_OF_RESPONSE_SIZE => self.control_area.response_size.into(),
1199            ControlArea::OFFSET_OF_RESPONSE_PHYSICAL_ADDRESS_LO => self.control_area.response_pa,
1200            ControlArea::OFFSET_OF_RESPONSE_PHYSICAL_ADDRESS_HI => {
1201                (self.control_area.response_pa & 0xffff_ffff_0000_0000) >> 32
1202            }
1203            _ => {
1204                return IoResult::Err(IoError::InvalidRegister);
1205            }
1206        };
1207
1208        let value_array = val.to_le_bytes();
1209        let byte_count = data.len();
1210        data[..byte_count].copy_from_slice(&value_array[byte_offset..(byte_offset + byte_count)]);
1211
1212        IoResult::Ok
1213    }
1214
1215    fn mmio_write(&mut self, address: u64, data: &[u8]) -> IoResult {
1216        if self.register_layout == TpmRegisterLayout::Mmio
1217            && (address == TPM_DEVICE_MMIO_PORT_CONTROL || address == TPM_DEVICE_MMIO_PORT_DATA)
1218            && data.len() == 4
1219        {
1220            return self.hyperv_port_write(address == TPM_DEVICE_MMIO_PORT_CONTROL, data);
1221        }
1222
1223        if !matches!(data.len(), 1 | 2 | 4) {
1224            return IoResult::Err(IoError::InvalidAccessSize);
1225        };
1226        if address & 0x3 != 0 {
1227            return IoResult::Err(IoError::UnalignedAccess);
1228        };
1229
1230        let mut val: u32 = 0;
1231        val.as_mut_bytes()[..data.len()].copy_from_slice(data);
1232        match (address - TPM_DEVICE_MMIO_REGION_BASE_ADDRESS) as usize {
1233            ControlArea::OFFSET_OF_LOC_STATE => {}
1234            ControlArea::OFFSET_OF_LOC_CTRL => self.requested_locality = val & 0x2 != 0x2,
1235            ControlArea::OFFSET_OF_LOC_STS => {}
1236            ControlArea::OFFSET_OF_CRB_INTF_ID => {}
1237            ControlArea::OFFSET_OF_REQUEST => {}
1238            ControlArea::OFFSET_OF_CANCEL => {
1239                self.control_area.cancel = if val == 0 { 0 } else { 1 };
1240                self.tpm_engine_helper
1241                    .tpm_engine
1242                    .set_cancel_flag(self.control_area.cancel == 1);
1243            }
1244            ControlArea::OFFSET_OF_START => {
1245                if val == 1 {
1246                    self.control_area.start = 1;
1247
1248                    let res = self
1249                        .rt
1250                        .mem
1251                        .read_at(self.control_area.command_pa, &mut self.command_buffer);
1252
1253                    if let Err(e) = res {
1254                        tracelimit::error_ratelimited!(
1255                            CVM_ALLOWED,
1256                            error = &e as &dyn std::error::Error,
1257                            "Failed to read TPM command from guest memory"
1258                        );
1259                        return IoResult::Ok;
1260                    }
1261
1262                    let cmd_header = tpm20proto::protocol::common::CmdHeader::ref_from_prefix(
1263                        &self.command_buffer,
1264                    )
1265                    .ok() // TODO: zerocopy: err (https://github.com/microsoft/openvmm/issues/759)
1266                    .and_then(|(cmd_header, _)| cmd_header.command_code.into_enum());
1267
1268                    tracing::debug!(
1269                        cmd = ?cmd_header,
1270                        "executing guest tpm cmd",
1271                    );
1272
1273                    if matches!(self.ak_cert_type, TpmAkCertType::HwAttested(_)) {
1274                        if let Some(CommandCodeEnum::NV_Read) = cmd_header {
1275                            self.refresh_device_attestation_data_on_nv_read()
1276                        }
1277                    }
1278
1279                    if let Err(e) = self.tpm_engine_helper.tpm_engine.execute_command(
1280                        &mut self.command_buffer,
1281                        &mut self.tpm_engine_helper.reply_buffer,
1282                    ) {
1283                        tracelimit::error_ratelimited!(
1284                            CVM_ALLOWED,
1285                            error = &e as &dyn std::error::Error,
1286                            "Error while executing TPM command"
1287                        );
1288                        return IoResult::Ok;
1289                    }
1290
1291                    tracing::debug!(
1292                        response_code = ?tpm20proto::protocol::common::ReplyHeader::ref_from_prefix(
1293                        &self.tpm_engine_helper.reply_buffer,
1294                        )
1295                        .map(|(reply, _)| reply.response_code), // TODO: zerocopy: manual: review carefully! (https://github.com/microsoft/openvmm/issues/759)
1296                        "response code from guest tpm cmd",
1297                    );
1298
1299                    let res = self.rt.mem.write_at(
1300                        self.control_area.response_pa,
1301                        &self.tpm_engine_helper.reply_buffer,
1302                    );
1303
1304                    if let Err(e) = res {
1305                        tracelimit::error_ratelimited!(
1306                            CVM_ALLOWED,
1307                            error = &e as &dyn std::error::Error,
1308                            "Failed to write TPM reply into guest memory"
1309                        );
1310                        return IoResult::Ok;
1311                    }
1312
1313                    self.control_area.start = 0;
1314                }
1315            }
1316            _ => return IoResult::Err(IoError::InvalidRegister),
1317        }
1318
1319        let res = pal_async::local::block_on(self.flush_pending_nvram());
1320        if let Err(e) = res {
1321            tracing::warn!(CVM_ALLOWED, "could not commit nvram to non-volatile store");
1322            tracing::warn!(
1323                CVM_CONFIDENTIAL,
1324                error = &e as &dyn std::error::Error,
1325                "could not commit nvram to non-volatile store"
1326            );
1327        };
1328
1329        IoResult::Ok
1330    }
1331
1332    fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u64>)] {
1333        &self.mmio_region
1334    }
1335}
1336
1337/// The IO port interface bespoke to the Hyper-V implementation of the vTPM.
1338mod io_port_interface {
1339    use inspect::Inspect;
1340    use zerocopy::FromBytes;
1341
1342    use zerocopy::Immutable;
1343    use zerocopy::IntoBytes;
1344    use zerocopy::KnownLayout;
1345
1346    open_enum::open_enum! {
1347        /// I/O port command definitions
1348        #[derive(Inspect, IntoBytes, Immutable, KnownLayout, FromBytes)]
1349        #[inspect(debug)]
1350        pub enum TpmIoCommand: u32 {
1351            /// It can be used for engine vs. guest version negotiation. Not used.
1352            VERSION = 0,
1353            /// Map command-response interface buffer.
1354            MAP_SHARED_MEMORY = 1,
1355            /// Query host if map is succeeded.
1356            ESTABLISHED = 2,
1357            /// Get pending TPM operation requested by the OS.
1358            PPI_GET_PENDING_OPERATION = 3,
1359            /// Get TPM Operation Response to OS.
1360            PPI_GET_LAST_OPERATION = 5,
1361            PPI_GET_LAST_RESULT = 6,
1362            /// Set TPM operation requested by the OS.
1363            /// TpmIoPPISetOperationArg3Integer1
1364            PPI_SET_OPERATION = 7,
1365            /// Get user confirmation status for operation. Used in PPI over ACPI.
1366            PPI_GET_USER_CONFIRMATION = 8,
1367            /// The command to set PPI func ID 7 Arg3 (Package) Integer 2.
1368            PPI_SET_OPERATION_ARG3_INTEGER2 = 32,
1369            /// Get Tcg Protocol Version.
1370            GET_TCG_PROTOCOL_VERSION = 64,
1371            /// Report the supported hash bitmap in TPM capability.
1372            CAPABILITY_HASH_ALG_BITMAP = 65,
1373        }
1374    }
1375
1376    #[expect(dead_code)]
1377    #[repr(u32)]
1378    #[derive(Debug, Copy, Clone)]
1379    pub enum TcgProtocol {
1380        TrEe = 0,
1381        Tcg2 = 1,
1382    }
1383
1384    open_enum::open_enum! {
1385        /// Table 2: Physical Presence Interface Operation Summary for TPM 2.0
1386        ///
1387        /// Part of the Physical Presence Interface Specification - TCG PC Client Platform
1388        #[derive(Inspect, IntoBytes, Immutable, KnownLayout, FromBytes)]
1389        #[inspect(debug)]
1390        pub enum PpiOperation: u32 {
1391            NO_OP = 0,
1392            ENABLE = 1,
1393            DISABLE = 2,
1394            ACTIVATE = 3,
1395            DEACTIVATE = 4,
1396            CLEAR = 5,
1397            ENABLE_ACTIVATE = 6,
1398            DEACTIVATE_DISABLE = 7,
1399            SET_OWNER_INSTALL_TRUE = 8,
1400            SET_OWNER_INSTALL_FALSE = 9,
1401            ENABLE_ACTIVATE_SET_OWNER_INSTALL_TRUE = 10,
1402            SET_OWNER_INSTALL_FALSE_DEACTIVATE_DISABLE = 11,
1403            CLEAR_ENABLE_ACTIVATE = 14,
1404            SET_NO_PPI_PROVISION_FALSE = 15,
1405            SET_NO_PPI_PROVISION_TRUE = 16,
1406            ENABLE_ACTIVATE_CLEAR = 21,
1407            ENABLE_ACTIVATE_CLEAR_ENABLE_ACTIVATE = 22,
1408            SET_PCR_BANKS = 23,
1409        }
1410    }
1411}
1412
1413mod persist_restore {
1414    use super::*;
1415
1416    mod state {
1417        use zerocopy::FromBytes;
1418
1419        use zerocopy::Immutable;
1420        use zerocopy::IntoBytes;
1421        use zerocopy::KnownLayout;
1422
1423        #[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
1424        #[repr(C)]
1425        pub struct PersistedPpiState {
1426            pub pending_ppi_operation: u32,
1427            pub in_query_ppi_operation: u32,
1428            pub set_ppi_operation_state: u32,
1429            pub last_ppi_operation: u32,
1430            pub last_ppi_state: u32,
1431            pub ppi_set_operation_arg3_integer2: u32,
1432            pub tpm_capability_hash_alg_bitmap: u32,
1433        }
1434    }
1435
1436    pub(crate) fn deserialize_ppi_state(buf: Vec<u8>) -> Option<PpiState> {
1437        let saved = state::PersistedPpiState::read_from_bytes(buf.as_bytes()).ok()?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
1438        let state::PersistedPpiState {
1439            pending_ppi_operation,
1440            in_query_ppi_operation,
1441            set_ppi_operation_state,
1442            last_ppi_operation,
1443            last_ppi_state,
1444            ppi_set_operation_arg3_integer2,
1445            tpm_capability_hash_alg_bitmap,
1446        } = saved;
1447
1448        Some(PpiState {
1449            pending_ppi_operation: PpiOperation(pending_ppi_operation),
1450            in_query_ppi_operation: PpiOperation(in_query_ppi_operation),
1451            set_ppi_operation_state,
1452            last_ppi_operation: PpiOperation(last_ppi_operation),
1453            last_ppi_state,
1454            ppi_set_operation_arg3_integer2,
1455            tpm_capability_hash_alg_bitmap,
1456        })
1457    }
1458
1459    pub(crate) fn serialize_ppi_state(state: PpiState) -> Vec<u8> {
1460        let PpiState {
1461            pending_ppi_operation,
1462            in_query_ppi_operation,
1463            set_ppi_operation_state,
1464            last_ppi_operation,
1465            last_ppi_state,
1466            ppi_set_operation_arg3_integer2,
1467            tpm_capability_hash_alg_bitmap,
1468        } = state;
1469
1470        state::PersistedPpiState {
1471            pending_ppi_operation: pending_ppi_operation.0,
1472            in_query_ppi_operation: in_query_ppi_operation.0,
1473            set_ppi_operation_state,
1474            last_ppi_operation: last_ppi_operation.0,
1475            last_ppi_state,
1476            ppi_set_operation_arg3_integer2,
1477            tpm_capability_hash_alg_bitmap,
1478        }
1479        .as_bytes()
1480        .to_vec()
1481    }
1482}
1483
1484mod save_restore {
1485    use super::*;
1486    use vmcore::save_restore::RestoreError;
1487    use vmcore::save_restore::SaveError;
1488    use vmcore::save_restore::SaveRestore;
1489
1490    mod state {
1491        use mesh::payload::Protobuf;
1492        use vmcore::save_restore::SavedStateRoot;
1493
1494        const RSA_2K_MODULUS_SIZE: usize = 256;
1495        const RSA_2K_EXPONENT_SIZE: usize = 3;
1496
1497        #[derive(Protobuf)]
1498        #[mesh(package = "tpm")]
1499        pub struct SavedControlArea {
1500            #[mesh(1)]
1501            pub request: u32,
1502            #[mesh(2)]
1503            pub status: u32,
1504            #[mesh(3)]
1505            pub cancel: u32,
1506            #[mesh(4)]
1507            pub start: u32,
1508            #[mesh(5)]
1509            pub command_size: u32,
1510            #[mesh(6)]
1511            pub command_pa: u64,
1512            #[mesh(7)]
1513            pub response_size: u32,
1514            #[mesh(8)]
1515            pub response_pa: u64,
1516        }
1517
1518        #[derive(Protobuf)]
1519        #[mesh(package = "tpm")]
1520        pub struct SavedPpiState {
1521            #[mesh(1)]
1522            pub pending_ppi_operation: u32,
1523            #[mesh(2)]
1524            pub in_query_ppi_operation: u32,
1525            #[mesh(3)]
1526            pub set_ppi_operation_state: u32,
1527            #[mesh(4)]
1528            pub last_ppi_operation: u32,
1529            #[mesh(5)]
1530            pub last_ppi_state: u32,
1531            #[mesh(6)]
1532            pub ppi_set_operation_arg3_integer2: u32,
1533            #[mesh(7)]
1534            pub tpm_capability_hash_alg_bitmap: u32,
1535        }
1536
1537        #[derive(Protobuf)]
1538        #[mesh(package = "tpm")]
1539        pub struct SavedTpmKeys {
1540            #[mesh(1)]
1541            pub ak_pub_modulus: [u8; RSA_2K_MODULUS_SIZE],
1542            #[mesh(2)]
1543            pub ak_pub_exponent: [u8; RSA_2K_EXPONENT_SIZE],
1544            #[mesh(3)]
1545            pub ek_pub_modulus: [u8; RSA_2K_MODULUS_SIZE],
1546            #[mesh(4)]
1547            pub ek_pub_exponent: [u8; RSA_2K_EXPONENT_SIZE],
1548        }
1549
1550        #[derive(Protobuf, SavedStateRoot)]
1551        #[mesh(package = "tpm")]
1552        pub struct SavedState {
1553            #[mesh(1)]
1554            pub control_area: SavedControlArea,
1555            #[mesh(2)]
1556            pub current_io_command: Option<u32>,
1557            #[mesh(3)]
1558            pub requested_locality: bool,
1559            #[mesh(4)]
1560            pub ppi_state: SavedPpiState,
1561            #[mesh(5)]
1562            pub tpm_state_blob: Vec<u8>,
1563            // Experimental fields to avoid breaking changes
1564            // TODO CVM: Remove the explicit numbering once live servicing design is finialized
1565            #[mesh(60)]
1566            pub auth_value: Option<u64>,
1567            #[mesh(61)]
1568            pub keys: Option<SavedTpmKeys>,
1569        }
1570    }
1571
1572    #[derive(Error, Debug)]
1573    pub enum TpmRestoreError {
1574        #[error("failed to restore tpm library runtime state")]
1575        TpmRuntimeLib(#[source] ms_tpm_20_ref::Error),
1576    }
1577
1578    #[derive(Error, Debug)]
1579    pub enum TpmSaveError {
1580        #[error("save is blocked when there is an outstanding AK Cert request")]
1581        OutstandingAkCertRequest,
1582    }
1583
1584    impl SaveRestore for Tpm {
1585        type SavedState = state::SavedState;
1586
1587        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
1588            // Block save requests when there is an outstanding ak cert request.
1589            //
1590            // DEVNOTE:
1591            // - The device itself does not save/restore the async request, and
1592            // we need to think more about what it means to save the outstanding request
1593            // and what the API should be.
1594            // - The existing implementation with the GET has a host issue where leaving
1595            // this request in-flight during a servicing operation can lead to bad host
1596            // behavior on older hosts.
1597            if self.async_ak_cert_request.is_some() {
1598                return Err(SaveError::Other(
1599                    TpmSaveError::OutstandingAkCertRequest.into(),
1600                ));
1601            }
1602
1603            let control_area = {
1604                let ControlArea {
1605                    request,
1606                    status,
1607                    cancel,
1608                    start,
1609                    command_size,
1610                    command_pa,
1611                    response_size,
1612                    response_pa,
1613                } = self.control_area;
1614
1615                state::SavedControlArea {
1616                    request,
1617                    status,
1618                    cancel,
1619                    start,
1620                    command_size,
1621                    command_pa,
1622                    response_size,
1623                    response_pa,
1624                }
1625            };
1626
1627            let ppi_state = {
1628                let PpiState {
1629                    pending_ppi_operation,
1630                    in_query_ppi_operation,
1631                    set_ppi_operation_state,
1632                    last_ppi_operation,
1633                    last_ppi_state,
1634                    ppi_set_operation_arg3_integer2,
1635                    tpm_capability_hash_alg_bitmap,
1636                } = self.ppi_state;
1637
1638                state::SavedPpiState {
1639                    pending_ppi_operation: pending_ppi_operation.0,
1640                    in_query_ppi_operation: in_query_ppi_operation.0,
1641                    set_ppi_operation_state,
1642                    last_ppi_operation: last_ppi_operation.0,
1643                    last_ppi_state,
1644                    ppi_set_operation_arg3_integer2,
1645                    tpm_capability_hash_alg_bitmap,
1646                }
1647            };
1648
1649            // TODO CVM: The design of live servicing for CVM is not finalized.
1650            //           This behavior is subject to change.
1651            let keys = self.keys.as_ref().map(|keys| state::SavedTpmKeys {
1652                ak_pub_modulus: keys.ak_pub.modulus,
1653                ak_pub_exponent: keys.ak_pub.exponent,
1654                ek_pub_modulus: keys.ek_pub.modulus,
1655                ek_pub_exponent: keys.ek_pub.exponent,
1656            });
1657
1658            let saved_state = state::SavedState {
1659                control_area,
1660                current_io_command: self.current_io_command.map(|x| x.0),
1661                requested_locality: self.requested_locality,
1662                ppi_state,
1663                tpm_state_blob: self.tpm_engine_helper.tpm_engine.save_state(),
1664                auth_value: self.auth_value,
1665                keys,
1666            };
1667
1668            Ok(saved_state)
1669        }
1670
1671        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
1672            let state::SavedState {
1673                control_area,
1674                current_io_command,
1675                requested_locality,
1676                ppi_state,
1677                tpm_state_blob,
1678                auth_value,
1679                keys,
1680            } = state;
1681
1682            self.control_area = {
1683                let state::SavedControlArea {
1684                    request,
1685                    status,
1686                    cancel,
1687                    start,
1688                    command_size,
1689                    command_pa,
1690                    response_size,
1691                    response_pa,
1692                } = control_area;
1693
1694                ControlArea {
1695                    request,
1696                    status,
1697                    cancel,
1698                    start,
1699                    command_size,
1700                    command_pa,
1701                    response_size,
1702                    response_pa,
1703                }
1704            };
1705            self.current_io_command = current_io_command.map(TpmIoCommand);
1706            self.ppi_state = {
1707                let state::SavedPpiState {
1708                    pending_ppi_operation,
1709                    in_query_ppi_operation,
1710                    set_ppi_operation_state,
1711                    last_ppi_operation,
1712                    last_ppi_state,
1713                    ppi_set_operation_arg3_integer2,
1714                    tpm_capability_hash_alg_bitmap,
1715                } = ppi_state;
1716
1717                PpiState {
1718                    pending_ppi_operation: PpiOperation(pending_ppi_operation),
1719                    in_query_ppi_operation: PpiOperation(in_query_ppi_operation),
1720                    set_ppi_operation_state,
1721                    last_ppi_operation: PpiOperation(last_ppi_operation),
1722                    last_ppi_state,
1723                    ppi_set_operation_arg3_integer2,
1724                    tpm_capability_hash_alg_bitmap,
1725                }
1726            };
1727            self.requested_locality = requested_locality;
1728            self.tpm_engine_helper
1729                .tpm_engine
1730                .restore_state(tpm_state_blob)
1731                .map_err(TpmRestoreError::TpmRuntimeLib)
1732                .map_err(|e| RestoreError::Other(e.into()))?;
1733
1734            self.auth_value = auth_value;
1735            self.keys = keys.map(|keys| TpmKeys {
1736                ak_pub: TpmRsa2kPublic {
1737                    modulus: keys.ak_pub_modulus,
1738                    exponent: keys.ak_pub_exponent,
1739                },
1740                ek_pub: TpmRsa2kPublic {
1741                    modulus: keys.ek_pub_modulus,
1742                    exponent: keys.ek_pub_exponent,
1743                },
1744            });
1745
1746            Ok(())
1747        }
1748    }
1749}