1#![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
91const 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
97const ATTESTATION_REPORT_DATA_SIZE: usize = 0x40;
100
101const AK_CERT_RENEW_PERIOD: std::time::Duration = std::time::Duration::new(24 * 60 * 60, 0);
103const REPORT_TIMER_PERIOD: std::time::Duration = std::time::Duration::new(2, 0);
105
106const 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#[derive(Debug, Copy, Clone, Inspect)]
137struct ControlArea {
138 pub request: u32,
140 pub status: u32,
142 pub cancel: u32,
144 pub start: u32,
146 pub command_size: u32,
148 pub command_pa: u64,
150 pub response_size: u32,
152 pub response_pa: u64,
154}
155
156#[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 ak_pub: TpmRsa2kPublic,
201 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
215pub type MonotonicTimer = Box<dyn Send + FnMut() -> std::time::Duration>;
217
218#[derive(InspectMut)]
219pub struct Tpm {
220 register_layout: TpmRegisterLayout,
222 refresh_tpm_seeds: bool,
223 #[inspect(skip)]
224 io_region: Option<(&'static str, RangeInclusive<u16>)>, #[inspect(skip)]
226 mmio_region: Vec<(&'static str, RangeInclusive<u64>)>,
227
228 rt: TpmRuntime,
230 #[inspect(skip)]
231 ak_cert_type: TpmAkCertType,
232 #[inspect(skip)]
233 logger: Option<Arc<dyn TpmLogger>>,
234
235 #[inspect(skip)]
237 tpm_engine_helper: TpmEngineHelper,
238
239 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 control_area: ControlArea,
254 current_io_command: Option<TpmIoCommand>,
255 requested_locality: bool,
256 ppi_state: PpiState,
257 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 {
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 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 fixup_16k_ak_cert = blob.len() == LEGACY_VTPM_SIZE;
478 } else {
479 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 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 {
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 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 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 self.tpm_engine_helper
562 .allocate_guest_attestation_nv_indices(
563 auth_value,
564 !self.refresh_tpm_seeds, matches!(self.ak_cert_type, TpmAkCertType::HwAttested(_)),
566 fixup_16k_ak_cert,
567 )
568 .map_err(TpmErrorKind::AllocateGuestAttestationNvIndices)?;
569
570 if matches!(self.ak_cert_type, TpmAkCertType::HwAttested(_)) {
574 self.renew_ak_cert()?;
575 }
576 }
577
578 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 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 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 response_code
789 } else {
790 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 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 fn create_ak_cert_request(&mut self) -> Result<Vec<u8>, TpmError> {
828 let mut guest_attestation_input = [0u8; ATTESTATION_REPORT_DATA_SIZE];
829 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 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 fn renew_ak_cert(&mut self) -> Result<(), TpmError> {
872 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 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 if let Some(waker) = self.waker.take() {
902 waker.wake();
903 }
904
905 Ok(())
906 }
907
908 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 let now = std::time::SystemTime::now();
916
917 self.async_ak_cert_request = None;
919
920 let response = match result {
922 Ok(data) if !data.is_empty() => {
923 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 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 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 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 if u16::from(nv_read.offset) != 0 {
995 return;
996 }
997
998 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 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 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 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 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, ControlArea::OFFSET_OF_LOC_STS => 0x1, ControlArea::OFFSET_OF_CRB_INTF_ID => 0x4011, 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() .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), "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
1337mod 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 #[derive(Inspect, IntoBytes, Immutable, KnownLayout, FromBytes)]
1349 #[inspect(debug)]
1350 pub enum TpmIoCommand: u32 {
1351 VERSION = 0,
1353 MAP_SHARED_MEMORY = 1,
1355 ESTABLISHED = 2,
1357 PPI_GET_PENDING_OPERATION = 3,
1359 PPI_GET_LAST_OPERATION = 5,
1361 PPI_GET_LAST_RESULT = 6,
1362 PPI_SET_OPERATION = 7,
1365 PPI_GET_USER_CONFIRMATION = 8,
1367 PPI_SET_OPERATION_ARG3_INTEGER2 = 32,
1369 GET_TCG_PROTOCOL_VERSION = 64,
1371 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 #[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()?; 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 #[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 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 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}