1 //*****************************************************************************
2 //
3 //! @file am_hal_security.c
4 //!
5 //! @brief Functions for on-chip security features
6 //!
7 //! @addtogroup security Security - On-Chip Security Functionality
8 //! @ingroup apollo3_hal
9 //! @{
10 //
11 //*****************************************************************************
12 
13 //*****************************************************************************
14 //
15 // Copyright (c) 2024, Ambiq Micro, Inc.
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are met:
20 //
21 // 1. Redistributions of source code must retain the above copyright notice,
22 // this list of conditions and the following disclaimer.
23 //
24 // 2. Redistributions in binary form must reproduce the above copyright
25 // notice, this list of conditions and the following disclaimer in the
26 // documentation and/or other materials provided with the distribution.
27 //
28 // 3. Neither the name of the copyright holder nor the names of its
29 // contributors may be used to endorse or promote products derived from this
30 // software without specific prior written permission.
31 //
32 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
33 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
36 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 // POSSIBILITY OF SUCH DAMAGE.
43 //
44 // This is part of revision release_sdk_3_2_0-dd5f40c14b of the AmbiqSuite Development Package.
45 //
46 //*****************************************************************************
47 #include <stdint.h>
48 #include <stdbool.h>
49 #include "am_mcu_apollo.h"
50 
51 //*****************************************************************************
52 //  Local defines.
53 //*****************************************************************************
54 //
55 //! ENABLE_EXTMEM_CRC
56 //! By default, the CRC engine can only operate on data located in internal
57 //! memory (i.e. flash or SRAM). This define enables am_hal_crc() to support
58 //! external memories, but requires a small amount of global SRAM allocated for
59 //! that purpose. If it is not desired to support this feature, set to 0.
60 //
61 #define ENABLE_EXTMEM_CRC   1
62 
63 //
64 //! Maximum iterations for hardware CRC to finish
65 //
66 #define MAX_CRC_WAIT        100000
67 
68 #define AM_HAL_SECURITY_LOCKSTAT_CUSTOMER       0x1
69 #define AM_HAL_SECURITY_LOCKSTAT_RECOVERY       0x40000000
70 
71 //*****************************************************************************
72 //
73 // Globals
74 //
75 //*****************************************************************************
76 #if ENABLE_EXTMEM_CRC
77 //
78 //! Set up a small global buffer that can be used am_hal_crc32() when
79 //! computing CRCs on external memory.
80 //
81 #define CRC_XFERBUF_SZ      (512)       // Reserve 512 bytes for the buffer
82 static uint32_t g_CRC_buffer[CRC_XFERBUF_SZ / 4];
83 #endif // ENABLE_EXTMEM_CRC
84 
85 //
86 // Assign ptr variables to avoid an issue with GCC reading from location 0x0.
87 //
88 const volatile uint32_t *g_pFlash0 = (uint32_t*)(AM_HAL_SBL_ADDRESS + 0);
89 const volatile uint32_t *g_pFlash4 = (uint32_t*)(AM_HAL_SBL_ADDRESS + 4);
90 
91 //*****************************************************************************
92 //
93 //! @brief  Hardcoded function - to Run supplied main program
94 //!
95 //! @param  r0 = vtor - address of the vector table
96 //!
97 //! @return Returns None
98 //
99 //*****************************************************************************
100 #if (defined (__ARMCC_VERSION)) && (__ARMCC_VERSION <  6000000)
101 static __asm void
bl_run_main(uint32_t * vtor)102 bl_run_main(uint32_t *vtor)
103 {
104     //
105     // Store the vector table pointer of the new image into VTOR.
106     //
107     movw    r3, #0xED08
108     movt    r3, #0xE000
109     str     r0, [r3, #0]
110 
111     //
112     // Load the new stack pointer into R1 and the new reset vector into R2.
113     //
114     ldr     r3, [r0, #0]
115     ldr     r2, [r0, #4]
116 
117     //
118     // Set the stack pointer for the new image.
119     //
120     mov     sp, r3
121 
122     //
123     // Jump to the new reset vector.
124     //
125     bx      r2
126 }
127 #elif (defined (__ARMCC_VERSION)) && (__ARMCC_VERSION >= 6000000)
128 __attribute__((naked))
129 static void
bl_run_main(uint32_t * vtor)130 bl_run_main(uint32_t *vtor)
131 {
132     __asm
133     (
134         "   movw    r3, #0xED08\n\t"    // Store the vector table pointer of the new image into VTOR.
135         "   movt    r3, #0xE000\n\t"
136         "   str     r0, [r3, #0]\n\t"
137         "   ldr     r3, [r0, #0]\n\t"   // Load the new stack pointer into R1 and the new reset vector into R2.
138         "   ldr     r2, [r0, #4]\n\t"
139         "   mov     sp, r3\n\t"         // Set the stack pointer for the new image.
140         "   bx      r2\n\t"            // Jump to the new reset vector.
141     );
142 }
143 #elif defined(__GNUC_STDC_INLINE__)
144 __attribute__((naked))
145 static void
bl_run_main(uint32_t * vtor)146 bl_run_main(uint32_t *vtor)
147 {
148     __asm
149     (
150         "   movw    r3, #0xED08\n\t"    // Store the vector table pointer of the new image into VTOR.
151         "   movt    r3, #0xE000\n\t"
152         "   str     r0, [r3, #0]\n\t"
153         "   ldr     r3, [r0, #0]\n\t"   // Load the new stack pointer into R1 and the new reset vector into R2.
154         "   ldr     r2, [r0, #4]\n\t"
155         "   mov     sp, r3\n\t"         // Set the stack pointer for the new image.
156         "   bx      r2\n\t"            // Jump to the new reset vector.
157     );
158 }
159 #elif defined(__IAR_SYSTEMS_ICC__)
160 __stackless static inline void
bl_run_main(uint32_t * vtor)161 bl_run_main(uint32_t *vtor)
162 {
163     __asm volatile (
164           "    movw    r3, #0xED08\n"    // Store the vector table pointer of the new image into VTOR.
165           "    movt    r3, #0xE000\n"
166           "    str     r0, [r3, #0]\n"
167           "    ldr     r3, [r0, #0]\n"   // Load the new stack pointer into R1 and the new reset vector into R2.
168           "    ldr     r2, [r0, #4]\n"
169           "    mov     sp, r3\n"         // Set the stack pointer for the new image.
170           "    bx      r2\n"            // Jump to the new reset vector.
171           );
172 }
173 #else
174 #error Compiler is unknown, please contact Ambiq support team
175 #endif
176 
177 //
178 // Pre- SBLv2 known versions that do not support callback
179 //
180 static uint32_t sblPreV2[][4] =
181 {
182     //
183     // flash0, flash4, sblVersion, sblVersionAddInfo
184     //
185     {0xA3007860, 0x2E2638FB, 0 , 0},
186     {0xA3007E14, 0x5EE4E461, 1 , 0},
187     {0xA3008290, 0xB49CECD5, 2 , 0},
188 };
189 
190 //*****************************************************************************
191 //
192 // @brief  Get Device Security Info
193 //
194 // @param  pSecInfo -  Pointer to structure for returned security info
195 //
196 // This will retrieve the security information for the device
197 //
198 // @return Returns AM_HAL_STATUS_SUCCESS on success
199 //
200 //*****************************************************************************
201 uint32_t
am_hal_security_get_info(am_hal_security_info_t * pSecInfo)202 am_hal_security_get_info(am_hal_security_info_t *pSecInfo)
203 {
204     if ( !pSecInfo )
205     {
206         return AM_HAL_STATUS_INVALID_ARG;
207     }
208 
209     pSecInfo->info0Version = AM_REGVAL(0x50020040);
210     pSecInfo->bInfo0Valid = MCUCTRL->SHADOWVALID_b.INFO0_VALID;
211 
212     if ( MCUCTRL->BOOTLOADER_b.SECBOOTFEATURE )
213     {
214         uint32_t ux, flash0, flash4;
215 
216         //
217         // Check if we're running pre-SBLv2
218         //
219         flash0 = *g_pFlash0;
220         flash4 = *g_pFlash4;
221 
222         //
223         // Check if SBL is installed
224         //
225         if ( (flash0 >> 24) != AM_IMAGE_MAGIC_SBL )
226         {
227             return AM_HAL_STATUS_FAIL;
228         }
229 
230         for ( ux = 0; ux < sizeof(sblPreV2) / sizeof(sblPreV2[0]); ux++ )
231         {
232             if ((sblPreV2[ux][0] == flash0) && (sblPreV2[ux][1] == flash4))
233             {
234                 // This is a device prior to SBLv2
235                 pSecInfo->sblVersion = sblPreV2[ux][2];
236                 pSecInfo->sblVersionAddInfo = sblPreV2[ux][3];
237                 break;
238             }
239         }
240 
241         if ( ux == (sizeof(sblPreV2) / sizeof(sblPreV2[0])) )
242         {
243             //
244             // SBLv2 or beyond
245             // Use SBL jump table function
246             //
247             uint32_t status;
248             uint32_t sblVersion;
249             uint32_t (*pFuncVersion)(uint32_t *) = (uint32_t (*)(uint32_t *))(AM_HAL_SBL_ADDRESS + 0x1D1);
250             status = pFuncVersion(&sblVersion);
251 
252             if (status != AM_HAL_STATUS_SUCCESS)
253             {
254                 return status;
255             }
256 
257             pSecInfo->sblVersion = sblVersion & 0x7FFF;
258             pSecInfo->sblVersionAddInfo = sblVersion >> 15;
259         }
260     }
261     else
262     {
263         return AM_HAL_STATUS_FAIL;
264     }
265 
266     return AM_HAL_STATUS_SUCCESS;
267 
268 } // am_hal_security_get_info()
269 
270 //*****************************************************************************
271 //
272 // @brief  Set the key for specified lock
273 //
274 // @param  lockType - The lock type to be operated upon
275 // @param  pKey -  Pointer to 128b key value
276 //
277 // This will program the lock registers for the specified lock and key
278 //
279 // @return Returns AM_HAL_STATUS_SUCCESS on success
280 //
281 //*****************************************************************************
282 uint32_t
am_hal_security_set_key(am_hal_security_locktype_t lockType,am_hal_security_128bkey_t * pKey)283 am_hal_security_set_key(am_hal_security_locktype_t lockType, am_hal_security_128bkey_t *pKey)
284 {
285 #ifndef AM_HAL_DISABLE_API_VALIDATION
286     if (pKey == NULL)
287     {
288         return AM_HAL_STATUS_INVALID_ARG;
289     }
290     switch (lockType)
291     {
292         case AM_HAL_SECURITY_LOCKTYPE_CUSTOMER:
293         case AM_HAL_SECURITY_LOCKTYPE_RECOVERY:
294             break;
295         default:
296             return AM_HAL_STATUS_INVALID_ARG;
297     }
298 #endif // AM_HAL_DISABLE_API_VALIDATION
299 
300 #if defined(__GNUC_STDC_INLINE__)
301 //
302 // The GCC compiler flags the following accesses to key1, key2, and key3 as
303 // "may be used uninitialized in this function". Online comments suggest that
304 // this may be a compiler bug because how would it possibly know that they're
305 // uninitialized? Ignore this warning with this ugly workaround.
306 //
307 #pragma GCC diagnostic push
308 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
309 #endif
310     SECURITY->LOCKCTRL = lockType;
311     SECURITY->KEY0 = pKey->keys.key0;
312     SECURITY->KEY1 = pKey->keys.key1;
313     SECURITY->KEY2 = pKey->keys.key2;
314     SECURITY->KEY3 = pKey->keys.key3;
315 #if defined(__GNUC_STDC_INLINE__)
316 #pragma GCC diagnostic pop
317 #endif
318 
319     return AM_HAL_STATUS_SUCCESS;
320 } // am_hal_security_set_key()
321 
322 //*****************************************************************************
323 //
324 // @brief  Get the current status of the specified lock
325 //
326 // @param  lockType - The lock type to be operated upon
327 // @param  pbUnlockStatus -  Pointer to return variable with lock status
328 //
329 // This will get the lock status for specified lock - true implies unlocked
330 // Note that except for customer lock, other locks are self-locking on status read
331 //
332 // @return Returns AM_HAL_STATUS_SUCCESS on success
333 //
334 //*****************************************************************************
335 uint32_t
am_hal_security_get_lock_status(am_hal_security_locktype_t lockType,bool * pbUnlockStatus)336 am_hal_security_get_lock_status(am_hal_security_locktype_t lockType, bool *pbUnlockStatus)
337 {
338     uint32_t unlockMask;
339 #ifndef AM_HAL_DISABLE_API_VALIDATION
340     if (pbUnlockStatus == NULL)
341     {
342         return AM_HAL_STATUS_INVALID_ARG;
343     }
344 #endif // AM_HAL_DISABLE_API_VALIDATION
345     switch (lockType)
346     {
347         case AM_HAL_SECURITY_LOCKTYPE_CUSTOMER:
348             unlockMask = AM_HAL_SECURITY_LOCKSTAT_CUSTOMER;
349             break;
350         case AM_HAL_SECURITY_LOCKTYPE_RECOVERY:
351             unlockMask = AM_HAL_SECURITY_LOCKSTAT_RECOVERY;
352             break;
353         default:
354             return AM_HAL_STATUS_INVALID_ARG;
355     }
356 
357     *pbUnlockStatus = SECURITY->LOCKSTAT & unlockMask;
358 
359     return AM_HAL_STATUS_SUCCESS;
360 
361 } // am_hal_security_get_lock_status()
362 
363 //*****************************************************************************
364 //
365 // @brief  Initialize CRC32 engine
366 //
367 // This will initialize the hardware engine to compute CRC32 on an arbitrary data payload
368 //
369 // @return Returns AM_HAL_STATUS_SUCCESS on success
370 //
371 //*****************************************************************************
372 uint32_t
am_hal_crc32_init(void)373 am_hal_crc32_init(void)
374 {
375     if (SECURITY->CTRL_b.ENABLE)
376     {
377         return AM_HAL_STATUS_IN_USE;
378     }
379 
380     //
381     // Program the CRC engine to compute the crc
382     //
383     SECURITY->RESULT = 0xFFFFFFFF;
384     SECURITY->CTRL_b.FUNCTION = SECURITY_CTRL_FUNCTION_CRC32;
385 
386     return AM_HAL_STATUS_SUCCESS;
387 } // am_hal_crc32_init()
388 
389 //*****************************************************************************
390 //
391 // @brief  Accumulate CRC32 for a specified payload
392 //
393 // @param  ui32StartAddr - The start address of the payload
394 // @param  ui32SizeBytes - The length of payload in bytes
395 // @param  pui32Crc -  Pointer to accumulated CRC
396 //
397 // This will use the hardware engine to compute CRC32 on an arbitrary data payload
398 //
399 // @return Returns AM_HAL_STATUS_SUCCESS on success
400 //
401 //*****************************************************************************
402 uint32_t
am_hal_crc32_accum(uint32_t ui32StartAddr,uint32_t ui32SizeBytes,uint32_t * pui32Crc)403 am_hal_crc32_accum(uint32_t ui32StartAddr, uint32_t ui32SizeBytes, uint32_t *pui32Crc)
404 {
405     uint32_t status, ui32CRC32;
406     bool bInternal;
407 
408 #ifndef AM_HAL_DISABLE_API_VALIDATION
409     if (pui32Crc == NULL)
410     {
411         return AM_HAL_STATUS_INVALID_ARG;
412     }
413 
414     //
415     // Make sure size is multiple of 4 bytes
416     //
417     if (ui32SizeBytes & 0x3)
418     {
419         return AM_HAL_STATUS_INVALID_ARG;
420     }
421 #endif // AM_HAL_DISABLE_API_VALIDATION
422 
423     status = AM_HAL_STATUS_OUT_OF_RANGE;    // Default status
424 
425     //
426     // Determine whether the startaddr is in internal flash or SRAM.
427     //
428     bInternal = ISADDRFLASH(ui32StartAddr) || ISADDRSRAM(ui32StartAddr);
429 
430     if ( bInternal )
431     {
432         SECURITY->SRCADDR = ui32StartAddr;
433         SECURITY->LEN_b.LEN = (ui32SizeBytes >> SECURITY_LEN_LEN_Pos);
434         // Start the CRC
435         SECURITY->CTRL_b.ENABLE = 1;
436 
437         //
438         // Wait for CRC to finish
439         //
440         status = am_hal_flash_delay_status_change(MAX_CRC_WAIT,
441                     (uint32_t)&SECURITY->CTRL, SECURITY_CTRL_ENABLE_Msk, 0);
442 
443 
444         if ( (status == AM_HAL_STATUS_SUCCESS)  &&  !SECURITY->CTRL_b.CRCERROR )
445         {
446             *pui32Crc = SECURITY->RESULT;
447         }
448         else if ( SECURITY->CTRL_b.CRCERROR )
449         {
450             status = AM_HAL_STATUS_HW_ERR;
451         }
452         else
453         {
454             //
455             // Error from status_change function.
456             // Return the CRC value we do have, but return an error.
457             //
458         }
459 
460         return status;
461     }
462 
463 #if ENABLE_EXTMEM_CRC
464     uint32_t ui32XferSize, ui32cnt;
465     uint32_t *pui32Buf, *pui32Data;
466 
467     //
468     // If we're here, the source data resides in non-internal memory (that is,
469     // not flash or SRAM).
470     //
471     // Begin the loop for computing the CRC of the external memory. The data
472     //  will first be copied to the SRAM buffer.
473     //
474     // Program the parts of the CRC engine that will not need to change
475     // inside the loop:  SRCADDR.
476     // While inside the loop, only the LEN will need to be provided.
477     //
478     ui32CRC32                 = *pui32Crc;
479     SECURITY->SRCADDR         = (uint32_t)&g_CRC_buffer[0];
480     pui32Data = (uint32_t*)ui32StartAddr;
481     while ( ui32SizeBytes )
482     {
483         //
484         // First copy a chunk of payload data to SRAM where the CRC engine
485         // can operate on it.
486         //
487         ui32XferSize = (ui32SizeBytes >= CRC_XFERBUF_SZ) ?
488                        CRC_XFERBUF_SZ : ui32SizeBytes;
489         ui32SizeBytes -= ui32XferSize;
490         ui32cnt      = ui32XferSize / 4;
491         pui32Buf     = &g_CRC_buffer[0];
492         while ( ui32cnt-- )
493         {
494             *pui32Buf++ = *pui32Data++;
495         }
496 
497         //
498         // Program the CRC engine's LEN parameter.
499         // All other parameters were preprogrammed: SRCADDR, FUNCTION, RESULT.
500         //
501         SECURITY->LEN_b.LEN = (ui32XferSize >> SECURITY_LEN_LEN_Pos);
502 
503         //
504         // Start the CRC
505         //
506         SECURITY->CTRL_b.ENABLE = 1;
507 
508         //
509         // Wait for CRC to finish
510         //
511         status = am_hal_flash_delay_status_change(MAX_CRC_WAIT,
512                     (uint32_t)&SECURITY->CTRL, SECURITY_CTRL_ENABLE_Msk, 0);
513 
514         if ( (status == AM_HAL_STATUS_SUCCESS)  &&  !SECURITY->CTRL_b.CRCERROR )
515         {
516             ui32CRC32 = SECURITY->RESULT;
517         }
518         else if ( SECURITY->CTRL_b.CRCERROR )
519         {
520             return AM_HAL_STATUS_HW_ERR;
521         }
522         else
523         {
524             //
525             // Error from status_change function.
526             // Return the (partial) CRC value we do have, but return an error.
527             //
528             break;
529         }
530     }
531 
532     //
533     // Return result to caller
534     //
535     *pui32Crc = ui32CRC32;
536 #endif // ENABLE_EXTMEM_CRC
537 
538     return status;
539 
540 } // am_hal_crc32_accum()
541 
542 //*****************************************************************************
543 //
544 // @brief  Compute CRC32 for a specified payload
545 //
546 // @param  ui32StartAddr - The start address of the payload.
547 // @param  ui32SizeBytes - The length of payload in bytes.
548 // @param  pui32Crc      - Pointer to variable to return the computed CRC.
549 //
550 // This function uses the hardware engine to compute CRC32 on an arbitrary data
551 // payload.  The payload can reside in any contiguous memory including external
552 // memory.
553 //
554 // @return Returns AM_HAL_STATUS_SUCCESS on success
555 //
556 //*****************************************************************************
557 uint32_t
am_hal_crc32(uint32_t ui32StartAddr,uint32_t ui32SizeBytes,uint32_t * pui32Crc)558 am_hal_crc32(uint32_t ui32StartAddr, uint32_t ui32SizeBytes, uint32_t *pui32Crc)
559 {
560     uint32_t status;
561 
562     status = am_hal_crc32_init();
563 
564     if (status == AM_HAL_STATUS_SUCCESS)
565     {
566         status = am_hal_crc32_accum(ui32StartAddr, ui32SizeBytes, pui32Crc);
567     }
568 
569     return status;
570 
571 } // am_hal_crc32()
572 
573 //*****************************************************************************
574 //
575 // @brief  Helper function to Perform exit operations for a secondary bootloader
576 //
577 // @param  pImage - The address of the image to give control to
578 //
579 // This function does the necessary security operations while exiting from a
580 // a secondary bootloader program. If still open, it locks the info0 key region,
581 // as well as further updates to the flash protection register.
582 // It also checks if it needs to halt to honor a debugger request.
583 // If an image address is specified, control is transferred to the same on exit.
584 //
585 // @return Returns AM_HAL_STATUS_SUCCESS on success, if no image address specified
586 // If an image address is provided, a successful execution results in transfer to
587 // the image - and this function does not return.
588 //
589 //*****************************************************************************
590 uint32_t
am_hal_bootloader_exit(uint32_t * pImage)591 am_hal_bootloader_exit(uint32_t *pImage)
592 {
593     uint32_t status = AM_HAL_STATUS_SUCCESS;
594 
595     //
596     // Lock the assets
597     //
598     if ( MCUCTRL->SHADOWVALID_b.INFO0_VALID  &&
599          MCUCTRL->BOOTLOADER_b.PROTLOCK )
600     {
601         am_hal_security_128bkey_t keyVal;
602         uint32_t *pCustKey = (uint32_t *)0x50021A00;
603         bool bLockStatus;
604 
605         //
606         // PROTLOCK Open
607         // This should also mean that Customer key is accessible
608         // Now lock the key by writing an incorrect value
609         //
610         keyVal.keyword[0] = ~pCustKey[0];
611         am_hal_security_set_key(AM_HAL_SECURITY_LOCKTYPE_CUSTOMER, &keyVal);
612 
613         status = am_hal_security_get_lock_status(AM_HAL_SECURITY_LOCKTYPE_CUSTOMER, &bLockStatus);
614 
615         if ((status != AM_HAL_STATUS_SUCCESS) || (bLockStatus))
616         {
617             return AM_HAL_STATUS_FAIL;
618         }
619 
620         //
621         // Lock the protection register to prevent further region locking
622         // CAUTION!!! - Can not do RMW on BOOTLOADER register as all writable
623         //              bits in this register are Write 1 to clear
624         //
625         MCUCTRL->BOOTLOADER = _VAL2FLD(MCUCTRL_BOOTLOADER_PROTLOCK, 1);
626 
627         //
628         // Check if we need to halt (debugger request)
629         //
630         if (MCUCTRL->SCRATCH0 & 0x1)
631         {
632             //
633             // Debugger wants to halt
634             //
635             uint32_t dhcsr = AM_REGVAL(0xE000EDF0);
636 
637             //
638             // Clear the flag in Scratch register
639             //
640             MCUCTRL->SCRATCH0 &= ~0x1;
641 
642             //
643             // Halt the core
644             //
645             dhcsr = ((uint32_t)0xA05F << 16) | (dhcsr & 0xFFFF) | 0x3;
646             AM_REGVAL(0xE000EDF0) = dhcsr;
647 
648             //
649             // Resume from halt
650             //
651         }
652     }
653 
654     //
655     // Give control to supplied image
656     //
657     if (pImage)
658     {
659         bl_run_main(pImage);
660 
661         //
662         // Does not return
663         //
664     }
665 
666     return status;
667 
668 } // am_hal_bootloader_exit()
669 //*****************************************************************************
670 //
671 // End Doxygen group.
672 //! @}
673 //
674 //*****************************************************************************
675