Skip to content

Commit

Permalink
Merge branch 'main-2.x' into vmhatre/uds-provisioning-doc-update
Browse files Browse the repository at this point in the history
  • Loading branch information
mhatrevi authored Dec 20, 2024
2 parents 7677214 + d24136f commit ead9009
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 42 deletions.
60 changes: 34 additions & 26 deletions drivers/src/dma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,26 @@ use zerocopy::AsBytes;
pub enum DmaReadTarget {
Mbox,
AhbFifo,
AxiWr(usize),
AxiWr(AxiAddr),
}

#[derive(Debug, Clone, Copy)]
pub struct AxiAddr {
pub lo: u32,
pub hi: u32,
}

impl From<u64> for AxiAddr {
fn from(addr: u64) -> Self {
Self {
lo: addr as u32,
hi: (addr >> 32) as u32,
}
}
}

pub struct DmaReadTransaction {
pub read_addr: usize,
pub read_addr: AxiAddr,
pub fixed_addr: bool,
pub length: u32,
pub target: DmaReadTarget,
Expand All @@ -35,11 +50,11 @@ pub struct DmaReadTransaction {
pub enum DmaWriteOrigin {
Mbox,
AhbFifo,
AxiRd(usize),
AxiRd(AxiAddr),
}

pub struct DmaWriteTransaction {
pub write_addr: usize,
pub write_addr: AxiAddr,
pub fixed_addr: bool,
pub length: u32,
pub origin: DmaWriteOrigin,
Expand Down Expand Up @@ -78,16 +93,13 @@ impl Dma {
fn setup_dma_read(&mut self, read_transaction: DmaReadTransaction) {
let dma = self.dma.regs_mut();

let read_addr: usize = read_transaction.read_addr;
#[cfg(target_pointer_width = "64")]
dma.src_addr_h().write(|_| (read_addr >> 32) as u32);
dma.src_addr_l().write(|_| (read_addr & 0xffff_ffff) as u32);
let read_addr = read_transaction.read_addr;
dma.src_addr_l().write(|_| read_addr.lo);
dma.src_addr_h().write(|_| read_addr.hi);

if let DmaReadTarget::AxiWr(target_addr) = read_transaction.target {
#[cfg(target_pointer_width = "64")]
dma.dst_addr_h().write(|_| (target_addr >> 32) as u32);
dma.dst_addr_l()
.write(|_| (target_addr & 0xffff_ffff) as u32);
dma.dst_addr_l().write(|_| target_addr.lo);
dma.dst_addr_h().write(|_| target_addr.hi);
}

dma.ctrl().modify(|c| {
Expand All @@ -110,16 +122,12 @@ impl Dma {
let dma = self.dma.regs_mut();

let write_addr = write_transaction.write_addr;
#[cfg(target_pointer_width = "64")]
dma.dst_addr_h().write(|_| (write_addr >> 32) as u32);
dma.dst_addr_l()
.write(|_| (write_addr & 0xffff_ffff) as u32);
dma.dst_addr_l().write(|_| write_addr.lo);
dma.dst_addr_h().write(|_| write_addr.hi);

if let DmaWriteOrigin::AxiRd(origin_addr) = write_transaction.origin {
#[cfg(target_pointer_width = "64")]
dma.dst_addr_h().write(|_| (origin_addr >> 32) as u32);
dma.dst_addr_l()
.write(|_| (origin_addr & 0xffff_ffff) as u32);
dma.dst_addr_l().write(|_| origin_addr.lo);
dma.dst_addr_h().write(|_| origin_addr.hi);
}

dma.ctrl().modify(|c| {
Expand Down Expand Up @@ -193,18 +201,18 @@ impl Dma {

let status0 = dma.status0().read();
if status0.busy() {
return Err(CaliptraError::DRIVER_DMA_TRANSACTION_ALREADY_BUSY);
Err(CaliptraError::DRIVER_DMA_TRANSACTION_ALREADY_BUSY)?;
}

if status0.error() {
return Err(CaliptraError::DRIVER_DMA_TRANSACTION_ERROR);
Err(CaliptraError::DRIVER_DMA_TRANSACTION_ERROR)?;
}

dma.ctrl().modify(|c| c.go(true));

while dma.status0().read().busy() {
if dma.status0().read().error() {
return Err(CaliptraError::DRIVER_DMA_TRANSACTION_ERROR);
Err(CaliptraError::DRIVER_DMA_TRANSACTION_ERROR)?;
}
}

Expand All @@ -220,7 +228,7 @@ impl Dma {
/// # Returns
///
/// * `CaliptraResult<u32>` - Read value or error code
pub fn read_dword(&mut self, read_addr: usize) -> CaliptraResult<u32> {
pub fn read_dword(&mut self, read_addr: AxiAddr) -> CaliptraResult<u32> {
let mut read_val: u32 = 0;

self.flush();
Expand Down Expand Up @@ -248,7 +256,7 @@ impl Dma {
/// # Returns
///
/// * `CaliptraResult<()>` - Success or error code
pub fn write_dword(&mut self, write_addr: usize, write_val: u32) -> CaliptraResult<()> {
pub fn write_dword(&mut self, write_addr: AxiAddr, write_val: u32) -> CaliptraResult<()> {
self.flush();

let write_transaction = DmaWriteTransaction {
Expand Down Expand Up @@ -279,7 +287,7 @@ impl Dma {
/// * `CaliptraResult<()>` - Success or error code
pub fn transfer_payload_to_mbox(
&mut self,
read_addr: usize,
read_addr: AxiAddr,
payload_len_bytes: u32,
fixed_addr: bool,
block_size: u32,
Expand Down
4 changes: 3 additions & 1 deletion drivers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ pub use bounded_address::{BoundedAddr, MemBounds, RomAddr};
pub use caliptra_error::{CaliptraError, CaliptraResult};
pub use csrng::{Csrng, HealthFailCounts as CsrngHealthFailCounts, Seed as CsrngSeed};
pub use data_vault::{ColdResetEntries, DataVault, WarmResetEntries};
pub use dma::{Dma, DmaReadTarget, DmaReadTransaction, DmaWriteOrigin, DmaWriteTransaction};
pub use dma::{
AxiAddr, Dma, DmaReadTarget, DmaReadTransaction, DmaWriteOrigin, DmaWriteTransaction,
};
pub use doe::DeobfuscationEngine;
pub use ecc384::{
Ecc384, Ecc384PrivKeyIn, Ecc384PrivKeyOut, Ecc384PubKey, Ecc384Result, Ecc384Scalar,
Expand Down
17 changes: 11 additions & 6 deletions drivers/test-fw/src/bin/dma_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Abstract:
#![no_std]
#![no_main]

use caliptra_drivers::{memory_layout, Dma, Mailbox};
use caliptra_drivers::{memory_layout, AxiAddr, Dma, Mailbox};
use caliptra_registers::{axi_dma::AxiDmaReg, ecc::EccReg, mbox::MboxCsr};
use caliptra_test_harness::test_suite;
use core::slice;
Expand All @@ -27,7 +27,7 @@ fn test_dma_read_from_periph() {
let ecc_regs = unsafe { EccReg::new() };
let ecc_name = ecc_regs.regs().name().ptr();

let dword = dma.read_dword(ecc_name as usize).unwrap();
let dword = dma.read_dword(AxiAddr::from(ecc_name as u64)).unwrap();
assert_eq!(dword.to_ne_bytes(), [0x70, 0x63, 0x65, 0x73]); // secp
}

Expand All @@ -40,8 +40,8 @@ fn test_dma_write_to_periph() {

let data: u32 = 0xdead_beef;

dma.write_dword(ecc_iv as usize, data).unwrap();
let dword = dma.read_dword(ecc_iv as usize).unwrap();
dma.write_dword(AxiAddr::from(ecc_iv as u64), data).unwrap();
let dword = dma.read_dword(AxiAddr::from(ecc_iv as u64)).unwrap();
assert_eq!(dword, data);
}

Expand All @@ -59,8 +59,13 @@ fn test_read_rri_to_mailbox() {
let mut txn = mbox_driver.try_start_send_txn().unwrap();
txn.send_request(0xdead_beef, b"").unwrap();

dma.transfer_payload_to_mbox(rri_regs, test_image.len() as u32, true, block_size)
.unwrap();
dma.transfer_payload_to_mbox(
AxiAddr::from(rri_regs as u64),
test_image.len() as u32,
true,
block_size,
)
.unwrap();

let mbox_fifo = unsafe {
slice::from_raw_parts(
Expand Down
38 changes: 38 additions & 0 deletions rom/dev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,42 @@ Following is the sequence of steps that are performed to download the firmware i

See Firmware [Image Validation Process](#firmware-image-validation-process).

### Derivation of the key ladder for Stable Identity

Stable Identity calls for a secret that remains stable across firmware updates, but which can ratchet forward when major firmware vulnerabilities are fixed. Caliptra ROM implements this feature in terms of a "key ladder".

The key ladder is initialized from the LDevID CDI during cold-boot. The key ladder length is inversely related to the firmware's SVN. Each step of the ladder is an SVN-unique key. The key for SVN X can be obtained by applying a one-way cryptographic operation to the key for SVN X+1. In this manner, firmware with a given SVN can wield keys bound to its SVN or older, but cannot wield keys bound to newer SVNs.

To comply with FIPS, the one-way cryptographic operation used to compute keys is an SP 800-108 KDF.

When the key ladder is initialized at cold-boot, it is bound to the lifecycle state, debug-locked state, and the firmware's "epoch" from the image header. This ensures that across lifecycle or debug state transtions, or across intentional epoch changes, the keys of the ladder will change.

Across update-resets, ROM tracks the minimum SVN that has run since cold-boot. It ensures that the ladder's length always corresponds to that minimum SVN. The key ladder can only be shortened (and thereby give access to newer SVNs' keys) by cold-booting into firmware with a newer SVN and re-initializing the ladder.

#### Cold-boot

ROM initializes a key ladder for the firmware. LDevID CDI in Key Vault Slot6 is used as an HMAC Key, and the data is a fixed string. The resultant MAC is stored in Slot 2.

KeyLadderContext = lifecycle state || debug_locked state || firmware epoch

hmac512_kdf(KvSlot6, label: b"si_init", context: KeyLadderContext, KvSlot2)

Loop (MAX_FIRMWARE_SVN - (current firmware SVN)) times:

hmac512_kdf(KvSlot2, label: b"si_extend", context: None, KvSlot2)

#### Update-reset

During update-reset, the key ladder initialized at cold boot is lengthened if necessary, such that its length always corresponds with the minimum SVN since cold boot.

old_min_svn = [retrieved from data vault]
new_min_svn = min(old_min_svn, new_fw_svn)
[store new_min_svn in data vault]

Loop (`old_min_svn` - `new_min_svn`) times:

hmac512_kdf(KvSlot2, label: b"si_extend", context: None, KvSlot2)

### Alias FMC DICE layer & PCR extension

Alias FMC Layer includes the measurement of the FMC and other security states. This layer is used to assert a composite identity which includes the security state, FMC measurement along with the previous layer identities.
Expand Down Expand Up @@ -1085,6 +1121,8 @@ Compare the computed hash with the hash specified in the RT TOC.
- Validate the toc exactly like in cold boot.
- We still need to make sure that the digest of the FMC which was stored in the data vault register at cold boot
still matches the FMC image section.
- Store the minimum firmware SVN that has run since cold-boot in the data vault.
- Ratchet the key ladder if necessary.
- If validation fails during ROM boot, the new RT image will not be copied from
the mailbox. ROM will boot the existing FMC/Runtime images. Validation
errors will be reported via the CPTRA_FW_ERROR_NON_FATAL register.
Expand Down
52 changes: 43 additions & 9 deletions sw-emulator/lib/cpu/src/csr_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ impl Csr {
/// PMP configuration register range start, inclusive
pub const PMPCFG_START: RvAddr = 0x3A0;
/// PMP configuration register range end, inclusive
pub const PMPCFG_END: RvAddr = 0x3A3;
pub const PMPCFG_END: RvAddr = 0x3AF;
/// PMP address register range start, inclusive
pub const PMPADDR_START: RvAddr = 0x3B0;
/// PMP address register range end, inclusive
pub const PMPADDR_END: RvAddr = 0x3C0;
pub const PMPADDR_END: RvAddr = 0x3EF;
/// Number of PMP address/cfg registers
pub const PMPCOUNT: usize = 16;
pub const PMPCOUNT: usize = 64;

/// Create a new Configurations and Status register
///
Expand Down Expand Up @@ -669,8 +669,9 @@ impl CsrFile {
return Ok(false);
}

let addr = addr as u64;
let pmpaddr = self.any_read(RvPrivMode::M, Csr::PMPADDR_START + index as RvAddr)?;
let pmpaddr_shift = pmpaddr << 2;
let pmpaddr_shift = (pmpaddr as u64) << 2;
let addr_top;
let addr_bottom;

Expand All @@ -680,7 +681,9 @@ impl CsrFile {
// otherwise it's the previous one
addr_top = pmpaddr_shift;
addr_bottom = if index > 0 {
self.any_read(RvPrivMode::M, Csr::PMPADDR_START + (index - 1) as RvAddr)? << 2
(self.any_read(RvPrivMode::M, Csr::PMPADDR_START + (index - 1) as RvAddr)?
as u64)
<< 2
} else {
0
};
Expand All @@ -691,13 +694,12 @@ impl CsrFile {
addr_bottom = pmpaddr_shift;
}
RvPmpAddrMode::Napot => {
// Range from 8..32
addr_top = pmpaddr_shift + (1 << (pmpaddr.trailing_ones() + 3));
addr_bottom = pmpaddr_shift;
let (bot, top) = decode_napot_pmpaddr(pmpaddr);
addr_bottom = bot;
addr_top = top;
}
_ => unreachable!(),
}

Ok(addr >= addr_bottom && addr < addr_top)
}

Expand Down Expand Up @@ -748,11 +750,43 @@ impl CsrFile {
}
}

// Returns the base address (inclusive) and end address (exclusive) of a NAPOT PMP address
fn decode_napot_pmpaddr(addr: u32) -> (u64, u64) {
let bits = addr.trailing_ones();
let addr = addr as u64;
let base = (addr & !((1 << bits) - 1)) << 2;
(base, base + (1 << (bits + 3)))
}

#[cfg(test)]
mod tests {

use super::*;

#[test]
fn test_decode_napot_pmpaddr() {
assert_eq!((0x0000_0000, 0x0000_0008), decode_napot_pmpaddr(0x00000000));
assert_eq!((0x0000_0000, 0x0000_0010), decode_napot_pmpaddr(0x00000001));
assert_eq!((0x0040_0000, 0x0040_8000), decode_napot_pmpaddr(0x00100fff));
assert_eq!((0x1000_0000, 0x2000_0000), decode_napot_pmpaddr(0x05ffffff));
assert_eq!(
(0x0000_0000, 0x1_0000_0000),
decode_napot_pmpaddr(0x1fffffff)
);
assert_eq!(
(0x0000_0000, 0x2_0000_0000),
decode_napot_pmpaddr(0x3fffffff)
);
assert_eq!(
(0x0000_0000, 0x4_0000_0000),
decode_napot_pmpaddr(0x7fffffff)
);
assert_eq!(
(0x0000_0000, 0x8_0000_0000),
decode_napot_pmpaddr(0xffffffff)
);
}

#[test]
fn test_u_mode_read_m_mode_csr() {
let clock = Clock::new();
Expand Down

0 comments on commit ead9009

Please sign in to comment.