Skip to content

Commit

Permalink
rom: Implement debug unlock requests
Browse files Browse the repository at this point in the history
This adds both the manufactoring and production debug unlock codepaths.

There are a few details that need to be resolved in TODOs that are
unclear from the documentation.

Signed-off-by: Arthur Heymans <[email protected]>
  • Loading branch information
ArthurHeymans committed Dec 19, 2024
1 parent 55790bd commit ff3630f
Show file tree
Hide file tree
Showing 12 changed files with 596 additions and 5 deletions.
109 changes: 109 additions & 0 deletions api/src/mailbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ impl CommandId {

// The get IDevID CSR command.
pub const GET_IDEV_CSR: Self = Self(0x4944_4352); // "IDCR"

// Debug unlock commands
pub const MANUF_DEBUG_UNLOCK_REQ_TOKEN: Self = Self(0x4d445554); // "MDUT"
pub const PRODUCTION_AUTH_DEBUG_UNLOCK_REQ: Self = Self(0x50445552); // "PDUR"
pub const PRODUCTION_AUTH_DEBUG_UNLOCK_TOKEN: Self = Self(0x50445554); // "PDUT"
}

impl From<u32> for CommandId {
Expand Down Expand Up @@ -1092,6 +1097,110 @@ pub struct AuthorizeAndStashResp {
}
impl Response for AuthorizeAndStashResp {}

// MANUF_DEBUG_UNLOCK_REQ_TOKEN
#[repr(C)]
#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq, Default)]
pub struct ManufDebugUnlockTokenReq {
pub hdr: MailboxReqHeader,
pub token: [u8; 16],
}
impl Request for ManufDebugUnlockTokenReq {
const ID: CommandId = CommandId::MANUF_DEBUG_UNLOCK_REQ_TOKEN;
type Resp = MailboxRespHeader;
}

// PRODUCTION_AUTH_DEBUG_UNLOCK_REQ
#[repr(C)]
#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq, Default)]
pub struct ProductionAuthDebugUnlockReq {
pub hdr: MailboxReqHeader,
pub vendor_id: u16, // Vendor ID (2 bytes)
pub object_data_type: u8, // Object Data Type (1 byte)
pub _reserved_1: u8, // Reserved (1 byte)
pub length: [u8; 3], // Length (3 bytes, should be ensured as 3 DWORDs)
pub _reserved_2: u8, // Reserved (1 byte)
pub unlock_category: [u8; 3], // Unlock Category (3 bytes, Bits[0:3] - Debug unlock Level)
pub _reserved_3: u8, // Reserved (1 byte)
}

impl Request for ProductionAuthDebugUnlockReq {
const ID: CommandId = CommandId::PRODUCTION_AUTH_DEBUG_UNLOCK_REQ;
type Resp = ProductionAuthDebugUnlockChallenge;
}

// PRODUCTION_AUTH_DEBUG_UNLOCK_CHALLENGE
#[repr(C)]
#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)]
pub struct ProductionAuthDebugUnlockChallenge {
pub hdr: MailboxRespHeader,
pub vendor_id: u16, // Vendor ID (2 bytes)
pub object_data_type: u8, // Object Data Type (1 byte)
pub _reserved_1: u8, // Reserved (1 byte)
pub length: [u8; 3], // Length (3 bytes, should be ensured as 8 (TODO?) DWORDs)
pub _reserved_2: u8, // Reserved (1 byte)
pub unique_device_identifier: [u8; 32], // Device identifier of the Caliptra Device
pub challenge: [u8; 48], // Random number
}
impl Default for ProductionAuthDebugUnlockChallenge {
fn default() -> Self {
Self {
hdr: Default::default(),
vendor_id: Default::default(),
object_data_type: Default::default(),
_reserved_1: Default::default(),
length: Default::default(),
_reserved_2: Default::default(),
unique_device_identifier: Default::default(),
challenge: [0; 48],
}
}
}
impl Response for ProductionAuthDebugUnlockChallenge {}

// PRODUCTION_AUTH_DEBUG_UNLOCK_TOKEN
#[repr(C)]
#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)]
pub struct ProductionAuthDebugUnlockToken {
pub hdr: MailboxReqHeader,
pub vendor_id: u16, // Vendor ID (2 bytes)
pub object_data_type: u8, // Object Data Type (1 byte)
pub _reserved_1: u8, // Reserved (1 byte)
pub length: [u8; 3], // Length (3 bytes, should be ensured as 0x754)
pub _reserved_2: u8, // Reserved (1 byte)
pub unique_device_identifier: [u8; 32], // Device identifier of the Caliptra Device
pub unlock_category: [u8; 3], // Unlock Category (3 bytes, Bits[0:3] - Debug unlock Level)
pub _reserved_3: u8, // Reserved (1 byte)
pub challenge: [u8; 48], // Random number
pub ecc_public_key: [u8; 96], // ECC public key
pub mldsa_public_key: [u8; 2592], // MLDSA public key
pub ecc_signature: [u8; 96], // ECC P-384 signature of the Message hashed using SHA2-384. R-Coordinate: Random Point (48 bytes) S-Coordinate: Proof (48 bytes)
pub mldsa_signature: [u8; 4628], // MLDSA signature of the Message hashed using SHA2-512. (4627 bytes + 1 Reserved byte).
}
impl Default for ProductionAuthDebugUnlockToken {
fn default() -> Self {
Self {
hdr: Default::default(),
vendor_id: Default::default(),
object_data_type: Default::default(),
_reserved_1: Default::default(),
length: Default::default(),
_reserved_2: Default::default(),
unique_device_identifier: Default::default(),
unlock_category: Default::default(),
_reserved_3: Default::default(),
challenge: [0; 48],
ecc_public_key: [0; 96],
mldsa_public_key: [0; 2592],
ecc_signature: [0; 96],
mldsa_signature: [0; 4628],
}
}
}
impl Request for ProductionAuthDebugUnlockToken {
const ID: CommandId = CommandId::PRODUCTION_AUTH_DEBUG_UNLOCK_TOKEN; // TODO
type Resp = MailboxRespHeader; // TODO Check
}

/// Retrieves dlen bytes from the mailbox.
pub fn mbox_read_response(
mbox: mbox::RegisterBlock<impl MmioMut>,
Expand Down
16 changes: 15 additions & 1 deletion drivers/src/fuse_bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Abstract:
--*/

use crate::Array4x12;
use crate::{Array4x12, Array4x4};
use caliptra_cfi_derive::Launder;
use caliptra_registers::soc_ifc::SocIfcReg;
use zerocopy::AsBytes;
Expand Down Expand Up @@ -312,6 +312,20 @@ impl FuseBank<'_> {
let soc_ifc_regs = self.soc_ifc.regs();
soc_ifc_regs.fuse_lms_revocation().read()
}

/// Get the manufactoring debug unlock token
///
/// # Arguments
/// * None
///
/// # Returns
/// manufactoring debug unlock token
///
pub fn manuf_dbg_unlock_token(&self) -> Array4x4 {
let soc_ifc_regs = self.soc_ifc.regs();
Array4x4::read_from_reg(soc_ifc_regs.fuse_manuf_dbg_unlock_token())
}
}

