AM261x MCU+ SDK: SIPC Notify

| AM261x MCU+ SDK 26.00.00 |

SIPC Notify

Note
  Secure IPC Notify driver enables IPC communication between R5F cores and HSM M4 core. This driver cannot be used without encrypted HSMRt binary for HS-FS devices (will be available in 8.4) and TIFS-MCU for HS-SE devices (available on mySecureSW).

# Introduction

- The secure IPC notify(SIPC Notify) driver enables IPC communication between R5FSSx-x cores and HSM M4 core for HS-SE devices.
- SIPC provides a means of secure communication between R5FSSx and HSM M4 for using HSM services. The underlying implementation uses mailbox HW interrupts and HSM MBOX shared memory(refer Mailbox mechanism for SIPC) to pass messages from R5FSSx-x to HSM M4 and vice versa.
- The trusted R5FSSx-x which can post a request for services from HSM are termed as secure hosts. A non-secure host cannot communicate with HSM, moreover, the shared memory used in SIPC communication is not accessible to non-secure hosts. The cores that are going to be secure hosts are defined via sysconfig and this will be a static configuration at compile time. At a time there can only be atmost two secure hosts which can talk to HSM via SIPC, thus the name secure IPC.

Note
  Sysconfig initialization has to be handled by HSM client. For Secure IPC, Sysconfig is not supported in this release. It will be available with upcoming SDK releases along with HSM client

# Secure IPC design description

- SIPC supports RTOS-based implementation on the R5F side. The concept of CLIENT IDs is borrowed from IPC Notify, where in the RTOS scenario each task has a callback registered of type SIPC_FxnCallback. This function will be called when a message is received for a respective CLIENT ID. Refer to Understanding inter-processor communication (IPC) to know more about CLIENT IDs.
- HSM client is a wrapper on top of SIPC which asks for specific services from TIFS-MCU. User can install TIFS package from MySecureSw portal.

SIPC communication model

- The maximum number of client Ids available on R5F is SIPC_CLIENT_ID_MAX = 5. On the HSM M4 side the SIPC_CLIENT_ID_MAX = 2 as it doesn't run RTOS applications.

## Mailbox mechanism for SIPC

- SIPC uses HSM_MBOX (2KB) memory for secure communication with HSM. It uses MBOX_READ_DONE_ACK interrupt to notify target core to read from the dedicated message queue residing in HSM_MBOX memory. IPC Notify uses MSS_MBOX(16KB) memory for R5<-->R5 communication.

SIPC shared memory configuration

- The swQx is a struct holding pointer to actual queue location in HSM MBOX. There can be atmost 2 secure hosts at a time, so the total number of queues will be 4. If R5F wants to send a message to HSM then R5F writes into R5F -> HSM queue and sends an interrupt to HSM M4, once acknowledged HSM will read the message from the same queue. Similarly, If HSM wants to send a message then HSM will write into HSM --> R5 queues and sends an interrupt to R5F.

Note
  The entire HSM MBOX memory must be read and write protected for non-secure hosts. setting up this firewall configuration is the responsibility of the HSM server running on M4.

## SIPC initialization

- Following parameters are user-configurable.
  1. Queue Depth. -: Number of elements per queue.
  2. Number of secure hosts -: Could be 1 or 2.
  3. Secure hosts core Ids -: which R5F cores will be secure hosts.

These parameters will be initialized via sysconfig.

/* Sysconfig generated parameter QUEUE LENGTH */
#define SIPC_QUEUE_LENGTH (32u)
/* Sysconfig generated parameter TOTAL R5 CORES [1,2] */
#define SIPC_NUM_R5_CORES (2u)
/* Memory assigned for each R5x <-> HSM channel */
uint8_t gQueue_R5ToHsm[SIPC_NUM_R5_CORES][SIPC_QUEUE_LENGTH*SIPC_MSG_SIZE] __attribute__((aligned(8),section(".bss.sipc_hsm_queue_mem")));
uint8_t gQueue_HsmToR5[SIPC_NUM_R5_CORES][SIPC_QUEUE_LENGTH*SIPC_MSG_SIZE] __attribute__((aligned(8),section(".bss.sipc_r5f_queue_mem")));

