The S32K3 chip has rich encryption functions. For encryption, an independent arm core is even allocated for HSE. HSE firmware needs to be installed separately. For the encryption debug interface, there are usually two ways:
The default chip does not install HSE firmware. In order to verify the secure debug, this article will use the RTD400HF01 version on the K312 chip to do static secure debug interface without installing HSE firmware. Other K3 chips are similar.
The outline of this article is as follows:
Fig 1
For the hardware, you can use any K3 board, such as the K312-EVB development board, or a self-developed board. In order to facilitate chip replacement in this article, an evaluation board with a socket is used:
Fig 2
If need to configure to the K312, the board jumper need to do the following configuration:
J92:2-3,J28:2-3 ,J64:2-3 ,J114:2-3,J71:2-3 ,J118:2-3,J57:2-3 ,J72:2-3 ,J99:2-3
The flash controller of K3 is a PFLASH module. The chip comes with c40asf flash memory. Different K3 chips have different flash sizes and block numbers. Since the flash in the chip cannot implement the RWW function, when doing flash operations, you need to consider RWW and copy the flash operation code to the internal RAM for execution.
This article will take the LLD routine C40_Ip_Example routine of the RTD400HF01 version as the starting point. Since the RTD400HF01 version does not directly come with the K312 example, you first need to create a new K312 project, and then configure the LLD configuration by yourself. You can refer to the relevant LLD configuration of K344, but you need to pay attention to the clock situation to meet the requirements of K312. This article uses the option B clock mode, the clock is 120Mhz.
The most feasible way to verify the code for successful flash operation is to be able to operate the flash area and the code area in one block. Of course, it is necessary to pay attention to the flash sector and app code to be erased and written. Do not overlap. This can be checked according to the map file. For example:
#define EXAMPLE_SECTOR_START_ADDR (0x004FE000)
#define EXAMPLE_SECTOR_TEST (C40_CODE_ARRAY_0_BLOCK_0_S127)
Fig 3
Usually, in order to consider a larger margin, you can directly exceed the maximum hex value compiled, and then take the last sector of the current block to ensure that this address and the compiled size do not overlap in sectors.
For flash operations considering RWW, there are two methods for RTD500/RTD400. One thing is that it is not enough to just put the Flash operation code into RAM. Here are two methods that can be run, both of which have been tested and can work stably:
This requires two steps: first put the C40_Ip.c, C40_Ip.h related flash operation code into RAM, then wait for the flash controller to be idle before doing other operations.
(1) Open C40_Ip.c in the RTD/src folder
Two places need to be modified:
Modify the start of the file:
#define MEM_43_INFLS_START_SEC_CODE
to:
#define MEM_43_INFLS_START_SEC_RAMCODE
Modify the file ending area:
#define MEM_43_INFLS_STOP_SEC_CODE
To :
#define MEM_43_INFLS_STOP_SEC_RAMCODE
(2)Open C40_Ip.h in the RTD/include folder
Two places need to be modified:
Modify the beginning of the file:
#define MEM_43_INFLS_START_SEC_CODE
To :
#define MEM_43_INFLS_START_SEC_RAMCODE
Modify the file ending area:
#define MEM_43_INFLS_STOP_SEC_CODE
To :
#define MEM_43_INFLS_STOP_SEC_RAMCODE
The purpose of doing this is to put all the functions in C40_Ip into RAM. After compiling, check the ramcode area in the map file:
Fig 4
As you can see, after the above modification, all relevant C40 functions have been put into the internal RAM.
But please note that if you only do this and directly operate the flash, especially when erasing block0, hard fault will occur. You also need to wait for the FLASH controller to be idle.
Add a code to wait for the FLASH controller to be idle in main, and also put it in RAM. After operating the flash erase or write, call the function that waits for the FLASH controller to be idle. Before operating the flash code, you also need to call :
C40_Ip_SetAsyncMode(FALSE);
Refer to the following code:
#define MEM_43_INFLS_START_SEC_RAMCODE
#include "Mem_43_INFLS_MemMap.h"
void MemInfls_AccessCodeLoadToRam(void);
#define MEM_43_INFLS_STOP_SEC_RAMCODE
#include "Mem_43_INFLS_MemMap.h"
#define MEM_43_INFLS_START_SEC_RAMCODE
#include "Mem_43_INFLS_MemMap.h"
/* @violates @ref fls_flash_c_REF_20 Object/function previously declared */
void MemInfls_AccessCodeLoadToRam(void)
{
#if (C40_IP_TIMEOUT_SUPERVISION_ENABLED == STD_ON)
uint32 ValueTimeOut = C40_Ip_u32TimeoutTicks;
uint32 WaitedTicks;
#endif
/* Start internal erase/program sequence */
C40_Ip_pFlashBaseAddress->MCR |= FLASH_MCR_EHV_MASK;
/* Wait until operation finishes or write/erase timeout is reached */
while (0U == (C40_Ip_pFlashBaseAddress->MCRS & FLASH_MCRS_DONE_MASK))
{
#if (C40_IP_TIMEOUT_SUPERVISION_ENABLED == STD_ON)
ValueTimeOut--;
if (0U == ValueTimeOut)
{
break;
}
#endif
}
/* Disable HV to finalize/abort the operation */
C40_Ip_pFlashBaseAddress->MCR &= ~FLASH_MCR_EHV_MASK;
/* Wait until done or abort timeout is reached */
while (0U == (C40_Ip_pFlashBaseAddress->MCRS & FLASH_MCRS_DONE_MASK))
{
#if (C40_IP_TIMEOUT_SUPERVISION_ENABLED == STD_ON)
ValueTimeOut--;
if (0U == ValueTimeOut)
{
break;
}
#endif
}
#if (C40_IP_TIMEOUT_SUPERVISION_ENABLED == STD_ON)
/* Update the timeout counter */
WaitedTicks = C40_Ip_u32TimeoutTicks - ValueTimeOut;
C40_Ip_u32ElapsedTicks += WaitedTicks;
C40_Ip_u32CurrentTicks += WaitedTicks;
#endif
}
#define MEM_43_INFLS_STOP_SEC_RAMCODE
#include "Mem_43_INFLS_MemMap.h"
Main code related erase and program is:
#define EXAMPLE_SECTOR_START_ADDR (0x004FE000)
#define EXAMPLE_SECTOR_TEST (C40_CODE_ARRAY_0_BLOCK_0_S127)
C40_Ip_SetAsyncMode(FALSE);
t_SectorAddr = EXAMPLE_SECTOR_START_ADDR;
t_SectorNumber = EXAMPLE_SECTOR_TEST;
/* Unlock sector */
if (C40_IP_STATUS_SECTOR_PROTECTED == C40_Ip_GetLock(t_SectorNumber))
{
C40_Ip_ClearLock(t_SectorNumber, EXAMPLE_MASTER_ID);
}
/* Erase sector */
C40_Ip_MainInterfaceSectorErase(t_SectorNumber, EXAMPLE_MASTER_ID);
MemInfls_AccessCodeLoadToRam();
do
{
t_ReturnValue = C40_Ip_MainInterfaceSectorEraseStatus();
}
while (C40_IP_STATUS_BUSY == t_ReturnValue);
Example_CheckAssert(t_ReturnValue == C40_IP_STATUS_SUCCESS);
/* Write data */
C40_Ip_MainInterfaceWrite(t_SectorAddr, t_BufferSize, TxBuffer, EXAMPLE_MASTER_ID);
MemInfls_AccessCodeLoadToRam();
do
{
t_ReturnValue = C40_Ip_MainInterfaceWriteStatus();
}
while (C40_IP_STATUS_BUSY == t_ReturnValue);
Example_CheckAssert(t_ReturnValue == C40_IP_STATUS_SUCCESS);
Compile the code, burn the code and test the results as follows:
Fig 5
Fig 6
Fig 7
The above method requires more changes, and special waiting code needs to be added. Here is another method, which can directly modify the link file to directly drop all related .o files into RAM at once, and then for the user program, the RTD driver does not need to modify or add additional code.
Open the project Project_Settings->Linker_Files->linker_flash_s32k312.ld
Two points need to be modified:
(1) .pflash Modify the following code:
.pflash :
{
KEEP(*(.boot_header))
. = ALIGN(8192); /* The minimum erase size of C40 Flash IP is 8kb */
__text_start = .;
__interrupts_init_start = .;
KEEP(*(.intc_vector))
. = ALIGN(4);
__interrupts_init_end = .;
KEEP(*(.core_loop))
. = ALIGN(4);
*(.startup)
. = ALIGN(4);
*(.systeminit)
. = ALIGN(4);
*(.text.startup)
. = ALIGN(4);
*(EXCLUDE_FILE (*C40_Ip.o *OsIf_Timer.o *OsIf_Timer_System.o) .text)
*(EXCLUDE_FILE (*C40_Ip.o *OsIf_Timer.o *OsIf_Timer_System.o) .text*)
. = ALIGN(4);
*(EXCLUDE_FILE (*C40_Ip.o *OsIf_Timer.o *OsIf_Timer_System.o) .mcal_text)
. = ALIGN(4);
*(.acmcu_code_rom)
. = ALIGN(4);
__acfls_code_rom_start = .;
*(.acfls_code_rom)
. = ALIGN(4);
__acfls_code_rom_end = .;
__acmem_43_infls_code_rom_start = .;
*(.acmem_43_infls_code_rom)
. = ALIGN(4);
__acmem_43_infls_code_rom_end = .;
KEEP(*(.init))
. = ALIGN(4);
KEEP(*(.fini))
. = ALIGN(4);
*(.rodata)
*(.rodata*)
. = ALIGN(4);
*(.mcal_const_cfg)
. = ALIGN(4);
*(.mcal_const)
. = ALIGN(4);
__init_table = .;
KEEP(*(.init_table))
. = ALIGN(4);
__zero_table = .;
KEEP(*(.zero_table))
} > int_pflash
(2).pflash section add the following code
.itcm_text : AT(__tcm_code_rom_start)
{
. = ALIGN(4);
__itcm_start__ = .;
*(.itcm_text*)
. = ALIGN(4);
KEEP(*C40_Ip.o (.mcal_text*))
KEEP(*C40_Ip.o (.text*))
KEEP(*C40_Ip.o (.text))
KEEP(*OsIf_Timer.o (.mcal_text*))
KEEP(*OsIf_Timer.o (.text*))
KEEP(*OsIf_Timer.o (.text))
KEEP(*OsIf_Timer_System.o (.mcal_text*))
KEEP(*OsIf_Timer_System.o (.text*))
KEEP(*OsIf_Timer_System.o (.text))
. = ALIGN(4);
__itcm_end__ = .;
} > int_itcm
From here, we can see that in fact, not only the C40 code needs to be copied to RAM, but also the OsIf_Timer and OsIf_Timer_System related codes need to be put into RAM.
The test results are the same as those in Figure 5.6.7, and the erase and write actions of the corresponding block0 flash sector can be realized.
For secure debug, there are two static key addresses in the UTEST area: 0X1B000080, 0X1B000370.
0X1B000370 is the static encryption key address for use after the HSE firmware is installed. 0X1B000080 is the static encryption key address without HSE firmware.
Fig 8
So the following operation needs to be performed to burn the key to the address of UTEST 0X1B000080. After the above flash preparation, it is very clear to burn the key, but there is no need to erase it. When it is determined that the key address is not all 0XFF, the key operation can be performed directly. The code is as follows:
Generate->include->C40_Ip_Cfg.h
#define C40_IP_MAX_VIRTUAL_SECTOR (272U)//(271U)
If this is not modified, it will get stuck when executing sector C40_Ip_GetLock.
#define C40_UTEST_ARRAY_0_S000 (272U) /* 0x1B000000 */
#define FLS_SECTOR_ADDR 0x1B000080U
#define FLS_SECTOR_TEST C40_UTEST_ARRAY_0_S000
uint8 ADKP_Buffer[16]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x10,0x11,0x12,0x13,0x14,0x15};
if( (IP_DCM->DCMLCC & 0x77) == 0x33 ) // check LC is CUST_DEL or not
{
#if 1
//read the UTEST password data, if 0XFF, write.
/* Read data */
t_SectorAddr = FLS_SECTOR_ADDR;
t_SectorNumber = FLS_SECTOR_TEST;
t_ReturnValue = C40_Ip_Read(t_SectorAddr, 16, RxBuffer);
Example_CheckAssert(t_ReturnValue == C40_IP_STATUS_SUCCESS);
KeyEmptyFlag = 1;
for(uint32_t i=0; i<16; i++)
{
if(RxBuffer[i] != 0XFF)
{
KeyEmptyFlag = 0;
break;
}
}
//write ADKP to UTEST, just write once!
if(KeyEmptyFlag == 1)
{
/* Unlock sector */
if (C40_IP_STATUS_SECTOR_PROTECTED == C40_Ip_GetLock(t_SectorNumber))
{
C40_Ip_ClearLock(t_SectorNumber, FLS_MASTER_ID);
}
/* Write data */
C40_Ip_MainInterfaceWrite(FLS_SECTOR_ADDR, 16, ADKP_Buffer, FLS_MASTER_ID);
MemInfls_AccessCodeLoadToRam();//kerry
do
{
t_ReturnValue = C40_Ip_MainInterfaceWriteStatus();
}
while (C40_IP_STATUS_BUSY == t_ReturnValue);
Example_CheckAssert(t_ReturnValue == C40_IP_STATUS_SUCCESS);
//Power_Ip_PerformReset(&Power_Ip_HwIPsConfigPB);
}
#endif
Test conditions before Key burning:
Fig 9
Test results after Key burning:
Fig 10
It can be seen that key: HEX 00010203040506070809101112131415 is successfully burned into the UTEST 0X1B000080 area.
To really make secure debug work, the lifecycle needs to be evolved to non-CUST_DEL and FA. This article tests the evolution of LC to the IN_FIELD stage:
LCW = 0xDADADADA advances LC to OEM_PROD
LCW = 0xBABABABA advances LC to IN_FIELD
The address corresponding to LC (lifecyle) is the offset 0X24 of IVT, and must be 4-byte aligned. You can specify the address of IVT->LC to a flash area, and then burn the corresponding flash value to the IN_FIELD value: 0xBABABABA
Evolution method:
(1) Set the flash address of IVT LC
(2) Write the IN_FIELD value 0xBABABABA to the corresponding flash address
(3) Software reset
After evolution, you can also check whether the evolution is successful by checking the LC slot combination starting from address 0X1B000220 in the UTEST area. Of course, after the evolution is successful, the debug interface will also be locked, which can be unlocked to view. In addition, you can also view the evolved LC results through the DCM_DCMLCC register.
Fig 11
Here are the relevant codes:
(1)Project_Settings->Startup_Code->startup_cm7.s
#define LF_CONFIG_ADDR (0x00500000U)/*(0)*/
.section ".boot_header","ax"
/*00h*/ .long SBAF_BOOT_MARKER /* IVT marker */
/*04h*/ .long (CM7_0_ENABLE << CM7_0_ENABLE_SHIFT) | (CM7_1_ENABLE << CM7_1_ENABLE_SHIFT) | (CM7_2_ENABLE << CM7_2_ENABLE_SHIFT) /* Boot configuration word */
/*08h*/ .long 0 /* Reserved */
/*0Ch*/ .long CM7_0_VTOR_ADDR /* CM7_0 Start address */
/*10h*/ .long 0 /* Reserved */
/*14h*/ .long CM7_1_VTOR_ADDR /* CM7_1 Start address */
/*18h*/ .long 0 /* Reserved */
/*1Ch*/ .long CM7_2_VTOR_ADDR /* CM7_2 Start address */
/*20h*/ .long 0 /* Reserved */
/* .long XRDC_CONFIG_ADDR*/ /* XRDC configuration pointer */
/*24h*/ .long LF_CONFIG_ADDR /* Lifecycle configuration pointer */
/*28h*/ .long 0 /* Reserved */
It can be seen that the flash address specified by IVT offset 0X24 is 0X00500000
(2) Add flash0X00500000 area and write code
#define FLS_SECTOR_ADDR_LC (0x00500000U)
#define FLS_SECTOR_ADDR_LC_TEST (C40_CODE_ARRAY_0_BLOCK_1_S128)
uint8 lcwValue[8] = {0xBA,0xBA,0xBA,0xBA,0x00,0x00,0x00,0x00};
t_SectorAddr = FLS_SECTOR_ADDR_LC;
t_SectorNumber = FLS_SECTOR_ADDR_LC_TEST;
t_ReturnValue = C40_Ip_Read(t_SectorAddr, 16, RxBuffer);
Example_CheckAssert(t_ReturnValue == C40_IP_STATUS_SUCCESS);
LCEmptyFlag = 1;
for(uint32_t i=0; i<16; i++)
{
if(RxBuffer[i] != 0XFF)
{
LCEmptyFlag = 0;
break;
}
}
//write LC, just write once!
if(LCEmptyFlag == 1)
{
if (C40_IP_STATUS_SECTOR_PROTECTED == C40_Ip_GetLock(t_SectorNumber))
{
C40_Ip_ClearLock(t_SectorNumber, EXAMPLE_MASTER_ID);
}
/* Write data */
C40_Ip_MainInterfaceWrite(t_SectorAddr, 8, lcwValue, FLS_MASTER_ID);//IN FIELD
MemInfls_AccessCodeLoadToRam();//kerry
do
{
t_ReturnValue = C40_Ip_MainInterfaceWriteStatus();
}
while (C40_IP_STATUS_BUSY == t_ReturnValue);
Example_CheckAssert(t_ReturnValue == C40_IP_STATUS_SUCCESS);
Power_Ip_PerformReset(&Power_Ip_HwIPsConfigPB);
}
Of course, it should be noted here that if the corresponding flash address has been written with other values, you must first perform an erase operation before writing, or erase the entire chip once before downloading the code.
The test results are as follows:
Before LC evolution:
The 16 bytes of 0X1B000220 are the location of LC slot1. It can be seen that LC slot1 CUST_DEL corresponds to the Active state: that is, the lower 64 bits are Marked, and the upper 64 bytes are Erased
Mared: 0X55AA_50AF_55AA_50AF
Erased: 0XFFFF_FFFF_FFFF_FFFF
The other LC slot2, LC slot3, LC slot4, LC slot5 are all Erased. Figure 8 and Figure 11 can help understand.
Fig 12
IVT LC related flash 0X00500000 data result:
Fig 13
The software reset code is as follows:
Power_Ip_PerformReset(&Power_Ip_HwIPsConfigPB);
After the software reset, you can see the PE Multilink connection results as follows:
Fig 14
The results of JLINK locking are as follows:
Fig 15
The results of the Lauterbach lock are as follows:
Fig 16
So far, the K312 debug interface has been encrypted. The following will explain how to decrypt it.
For PE Multilink, the decryption link has been given from the encryption. You can refer to the following link of the PE official website:
https://www.pemicro.com/blog/index.cfm?post_id=216
for detailed tutorials. You need to download the python script: http://www.pemicro.com/downloads/download_file.cfm?download_id=450
then unzip and open: \supportFiles_ARM\supportFiles_ARM\NXP\S32K3xx\ authenticate_password_mode.py
Fig17
Save the script, open the computer cmd command window, enter the script path, and enter:
python authenticate_password_mode.py
The decryption result is as follows:
Fig 18
As you can see, the decryption is complete. At this point, you can download a simple project to try, you can directly download the code and enter debug mode. Of course, if the power is turned off and then on again, you need to decrypt again.
In your new app project, please modify the device to the : S32K312-SECUREDEBUG, otherwise, if you just use the S32K312, it will always report the secure issues.
Prepare one .cmm file, then change the content to the following content:
Global &SDAP_AUTHSTTS
Global &SDAP_AUTHCTL
Global &SDAP_KEYCHAL_0
Global &SDAP_KEYCHAL_1
Global &SDAP_KEYCHAL_2
Global &SDAP_KEYCHAL_3
Global &SDAP_KEYCHAL_4
Global &SDAP_KEYCHAL_5
Global &SDAP_KEYCHAL_6
Global &SDAP_KEYCHAL_7
GLOBAL &UID0 &UID1
Global &SDAP_AUTHSTTS_Read
GLOBAL &CHAL0 &CHAL1 &CHAL2 &CHAL3 &CHAL4 &CHAL5 &CHAL6 &CHAL7
SYStem.CPU S32K312-M7
sys.mode prepare
&SDAP_BASE_ADDRESS=0x40000700
&SDAP_AUTHSTTS = &SDAP_BASE_ADDRESS
&SDAP_AUTHCTL = &SDAP_BASE_ADDRESS+0x4
&SDAP_KEYCHAL_0 = &SDAP_BASE_ADDRESS+0x10
&SDAP_KEYCHAL_1 = &SDAP_BASE_ADDRESS+0x14
&SDAP_KEYCHAL_2 = &SDAP_BASE_ADDRESS+0x18
&SDAP_KEYCHAL_3 = &SDAP_BASE_ADDRESS+0x1C
&SDAP_KEYCHAL_4 = &SDAP_BASE_ADDRESS+0x20
&SDAP_KEYCHAL_5 = &SDAP_BASE_ADDRESS+0x24
&SDAP_KEYCHAL_6 = &SDAP_BASE_ADDRESS+0x28
&SDAP_KEYCHAL_7 = &SDAP_BASE_ADDRESS+0x2C
&SDAP_KEYRESP_0 = &SDAP_BASE_ADDRESS+0x40
&SDAP_KEYRESP_1 = &SDAP_BASE_ADDRESS+0x44
&SDAP_KEYRESP_2 = &SDAP_BASE_ADDRESS+0x48
&SDAP_KEYRESP_3 = &SDAP_BASE_ADDRESS+0x4C
&SDAP_KEYRESP_4 = &SDAP_BASE_ADDRESS+0x50
&SDAP_KEYRESP_5 = &SDAP_BASE_ADDRESS+0x54
&SDAP_KEYRESP_6 = &SDAP_BASE_ADDRESS+0x58
&SDAP_KEYRESP_7 = &SDAP_BASE_ADDRESS+0x5C
&SDAP_UID0 = &SDAP_BASE_ADDRESS+0x70
&SDAP_UID1 = &SDAP_BASE_ADDRESS+0x74
&SDAP_DBGENCTRL = &SDAP_BASE_ADDRESS+0x80
&SDAP_SDAAPRSTCTRL = &SDAP_BASE_ADDRESS+0x90
&SDAP_SDAAPGENCTRL0 = &SDAP_BASE_ADDRESS+0xA4
&SDAP_DAP_GEN1_CTRL = &SDAP_BASE_ADDRESS+0xB4
;prepare the key value
;sample adkp_value 5eba18b957523a0c10839b84c481f379
;need byte sway
&arg1 = 0x03020100
&arg2 = 0x07060504
&arg3 = 0x11100908
&arg4 = 0x15141312
&arg5 = 0x00000000
&arg6 = 0x00000000
&arg7 = 0x00000000
&arg8 = 0x00000000
;Enable SDAP Based Debug Authorization
data.set EDBG:&SDAP_SDAAPGENCTRL0 %LE %Long 0x00000001
local &rdata
;&rdata=Data.long(EDBG:&SDAP_AUTHSTTS)&0x00000001
;while &rdata!=0x1
;(
; &rdata=Data.long(EDBG:&SDAP_AUTHSTTS)&0x00000001
;)
&UID0=Data.long(EDBG:&SDAP_UID0)
&UID1=Data.long(EDBG:&SDAP_UID1)
&CHAL0=Data.long(EDBG:&SDAP_KEYCHAL_0)
&CHAL1=Data.long(EDBG:&SDAP_KEYCHAL_1)
&CHAL2=Data.long(EDBG:&SDAP_KEYCHAL_2)
&CHAL3=Data.long(EDBG:&SDAP_KEYCHAL_3)
&CHAL4=Data.long(EDBG:&SDAP_KEYCHAL_4)
&CHAL5=Data.long(EDBG:&SDAP_KEYCHAL_5)
&CHAL6=Data.long(EDBG:&SDAP_KEYCHAL_6)
&CHAL7=Data.long(EDBG:&SDAP_KEYCHAL_7)
print "UID0 is &UID0"
print "UID1 is &UID1"
data.set EDBG:&SDAP_KEYRESP_0 %LE %Long &arg1
data.set EDBG:&SDAP_KEYRESP_1 %LE %Long &arg2
data.set EDBG:&SDAP_KEYRESP_2 %LE %Long &arg3
data.set EDBG:&SDAP_KEYRESP_3 %LE %Long &arg4
data.set EDBG:&SDAP_KEYRESP_4 %LE %Long &arg5
data.set EDBG:&SDAP_KEYRESP_5 %LE %Long &arg6
data.set EDBG:&SDAP_KEYRESP_6 %LE %Long &arg7
data.set EDBG:&SDAP_KEYRESP_7 %LE %Long &arg8
;Writing SDA AP AUTHCTL.HSEAUTHREQ
data.set EDBG:&SDAP_AUTHCTL %LE %Long 0x00000001
wait 1ms
&SDAP_AUTHSTTS_Read = Data.long(EDBG:&SDAP_AUTHSTTS)
print "SDAP_AUTHSTTS is &SDAP_AUTHSTTS_Read"
local &rdata1
&rdata1 = Data.long(EDBG:&SDAP_AUTHSTTS)&0x60000004
print "rdata1 is &rdata1"
;IF(&SDAP_AUTHSTTS_Read==0x60000005)
IF (&rdata1==0x60000004)
(
print "debug function check ok, ready to open"
;Writing SDA AP DBGENCTRL.GDBGEN and DBGENCTRL.CDBGEN
data.set EDBG:&SDAP_DBGENCTRL %LE %Long 0x10000010
)
ELSE
(
PRINT "debug function still closed, please check your security setting and password"
ENDDO
)
SYStem.Option TRST OFF
SYStem.Option EnReset OFF
SYStem.Option DUALPORT ON
; system.config debugporttype swd
SYSTEM.JTAGclock 10Mhz
SYSTEM.Attach
print "debugger attached, but No flash driver"
break
ENDDO
&arg1 to &arg8 fill in the real used key。
After saving, run the script using Lauderbach trace32, the results are as follows:
Fig 19
Fig 20
Fig 21
As you can see, Lauderbach can be unlocked and mounted, and can directly enter debug mode after downloading new code.
From the current situation, Segger JLINK's non-HSE static unlocking is relatively convenient, because the latest Segger driver has added this function by default, which can directly identify whether it is locked, and then ask for the key value to be entered, and it can be automatically unlocked after entering. The driver version supported by the test is: JLink_V812a, open the JLINK commander, and do the connection action:
Fig 22
Fig 23
As you can see, JLINK has been unlocked successfully.
Of course, some lower-version drivers do not have this automatic lock recognition and require key input. In this case, you can unlock it by directly controlling the SDA_AP register.
The following is my actual command line operation method and results, for reference only:
J-Link>usb
Disconnecting from J-Link...O.K.
Disconnecting from J-Link...O.K.
Connecting to J-Link via USB...O.K.
Firmware: J-Link V11 compiled Dec 4 2024 17:53:35
Hardware version: V11.00
J-Link uptime (since boot): 0d 00h 00m 31s
S/N: 601012430
License(s): RDI, FlashBP, FlashDL, JFlash, GDB
USB speed mode: High speed (480 MBit/s)
VTref=3.315V
Device "S32K312" selected.
Connecting to target via SWD
ConfigTargetSettings() start
ConfigTargetSettings() end - Took 15us
InitTarget() start
SDA_AP detected
Unlocking device if necessary...
Locked device detected. Proceeding with the unlock procedure...
Locked S32K3xx device detected.
For proper debugger connection the device needs to be unlocked via an password.
Please specify the password via JLINK_ExecCommand() or by specifying a callback for an external dialog to enter the 16-byte password.
InitTarget() end - Took 7.53s
ConfigTargetSettings() start
ConfigTargetSettings() end - Took 13us
InitTarget() start
SDA_AP detected
Unlocking device if necessary...
Locked device detected. Proceeding with the unlock procedure...
Locked S32K3xx device detected.
For proper debugger connection the device needs to be unlocked via an password.
Please specify the password via JLINK_ExecCommand() or by specifying a callback for an external dialog to enter the 16-byte password.
InitTarget() end - Took 1.96s
Error occurred: Could not connect to the target device.
For troubleshooting steps visit: https://wiki.segger.com/J-Link_Troubleshooting
J-Link>WriteDP 2 0X070000FC
Writing DP register 2 = 0x070000FC (0 write repetitions needed)
J-Link>ReadAP 3
Reading AP register 3 = 0x001C0040 (0 read repetitions needed)
J-Link>WriteDP 2 0X07000070
Writing DP register 2 = 0x07000070 (0 write repetitions needed)
J-Link>ReadAP 0
Reading AP register 0 = 0x54030F33 (0 read repetitions needed)
J-Link>ReadAP 1
Reading AP register 1 = 0x23100265 (0 read repetitions needed)
J-Link>WriteDP 2 0x07000040
Writing DP register 2 = 0x07000040 (0 write repetitions needed)
J-Link>WriteAP 0 0x03020100
Writing AP register 0 = 0x03020100 (0 write repetitions needed)
J-Link>WriteAP 1 0X07060504
Writing AP register 1 = 0x07060504 (0 write repetitions needed)
J-Link>WriteAP 2 0X11100908
Writing AP register 2 = 0x11100908 (0 write repetitions needed)
J-Link>WriteAP 3 0X15141312
Writing AP register 3 = 0x15141312 (0 write repetitions needed)
J-Link>WriteDP 2 0X07000050
Writing DP register 2 = 0x07000050 (0 write repetitions needed)
J-Link>WriteAP 0 0X00000000
Writing AP register 0 = 0x00000000 (0 write repetitions needed)
J-Link>WriteAP 1 0X00000000
Writing AP register 1 = 0x00000000 (0 write repetitions needed)
J-Link>WriteAP 2 0X00000000
Writing AP register 2 = 0x00000000 (0 write repetitions needed)
J-Link>WriteAP 3 0X00000000
Writing AP register 3 = 0x00000000 (0 write repetitions needed)
J-Link>WriteDP 2 0X07000040
Writing DP register 2 = 0x07000040 (0 write repetitions needed)
J-Link>ReadAP 0
Reading AP register 0 = 0x03020100 (0 read repetitions needed)
J-Link>ReadAP 1
Reading AP register 1 = 0x07060504 (0 read repetitions needed)
J-Link>ReadAP 2
Reading AP register 2 = 0x11100908 (0 read repetitions needed)
J-Link>ReadAP 3
Reading AP register 3 = 0x15141312 (0 read repetitions needed)
J-Link>WriteDP 2 0x07000000
Writing DP register 2 = 0x07000000 (0 write repetitions needed)
J-Link>WriteAP 1 0X00000001
Writing AP register 1 = 0x00000001 (0 write repetitions needed)
J-Link>WriteDP 2 0x07000000
Writing DP register 2 = 0x07000000 (0 write repetitions needed)
J-Link>ReadAP 0
Reading AP register 0 = 0x60000004 (0 read repetitions needed)
J-Link>connect
Device "S32K312" selected.
Connecting to target via SWD
ConfigTargetSettings() start
ConfigTargetSettings() end - Took 12us
InitTarget() start
SDA_AP detected
Unlocking device if necessary...
Device is not locked. Proceeding without the unlock procedure.
Checking if debug access is already enabled...
Debug access is not enabled yet. Performing enable debug access sequence...
Debug access enabled
Checking if HSE firmware is installed...
HSE firmware not installed
Checking if Cortex-M7_0 and Cortex-M7_1 are operating in lockstep mode
Lock step mode disabled or not available
InitTarget() end - Took 16.5ms
Found SW-DP with ID 0x6BA02477
DPIDR: 0x6BA02477
CoreSight SoC-400 or earlier
AP map detection skipped. Manually configured AP map found.
AP[0]: MEM-AP (IDR: Not set, ADDR: 0x00000000)
AP[1]: APB-AP (IDR: Not set, ADDR: 0x00000000)
AP[2]: MEM-AP (IDR: Not set, ADDR: 0x00000000)
AP[3]: AHB-AP (IDR: Not set, ADDR: 0x00000000)
AP[4]: AHB-AP (IDR: Not set, ADDR: 0x00000000)
AP[5]: AHB-AP (IDR: Not set, ADDR: 0x00000000)
AP[6]: MEM-AP (IDR: Not set, ADDR: 0x00000000)
AP[7]: MEM-AP (IDR: Not set, ADDR: 0x00000000)
AP[4]: Skipped ROMBASE read. CoreBaseAddr manually set by user
AP[4]: Core found
CPUID register: 0x411FC272. Implementer code: 0x41 (ARM)
Cache: L1 I/D-cache present
Found Cortex-M7 r1p2, Little endian.
FPUnit: 8 code (BP) slots and 0 literal slots
ROM table scan skipped. CoreBaseAddr manually set by user: 0x40250400
I-Cache L1: 8 KB, 128 Sets, 32 Bytes/Line, 2-Way
D-Cache L1: 8 KB, 64 Sets, 32 Bytes/Line, 4-Way
SetupTarget() start
Initializing ECC RAM...
RAMCodeAddr: 0x20000000
RAMInitAddr: 0x20000010
RAMInitSize: 0x00007FF0
InitPattern: 0xDEADBEEF
ECC RAM initialized successfully
Initializing ECC RAM...
RAMCodeAddr: 0x20000000
RAMInitAddr: 0x20400000
RAMInitSize: 0x00004000
InitPattern: 0xDEADBEEF
ECC RAM initialized successfully
SetupTarget() end - Took 12.0ms
Memory zones:
Zone: "Default" Description: Default access mode
Cortex-M7 identified.
J-Link>
It should be noted here that for SDA_AP control, the addresses of the kernel and DAPBUS control are different, which is why the above command line controls the address starting from 0x07000000. It can be seen that the above command line method finally successfully injected the key and unlocked it successfully.
Fig 24
After unlocking, check the UTEST and DCM register values of LC as follows:
Fig 25
Fig 26
As you can see, it is already in IN_FIELD. The values in the UTEST area also meet the following conditions:
LC slot1 is Inactive,
LC slot2 is Inactive,
LC slot3 is Active,
LC slot4 is Erased,
LC slot5 is Erased,
After the above detailed description, the static encryption of K312's non-HSE firmware is completed, and the use of different emulators: JLINK, PE Mulitlink, lauterbach for decryption is also implemented.
Attachments:
S32K312_C40_Ip_RTD400HF01_flash.zip: flash waiting operation
S32K312_C40_Ip_RTD400HF01_flash_link.zip: link operation file to RAM
S32K312_C40_Ip_RTD400HF01_flash_SJG.zip: secure debug encryption code
S32K312_helloworld_RTD400HF01.zip: application test print helloworld code
When doing this set of functions, I also encountered many pitfalls, which are summarized here.
When a DFAE first reported this problem, I was still suspecting because after I copied the C40 code to RAM, I could still operate the flash successfully. But later I found that if I changed the erase area to block0, that is, when it was in the same block as the code, although the C40 code was copied to RAM successfully, and the map file was also in the ramcode area, when running it, it would go into hardfault and the error reported was still RWW error:
Fig 27
After many tests and final summary, we got the conclusion of Chapter 3.1. In fact, the code for processing C40 and OSIF was called without completing the flash operation, which led to the RWW problem. Therefore, after doing flash busy waiting or putting OSIF into RAM, the final test verification was successful.
After completing the key burning, the test of the flash value IN_FIELD of the LC area specified by IVT was OK, and then reset, even power on again, it can still enter the debug mode smoothly. This situation means that the secure debug is not successful at all. I was stuck at this point for eve one day, looking at the principles, registers, and memory. Finally, I was helpless and went back to the .s file to check the position of IVT. Only then did I find that the IVT and LC position of RTD400 were wrong, and it was actually written to the offset 0X28. After the modification, it was successful, and the emulator reminded that debug had been locked. This point of RTD500 has been fixed.
Fig 28
This problem is also quite strange, because the same Lauterbach script was successfully unlocked by my colleagues and customers before, but it just couldn't be unlocked when I did it, and it was always locked. Later, After debug the Lauterbach script , we found that there were two points that were stuck, both about challenge is ready, but my chip was not challenge ready. Because this kind of static encryption does not need challenge at all, just take care of the response, so I skipped the challenge check directly later, such as blocking the following code:
&rdata=Data.long(EDBG:&SDAP_AUTHSTTS)&0x00000001
while &rdata!=0x1
(
&rdata=Data.long(EDBG:&SDAP_AUTHSTTS)&0x00000001
)
Modify the script:
&SDAP_AUTHSTTS_Read = Data.long(EDBG:&SDAP_AUTHSTTS)
print "SDAP_AUTHSTTS is &SDAP_AUTHSTTS_Read"
IF (&SDAP_AUTHSTTS_Read==0x60000005)
(
print "debug function check ok, ready to open"
;Writing SDA AP DBGENCTRL.GDBGEN and DBGENCTRL.CDBGEN
data.set EDBG:&SDAP_DBGENCTRL %LE %Long 0x10000010
)
To:
&rdata1 = Data.long(EDBG:&SDAP_AUTHSTTS)&0x60000004
print "rdata1 is &rdata1"
;IF(&SDAP_AUTHSTTS_Read==0x60000005)
IF (&rdata1==0x60000004)
(
print "debug function check ok, ready to open"
;Writing SDA AP DBGENCTRL.GDBGEN and DBGENCTRL.CDBGEN
data.set EDBG:&SDAP_DBGENCTRL %LE %Long 0x10000010
)
That is, the status register does not identify the challenge, and then unlock is working.
For the RTD500 C40 FLASH operation linker method, as RTD500 flash driver also calls the SchM_Mem_43_INFLS related driver, so in the linker file, also need to put the SchM_Mem_43_INFLS.o to RAM.
The related linker file is:
Modify C40_Ip_Example_S32K344 Project_Settings->Linker_Files->Linker_flash_s32k344.ld
.pflash :
{
KEEP(*(.boot_header))
. = ALIGN(8192); /* The minimum erase size of C40 Flash IP is 8kb */
__text_start = .;
__interrupts_init_start = .;
KEEP(*(.intc_vector))
. = ALIGN(4);
__interrupts_init_end = .;
KEEP(*(.core_loop))
. = ALIGN(4);
*(.startup)
. = ALIGN(4);
*(.systeminit)
. = ALIGN(4);
*(.text.startup)
. = ALIGN(4);
*(EXCLUDE_FILE (*C40_Ip.o *OsIf_Timer.o *OsIf_Timer_System.o *SchM_Mem_43_INFLS.o) .text)
*(EXCLUDE_FILE (*C40_Ip.o *OsIf_Timer.o *OsIf_Timer_System.o *SchM_Mem_43_INFLS.o) .text*)
. = ALIGN(4);
*(EXCLUDE_FILE (*C40_Ip.o *OsIf_Timer.o *OsIf_Timer_System.o *SchM_Mem_43_INFLS.o) .mcal_text)
. = ALIGN(4);
. = ALIGN(4);
*(.acmcu_code_rom)
. = ALIGN(4);
__acfls_code_rom_start = .;
*(.acfls_code_rom)
. = ALIGN(4);
__acfls_code_rom_end = .;
__acmem_43_infls_code_rom_start = .;
*(.acmem_43_infls_code_rom)
. = ALIGN(4);
__acmem_43_infls_code_rom_end = .;
KEEP(*(.init))
. = ALIGN(4);
KEEP(*(.fini))
. = ALIGN(4);
*(.rodata)
*(.rodata*)
. = ALIGN(4);
*(.mcal_const_cfg)
. = ALIGN(4);
*(.mcal_const)
. = ALIGN(4);
__init_table = .;
KEEP(*(.init_table))
. = ALIGN(4);
__zero_table = .;
KEEP(*(.zero_table))
} > int_pflash
.itcm_text : AT(__tcm_code_rom_start)
{
. = ALIGN(4);
__itcm_start__ = .;
*(.itcm_text*)
. = ALIGN(4);
KEEP(*C40_Ip.o (.mcal_text*))
KEEP(*C40_Ip.o (.text*))
KEEP(*C40_Ip.o (.text))
KEEP(*OsIf_Timer.o (.mcal_text*))
KEEP(*OsIf_Timer.o (.text*))
KEEP(*OsIf_Timer.o (.text))
KEEP(*OsIf_Timer_System.o (.mcal_text*))
KEEP(*OsIf_Timer_System.o (.text*))
KEEP(*OsIf_Timer_System.o (.text))
KEEP(**SchM_Mem_43_INFLS.o (.mcal_text*))
KEEP(**SchM_Mem_43_INFLS.o (.text*))
KEEP(**SchM_Mem_43_INFLS.o (.text))
. = ALIGN(4);
__itcm_end__ = .;
} > int_itcm
Modify C40_Ip_Example_S32K344 main.c
#define EXAMPLE_SECTOR_START_ADDR (0x004FE000)//(0x00600000U)
#define EXAMPLE_SECTOR_TEST (C40_CODE_ARRAY_0_BLOCK_0_S127)//(C40_CODE_ARRAY_0_BLOCK_2_S256)
Others no modification, test result: