1 /*
2  *     Copyright 2020-2023 NXP
3  *     All rights reserved.
4  *
5  *     SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_iped.h"
9 
10 /*******************************************************************************
11  * Definitions
12  ******************************************************************************/
13 /* Component ID definition, used by tools. */
14 #ifndef FSL_COMPONENT_ID
15 #define FSL_COMPONENT_ID "platform.drivers.iped"
16 #endif
17 /*******************************************************************************
18  * Prototypes
19  ******************************************************************************/
20 static status_t IPED_ELS_check_key(uint8_t keyIdx, mcuxClEls_KeyProp_t *pKeyProp);
21 static status_t IPED_ELS_gen_iv_key(void);
22 static status_t IPED_ELS_enable(void);
23 static status_t IPED_ELS_calculate_iv(uint32_t *IvReg);
24 /*******************************************************************************
25  * Code
26  ******************************************************************************/
IPED_SetRegionAddressRange(FLEXSPI_Type * base,iped_region_t region,uint32_t start_address,uint32_t end_address)27 status_t IPED_SetRegionAddressRange(FLEXSPI_Type *base,
28                                     iped_region_t region,
29                                     uint32_t start_address,
30                                     uint32_t end_address)
31 {
32     status_t status = kStatus_Fail;
33 
34     do
35     {
36         /* Check if region is not locked */
37         if ((base->IPEDCTXCTRL[1] & (FLEXSPI_IPEDCTXCTRLX_IPEDCTXCTRL_CTX0_FREEZE1_MASK << (region * 2u))) !=
38             (IPED_RW_ENABLE_VAL << (region * 2u)))
39         {
40             status = kStatus_IPED_RegionIsLocked;
41             break;
42         }
43 
44         /* Disable soft lock for given region first */
45         base->IPEDCTXCTRL[0] =
46             (base->IPEDCTXCTRL[0] & ~(FLEXSPI_IPEDCTXCTRLX_IPEDCTXCTRL_CTX0_FREEZE0_MASK << (region * 2u))) |
47             (IPED_RW_ENABLE_VAL << (region * 2u));
48 
49         switch (region)
50         {
51             case kIPED_Region0:
52                 base->IPEDCTX0START = start_address;
53                 base->IPEDCTX0END   = end_address;
54                 status              = kStatus_Success;
55                 break;
56 
57             case kIPED_Region1:
58                 base->IPEDCTX1START = start_address;
59                 base->IPEDCTX1END   = end_address;
60                 status              = kStatus_Success;
61                 break;
62 
63             case kIPED_Region2:
64                 base->IPEDCTX2START = start_address;
65                 base->IPEDCTX2END   = end_address;
66                 status              = kStatus_Success;
67                 break;
68 
69             case kIPED_Region3:
70                 base->IPEDCTX3START = start_address;
71                 base->IPEDCTX3END   = end_address;
72                 status              = kStatus_Success;
73                 break;
74 
75             default:
76                 status = kStatus_InvalidArgument;
77                 break;
78         }
79 
80         /* Re-enable soft lock for given region */
81         base->IPEDCTXCTRL[0] =
82             (base->IPEDCTXCTRL[0] & ~(FLEXSPI_IPEDCTXCTRLX_IPEDCTXCTRL_CTX0_FREEZE0_MASK << (region * 2u))) |
83             (IPED_RW_DISABLE_VAL << (region * 2u));
84     } while (0);
85 
86     return status;
87 }
88 
IPED_GetRegionAddressRange(FLEXSPI_Type * base,iped_region_t region,uint32_t * start_address,uint32_t * end_address)89 status_t IPED_GetRegionAddressRange(FLEXSPI_Type *base,
90                                     iped_region_t region,
91                                     uint32_t *start_address,
92                                     uint32_t *end_address)
93 {
94     status_t status = kStatus_Fail;
95 
96     switch (region)
97     {
98         case kIPED_Region0:
99             *start_address = base->IPEDCTX0START;
100             *end_address   = base->IPEDCTX0END;
101             status         = kStatus_Success;
102             break;
103 
104         case kIPED_Region1:
105             *start_address = base->IPEDCTX1START;
106             *end_address   = base->IPEDCTX1END;
107             status         = kStatus_Success;
108             break;
109 
110         case kIPED_Region2:
111             *start_address = base->IPEDCTX2START;
112             *end_address   = base->IPEDCTX2END;
113             status         = kStatus_Success;
114             break;
115 
116         case kIPED_Region3:
117             *start_address = base->IPEDCTX3START;
118             *end_address   = base->IPEDCTX3END;
119             status         = kStatus_Success;
120             break;
121 
122         default:
123             status = kStatus_InvalidArgument;
124             break;
125     }
126 
127     return status;
128 }
129 
IPED_SetRegionIV(FLEXSPI_Type * base,iped_region_t region,const uint8_t iv[8])130 status_t IPED_SetRegionIV(FLEXSPI_Type *base, iped_region_t region, const uint8_t iv[8])
131 {
132     status_t status = kStatus_Fail;
133 
134     switch (region)
135     {
136         case kIPED_Region0:
137             base->IPEDCTX0IV0 = ((uint32_t *)(uintptr_t)iv)[0];
138             base->IPEDCTX0IV1 = ((uint32_t *)(uintptr_t)iv)[1];
139             status            = kStatus_Success;
140             break;
141 
142         case kIPED_Region1:
143             base->IPEDCTX1IV0 = ((uint32_t *)(uintptr_t)iv)[0];
144             base->IPEDCTX1IV1 = ((uint32_t *)(uintptr_t)iv)[1];
145             status            = kStatus_Success;
146             break;
147 
148         case kIPED_Region2:
149             base->IPEDCTX2IV0 = ((uint32_t *)(uintptr_t)iv)[0];
150             base->IPEDCTX2IV1 = ((uint32_t *)(uintptr_t)iv)[1];
151             status            = kStatus_Success;
152             break;
153 
154         case kIPED_Region3:
155             base->IPEDCTX3IV0 = ((uint32_t *)(uintptr_t)iv)[0];
156             base->IPEDCTX3IV1 = ((uint32_t *)(uintptr_t)iv)[1];
157             status            = kStatus_Success;
158             break;
159 
160         default:
161             status = kStatus_InvalidArgument;
162             break;
163     }
164 
165     return status;
166 }
167 
168 /*!
169  * @brief Configures IPED setting.
170  *
171  * This function does the initial IPED configuration via ROM IAP API call.
172  * IPED_SR_x configuration for each region configuration is stored into FFR (CMPA).
173  * IPED IV erase counters (MCTR_INT_IV_CTRx) in CFPA are updated accordingly.
174  *
175  * Note: This function is expected to be called once in the device lifetime,
176  * typically during the initial device provisioning (especially if programming the CMPA pages in PFR flash is enabled).
177  * Note: Disabling IPED can be only done via ISP/SB file commands provided before ROM boot execution.
178  * these commands will have to set IPED start & end addresses and ENABLE bits in CMPA page accordingly.
179  *
180  * @param coreCtx The pointer to the ROM API driver context structure.
181  * @param config The pointer to the IPED driver configuration structure.
182  * @param lock The CMPA IPEDx_START bits[1:0] 01 - Enabled, 10,11 - Enabled & locked
183  * @param writeCmpa If selected, IPED configuration will be programmed in PFR flash using ROM API. Note: This can not be
184  * reverted!!
185  *
186  * @retval #kStatus_Success
187  * @retval #kStatus_CommandUnsupported
188  * @retval #kStatus_InvalidArgument
189  * @retval #kStatus_FLASH_ModifyProtectedAreaDisallowed
190  * @retval #kStatusMemoryRangeInvalid
191  * @retval #kStatus_Fail
192  * @retval #kStatus_OutOfRange
193  * @retval #kStatus_SPI_BaudrateNotSupport
194  */
IPED_Configure(api_core_context_t * coreCtx,flexspi_iped_region_arg_t * config,iped_lock_t lock,iped_cmpa_t writeCmpa)195 status_t IPED_Configure(api_core_context_t *coreCtx,
196                         flexspi_iped_region_arg_t *config,
197                         iped_lock_t lock,
198                         iped_cmpa_t writeCmpa)
199 {
200     status_t status = kStatus_Fail;
201     flash_config_t flash_config;
202     IPED_CMPA_page cmpa_buffer = {0};
203 
204     /* Enable ELS and check keys */
205     if (kStatus_Success != IPED_ELS_enable())
206     {
207         return kStatus_Fail;
208     }
209 
210     /* Check input argument */
211     if (((lock != kIPED_RegionLock) && (lock != kIPED_RegionUnlock)) ||
212         ((writeCmpa != kIPED_SkipCMPA) && (writeCmpa != kIPED_WriteCMPA)))
213     {
214         return kStatus_InvalidArgument;
215     }
216 
217     /* Prepare IPED configuration structure */
218     uint32_t config_option_iped[3] = {(IPED_TAG << IPED_TAG_SHIFT) | config->option.iped_region, config->start,
219                                       config->end};
220 
221     /* Configure IPED via ROM API, ROM API will provide config parameter check */
222     status = MEM_Config(coreCtx, config_option_iped, kMemoryFlexSpiNor);
223     if (status != kStatus_Success)
224     {
225         return kStatus_Fail;
226     }
227 
228     if (writeCmpa == kIPED_WriteCMPA)
229     {
230         /* Clean up Flash driver structure and Init*/
231         memset(&flash_config, 0, sizeof(flash_config_t));
232         if (FLASH_Init(&flash_config) != kStatus_Success)
233         {
234             return kStatus_Fail;
235         }
236 
237         /* FFR Init */
238         if (FFR_Init(&flash_config) != kStatus_Success)
239         {
240             return kStatus_Fail;
241         }
242 
243         /* Read whole CMPA page */
244         status = FFR_GetCustomerData(&flash_config, (uint8_t *)&cmpa_buffer, 0u, CMPA_PAGE_SIZE);
245         if (kStatus_Success != status)
246         {
247             return kStatus_Fail;
248         }
249 
250         switch (config->option.iped_region)
251         {
252             case kIPED_Region0:
253                 cmpa_buffer.IPED0_START = (config->start & IPED_ADDRESS_MASK) | (lock & IPED_ENABLE_MASK);
254                 cmpa_buffer.IPED0_END   = config->end;
255                 break;
256             case kIPED_Region1:
257                 cmpa_buffer.IPED1_START = (config->start & IPED_ADDRESS_MASK) | (lock & IPED_ENABLE_MASK);
258                 cmpa_buffer.IPED1_END   = config->end;
259                 break;
260             case kIPED_Region2:
261                 cmpa_buffer.IPED2_START = (config->start & IPED_ADDRESS_MASK) | (lock & IPED_ENABLE_MASK);
262                 cmpa_buffer.IPED2_END   = config->end;
263                 break;
264             case kIPED_Region3:
265                 cmpa_buffer.IPED3_START = (config->start & IPED_ADDRESS_MASK) | (lock & IPED_ENABLE_MASK);
266                 cmpa_buffer.IPED3_END   = config->end;
267                 break;
268             default:
269                 return kStatus_InvalidArgument;
270         }
271         /* Write new CMPA page into FFR */
272         status = FFR_CustFactoryPageWrite(&flash_config, (uint8_t *)&cmpa_buffer, false /* do not seal PFR memory */);
273     }
274 
275     /* Lock the IPED IP setting if desired */
276     if (lock == kIPED_RegionLock)
277     {
278         IPED_SetLock(FLEXSPI0, config->option.iped_region);
279     }
280 
281     return status;
282 }
283 
284 /*!
285  * @brief Configures IPED setting.
286  *
287  * This function is used to re-configure IPED IP based on configuration stored in FFR.
288  * This function also needs to be called after wake up from power-down mode to regenerate IV
289  * encryption key in ELS key store whose presence is necessary for correct IPED operation
290  * during erase and write operations to encrypted regions of internal flash memory
291  * (dependency for correct operation of MEM_Erase() and MEM_Write() after wake up from power-down mode).
292  *
293  * @param coreCtx The pointer to the ROM API driver context structure.
294  * @param config The pointer to the IPED driver configuration structure. If NULL CMPA configuration is used instead.
295  * Note: when providing config structure, you have to call Reconfigure for each IPED region individually starting with
296  * Region 0. Region 0 must be enabled as a base region.
297  *
298  * @retval #kStatus_Success
299  * @retval #kStatus_Fail
300  */
IPED_Reconfigure(api_core_context_t * coreCtx,flexspi_iped_region_arg_t * config)301 status_t IPED_Reconfigure(api_core_context_t *coreCtx, flexspi_iped_region_arg_t *config)
302 {
303     status_t status   = kStatus_Fail;
304     uint32_t IvReg[4] = {0};
305     uint32_t ivEraseCounter[4];
306     uint32_t ipedConfig[8];
307     uint32_t ipedStart[4];
308     uint32_t ipedEnd[4];
309     uint8_t lockEnable[4];
310     uint32_t uuid[4];
311     flash_config_t flash_config;
312 
313     /* Enable ELS and check keys */
314     status = IPED_ELS_enable();
315     if (kStatus_Success != status)
316     {
317         return kStatus_Fail;
318     }
319 
320     /* Clean up Flash driver structure and Init*/
321     memset(&flash_config, 0, sizeof(flash_config_t));
322     if (FLASH_Init(&flash_config) != kStatus_Success)
323     {
324         return kStatus_Fail;
325     }
326 
327     /* FFR Init */
328     if (FFR_Init(&flash_config) != kStatus_Success)
329     {
330         return kStatus_Fail;
331     }
332 
333     /* Get UUID from FFR */
334     status = FFR_GetUUID(&flash_config, (uint8_t *)uuid);
335     if (kStatus_Success != status)
336     {
337         return kStatus_Fail;
338     }
339 
340     /* Check version of CFPA scratch first */
341     uint32_t cfpaScratchVer = 0u;
342     memcpy(&cfpaScratchVer, (void *)(CFPA_SCRATCH_VER), sizeof(uint32_t));
343 
344     /* Get CFPA version using FFR ROM API */
345     uint32_t cfpaVer = 0u;
346     if (kStatus_Success !=
347         FFR_GetCustomerInfieldData(&flash_config, (uint8_t *)&cfpaVer, CFPA_VER_OFFSET, sizeof(uint32_t)))
348     {
349         status = kStatus_Fail;
350         return status;
351     }
352 
353     /* Compare the version of CFPA scratch and version of CFPA returned by ROM API */
354     if (cfpaScratchVer > cfpaVer)
355     {
356         /* Get IPED_IV_CTRs from CFPA scratch */
357         memcpy(&ivEraseCounter, (void *)CFPA_SCRATCH_IV, sizeof(uint32_t) * IPED_REGION_COUNT);
358     }
359     else
360     {
361         /* Get IPED_IV_CTRs IVs from CFPA ping/pong page */
362         status = FFR_GetCustomerInfieldData(&flash_config, (uint8_t *)ivEraseCounter, CFPA_IPED_IV_OFFSET,
363                                             sizeof(uint32_t) * IPED_REGION_COUNT);
364         if (kStatus_Success != status)
365         {
366             return kStatus_Fail;
367         }
368     }
369 
370     if (config == NULL) /* Read configuration from CMPA */
371     {
372         /* Get IPED start address, end address and lock setting from FFR CMPA */
373         status = FFR_GetCustomerData(
374             &flash_config, (uint8_t *)ipedConfig, CMPA_IPED_START_OFFSET,
375             sizeof(uint32_t) * IPED_REGION_COUNT * 2u); // multiply by 2 because we are reading end and start address
376         if (kStatus_Success != status)
377         {
378             return kStatus_Fail;
379         }
380 
381         /* Prepare Lock and Enable values from FFR configuration into array */
382         lockEnable[0] = (ipedConfig[0] & IPED_START_ADDR_LOCK_EN_MASK);
383         lockEnable[1] = (ipedConfig[2] & IPED_START_ADDR_LOCK_EN_MASK);
384         lockEnable[2] = (ipedConfig[4] & IPED_START_ADDR_LOCK_EN_MASK);
385         lockEnable[3] = (ipedConfig[5] & IPED_START_ADDR_LOCK_EN_MASK);
386         /* Prepare Start address values from FFR configuration into array */
387         ipedStart[0] = (ipedConfig[0] & IPED_START_ADDR_MASK);
388         ipedStart[1] = (ipedConfig[2] & IPED_START_ADDR_MASK);
389         ipedStart[2] = (ipedConfig[4] & IPED_START_ADDR_MASK);
390         ipedStart[3] = (ipedConfig[6] & IPED_START_ADDR_MASK);
391         /* Prepare End address values from FFR configuration into array */
392         ipedEnd[0] = (ipedConfig[1] & IPED_END_ADDR_MASK);
393         ipedEnd[1] = (ipedConfig[3] & IPED_END_ADDR_MASK);
394         ipedEnd[2] = (ipedConfig[5] & IPED_END_ADDR_MASK);
395         ipedEnd[3] = (ipedConfig[7] & IPED_END_ADDR_MASK);
396 
397         /* Always use 12 rounds */
398         IPED_SetPrinceRounds(FLEXSPI0, kIPED_PrinceRounds12);
399 
400         /* Iterate for all internal IPED regions */
401         for (iped_region_t region = kIPED_Region0; region <= kIPED_Region3; region++)
402         {
403             iped_region_t region = kIPED_Region0;
404             /* If not enabled, skip to other region */
405             if (lockEnable[region] == 0u)
406             {
407                 continue;
408             }
409 
410             /* Write start & end addresses to IPED registers */
411             status = IPED_SetRegionAddressRange(FLEXSPI0, (iped_region_t)region, ipedStart[region], ipedEnd[region]);
412             if (status != kStatus_Success)
413             {
414                 break;
415             }
416 
417             /* Prepare ivSeed for current region */
418             IvReg[0] = uuid[0];
419             IvReg[1] = uuid[1];
420             IvReg[2] = uuid[2] ^ region;
421             IvReg[3] = ivEraseCounter[region];
422 
423             /* Calculate IV as IvReg = AES_ECB_ENC(DUK_derived_key, {ctx_erase_counter, ctx_id}) */
424             status = IPED_ELS_calculate_iv(IvReg);
425             if (status != kStatus_Success)
426             {
427                 return kStatus_Fail;
428             }
429 
430             /* Load IV into IPED registers */
431             status = IPED_SetRegionIV(FLEXSPI0, (iped_region_t)region, (uint8_t *)IvReg);
432             if (status != kStatus_Success)
433             {
434                 return kStatus_Fail;
435             }
436 
437             /* Lock region if required */
438             if ((lockEnable[region] == 0x2u) || (lockEnable[region] == 0x3u))
439             {
440                 IPED_SetLock(FLEXSPI0, region);
441             }
442         }
443     }
444     else /* Use provided config structure */
445     {
446         /* Write start & end addresses to IPED registers */
447         status =
448             IPED_SetRegionAddressRange(FLEXSPI0, (iped_region_t)config->option.iped_region, config->start, config->end);
449 
450         /* Prepare ivSeed for current region */
451         IvReg[0] = uuid[0];
452         IvReg[1] = uuid[1];
453         IvReg[2] = uuid[2] ^ (iped_region_t)config->option.iped_region;
454         IvReg[3] = ivEraseCounter[(iped_region_t)config->option.iped_region];
455 
456         /* Calculate IV as IvReg = AES_ECB_ENC(DUK_derived_key, {ctx_erase_counter, ctx_id}) */
457         status = IPED_ELS_calculate_iv(IvReg);
458         if (status != kStatus_Success)
459         {
460             return kStatus_Fail;
461         }
462 
463         /* Load IV into IPED registers */
464         status = IPED_SetRegionIV(FLEXSPI0, (iped_region_t)config->option.iped_region, (uint8_t *)IvReg);
465         if (status != kStatus_Success)
466         {
467             return kStatus_Fail;
468         }
469     }
470 
471     /* Check if error occured during IPED configuration */
472     if (status != kStatus_Success)
473     {
474         return kStatus_Fail;
475     }
476 
477     return status;
478 }
479 
IPED_ELS_check_key(uint8_t keyIdx,mcuxClEls_KeyProp_t * pKeyProp)480 static status_t IPED_ELS_check_key(uint8_t keyIdx, mcuxClEls_KeyProp_t *pKeyProp)
481 {
482     /* Check if ELS required keys are available in ELS keystore */
483     MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token,
484                                      mcuxClEls_GetKeyProperties(keyIdx, pKeyProp)); // Get key propertis from the ELS.
485     // mcuxClEls_GetKeyProperties is a flow-protected function: Check the protection token and the return value
486     if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_GetKeyProperties) != token) || (MCUXCLELS_STATUS_OK != result))
487         return kStatus_Fail;
488     MCUX_CSSL_FP_FUNCTION_CALL_END();
489 
490     return kStatus_Success;
491 }
492 
IPED_ELS_gen_iv_key(void)493 static status_t IPED_ELS_gen_iv_key(void)
494 {
495     /* The NXP_DIE_MEM_IV_ENC_SK is not loaded and needs to be regenerated (power-down wakeup) */
496     /* Set KDF mask and key properties for NXP_DIE_MEM_IV_ENC_SK */
497     SYSCON->ELS_KDF_MASK            = SYSCON_ELS_KDF_MASK;
498     static const uint32_t ddata2[3] = {0x62032504, 0x72f04280, 0x87a2bbae};
499     mcuxClEls_KeyProp_t keyProp;
500     /* Set key properties in structure */
501     keyProp.word.value = MCUXCLELS_KEYPROPERTY_VALUE_AES | MCUXCLELS_KEYPROPERTY_VALUE_GENERAL_PURPOSE_SLOT |
502                          MCUXCLELS_KEYPROPERTY_VALUE_ACTIVE;
503     status_t status = kStatus_Fail;
504 
505     /* Generate the key using CKDF */
506     MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(
507         result, token,
508         mcuxClEls_Ckdf_Sp800108_Async((mcuxClEls_KeyIndex_t)0, (mcuxClEls_KeyIndex_t)NXP_DIE_MEM_IV_ENC_SK, keyProp,
509                                       (uint8_t const *)ddata2));
510     if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_Ckdf_Sp800108_Async) != token) && (MCUXCLELS_STATUS_OK != result))
511     {
512         return kStatus_Fail;
513     }
514     MCUX_CSSL_FP_FUNCTION_CALL_END();
515 
516     /* Wait for CKDF to finish */
517     MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(result, token, mcuxClEls_WaitForOperation(MCUXCLELS_ERROR_FLAGS_CLEAR));
518     if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_WaitForOperation) == token) && (MCUXCLELS_STATUS_OK == result))
519     {
520         status = kStatus_Success;
521     }
522     MCUX_CSSL_FP_FUNCTION_CALL_END();
523 
524     return status;
525 }
526 
IPED_ELS_enable(void)527 static status_t IPED_ELS_enable(void)
528 {
529     mcuxClEls_KeyProp_t key_properties;
530     status_t status = kStatus_Fail;
531 
532     /* Enable ELS and related clocks */
533     status = ELS_PowerDownWakeupInit(ELS);
534     if (status != kStatus_Success)
535     {
536         return kStatus_Fail;
537     }
538 
539     /* Check if MEM_ENC_SK key is available in ELS keystore */
540     status = IPED_ELS_check_key(NXP_DIE_EXT_MEM_ENC_SK, &key_properties);
541     if (status != kStatus_Success || key_properties.bits.kactv != 1u)
542     {
543         return kStatus_Fail;
544     }
545 
546     /* Check if MEM_IV_ENC_SK key is available in ELS keystore */
547     status = IPED_ELS_check_key(NXP_DIE_MEM_IV_ENC_SK, &key_properties);
548     if (status != kStatus_Success || key_properties.bits.kactv != 1u)
549     {
550         return IPED_ELS_gen_iv_key();
551     }
552 
553     return kStatus_Success;
554 }
555 
IPED_ELS_calculate_iv(uint32_t * IvReg)556 static status_t IPED_ELS_calculate_iv(uint32_t *IvReg)
557 {
558     mcuxClEls_CipherOption_t cipherOptions = {0};
559     status_t status                        = kStatus_Fail;
560 
561     /* Configure ELS for AES ECB-128, using NXP_DIE_MEM_IV_ENC_SK key */
562     cipherOptions.bits.cphmde = MCUXCLELS_CIPHERPARAM_ALGORITHM_AES_ECB;
563     cipherOptions.bits.dcrpt  = MCUXCLELS_CIPHER_ENCRYPT;
564     cipherOptions.bits.extkey = MCUXCLELS_CIPHER_INTERNAL_KEY;
565 
566     do
567     {
568         /* Calculate IV as IvReg = AES_ECB_ENC(NXP_DIE_MEM_IV_ENC_SK, ivSeed[127:0]) */
569         /* ivSeed[127:0] = {UUID[96:0] ^ regionNumber[1:0], ivEraseCounter[31:0]} */
570         MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(
571             result, token,
572             mcuxClEls_Cipher_Async(cipherOptions, (mcuxClEls_KeyIndex_t)NXP_DIE_MEM_IV_ENC_SK, NULL,
573                                    MCUXCLELS_CIPHER_KEY_SIZE_AES_128, (uint8_t *)IvReg, MCUXCLELS_CIPHER_BLOCK_SIZE_AES,
574                                    NULL, (uint8_t *)IvReg));
575         if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_Cipher_Async) != token) || (MCUXCLELS_STATUS_OK_WAIT != result))
576             break;
577         MCUX_CSSL_FP_FUNCTION_CALL_END();
578 
579         MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(
580             result, token,
581             mcuxClEls_WaitForOperation(
582                 MCUXCLELS_ERROR_FLAGS_CLEAR)); // Wait for the mcuxClEls_Enable_Async operation to complete.
583         // mcuxClEls_WaitForOperation is a flow-protected function: Check the protection token and the return value
584         if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_WaitForOperation) == token) && (MCUXCLELS_STATUS_OK == result))
585         {
586             status = kStatus_Success;
587         }
588         MCUX_CSSL_FP_FUNCTION_CALL_END();
589     } while (0);
590 
591     return status;
592 }
593