#[cfg(test)]
Expand Down
8 changes: 8 additions & 0 deletions drivers/src/mailbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,14 @@ impl MailboxRecvTxn<'_> {
self.state = MailboxOpState::Idle;
Ok(())
}

///
/// Set UC TAP unlock
///
pub fn set_uc_tap_unlock(&mut self, enable: bool) {
let mbox = self.mbox.regs_mut();
mbox.tap_mode().modify(|w| w.enabled(enable))
}
}

impl Drop for MailboxRecvTxn<'_> {
Expand Down
112 changes: 112 additions & 0 deletions drivers/src/soc_ifc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Abstract:
--*/

use crate::{Array4x16, AxiAddr};
use bitfield::size_of;
use caliptra_cfi_derive::Launder;
use caliptra_error::{CaliptraError, CaliptraResult};
use caliptra_registers::soc_ifc::enums::DeviceLifecycleE;
Expand Down Expand Up @@ -75,6 +77,116 @@ impl SocIfc {
soc_ifc_regs.cptra_security_state().read().debug_locked()
}

/// Subsystem debug intent
fn ss_debug_intent(&self) -> bool {
let soc_ifc_regs = self.soc_ifc.regs();
soc_ifc_regs.ss_debug_intent().read().debug_intent()
}

/// Subsystem debug unlock requested
pub fn ss_debug_unlock_req(&self) -> CaliptraResult<bool> {
if !self.ss_debug_intent() {
return Ok(false);
}

let soc_ifc_regs = self.soc_ifc.regs();
let lifecycle = self.lifecycle();
let dbg_req = soc_ifc_regs.ss_dbg_manuf_service_reg_req().read();
let (manuf, prod) = (
dbg_req.manuf_dbg_unlock_req(),
dbg_req.prod_dbg_unlock_req(),
);

match (manuf, prod, lifecycle) {
(true, false, Lifecycle::Manufacturing) | (false, true, Lifecycle::Production) => {
Ok(true)
}
(true, true, _) | (true, false, _) | (false, true, _) => {
Err(CaliptraError::ROM_SS_DBG_UNLOCK_INVALID_REQ_REG_VALUE)
}
(false, false, _) => Ok(false),
}
}

