Skip to content

Commit 6199f73

Browse files
authored
runtime: Anchor MCU RT in Caliptra-managed DPE chain (#3729)
- Carry the SoC manifest SVN into the MCU RT DPE context created during recovery boot. - Keep MCU RT hitless update measurement changes on the internal backdoor-update path. - Generalize Caliptra-managed DPE context lookup and backdoor measurement updates for CCIV, MCFW, and future managed contexts. - Refactor AUTHORIZE_AND_STASH image verification so ACTIVATE_FIRMWARE can authorize MCU RT when the MCU image is activated, before updating MCFW internally. - Preserve public AUTHORIZE_AND_STASH semantics: SKIP_STASH only controls whether authorized measurements are stashed into DPE and PCR31.
1 parent fb46b49 commit 6199f73

12 files changed

Lines changed: 547 additions & 187 deletions

File tree

drivers/src/persistent.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ pub const CMB_AES_KEY_SHARE_SIZE: u32 = 32;
5757
pub const DOT_OWNER_PK_HASH_SIZE: u32 = 13 * 4;
5858
pub const CLEARED_NON_FATAL_FW_ERROR_SIZE: u32 = 4;
5959
pub const MCU_FIRMWARE_LOADED_SIZE: u32 = 4;
60+
#[cfg(feature = "runtime")]
61+
pub const SOC_MANIFEST_SVN_SIZE: u32 = 4;
6062
pub const _DPE_PL_CONTEXT_LIMITS_WITH_PAD_SIZE: u32 = 4; // u8 + u8 + 2 bytes padding
6163

6264
#[cfg(feature = "runtime")]
@@ -95,6 +97,15 @@ pub type StashMeasurementArray = [MeasurementLogEntry; MEASUREMENT_MAX_COUNT];
9597
pub type AuthManifestImageMetadataList =
9698
[AuthManifestImageMetadata; AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT];
9799

100+
#[cfg(feature = "runtime")]
101+
#[derive(Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout, Zeroize, Default)]
102+
#[repr(C)]
103+
pub struct CaliptraManagedDpeContextIndices {
104+
pub cciv: u8,
105+
pub mcu_rt: u8,
106+
pub reserved: [u8; 2],
107+
}
108+
98109
#[derive(Clone, Immutable, IntoBytes, KnownLayout, TryFromBytes, Zeroize)]
99110
#[repr(C)]
100111
pub struct Ecc384IdevIdCsr {
@@ -381,6 +392,14 @@ pub struct PersistentData {
381392
/// changing the frozen ROM binary. FMC writes this during handoff,
382393
/// runtime reads it for MLDSA signing operations.
383394
pub rt_mldsa_keypair_seed_kv_hdl: HandOffDataHandle,
395+
396+
/// SoC manifest SVN, stored after auth manifest validation.
397+
/// Used as the MCU RT current_svn when creating the MCU RT DPE context.
398+
#[cfg(feature = "runtime")]
399+
pub soc_manifest_svn: u32,
400+
401+
#[cfg(feature = "runtime")]
402+
pub caliptra_managed_dpe_context_indices: CaliptraManagedDpeContextIndices,
384403
}
385404

386405
impl PersistentData {
@@ -548,7 +567,30 @@ impl PersistentData {
548567
addr_of!((*P).dpe_pl0_context_limit) as u32,
549568
memory_layout::PERSISTENT_DATA_ORG + persistent_data_offset
550569
);
570+
persistent_data_offset += _DPE_PL_CONTEXT_LIMITS_WITH_PAD_SIZE;
571+
assert_eq!(
572+
addr_of!((*P).rt_mldsa_keypair_seed_kv_hdl) as u32,
573+
memory_layout::PERSISTENT_DATA_ORG + persistent_data_offset
574+
);
575+
persistent_data_offset += size_of::<HandOffDataHandle>() as u32;
576+
#[cfg(feature = "runtime")]
577+
{
578+
assert_eq!(
579+
addr_of!((*P).soc_manifest_svn) as u32,
580+
memory_layout::PERSISTENT_DATA_ORG + persistent_data_offset
581+
);
582+
persistent_data_offset += SOC_MANIFEST_SVN_SIZE;
583+
assert_eq!(
584+
addr_of!((*P).caliptra_managed_dpe_context_indices) as u32,
585+
memory_layout::PERSISTENT_DATA_ORG + persistent_data_offset
586+
);
587+
persistent_data_offset += size_of::<CaliptraManagedDpeContextIndices>() as u32;
588+
}
551589

590+
assert_eq!(
591+
memory_layout::PERSISTENT_DATA_ORG + persistent_data_offset,
592+
memory_layout::DATA_ORG
593+
);
552594
assert_eq!(P.add(1) as u32, memory_layout::DATA_ORG);
553595
}
554596
}

error/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,14 +1589,14 @@ impl CaliptraError {
15891589
"Runtime Error: Attested CSR COSE Sign1 encoding error"
15901590
),
15911591
(
1592-
RUNTIME_CCIV_CONTEXT_NOT_FOUND,
1592+
RUNTIME_DPE_CONTEXT_NOT_FOUND,
15931593
0x000E0093,
1594-
"Runtime Error: CCIV context not found"
1594+
"Runtime Error: DPE context not found"
15951595
),
15961596
(
1597-
RUNTIME_MULTIPLE_CCIV_CONTEXTS_FOUND,
1597+
RUNTIME_MULTIPLE_DPE_CONTEXTS_FOUND,
15981598
0x000E0094,
1599-
"Runtime Error: Multiple CCIV contexts found"
1599+
"Runtime Error: Multiple DPE contexts found"
16001600
),
16011601
(
16021602
RUNTIME_INVOKE_DPE_RESPONSE_TOO_LARGE,

runtime/src/activate_firmware.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::authorize_and_stash::{self, AuthorizeAndStashCmd};
1818
use crate::drivers::{McuFwStatus, McuResetReason};
1919
use crate::Drivers;
2020
use crate::{manifest::find_metadata_entry, mutrefbytes};
21-
use caliptra_api::mailbox::{AuthAndStashFlags, AuthorizeAndStashReq, ImageHashSource};
21+
use caliptra_api::mailbox::{AuthorizeAndStashReq, ImageHashSource};
2222
use caliptra_auth_man_types::ImageMetadataFlags;
2323
use caliptra_common::mailbox_api::{ActivateFirmwareReq, ActivateFirmwareResp, MailboxRespHeader};
2424
use caliptra_drivers::dma::MCU_SRAM_OFFSET;
@@ -202,28 +202,32 @@ impl ActivateFirmwareCmd {
202202
)
203203
.map_err(|_| ())?;
204204

205-
// Verify MCU after loading
205+
// Update MCU RT DPE context with new measurement.
206+
// SVN stays the same as the recovery boot value.
206207
let auth_and_stash_req = AuthorizeAndStashReq {
207208
fw_id: ActivateFirmwareReq::MCU_IMAGE_ID.to_le_bytes(),
208209
measurement: [0; 48],
209210
source: ImageHashSource::LoadAddress.into(),
210-
flags: AuthAndStashFlags::SKIP_STASH.bits(),
211211
image_size: mcu_image_size,
212212
..Default::default()
213213
};
214214

215215
let pl0_pauser_locality = drivers.persistent_data.get().manifest1.header.pl0_pauser;
216-
let authorization_result = AuthorizeAndStashCmd::authorize_and_stash(
217-
drivers,
218-
&auth_and_stash_req,
219-
pl0_pauser_locality,
220-
)
221-
.map_err(|_| ())?;
216+
let (authorization_result, measurement) =
217+
AuthorizeAndStashCmd::authorize_image(drivers, &auth_and_stash_req)
218+
.map_err(|_| ())?;
222219

223220
// Make sure the image was properly authorized.
224221
if authorization_result != authorize_and_stash::IMAGE_AUTHORIZED {
225222
return Err(());
226223
}
224+
drivers
225+
.update_caliptra_managed_measurement(
226+
Drivers::MCU_RT_TCI_TYPE,
227+
&measurement,
228+
Some(pl0_pauser_locality),
229+
)
230+
.map_err(|_| ())?;
227231

228232
drivers.persistent_data.get_mut().mcu_firmware_loaded = McuFwStatus::Loaded.into();
229233
}

runtime/src/authorize_and_stash.rs

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,39 @@ impl AuthorizeAndStashCmd {
6767
cmd: &AuthorizeAndStashReq,
6868
locality: u32,
6969
) -> CaliptraResult<u32> {
70+
let (auth_result, stash_measurement) = Self::authorize_image(drivers, cmd)?;
71+
72+
// Stash the measurement if the image is authorized.
73+
if auth_result == IMAGE_AUTHORIZED {
74+
let flags: AuthAndStashFlags = cmd.flags.into();
75+
if !flags.contains(AuthAndStashFlags::SKIP_STASH) {
76+
let dpe_result = StashMeasurementCmd::stash_measurement(
77+
drivers,
78+
&cmd.fw_id,
79+
&stash_measurement,
80+
cmd.svn,
81+
drivers.caller_privilege_level(),
82+
locality,
83+
)?;
84+
if dpe_result != DpeErrorCode::NoError {
85+
drivers
86+
.soc_ifc
87+
.set_fw_extended_error(dpe_result.get_error_code());
88+
89+
Err(CaliptraError::RUNTIME_AUTH_AND_STASH_MEASUREMENT_DPE_ERROR)?;
90+
}
91+
}
92+
}
93+
94+
Ok(auth_result)
95+
}
96+
97+
#[cfg_attr(feature = "cfi", cfi_impl_fn)]
98+
#[inline(never)]
99+
pub(crate) fn authorize_image(
100+
drivers: &mut Drivers,
101+
cmd: &AuthorizeAndStashReq,
102+
) -> CaliptraResult<(u32, [u8; 48])> {
70103
let source = ImageHashSource::from(cmd.source);
71104
if source == ImageHashSource::Invalid {
72105
Err(CaliptraError::RUNTIME_AUTH_AND_STASH_UNSUPPORTED_IMAGE_SOURCE)?;
@@ -82,7 +115,11 @@ impl AuthorizeAndStashCmd {
82115
let auth_manifest_image_metadata_col = &persistent_data.auth_manifest_image_metadata_col;
83116

84117
let cmd_fw_id = u32::from_le_bytes(cmd.fw_id);
118+
// The measurement used for both authorization and stashing/updating DPE.
119+
// For InRequest source, this is cmd.measurement.
120+
// For LoadAddress/StagingAddress, this is computed from the image in memory.
85121
let mut stash_measurement = cmd.measurement;
122+
86123
let auth_result = if let Some(metadata_entry) =
87124
find_metadata_entry(auth_manifest_image_metadata_col, cmd_fw_id)
88125
{
@@ -137,28 +174,7 @@ impl AuthorizeAndStashCmd {
137174
} else {
138175
IMAGE_NOT_AUTHORIZED
139176
};
140-
// Stash the measurement if the image is authorized.
141-
if auth_result == IMAGE_AUTHORIZED {
142-
let flags: AuthAndStashFlags = cmd.flags.into();
143-
if !flags.contains(AuthAndStashFlags::SKIP_STASH) {
144-
let dpe_result = StashMeasurementCmd::stash_measurement(
145-
drivers,
146-
&cmd.fw_id,
147-
&stash_measurement,
148-
cmd.svn,
149-
drivers.caller_privilege_level(),
150-
locality,
151-
)?;
152-
if dpe_result != DpeErrorCode::NoError {
153-
drivers
154-
.soc_ifc
155-
.set_fw_extended_error(dpe_result.get_error_code());
156177

157-
Err(CaliptraError::RUNTIME_AUTH_AND_STASH_MEASUREMENT_DPE_ERROR)?;
158-
}
159-
}
160-
}
161-
162-
Ok(auth_result)
178+
Ok((auth_result, stash_measurement))
163179
}
164180
}

0 commit comments

Comments
 (0)