Hello,
I am currently developing TF-M on the RDV3R1 S/W platform, and I would like to report a potential issue observed during runtime with the integrity_checker_compute_value() function in BL2.
Problem Description: Inside the integrity_checker_compute_value() function, the iccva register is set to the address of either the value or temp_val variable, as shown in the example below.
enum integrity_checker_error_t integrity_checker_compute_value(struct integrity_checker_dev_t *dev, enum integrity_checker_mode_t mode, const uint32_t *data, size_t size, uint32_t *value, size_t value_size, size_t *value_len) { volatile uint32_t __ALIGNED(INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) temp_val[INTEGRITY_CHECKER_OUTPUT_SIZE_SHA256 / sizeof(uint32_t)] = {0}; volatile uint32_t *value_ptr = value; struct _integrity_checker_reg_map_t* p_integrity_checker = (struct _integrity_checker_reg_map_t*)dev->cfg->base; enum integrity_checker_error_t err;
uint32_t iccval = 0;
err = check_mode_is_supported(dev, mode, true); if (err != INTEGRITY_CHECKER_ERROR_NONE) { return err; }
if (value_size < mode_sizes[mode]) { FATAL_ERR(INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_BUFFER_TOO_SMALL); return INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_BUFFER_TOO_SMALL; }
if (((uintptr_t)data % INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) != 0) { FATAL_ERR(INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_INVALID_ALIGNMENT); return INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_INVALID_ALIGNMENT; }
if ((size % INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) != 0) { FATAL_ERR(INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_INVALID_LENGTH); return INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_INVALID_LENGTH; }
if (((uintptr_t)value % INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) != 0) { value_ptr = temp_val; }
init_integrity_checker(dev, &iccval);
/* Set algorithm */ iccval |= (mode & 0b111) << 1;
/* Set to compute mode */ iccval |= 1 << 4;
/* Configure input data. Size is in words */ p_integrity_checker->icda = remap_addr(dev, (uintptr_t)data); p_integrity_checker->icdl = size / INTEGRITY_CHECKER_REQUIRED_ALIGNMENT;
/* Set output address */ p_integrity_checker->iccva = remap_addr(dev, (uintptr_t)value_ptr);
/* Start integrity checker */ iccval |= 1;
p_integrity_checker->icc = iccval;
/* Poll for any interrupts */ while(!p_integrity_checker->icis) {}
/* Check for any unusual error interrupts */ if (p_integrity_checker->icis & (~0b11)) { FATAL_ERR(INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_OPERATION_FAILED); return INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_OPERATION_FAILED; }
if (value_ptr != value) { for (int idx = 0; idx < mode_sizes[mode] / sizeof(uint32_t); idx++) { value[idx] = value_ptr[idx]; } }
if (value_len != NULL) { *value_len = mode_sizes[mode]; }
return INTEGRITY_CHECKER_ERROR_NONE; }
static void init_integrity_checker(struct integrity_checker_dev_t *dev, uint32_t *iccval) { struct _integrity_checker_reg_map_t* p_integrity_checker = (struct _integrity_checker_reg_map_t*)dev->cfg->base;
/* Set MatchTriggerDisable, as it is mandatory. */ *iccval |= 1 << 5;
/* Set EncompvalOut so the integrity checker writes the value pointer in * compute mode. */ *iccval |= 1 << 6;
*iccval |= (IC_APROT_SECURE_PRIVILEGED & 0b11) << 7; *iccval |= (IC_APROT_SECURE_PRIVILEGED & 0b11) << 9;
/* Disable all alarms */ p_integrity_checker->icae = 0;
/* Enable and clear all interrupts */ p_integrity_checker->icie = 0xFF; p_integrity_checker->icic = 0xFF; }
The issue arises because the memory areas pointed to by value and temp_val are cacheable (e.g., located in the vm0 memory region, which is configured as cacheable).
As a result, even though the integrity checker writes the output data to the physical address of value or temp_val, the CPU cache is not updated, and this leads to stale data being read later in software, resulting in runtime errors.
Expected Behavior: Ideally, the memory region used for integrity checker output should either be:
Non-cacheable, or
Explicitly synchronized (e.g., cache invalidated or cleaned) after the integrity checker operation.
Could you please confirm whether this is a known issue, and advise on the recommended way to handle integrity checker outputs with respect to cache settings?
Thank you.
JK Park
Hey JK
Yes you're right - we have this issue with every bus primary in the system. We had a fix for this for the cryptocell DMA which involved flushing the cache before and after the DMA operation, but missed it for the IC.
I've ported it to the IC as well: https://review.trustedfirmware.org/c/TF-M/trusted-firmware-m/+/40762
Would you mind testing this patch and then letting me know if it fixes your issue? Either via email or a vote on the patch
Thanks for the report!
Raef
________________________________________ From: 박진국 via TF-M tf-m@lists.trustedfirmware.org Sent: 15 July 2025 05:18 To: tf-m@lists.trustedfirmware.org Subject: [TF-M] Issue with integrity_checker_compute_value() and cache coherence on RDV3R1 with TF-M
Hello,
I am currently developing TF-M on the RDV3R1 S/W platform, and I would like to report a potential issue observed during runtime with the integrity_checker_compute_value() function in BL2.
Problem Description: Inside the integrity_checker_compute_value() function, the iccva register is set to the address of either the value or temp_val variable, as shown in the example below.
enum integrity_checker_error_t integrity_checker_compute_value(struct integrity_checker_dev_t *dev, enum integrity_checker_mode_t mode, const uint32_t *data, size_t size, uint32_t *value, size_t value_size, size_t *value_len) { volatile uint32_t __ALIGNED(INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) temp_val[INTEGRITY_CHECKER_OUTPUT_SIZE_SHA256 / sizeof(uint32_t)] = {0}; volatile uint32_t *value_ptr = value; struct _integrity_checker_reg_map_t* p_integrity_checker = (struct _integrity_checker_reg_map_t*)dev->cfg->base; enum integrity_checker_error_t err;
uint32_t iccval = 0;
err = check_mode_is_supported(dev, mode, true); if (err != INTEGRITY_CHECKER_ERROR_NONE) { return err; }
if (value_size < mode_sizes[mode]) { FATAL_ERR(INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_BUFFER_TOO_SMALL); return INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_BUFFER_TOO_SMALL; }
if (((uintptr_t)data % INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) != 0) { FATAL_ERR(INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_INVALID_ALIGNMENT); return INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_INVALID_ALIGNMENT; }
if ((size % INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) != 0) { FATAL_ERR(INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_INVALID_LENGTH); return INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_INVALID_LENGTH; }
if (((uintptr_t)value % INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) != 0) { value_ptr = temp_val; }
init_integrity_checker(dev, &iccval);
/* Set algorithm */ iccval |= (mode & 0b111) << 1;
/* Set to compute mode */ iccval |= 1 << 4;
/* Configure input data. Size is in words */ p_integrity_checker->icda = remap_addr(dev, (uintptr_t)data); p_integrity_checker->icdl = size / INTEGRITY_CHECKER_REQUIRED_ALIGNMENT;
/* Set output address */ p_integrity_checker->iccva = remap_addr(dev, (uintptr_t)value_ptr);
/* Start integrity checker */ iccval |= 1;
p_integrity_checker->icc = iccval;
/* Poll for any interrupts */ while(!p_integrity_checker->icis) {}
/* Check for any unusual error interrupts */ if (p_integrity_checker->icis & (~0b11)) { FATAL_ERR(INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_OPERATION_FAILED); return INTEGRITY_CHECKER_ERROR_COMPUTE_VALUE_OPERATION_FAILED; }
if (value_ptr != value) { for (int idx = 0; idx < mode_sizes[mode] / sizeof(uint32_t); idx++) { value[idx] = value_ptr[idx]; } }
if (value_len != NULL) { *value_len = mode_sizes[mode]; }
return INTEGRITY_CHECKER_ERROR_NONE; }
static void init_integrity_checker(struct integrity_checker_dev_t *dev, uint32_t *iccval) { struct _integrity_checker_reg_map_t* p_integrity_checker = (struct _integrity_checker_reg_map_t*)dev->cfg->base;
/* Set MatchTriggerDisable, as it is mandatory. */ *iccval |= 1 << 5;
/* Set EncompvalOut so the integrity checker writes the value pointer in * compute mode. */ *iccval |= 1 << 6;
*iccval |= (IC_APROT_SECURE_PRIVILEGED & 0b11) << 7; *iccval |= (IC_APROT_SECURE_PRIVILEGED & 0b11) << 9;
/* Disable all alarms */ p_integrity_checker->icae = 0;
/* Enable and clear all interrupts */ p_integrity_checker->icie = 0xFF; p_integrity_checker->icic = 0xFF; }
The issue arises because the memory areas pointed to by value and temp_val are cacheable (e.g., located in the vm0 memory region, which is configured as cacheable).
As a result, even though the integrity checker writes the output data to the physical address of value or temp_val, the CPU cache is not updated, and this leads to stale data being read later in software, resulting in runtime errors.
Expected Behavior: Ideally, the memory region used for integrity checker output should either be:
Non-cacheable, or
Explicitly synchronized (e.g., cache invalidated or cleaned) after the integrity checker operation.
Could you please confirm whether this is a known issue, and advise on the recommended way to handle integrity checker outputs with respect to cache settings?
Thank you.
JK Park
tf-m@lists.trustedfirmware.org