Expand description

ICM - Integrity Check Module

Used to calculate SHA digests of memory regions

Multiple modes available

  • Manual monitor of Internal SRAM (both contiguous and non-contiguous memory)
  • Active monitoring of Internal SRAM (both contiguous and non-contiguous memory)
  • Manual monitor of Internal Flash (both contiguous memory)
  • Generates Hash using SHA engine, useful for verifying content
  • ICM module has additional register protection and tamper detection

Reading the Interrupt Status Register (ISR) clears the register, to provide a workaround for cases where multiple bits needs parsing, the Icm::get_interrupt_status() and Region<I>::get_interrupt_status() are provided. These return a queryable structure containing the interrupt register contents. Allowing multiple different interrupts to be read.

IMPORTANT - Memory safety considerations

The ICM engine accesses the assigned DSCR memory address, so it must be available. Depending on the application, this might entail making Regions static.

The same goes for HashArea, but here it is even more important to ensure the memory is designated for HashArea usage, since the ICM controller will, depending on ICM configuration, write data to that address.

Setting HashArea static might be the safest path.

Another alternative is to utilise the singleton macro provided by cortex_m::singleton

use cortex_m::singleton;

let hasharea: &'static mut HashArea =
singleton!(: HashArea = HashArea::default()).unwrap();

Usage:

General ICM setup

Initialise the ICM engine Icm::new() and reset ICM via Icm::swrst()

Change any of the global options such as Icm::set_eomdis(), if required.

Enable and create the interface for required memory regions Icm::enable_region0() and enable it via Region::enable_monitoring()

Depending on the number of regions required, the helper Regions::default() alows setting up all 4 regions directly, if one region is sufficient, manually create [MainRegionDesc<Region0>:: default()].

Modify the MainRegionDesc, see documentation and cargo doc for all methods.

Set the DSCR address to the beginning of the MainRegionDesc via Icm::set_dscr_addr() (or via helper in MainRegionDesc<Region0>::set_dscr_addr())

Via Region, setup the desired interrupts depending on usecase.

To view which interrupts has been enabled in the debugger, check the ICM->IMR register.

Any object in memory can be used as the “Hash” area, but for convenience the provided HashArea allows indexing of the 4 regions and is correctly memory aligned.

Set the pointer to HashArea via Icm::set_hash_addr()

See note about memory safety above

Hash calculation

Assuming general setup is already done, modify the RegionConfiguration which is part of the MainRegionDesc:

Change RegionAddress to point to the object to SHA-sum with MainRegionDesc<RegionNumT>::set_region_address()

Memory monitoring

HashArea needs to contain the expected SHA-sums of the data to monitor, Icm::set_ascd() is provided to help with creating this data. Alternatively do it manually and then change mode, or prepopulate the HashArea with SHA-sums.

Assuming general setup is already done, modify the RegionConfiguration which is part of the MainRegionDesc:

Examples

Calculate SHA1, SHA224 and SHA256 sums, then switch to memory monitor

4 memory regions, SHA1 in region0 and region1. Region2 uses SHA224 and region3 SHA256.

This only covers the setup part, to achieve the functionality of first computing the SHA-sums and then do region monitoring handling of interrupts and changing mode is required.


// SHA Test data
static MESSAGE_REF0: [u32; 16] = [
    0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, 0x66666666, 0x77777777, 0x88888888,
    0x99999999, 0xaaaaaaaa, 0xbbbbbbbb, 0xcccccccc, 0xdddddddd, 0xeeeeeeee, 0xffffffff, 0x00000000,
];

static MESSAGE_REF1: [u32; 16] = [
    0x80636261, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x18000000,
];

// Expected SHA1 sum result
static MESSAGE_SHA1_RES: [u32; 8] = [
    0x363e99a9, 0x6a810647, 0x71253eba, 0x6cc25078, 0x9dd8d09c, 0x00000000, 0x00000000, 0x00000000,
];

static MESSAGE_SHA224_RES: [u32; 8] = [
    0x227d0923, 0x22d80534, 0x77a44286, 0xb355a2bd, 0xe4bcad2a, 0xf7b3a0bd, 0xa79d6ce3, 0x00000000,
];
static MESSAGE_SHA256_RES: [u32; 8] = [
    0xbf1678ba, 0xeacf018f, 0xde404141, 0x2322ae5d, 0xa36103b0, 0x9c7a1796, 0x61ff10b4, 0xad1500f2,
];
static mut HASH: HashArea = HashArea::default();
static mut ICM_REGION_DESC: Regions = Regions::default();

