Repository: chipsalliance/caliptra-sw
Pull request #498 (2023-07-17 20:29:42)

Implement PAUSER priv levels
Add the concept of a PL0 PAUSER to the image format and expose this information to RT firmware.

Additionally, add scaffolding for a status mailbox comment to report back firmware information. For now it only reports the PL0 PAUSER.

jhand2: > Will there need to be a hw-model update to provide the right PAUSER eventually?

Ya. I'll do this in a follow-up PR

Patch:
diff --git a/error/src/lib.rs b/error/src/lib.rs
index 2d80ed53..92e38d90 100644
--- a/error/src/lib.rs
+++ b/error/src/lib.rs
@@ -275,6 +275,7 @@ impl CaliptraError {
     pub const RUNTIME_UNEXPECTED_UPDATE_RETURN: CaliptraError =
         CaliptraError::new_const(0x000E0007);
     pub const RUNTIME_SHUTDOWN: CaliptraError = CaliptraError::new_const(0x000E0008);
+    pub const RUNTIME_NO_MANIFEST: CaliptraError = CaliptraError::new_const(0x000E0009);
 
     /// FMC Errors
     pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001);
diff --git a/image/fake-keys/src/lib.rs b/image/fake-keys/src/lib.rs
index b4e1617d..a426bf2f 100644
--- a/image/fake-keys/src/lib.rs
+++ b/image/fake-keys/src/lib.rs
@@ -338,6 +338,7 @@ pub const VENDOR_CONFIG_KEY_0: ImageGeneratorVendorConfig = ImageGeneratorVendor
     priv_keys: Some(VENDOR_PRIVATE_KEYS),
     not_before: [0u8; 15],
     not_after: [0u8; 15],