- sysconfig will also generate code to allocate memory for the queues in HSM MBOX based on Queue depth and number of secure hosts. The pointer to this queue will be passed to SIPC_init at init time. For the initialization on R5F side, the SIPC_Params::tx_SipcQueues will point to gQueue_R5ToHsm where R5F will write to HSM. Similarly, SIPC_Params::rx_SipcQueues will point to gQueueHsmToR5 from where R5F will read the message sent by HSM M4.
- As the size of HSM MBOX is 2KB the max Queue Depth is limited.
  1. if SIPC_NUM_R5_CORES is 2 then the total number of queues will be 4 and the max queue depth will be 492/ SIPC_MSG_SIZE .
  2. if SIPC_NUM_R5_CORES is 1 then the total number of queues will be 1 and the max queue depth will be 984/ SIPC_MSG_SIZE .
- SIPC Initialization from R5F secure host

/* Following SIPC init will be done by HSM client running on R5*/
void SIPC_R5F_secure_host_config()
{
SIPC_Params notifyParams;
int32_t status;
/* Initialize parameters to default */
SIPC_Params_init(&notifyParams);
/* List the cores that will do IPC Notify with this core
* Make sure to NOT list 'self' core in the list below
*/
notifyParams.numCores = 1;
notifyParams.coreIdList[0] = CORE_INDEX_HSM;
notifyParams.ipcQueue_eleSize_inBytes = SIPC_MSG_SIZE;
notifyParams.ipcQueue_length = SIPC_QUEUE_LENGTH ;
/* R5 -> HSM Queue
* HSM -> R5 queue will be configured by HSM core */
notifyParams.tx_SipcQueues[CORE_INDEX_HSM] = (uintptr_t)gQueue_R5ToHsm[0] ;
notifyParams.rx_SipcQueues[CORE_INDEX_HSM] = (uintptr_t)gQueue_HsmToR5[0] ;
notifyParams.secHostCoreId[CORE_INDEX_SEC_MASTER_0] = CORE_ID_R5FSS0_0 ;
notifyParams.secHostCoreId[CORE_INDEX_SEC_MASTER_1] = CORE_ID_R5FSS0_1 ;
/* Initialize the IPC Notify module */
status = SIPC_init(&notifyParams);
DebugP_assert(status==SystemP_SUCCESS);
}

- SIPC Initialization from HSM

/* Following SIPC init will be done by HSM server */
void SIPC_HSM_config(void)
{
SIPC_Params notifyParams;
int32_t status;
/* Initialize parameters to default */
SIPC_Params_init(&notifyParams);
/* List the cores that will do IPC Notify with this core
* Make sure to NOT list 'self' core in the list below
*/
notifyParams.numCores = 2;
notifyParams.coreIdList[0] = CORE_INDEX_SEC_MASTER_0 ;
notifyParams.coreIdList[1] = CORE_INDEX_SEC_MASTER_1 ;
notifyParams.ipcQueue_eleSize_inBytes = SIPC_MSG_SIZE;
notifyParams.ipcQueue_length = SIPC_QUEUE_LENGTH ;
/* This is HSM -> R50 queue */
notifyParams.tx_SipcQueues[CORE_INDEX_SEC_MASTER_0] = (uintptr_t)gQueue_HsmToR5[0] ;
/* This is HSM -> R51 queue */
notifyParams.tx_SipcQueues[CORE_INDEX_SEC_MASTER_1] = (uintptr_t)gQueue_HsmToR5[1] ;
notifyParams.rx_SipcQueues[CORE_INDEX_SEC_MASTER_0] = (uintptr_t)gQueue_R5ToHsm[0] ;
/* This is R5 -> HSM queue */
notifyParams.rx_SipcQueues[CORE_INDEX_SEC_MASTER_1] = (uintptr_t)gQueue_R5ToHsm[1] ;
notifyParams.secHostCoreId[CORE_INDEX_SEC_MASTER_0] = CORE_ID_R5FSS0_0 ;
notifyParams.secHostCoreId[CORE_INDEX_SEC_MASTER_1] = CORE_ID_R5FSS0_1 ;
/* Initialize the SIPC Notify module */
status = SIPC_init(&notifyParams);
DebugP_assert(status==SystemP_SUCCESS);
}

