Module atsamd_hal::icm
source · [−]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 makingRegions
static.The same goes for
HashArea
, but here it is even more important to ensure the memory is designated forHashArea
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
:
RegionConfiguration::set_rhien()
tofalse
to allow interrupts when calculation is doneRegionConfiguration::set_eom()
totrue
only for the last region
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
:
RegionConfiguration::set_dmien()
tofalse
to allow interrupts if mismatch occursRegionConfiguration::set_cdwbn()
totrue
to change to monitor modeRegionConfiguration::set_wrap()
totrue
only for the last region if continuous monitoring is desired
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
bitmask
argument
narrow it down to the specific set of RegionNum
of interest.RegionNum
Enums
Traits
MainRegionDesc