+    pl0_pauser: None,
 };
 
 pub const VENDOR_CONFIG_KEY_1: ImageGeneratorVendorConfig = ImageGeneratorVendorConfig {
diff --git a/image/gen/src/generator.rs b/image/gen/src/generator.rs
index 6d4abf37..f6123097 100644
--- a/image/gen/src/generator.rs
+++ b/image/gen/src/generator.rs
@@ -25,6 +25,7 @@ pub struct ImageGenerator<Crypto: ImageGeneratorCrypto> {
 
 impl<Crypto: ImageGeneratorCrypto> ImageGenerator<Crypto> {
     const DEFAULT_FLAGS: u32 = 0;
+    const PL0_PAUSER_FLAG: u32 = (1 << 0);
 
     /// Create an instance `ImageGenerator`
     pub fn new(crypto: Crypto) -> Self {
@@ -76,13 +77,7 @@ impl<Crypto: ImageGeneratorCrypto> ImageGenerator<Crypto> {
 
         // Create Header
         let toc_digest = self.toc_digest(&fmc_toc, &runtime_toc)?;
-        let header = self.gen_header(
-            config,
-            ecc_key_idx,
-            lms_key_idx,
-            Self::DEFAULT_FLAGS,
-            toc_digest,
-        )?;
+        let header = self.gen_header(config, ecc_key_idx, lms_key_idx, toc_digest)?;
 
         // Create Preamable
         let header_digest_vendor = self.header_digest_vendor(&header)?;
@@ -181,7 +176,6 @@ impl<Crypto: ImageGeneratorCrypto> ImageGenerator<Crypto> {
         config: &ImageGeneratorConfig<E>,
         ecc_key_idx: u32,
         lms_key_idx: u32,
-        flags: u32,
         digest: ImageDigest,
     ) -> anyhow::Result<ImageHeader>
     where
@@ -190,7 +184,7 @@ impl<Crypto: ImageGeneratorCrypto> ImageGenerator<Crypto> {
         let mut header = ImageHeader {
             vendor_ecc_pub_key_idx: ecc_key_idx,
             vendor_lms_pub_key_idx: lms_key_idx,
-            flags,
+            flags: Self::DEFAULT_FLAGS,
             toc_len: MAX_TOC_ENTRY_COUNT,
             toc_digest: digest,
             ..Default::default()
@@ -199,6 +193,11 @@ impl<Crypto: ImageGeneratorCrypto> ImageGenerator<Crypto> {
         header.vendor_data.vendor_not_before = config.vendor_config.not_before;
         header.vendor_data.vendor_not_after = config.vendor_config.not_after;
 
+        if let Some(pauser) = config.vendor_config.pl0_pauser {
+            header.flags |= Self::PL0_PAUSER_FLAG;
+            header.pl0_pauser = pauser;
+        }
+
         if let Some(owner_config) = &config.owner_config {
             header.owner_data.owner_not_before = owner_config.not_before;
             header.owner_data.owner_not_after = owner_config.not_after;
diff --git a/image/gen/src/lib.rs b/image/gen/src/lib.rs
index d58db9b3..2b23e8b3 100644
--- a/image/gen/src/lib.rs
+++ b/image/gen/src/lib.rs
@@ -77,6 +77,8 @@ pub struct ImageGeneratorVendorConfig {
     pub not_before: [u8; 15],
 
     pub not_after: [u8; 15],
+
+    pub pl0_pauser: Option<u32>,
 }
 
 /// Image Generator Owner Configuration
diff --git a/image/types/src/lib.rs b/image/types/src/lib.rs
index 8f3fa149..4e836006 100644
--- a/image/types/src/lib.rs
+++ b/image/types/src/lib.rs
@@ -307,11 +307,16 @@ pub struct ImageHeader {
     pub vendor_lms_pub_key_idx: u32,
 
     /// Flags
+    /// Bit 0: Interpret the pl0_pauser field. If not set, all PAUSERs are PL1.
     pub flags: u32,
 
     /// TOC Entry Count
     pub toc_len: u32,
 
+    /// The PAUSER with PL0 privileges. The SoC integration must choose
+    /// only one PAUSER to be PL0.
+    pub pl0_pauser: u32,
+
     /// TOC Digest
     pub toc_digest: ImageDigest,
 
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index cf44e5fc..69f3364c 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -11,6 +11,7 @@ caliptra-drivers = { path = "../drivers" }
 caliptra-registers = { path = "../registers" }
 caliptra_common = { path = "../common", default-features = false }
 caliptra-x509 = { path = "../x509", default-features = false }
+caliptra-image-types = { path = "../image/types", default-features = false }
 ufmt = "0.2.0"
 zerocopy = "0.6.1"
 
@@ -27,7 +28,6 @@ caliptra-image-fake-keys = { path = "../image/fake-keys" }
 caliptra-image-gen = { path = "../image/gen" }
 caliptra-image-openssl = { path = "../image/openssl" }
 caliptra-image-serde = { path = "../image/serde" }
-caliptra-image-types = { path = "../image/types" }
 openssl = { version = "0.10", features = ["vendored"] }
 
 [features]
diff --git a/runtime/src/info.rs b/runtime/src/info.rs
new file mode 100644
index 00000000..8bc624eb
--- /dev/null
+++ b/runtime/src/info.rs
@@ -0,0 +1,30 @@
+// Licensed under the Apache-2.0 license
+
+use crate::{CommandId, Drivers};
+use caliptra_common::checksum::calc_checksum;
+use core::mem::size_of;
+use zerocopy::{AsBytes, FromBytes};
+
+pub struct FwInfoCmd;
+
+impl FwInfoCmd {
+    pub(crate) fn execute(drivers: &Drivers) -> FwInfoResp {
+        let mut resp = FwInfoResp {
+            chksum: 0,
+            pl0_pauser: drivers.manifest.header.pl0_pauser,
+        };
+
+        let bytes = &resp.as_bytes()[size_of::<i32>()..];
+        resp.chksum = calc_checksum(u32::from(CommandId::FW_INFO), bytes);
+        resp
+    }
+}
+
+#[repr(C)]
+#[derive(AsBytes, FromBytes)]
+pub struct FwInfoResp {
+    pub chksum: i32,
+    pub pl0_pauser: u32,
+    // TODO: Decide what other information to report for general firmware
+    // status.
+}
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index c5e4fe72..a7db5cb3 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -3,6 +3,7 @@
 #![no_std]
 
 pub mod dice;
+pub mod info;
 mod update;
 mod verify;
 
@@ -11,9 +12,9 @@ pub(crate) mod fips;
 pub mod mailbox;
 
 use mailbox::Mailbox;
-
 pub mod packet;
 pub use fips::{FipsModule, VersionResponse};
+use info::FwInfoCmd;
 use packet::Packet;
 
 use caliptra_common::memory_layout::{
@@ -23,6 +24,7 @@ use caliptra_common::memory_layout::{
 };
 use caliptra_common::{cprintln, FirmwareHandoffTable};
 use caliptra_drivers::{CaliptraError, CaliptraResult, DataVault, Ecc384};
+use caliptra_image_types::ImageManifest;
 use caliptra_registers::{
     dv::DvReg,
     ecc::EccReg,
@@ -59,6 +61,7 @@ impl CommandId {
     pub const ECDSA384_VERIFY: Self = Self(0x53494756); // "SIGV"
     pub const STASH_MEASUREMENT: Self = Self(0x4D454153); // "MEAS"
     pub const INVOKE_DPE: Self = Self(0x44504543); // "DPEC"
+    pub const FW_INFO: Self = Self(0x494E464F); // "INFO"
 
     pub const TEST_ONLY_GET_LDEV_CERT: Self = Self(0x4345524c); // "CERL"
     pub const TEST_ONLY_GET_FMC_ALIAS_CERT: Self = Self(0x43455246); // "CERF"
@@ -90,6 +93,9 @@ pub struct Drivers<'a> {
     pub soc_ifc: SocIfcReg,
     pub regions: MemoryRegions,
     pub fht: &'a mut FirmwareHandoffTable,
+
+    /// A copy of the ImageHeader for the currently running image
+    pub manifest: ImageManifest,
 }
 impl<'a> Drivers<'a> {
     /// # Safety
@@ -97,8 +103,15 @@ impl<'a> Drivers<'a> {
     /// Callers must ensure that this function is called only once, and that
     /// any concurrent access to these register blocks does not conflict with
     /// these drivers.
-    pub unsafe fn new_from_registers(fht: &'a mut FirmwareHandoffTable) -> Self {
-        Self {
+    pub unsafe fn new_from_registers(fht: &'a mut FirmwareHandoffTable) -> CaliptraResult<Self> {
+        let manifest_slice = unsafe {
+            let ptr = MAN1_ORG as *mut u32;
+            core::slice::from_raw_parts_mut(ptr, core::mem::size_of::<ImageManifest>() / 4)
+        };
+        let manifest = ImageManifest::read_from(manifest_slice.as_bytes())
+            .ok_or(CaliptraError::RUNTIME_NO_MANIFEST)?;
+
+        Ok(Self {
             mbox: Mailbox::new(MboxCsr::new()),
             sha_acc: Sha512AccCsr::new(),
             ecdsa: Ecc384::new(EccReg::new()),
@@ -106,7 +119,8 @@ impl<'a> Drivers<'a> {
             soc_ifc: SocIfcReg::new(),
             regions: MemoryRegions::new(),
             fht,
-        }
+            manifest,
+        })
     }
 }
 #[repr(C)]
@@ -170,6 +184,12 @@ fn handle_command(drivers: &mut Drivers) -> CaliptraResult<MboxStatusE> {
         }
         CommandId::STASH_MEASUREMENT => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND),
         CommandId::INVOKE_DPE => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND),
+        CommandId::FW_INFO => {
+            let resp = FwInfoCmd::execute(drivers);
+            drivers.mbox.write_response(resp.as_bytes())?;
+            Ok(MboxStatusE::DataReady)
+        }
+
         #[cfg(feature = "test_only_commands")]
         CommandId::TEST_ONLY_GET_LDEV_CERT => {
             let mut cert = [0u8; 1024];
@@ -198,6 +218,12 @@ fn handle_command(drivers: &mut Drivers) -> CaliptraResult<MboxStatusE> {
 }
 
 pub fn handle_mailbox_commands(drivers: &mut Drivers) {
+    // Indicator to SOC that RT firmware is ready
+    drivers
+        .soc_ifc
+        .regs_mut()
+        .cptra_flow_status()
+        .write(|w| w.ready_for_runtime(true));
     caliptra_drivers::report_boot_status(RtBootStatus::RtReadyForCommands.into());
     loop {
         wait_for_cmd(&mut drivers.mbox);
diff --git a/runtime/src/main.rs b/runtime/src/main.rs
index ec3e62dc..e8cc9244 100644
--- a/runtime/src/main.rs
+++ b/runtime/src/main.rs
@@ -36,14 +36,15 @@ const BANNER: &str = r#"
 pub extern "C" fn entry_point() -> ! {
     cprintln!("{}", BANNER);
     if let Some(mut fht) = caliptra_common::FirmwareHandoffTable::try_load() {
-        let mut drivers = unsafe { Drivers::new_from_registers(&mut fht) };
-
-        // Indicator to SOC that RT firmware is ready
-        drivers
-            .soc_ifc
-            .regs_mut()
-            .cptra_flow_status()
-            .write(|w| w.ready_for_runtime(true));
+        let mut drivers = match unsafe { Drivers::new_from_registers(&mut fht) } {
+            Ok(drivers) => drivers,
+            Err(e) => {
+                caliptra_common::report_handoff_error_and_halt(
+                    "Runtime can't load create drivers",
+                    e.into(),
+                );
+            }
+        };
 
         cprintln!("Caliptra RT listening for mailbox commands...");
         caliptra_runtime::handle_mailbox_commands(&mut drivers);


Test patch:
diff --git a/runtime/test-fw/src/cert_tests.rs b/runtime/test-fw/src/cert_tests.rs
index 1854d093..0dde7a92 100644
--- a/runtime/test-fw/src/cert_tests.rs
+++ b/runtime/test-fw/src/cert_tests.rs
@@ -12,7 +12,7 @@ use zerocopy::AsBytes;
 
 fn mbox_responder() {
     let mut fht = caliptra_common::FirmwareHandoffTable::try_load().unwrap();
-    let drivers = unsafe { Drivers::new_from_registers(&mut fht) };
+    let drivers = unsafe { Drivers::new_from_registers(&mut fht).unwrap() };
     let mut mbox = drivers.mbox;
 
     loop {
diff --git a/runtime/test-fw/src/mbox_tests.rs b/runtime/test-fw/src/mbox_tests.rs
index 71fd5372..4d1549eb 100644
--- a/runtime/test-fw/src/mbox_tests.rs
+++ b/runtime/test-fw/src/mbox_tests.rs
@@ -21,7 +21,7 @@ use caliptra_test_harness::test_suite;
 
 fn test_mbox_cmd() {
     let mut fht = caliptra_common::FirmwareHandoffTable::default();
-    let mut drivers = unsafe { Drivers::new_from_registers(&mut fht) };
+    let mut drivers = unsafe { Drivers::new_from_registers(&mut fht).unwrap() };
     let mut soc_ifc = unsafe { SocIfcReg::new() };
 
     // Unlock the sha_acc peripheral for use by the SoC
diff --git a/runtime/tests/common.rs b/runtime/tests/common.rs
index 9076ddf2..e2cc9737 100644
--- a/runtime/tests/common.rs
+++ b/runtime/tests/common.rs
@@ -42,12 +42,11 @@ pub fn run_rt_test(test_bin_name: Option<&'static str>) -> DefaultHwModel {
 
     let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_UART).unwrap();
 
-    let image = caliptra_builder::build_and_sign_image(
-        &FMC_WITH_UART,
-        &runtime_fwid,
-        ImageOptions::default(),
-    )
-    .unwrap();
+    let mut image_options = ImageOptions::default();
+    image_options.vendor_config.pl0_pauser = Some(0xFFFF0000);
+    let image =
+        caliptra_builder::build_and_sign_image(&FMC_WITH_UART, &runtime_fwid, image_options)
+            .unwrap();
 
     let mut model = caliptra_hw_model::new(BootParams {
         init_params: InitParams {
diff --git a/runtime/tests/integration_tests.rs b/runtime/tests/integration_tests.rs
index 5abb3465..f0ce6356 100644
--- a/runtime/tests/integration_tests.rs
+++ b/runtime/tests/integration_tests.rs
@@ -4,7 +4,7 @@ pub mod common;
 use caliptra_builder::{ImageOptions, APP_WITH_UART, FMC_WITH_UART};
 use caliptra_drivers::Ecc384PubKey;
 use caliptra_hw_model::{HwModel, ModelError, ShaAccMode};
-use caliptra_runtime::{CommandId, EcdsaVerifyCmd, VersionResponse};
+use caliptra_runtime::{info::FwInfoResp, CommandId, EcdsaVerifyCmd, VersionResponse};
 use common::{run_rom_test, run_rt_test};
 use openssl::{
     bn::BigNum,
@@ -96,6 +96,19 @@ fn test_rom_certs() {
         .unwrap());
 }
 
+#[test]
+fn test_fw_info() {
+    let mut model = run_rt_test(None);
+
+    let resp = model
+        .mailbox_execute(u32::from(CommandId::FW_INFO), &[])
+        .unwrap()
+        .unwrap();
+
+    let info = FwInfoResp::read_from(resp.as_slice()).unwrap();
+    assert_eq!(info.pl0_pauser, 0xFFFF0000);
+}
+
 #[test]
 fn test_verify_cmd() {
     let mut model = run_rom_test("mbox");
diff --git a/test-harness/scripts/rom.ld b/test-harness/scripts/rom.ld
index fb6316ef..820a5998 100644
--- a/test-harness/scripts/rom.ld
+++ b/test-harness/scripts/rom.ld
@@ -23,6 +23,7 @@ MEMORY
   DCCM_CTL (rwx) : ORIGIN = 0x7FF8, LENGTH = 8
   ICCM (rwx) : ORIGIN = 0x40000000, LENGTH = 128K
   DCCM (rwx) : ORIGIN = 0x50000000, LENGTH = 128K
+  DATA (rw) : ORIGIN  = 0x50004800, LENGTH = 94K
 }
 
 STACK_SIZE = 0x8000;
@@ -47,7 +48,7 @@ SECTIONS
         . = ALIGN(4);
         PROVIDE( __global_pointer$ = . + 0x800 );
         . = ALIGN(4);
-    } > DCCM
+    } > DATA
 
     .iccm.ctl : { LONG(0x40000000); LONG(0x40020000) } > ICCM_CTL    
 
@@ -60,7 +61,7 @@ SECTIONS
         *(.sbss*)
         *(COMMON)
         . = ALIGN(4);
-    } > DCCM
+    } > DATA
 
     .stack (NOLOAD):
     {
@@ -68,7 +69,7 @@ SECTIONS
         . = . + STACK_SIZE;
         . = ALIGN(4);
         PROVIDE(STACK_START = . );
-    } > DCCM
+    } > DATA
 
     _end = . ;
 }