- linker command file
- Following sections need to be defined as described below. The SIPC queues will be initialized in then mentioned memory sections.
  SECTIONS
  {
  /*memory sections configurations*/
  /* this is used only when Secure IPC is enabled */
  .bss.sipc_hsm_queue_mem (NOLOAD) : {} > MAILBOX_HSM
  .bss.sipc_r5f_queue_mem (NOLOAD) : {} > MAILBOX_R5F
  }
  MEMORY
  {
  /* definitions of memory sections */
  /* MSS mailbox memory is used as shared memory, we dont use bottom 32*12 bytes, since its used as SW queue by ipc_notify */
  RTOS_NORTOS_IPC_SHM_MEM : ORIGIN = 0x72000000, LENGTH = 0x3E80
  MAILBOX_HSM: ORIGIN = <HSM MBOX START ADDR> , LENGTH = 0x000003CE
  MAILBOX_R5F: ORIGIN = <HSM MBOX START ADDR + 0x000003CE> , LENGTH = 0x000003CE
  }
- In order to inherit the HSM services, the application must comply the uses of IPC via shared memory i.e. a memory region should be designated to both the CPUs. Make sure to mark this section as non-cached in the R5F MPU. However if the cache is enabled, for the shared region its application responsibility to bypass cache or invalidate the cache.

## Message format.

Unlike IPC Notify the message size of SIPC can be in the range of 3 to 100 bytes.

- The minimum message size is 3 bytes i.e [ Dest ClientId ][ Src ClientId ][data]. Users can change SIPC_MSG_SIZE based on a protocol that is implemented on top of SIPC.
- Following is an example of a message structure used by HSM client to communicate with HSM via SIPC. The size of a message is 13 bytes refer to HSM_CLIENT.

message format used by HSM client

Note
  It is recommended to use 13 bytes message format as mentioned above for optimal latency.

# Example usage.

- Consider a use case where R5F sends a message to HSM and HSM echoes back the same message.
  1. Do SIPC_init.
  2. Register a callback to handle the message from HSM SIPC_registerClient.
  3. Sends a message using SIPC_sendMsg and pend on a semaphore to Wait for a message.
  4. Once the message is received post the semaphore SemaphoreP_post and exit.

/* client ID that is used to send and receive messages */
uint32_t gSrcClientId0 = 3u;
uint32_t gSrcClientId1 = 4u;
/* R5 cores will send a message to HSM and it supports only 2 client IDs i.e
* ClientId = 0 for BootNotify message.
* ClientId = 1 for general message passing */
uint32_t gDestClientId = 1u;
/* Sec Host core that starts the message exchange */
uint32_t gSecMasterId0 = CORE_INDEX_SEC_MASTER_0 ;
uint32_t gSecMasterId1 = CORE_INDEX_SEC_MASTER_1 ;
/* Remote cores that echo messages from main core,
* Make sure to NOT list main core in this list */
uint32_t gRemoteCoreId = CORE_INDEX_HSM;
/* Semaphore's used to indicate a main core has finished all message exchanges */
SemaphoreP_Object gMainDoneSem;
void sipc_sec_host_msg_handler(uint8_t remoteCoreId, uint8_t localClientId, uint8_t remoteClientId, uint8_t* msgValue, void *args)
{
/* Handle the received message */
/* post the semaphore */
SemaphoreP_post(@gMainDoneSem);
}
void sipc_sec_host_start(void)
{
int32_t status;
/* Create completion semaphore for this core */
SemaphoreP_constructBinary(&gMainDoneSem, 0);
/* Register a handler to receive messages */
status = SIPC_registerClient(gSrcClientId1, sipc_sec_host_msg_handler, NULL);
DebugP_assert(status==SystemP_SUCCESS);
uint8_t msgValue[SIPC_MSG_SIZE] = { 0x00 , 0x00 ,0x02,0x03,0x04,0x05,0x06,0x07};
status = SIPC_sendMsg(gRemoteCoreId, gDestClientId, gSrcClientId1 ,msgValue, 1);
DebugP_assert(status==SystemP_SUCCESS);
/* Wait for message to be echoed back by HSM */
SemaphoreP_pend(&gMainDoneSem, SystemP_WAIT_FOREVER);
}
void main()
{
/* sipc initialization which will be done via sysconfig. */
SIPC_R5F_secure_host_config();
sipc_sec_host_start();
}