/// Set debug unlock in progress
pub fn set_ss_dbg_unlock_in_progress(&mut self, process: bool) {
let lifecycle = self.lifecycle();
let soc_ifc_regs = self.soc_ifc.regs_mut();
match lifecycle {
Lifecycle::Manufacturing => soc_ifc_regs
.ss_dbg_manuf_service_reg_rsp()
.write(|w| w.manuf_dbg_unlock_in_progress(process)),
DeviceLifecycleE::Production => soc_ifc_regs
.ss_dbg_manuf_service_reg_rsp()
.write(|w| w.prod_dbg_unlock_in_progress(process)),
_ => (),
}
}

/// Set debug unlock as finished with either failure or success
pub fn finish_ss_dbg_unluck(&mut self, succes: bool) {
let lifecycle = self.lifecycle();
let soc_ifc_regs = self.soc_ifc.regs_mut();
if succes {
match lifecycle {
Lifecycle::Manufacturing => {
soc_ifc_regs.ss_dbg_manuf_service_reg_rsp().write(|w| {
w.manuf_dbg_unlock_in_progress(false)
.manuf_dbg_unlock_success(true)
})
}
DeviceLifecycleE::Production => {
soc_ifc_regs.ss_dbg_manuf_service_reg_rsp().write(|w| {
w.prod_dbg_unlock_in_progress(false)
.prod_dbg_unlock_success(true)
})
}
_ => (),
}
} else {
match lifecycle {
Lifecycle::Manufacturing => {
soc_ifc_regs.ss_dbg_manuf_service_reg_rsp().write(|w| {
w.manuf_dbg_unlock_in_progress(false)
.manuf_dbg_unlock_fail(true)
})
}
DeviceLifecycleE::Production => {
soc_ifc_regs.ss_dbg_manuf_service_reg_rsp().write(|w| {
w.prod_dbg_unlock_in_progress(false)
.prod_dbg_unlock_fail(true)
})
}
_ => (),
}
}
}

/// MCI AXI base address
pub fn ss_mci_axi_base(&self) -> AxiAddr {
let soc_ifc_regs = self.soc_ifc.regs();
AxiAddr {
lo: soc_ifc_regs.ss_caliptra_base_addr_l().read(),
hi: soc_ifc_regs.ss_caliptra_base_addr_h().read(),
}
}