// Alternatively
//use cortex_m::singleton;
//let hasharea: &'static mut HashArea = singleton!(: HashArea = HashArea::default()).unwrap();

// Enable ICM apb clock
// Clock v1
//mclk.apbcmask.modify(|_, w| w.icm_().set_bit());
// Clock v2
//tokens.apbs.icm.enable();

let mut peripherals = Peripherals::take().unwrap();

// Create new ICM
let mut icm = Icm::new(peripherals.ICM);

// Reset the ICM, clearing past error states
icm.swrst();

// End of Monitoring is permitted
icm.set_eomdis(false);
// Write Back is permitted
icm.set_wbdis(false);
// Secondary List branching is forbidden
icm.set_slbdis(false);
// Automatic Switch to Compare is disabled
icm.set_ascd(false);

// Region Descriptor
let mut icm_region_desc = Regions::default();

// Get the interface for Region0 and enable monitoring
let icm_region0 = icm.enable_region0();
icm_region0.enable_monitoring();

// Setup desired interrupts
//
// Region Hash Completed
icm_region0.set_rhc_int();

// Region0 raddr
icm_region_desc.region0.set_region_address(MESSAGE_REF0.as_ptr());

// Configure the RCFG

// Some are default values, just as an example

// Activate Write back (should be true when comparing memory)
icm_region_desc.region0.rcfg.set_cdwbn(false);
// Should the ICM controller loop back to DSCR after this region?
icm_region_desc.region0.rcfg.set_wrap(false);
// Set this as the end of descriptor linked list
icm_region_desc.region0.rcfg.set_eom(false);
// The RHC flag is set when the field NEXT = 0
// in a descriptor of the main or second list
icm_region_desc.region0.rcfg.set_rhien(false);
// Set Algorithm to SHA1
icm_region_desc.region0.rcfg.set_algo(icm_algorithm::SHA1);

// Get the interface for region1
let icm_region1 = icm.enable_region1();

// Enable region monitoring
icm_region1.enable_monitoring();

// Setup desired interrupts
//
// Region Hash Completed
icm_region1.set_rhc_int();

// Region1 raddr
icm_region_desc.region1.set_region_address(MESSAGE_REF1.as_ptr());

// Configure the RCFG
// The RHC flag is set when the field NEXT = 0
// in a descriptor of the main or second list
icm_region_desc.region1.rcfg.set_rhien(false);
// Set Algorithm to SHA1
icm_region_desc.region1.rcfg.set_algo(icm_algorithm::SHA1);

// Get the interface for region2
let icm_region2 = icm.enable_region2();

// Enable region monitoring
icm_region2.enable_monitoring();

// Setup desired interrupts
//
// Region Hash Completed
icm_region2.set_rhc_int();

// Region2 raddr
icm_region_desc.region2.set_region_address(MESSAGE_REF1.as_ptr());

// Configure the RCFG
// The RHC flag is set when the field NEXT = 0
// in a descriptor of the main or second list
icm_region_desc.region2.rcfg.set_rhien(false);
// Set Algorithm to SHA224
icm_region_desc.region2.rcfg.set_algo(icm_algorithm::SHA224);

// Get the interface for region3
let icm_region3 = icm.enable_region3();

// Enable region monitoring
icm_region3.enable_monitoring();

// Setup desired interrupts
//
// Region Hash Completed
icm_region3.set_rhc_int();

// Region3 raddr
icm_region_desc.region3.set_region_address(MESSAGE_REF1.as_ptr());

// Configure the RCFG
//
// Set this as the end of descriptor linked list
icm_region_desc.region3.rcfg.set_eom(true);
// The RHC flag is set when the field NEXT = 0
// in a descriptor of the main or second list
icm_region_desc.region3.rcfg.set_rhien(false);
// Set Algorithm to SHA256
icm_region_desc.region3.rcfg.set_algo(icm_algorithm::SHA256);

unsafe {
    // Hash Area
    // Set HASH addr to the beginning of the Hash area
    icm.set_hash_addr(&HASH);
}

unsafe {
    // Move the icm_region_desc into static
    ICM_REGION_DESC = icm_region_desc;
    // Set DSCR to the beginning of the region descriptor
    icm.set_dscr_addr(&ICM_REGION_DESC.region0);
    // the same but via helper function
    //ICM_REGION_DESC.region0.set_dscr_addr(&icm);
}

// Start the ICM calculation
icm.enable();

// Setup memory region monitoring
// Monitor all 4 memory regions