- Refer Secure IPC example for more info.

Note
  The secure IPC example project is a part of TIFS package for HS-SE devices. To use this example user needs to sign an NDA(non disclosure agreement). TIFS package can installed via mysecure software.

# API

APIs for Secure IPC Notify

SIPC_Params
Parameters used by SIPC_init.
Definition: sipc_notify.h:84
args
void * args
Definition: hsmclient_msg.h:4
SIPC_Params::tx_SipcQueues
uintptr_t tx_SipcQueues[MAX_SEC_CORES_WITH_HSM]
Definition: sipc_notify.h:98
SystemP_WAIT_FOREVER
#define SystemP_WAIT_FOREVER
Value to use when needing a timeout of infinity or wait forver until resource is available.
Definition: SystemP.h:83
SIPC_Params::numCores
uint32_t numCores
Definition: sipc_notify.h:86
CORE_ID_R5FSS0_0
#define CORE_ID_R5FSS0_0
Definition: sipc_notify_cfg.h:111
CORE_INDEX_HSM
#define CORE_INDEX_HSM
Definition: sipc_notify_cfg.h:129
__attribute__
struct lld_sockaddr __attribute__
This is the SecureBoot Stream type which holds the data for a specific bootloader to HSM call....
SIPC_Params_init
void SIPC_Params_init(SIPC_Params *params)
Set default value to SIPC_Params.
SIPC_MSG_SIZE
#define SIPC_MSG_SIZE
SIPC message size in bytes each element of queue will be of this size.
Definition: sipc_notify_cfg.h:47
SIPC_Params::coreIdList
uint32_t coreIdList[MAX_SEC_CORES_WITH_HSM]
Definition: sipc_notify.h:89
SIPC_Params::ipcQueue_eleSize_inBytes
uint16_t ipcQueue_eleSize_inBytes
Definition: sipc_notify.h:95
SystemP_SUCCESS
#define SystemP_SUCCESS
Return status when the API execution was successful.
Definition: SystemP.h:56
SIPC_init
int32_t SIPC_init(SIPC_Params *params)
Initialize Secure IPC notify module.
SIPC_registerClient
int32_t SIPC_registerClient(uint8_t localClientId, SIPC_FxnCallback msgCallback, void *args)
Register a callback to handle messages received from a specific remote core and for a specific local ...
CORE_ID_R5FSS0_1
#define CORE_ID_R5FSS0_1
Definition: sipc_notify_cfg.h:112
SIPC_Params::secHostCoreId
uint32_t secHostCoreId[MAX_SEC_CORES_WITH_HSM - 1U]
Definition: sipc_notify.h:104
SIPC_sendMsg
int32_t SIPC_sendMsg(uint8_t remoteSecCoreId, uint8_t remoteClientId, uint8_t localClientId, uint8_t *msgValue, SIPC_fifoFlags waitForFifoNotFull)
Send message to a specific remote core and specific client ID on that remote core.
SemaphoreP_post
void SemaphoreP_post(SemaphoreP_Object *obj)
Post a semaphore object or unlock a mutex.
SIPC_Params::rx_SipcQueues
uintptr_t rx_SipcQueues[MAX_SEC_CORES_WITH_HSM]
Definition: sipc_notify.h:100
SemaphoreP_constructBinary
int32_t SemaphoreP_constructBinary(SemaphoreP_Object *obj, uint32_t initValue)
Create a binary semaphore object.
DebugP_assert
#define DebugP_assert(expression)
Function to call for assert check.
Definition: DebugP.h:187
CORE_INDEX_SEC_MASTER_1
#define CORE_INDEX_SEC_MASTER_1
Definition: sipc_notify_cfg.h:128
CORE_INDEX_SEC_MASTER_0
#define CORE_INDEX_SEC_MASTER_0
Definition: sipc_notify_cfg.h:127
SemaphoreP_pend
int32_t SemaphoreP_pend(SemaphoreP_Object *obj, uint32_t timeToWaitInTicks)
Pend on a semaphore object or lock a mutex.
SIPC_Params::ipcQueue_length
uint16_t ipcQueue_length
Definition: sipc_notify.h:94