/// Debug unlock memory offset
pub fn debug_unlock_pk_hash_offset(&self, level: usize) -> CaliptraResult<usize> {
let soc_ifc_regs = self.soc_ifc.regs();
let fusebank_offset = soc_ifc_regs
.ss_prod_debug_unlock_auth_pk_hash_reg_bank_offset()
.read() as usize;
let num_of_debug_pk_hashes = soc_ifc_regs
.ss_num_of_prod_debug_unlock_auth_pk_hashes()
.read();
if level as u32 >= num_of_debug_pk_hashes {
Err(CaliptraError::ROM_SS_DBG_UNLOCK_PROD_INVALID_LEVEL)?
}
// DEBUG_AUTH_PK_HASH_REG_BANK_OFFSET register value + ( Debug Unlock Level * SHA2-512 hash size (64 bytes) )
Ok(fusebank_offset + size_of::<Array4x16>() * level)
}

pub fn mbox_valid_pauser(&self) -> [u32; 5] {
let soc_ifc_regs = self.soc_ifc.regs();
soc_ifc_regs.cptra_mbox_valid_axi_user().read()
Expand Down
18 changes: 18 additions & 0 deletions error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,24 @@ impl CaliptraError {
// TODO: What base value is right for this?
// FIPS Hooks
pub const FIPS_HOOKS_INJECTED_ERROR: CaliptraError = CaliptraError::new_const(0x90100000);

// Debug unlock errors
pub const ROM_SS_DBG_UNLOCK_INVALID_REQ_REG_VALUE: CaliptraError =
CaliptraError::new_const(0xa0000000);
pub const ROM_SS_DBG_UNLOCK_MANUF_INVALID_MBOX_CMD: CaliptraError =
CaliptraError::new_const(0xa0000001);
pub const ROM_SS_DBG_UNLOCK_MANUF_INVALID_TOKEN: CaliptraError =
CaliptraError::new_const(0xa0000002);
pub const ROM_SS_DBG_UNLOCK_PROD_INVALID_REQ_MBOX_CMD: CaliptraError =
CaliptraError::new_const(0xa0000003);
pub const ROM_SS_DBG_UNLOCK_PROD_INVALID_REQ: CaliptraError =
CaliptraError::new_const(0xa0000003);
pub const ROM_SS_DBG_UNLOCK_PROD_INVALID_LEVEL: CaliptraError =
CaliptraError::new_const(0xa0000004);
pub const ROM_SS_DBG_UNLOCK_PROD_INVALID_TOKEN_MBOX_CMD: CaliptraError =
CaliptraError::new_const(0xa0000005);
pub const ROM_SS_DBG_UNLOCK_PROD_INVALID_TOKEN: CaliptraError =
CaliptraError::new_const(0xa0000006);
}

impl From<core::num::NonZeroU32> for crate::CaliptraError {
Expand Down
1 change: 1 addition & 0 deletions rom/dev/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"
rust-version = "1.70"

[dependencies]
caliptra-api.workspace = true
caliptra-cfi-lib = { workspace = true, default-features = false, features = [
"cfi",
"cfi-counter",
Expand Down
2 changes: 1 addition & 1 deletion rom/dev/src/flow/cold_reset/fw_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ impl FirmwareProcessor {
/// # Returns
/// * `()` - Ok
/// Error code on failure.
fn copy_req_verify_chksum(txn: &mut MailboxRecvTxn, data: &mut [u8]) -> CaliptraResult<()> {
pub fn copy_req_verify_chksum(txn: &mut MailboxRecvTxn, data: &mut [u8]) -> CaliptraResult<()> {
// NOTE: Currently ROM only supports commands with a fixed request size
// This check will need to be updated if any commands are added with a variable request size
if txn.dlen() as usize != data.len() {
Expand Down
2 changes: 1 addition & 1 deletion rom/dev/src/flow/cold_reset/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Abstract:

mod dice;
mod fmc_alias;
mod fw_processor;
pub mod fw_processor;
mod idev_id;
mod ldev_id;
mod x509;
Expand Down
Loading

0 comments on commit ff3630f

Please sign in to comment.