// Setup the compare regions
let mut message_region0_sha1 = MESSAGE_REF0;
let mut message_region1_sha1 = MESSAGE_REF1;
let mut message_region2_sha224 = MESSAGE_REF1;
let mut message_region3_sha256 = MESSAGE_REF1;

// Reset the ICM, clearing past error states
icm.swrst();

// End of Monitoring is permitted
icm.set_eomdis(false);
// Write Back is permitted
icm.set_wbdis(false);
// Secondary List branching is forbidden
icm.set_slbdis(false);
// Automatic Switch to Compare is disabled
icm.set_ascd(false);

// Also possible to directly edit `ICM_REGION_DESC`
// in an unsafe block
let mut icm_region_desc = Regions::default();

// Setup region 0 to monitor memory
icm_region_desc
    .region0
    .set_region_address(&message_region0_sha1);
icm_region_desc.region0.rcfg.reset_region_configuration_to_default();
icm_region_desc.region0.rcfg.set_algo(icm_algorithm::SHA1);
// Activate Compare Digest (should be true when comparing memory)
icm_region_desc.region0.rcfg.set_cdwbn(true);
// Digest Mismatch Interrupt Disable (enabled)
icm_region_desc.region0.rcfg.set_dmien(false);

// Set Region Mismatch Interrupt
icm_region0.set_rdm_int();

// Setup region 1 to monitor memory
icm_region_desc
    .region1
    .set_region_address(&message_region1_sha1);
icm_region_desc.region1.rcfg.reset_region_configuration_to_default();
icm_region_desc.region1.rcfg.set_algo(icm_algorithm::SHA1);
// Activate Compare Digest (should be true when comparing memory)
icm_region_desc.region1.rcfg.set_cdwbn(true);
// Digest Mismatch Interrupt Disable (enabled)
icm_region_desc.region1.rcfg.set_dmien(false);

// Set Region Mismatch Interrupt
icm_region1.set_rdm_int();

// Setup region 2 to monitor memory
icm_region_desc
    .region2
    .set_region_address(&message_region2_sha224);
icm_region_desc.region2.rcfg.reset_region_configuration_to_default();
icm_region_desc.region2.rcfg.set_algo(icm_algorithm::SHA224);
// Activate Compare Digest (should be true when comparing memory)
icm_region_desc.region2.rcfg.set_cdwbn(true);
// Digest Mismatch Interrupt Disable (enabled)
icm_region_desc.region2.rcfg.set_dmien(false);

// Set Region Mismatch Interrupt
icm_region2.set_rdm_int();

// Setup region 3 to monitor memory
icm_region_desc
    .region3
    .set_region_address(&message_region3_sha256);
icm_region_desc.region3.rcfg.reset_region_configuration_to_default();
icm_region_desc.region3.rcfg.set_algo(icm_algorithm::SHA256);
// Activate Compare Digest (should be true when comparing memory)
icm_region_desc.region3.rcfg.set_cdwbn(true);
// Digest Mismatch Interrupt Disable (enabled)
icm_region_desc.region3.rcfg.set_dmien(false);
// Wrap
icm_region_desc.region3.rcfg.set_wrap(true);

// Set Region Mismatch Interrupt
icm_region3.set_rdm_int();

// Modify regions to trigger interrupts
message_region0_sha1[3] = 0xDEAD_BEEF;
message_region1_sha1[4] = 0xDEAD_BEEF;
message_region2_sha224[5] = 0xDEAD_BEEF;
message_region3_sha256[6] = 0xDEAD_BEEF;

icm.enable()

Structs

ICM Hash Area
ICM Peripheral
Struct useful for returning the interrupt status of the ICM. Provides methods for easy parsing of all the regions or via the bitmask argument narrow it down to the specific set of RegionNum of interest.
Structure ICM Region Descriptor area.
Region provides access to region-specific settings like interrupts and status
Region Start Address Structure
Region Bus Error interrupt
Region Configuration Structure
Region Control Structure
Region Digest Mismatch interrupt
Region End Condition detected interrupt
Region Hash Completed interrupt
Struct useful for returning the interrupt status of the ICM. Provides methods for easy parsing of the region specific RegionNum
Region Next Address Structure
Region Status Update detected interrupt
Region Wrap Condition detected interrupt
Helper for creating the Region Descriptor structure
Structure ICM Secondary Region Descriptor area.

Enums

Processing Delay
ICM Region 0
ICM Region 1
ICM Region 2
ICM Region 3
Reexport the User SHA Algorithm User SHA Algorithm

Traits

Functions required by MainRegionDesc
Trait providing numerical identifier and offset for each ICM Region