From 68dd87f4df9ad1e19240136b931867efbcbc23d0 Mon Sep 17 00:00:00 2001 From: Royyan Zahir Date: Thu, 24 Sep 2026 15:56:44 +0400 Subject: [PATCH] arch/arm64/imx9: add key store, signing and persistence to the ELE The EdgeLock Enclave offers a key store the mailbox driver did not reach. A key generated in there is permitted one algorithm and one usage, and export can be withheld, so the private half has no command that returns it. Adds the session, key store and key management services, key generation, signing by handle, and the storage exchange that makes a key store outlive a boot. Storage runs the other way round from every other command: the enclave asks the host to write its key store down and to give it back, and those requests arrive while a command of this side's is still outstanding, so the reply tag is what tells them apart. Two things a port has to know and neither reference nor header says. Key store commands carry a trailing crc, the exclusive or of every word including the header, without which the enclave answers rating 0xb9. And a persistent key lifetime is a statement of intent: the strict flag on key generation is what writes the key to the store, and without it a store exported around the key comes back without it. Every mailbox wait is bounded. An enclave that stops answering must not take the calling thread with it, and a reply buffer is a kilobyte, which does not belong on the stack of whatever task asked for a signature. Tested on an i.MX93: a P-256 key generated in the enclave, signing a digest whose signature verifies against the returned public half on a host, and still doing so after the board has been powered off. Signed-off-by: Royyan Zahir --- arch/arm64/src/imx9/hardware/imx9_ele.h | 58 + arch/arm64/src/imx9/imx9_ele.c | 1277 ++++++++++++++++++++++- arch/arm64/src/imx9/imx9_ele.h | 155 +++ 3 files changed, 1466 insertions(+), 24 deletions(-) diff --git a/arch/arm64/src/imx9/hardware/imx9_ele.h b/arch/arm64/src/imx9/hardware/imx9_ele.h index 48e5b513382..f8757b73acb 100644 --- a/arch/arm64/src/imx9/hardware/imx9_ele.h +++ b/arch/arm64/src/imx9/hardware/imx9_ele.h @@ -57,6 +57,64 @@ #define ELE_VERIFY_IMAGE_REQ 0x88 #define ELE_COMMIT_REQ 0xa8 +/* Key store services. These answer to ELE_VERSION_FW, not ELE_VERSION, and + * every one of them is a session the caller has to close. + */ + +#define ELE_SAB_INIT_REQ 0x17 +#define ELE_SESSION_OPEN_REQ 0x10 +#define ELE_SESSION_CLOSE_REQ 0x11 +#define ELE_KEY_STORE_OPEN_REQ 0x30 +#define ELE_KEY_STORE_CLOSE_REQ 0x31 +#define ELE_KEY_MGMT_OPEN_REQ 0x40 +#define ELE_KEY_MGMT_CLOSE_REQ 0x41 +#define ELE_GENERATE_KEY_REQ 0x42 +#define ELE_DELETE_KEY_REQ 0x4e +#define ELE_SIG_GEN_OPEN_REQ 0x70 +#define ELE_STORAGE_OPEN_REQ 0xe0 +#define ELE_STORAGE_CLOSE_REQ 0xe1 +#define ELE_STORAGE_IMPORT_REQ 0xe2 +#define ELE_STORAGE_EXPORT_START 0xe3 +#define ELE_STORAGE_EXPORT_FINISH 0xe4 +#define ELE_STORAGE_CHUNK_EXPORT 0xe5 +#define ELE_STORAGE_CHUNK_GET 0xe6 +#define ELE_STORAGE_CHUNK_GET_DONE 0xe7 +#define ELE_SIG_GEN_CLOSE_REQ 0x71 +#define ELE_SIGNATURE_GEN_REQ 0x72 + +#define ELE_KEY_STORE_FLAG_CREATE 0x01 + +/* Key attributes, from NXP's key management API. SECP_R1 covers the NIST + * curves; the enclave offers no Edwards or Montgomery curve. + */ + +#define ELE_KEY_TYPE_ECC_PAIR_SECP_R1 0x7112 +#define ELE_KEY_USAGE_SIGN_HASH 0x00001000 +#define ELE_KEY_USAGE_VERIFY_HASH 0x00002000 +#define ELE_KEY_LIFETIME_PERSISTENT 0x00000001 +#define ELE_KEY_LIFECYCLE_OPEN 0x01 +#define ELE_KEY_LIFECYCLE_CLOSED 0x02 +#define ELE_ALGO_ECDSA_SHA256 0x06000609 +#define ELE_KEY_GROUP_PERSISTENT 1 + +/* Without the strict flag a generated key lives in the enclave's own memory + * and is never written to the key store, so it does not survive the store + * being closed, let alone a reboot. + */ + +#define ELE_KEY_FLAG_STRICT 0x80 + +/* The input to a signature is a message the enclave hashes itself, or a + * digest the caller hashed. A CSR is signed over a digest. Bit 0 selects the + * message, not the digest, which the part settles: setting it produced a + * signature over sha256 of the digest that was passed in. + */ + +#define ELE_SIG_FLAG_INPUT_DIGEST 0x00 +#define ELE_SIG_FLAG_INPUT_MESSAGE 0x01 +#define ELE_KEY_STORE_FLAG_SYNC 0x80 +#define ELE_KEY_STORE_FLAG_MONINC 0x20 + /* Messaging Unit registers. */ #define ELE_MU_TCR (IMX9_S3MUA_BASE + 0x120) diff --git a/arch/arm64/src/imx9/imx9_ele.c b/arch/arm64/src/imx9/imx9_ele.c index ae2642a3a65..d689953eeaf 100644 --- a/arch/arm64/src/imx9/imx9_ele.c +++ b/arch/arm64/src/imx9/imx9_ele.c @@ -25,8 +25,11 @@ ****************************************************************************/ #include -#include +#include +#include #include +#include +#include #include "chip.h" #include "arm64_internal.h" @@ -46,6 +49,26 @@ #define lower_32_bits(n) ((uint32_t)(n)) #define ELE_RNG_TIMEOUT_US 5000 +#define ELE_POLL_SLEEP_US 1 + +/* A key generation is the slowest call, a few hundred milliseconds. */ + +#define ELE_REPLY_TIMEOUT_US 2000000 + +/* The enclave reports how much it means to write before writing it, so this + * only has to be larger than a key store, not exactly its size. + */ + +/* A key store is a master blob plus a chunk per key group. */ + +#define ELE_BLOB_SLOTS 8 +#define ELE_BLOB_SIZE 2048 +#define ELE_BLOB_MASTER_ID 0xffffffff +#define ELE_SIG_SIZE 192 +#define ELE_CHUNK_GET_SUCCESS 0xca3bb3acu + +#define ELE_STORAGE_FAILURE 0x29 +#define ELE_EXPORT_STATUS_SUCCESS 0xba2cc2abu #define ELE_RNG_SLEEP_US 100 #define ELE_TRNG_STATUS_READY 0x3 #define ELE_CSAL_STATUS_READY 0x2 @@ -56,6 +79,122 @@ struct ele_msg msg; +/* One mailbox and one msg: two threads' commands must never interleave. */ + +static rmutex_t g_ele_lock = NXRMUTEX_INITIALIZER; + +/* Arrives mid-command, so it cannot be allocated at the point of need. */ + +static uint8_t g_ele_blob_data[ELE_BLOB_SLOTS][ELE_BLOB_SIZE] + aligned_data(ARMV8A_DCACHE_LINESIZE); + +struct ele_blob_s +{ + uint32_t id; + uint32_t id_ext; + uint32_t len; + bool valid; + bool pending; +}; + +static struct ele_blob_s g_ele_blob[ELE_BLOB_SLOTS]; + +static uint8_t g_ele_sig[ELE_SIG_SIZE] aligned_data(ARMV8A_DCACHE_LINESIZE); + +/* The command payloads, as the enclave's message interface lays them out. */ + +begin_packed_struct struct ele_session_open_s +{ + uint8_t rsvd1; + uint8_t interrupt_num; + uint16_t rsvd2; + uint8_t priority; + uint8_t op_mode; + uint16_t rsvd3; +} end_packed_struct; + +begin_packed_struct struct ele_key_store_open_s +{ + uint32_t session_handle; + uint32_t key_store_id; + uint32_t auth_nonce; + uint16_t rsvd1; + uint8_t flags; + uint8_t rsvd2; + uint32_t crc; +} end_packed_struct; + +begin_packed_struct struct ele_key_mgmt_open_s +{ + uint32_t key_store_handle; + uint32_t msbi; + uint32_t msbo; + uint8_t flags; + uint8_t reserved[3]; + uint32_t crc; +} end_packed_struct; + +begin_packed_struct struct ele_generate_key_s +{ + uint32_t key_mgmt_handle; + uint32_t key_id; + uint16_t public_key_size; + uint16_t key_group; + uint16_t key_type; + uint16_t key_size; + uint32_t key_lifetime; + uint32_t key_usage; + uint32_t permitted_algo; + uint32_t key_lifecycle; + uint8_t flags; + uint8_t reserved[3]; + uint32_t public_key_addr; + uint32_t crc; +} end_packed_struct; + +begin_packed_struct struct ele_sig_gen_open_s +{ + uint32_t key_store_handle; + uint32_t msbi; + uint32_t msbo; + uint8_t flags; + uint8_t reserved[3]; + uint32_t crc; +} end_packed_struct; + +begin_packed_struct struct ele_sign_s +{ + uint32_t sig_gen_handle; + uint32_t key_identifier; + uint32_t message_addr; + uint32_t signature_addr; + uint32_t message_size; + uint16_t signature_size; + uint8_t flags; + uint8_t reserved; + uint32_t scheme_id; + uint16_t salt_len; + uint16_t reserved2; + uint32_t crc; +} end_packed_struct; + +begin_packed_struct struct ele_storage_open_s +{ + uint32_t session_handle; + uint32_t msbi; + uint32_t msbo; + uint8_t flags; + uint8_t reserved[3]; + uint32_t crc; +} end_packed_struct; + +begin_packed_struct struct ele_master_import_s +{ + uint32_t storage_handle; + uint32_t key_store_addr; + uint32_t key_store_size; +} end_packed_struct; + struct ele_trng_state { uint8_t trng_state; @@ -67,6 +206,8 @@ struct ele_trng_state * Private Function Prototypes ****************************************************************************/ +static void imx9_ele_service_request(struct ele_msg *req); + /**************************************************************************** * Name: imx9_ele_sendmsg * @@ -83,29 +224,46 @@ struct ele_trng_state * ****************************************************************************/ -static void imx9_ele_sendmsg(struct ele_msg *msg_ptr) +static int imx9_ele_wait_tx(int channel) { - /* Check that ele is ready to receive */ + uint32_t waited; - while (!((1) & getreg32(ELE_MU_TSR))); + for (waited = 0; !(getreg32(ELE_MU_TSR) & (1 << channel)); + waited += ELE_POLL_SLEEP_US) + { + if (waited >= ELE_REPLY_TIMEOUT_US) + { + return -ETIMEDOUT; + } - /* write header to slog 0 */ + up_udelay(ELE_POLL_SLEEP_US); + } + + return 0; +} + +static int imx9_ele_sendmsg(struct ele_msg *msg_ptr) +{ + if (imx9_ele_wait_tx(0) < 0) + { + return -ETIMEDOUT; + } putreg32(msg_ptr->header.data, ELE_MU_TR(0)); - /* write data */ - for (int i = 1; i < msg_ptr->header.size; i++) { - int tx_channel; + int tx_channel = i % ELE_TR_NUM; - tx_channel = i % ELE_TR_NUM; - while (!((1 << tx_channel) & getreg32(ELE_MU_TSR))); - - /* Write data */ + if (imx9_ele_wait_tx(tx_channel) < 0) + { + return -ETIMEDOUT; + } putreg32(msg_ptr->data[i - 1], ELE_MU_TR(tx_channel)); } + + return 0; } /**************************************************************************** @@ -124,27 +282,81 @@ static void imx9_ele_sendmsg(struct ele_msg *msg_ptr) * ****************************************************************************/ -static void imx9_ele_receivemsg(struct ele_msg *msg_ptr) +static int imx9_ele_wait_rx(int channel) { - /* Check if data ready */ + uint32_t waited; - while (!((1) & getreg32(ELE_MU_RSR))); + for (waited = 0; !(getreg32(ELE_MU_RSR) & (1 << channel)); + waited += ELE_POLL_SLEEP_US) + { + if (waited >= ELE_REPLY_TIMEOUT_US) + { + return -ETIMEDOUT; + } - /* Read Header from slot 0 */ + up_udelay(ELE_POLL_SLEEP_US); + } + + return 0; +} + +static int imx9_ele_receivemsg_raw(struct ele_msg *msg_ptr) +{ + /* An enclave that never answers must not take the caller with it. */ + + if (imx9_ele_wait_rx(0) < 0) + { + return -ETIMEDOUT; + } msg_ptr->header.data = getreg32(ELE_MU_RR(0)); for (int i = 1; i < msg_ptr->header.size; i++) { - /* Check if empty */ - int rx_channel = (i) % ELE_RR_NUM; - while (!((1 << rx_channel) & getreg32(ELE_MU_RSR))); - /* Read data */ + if (imx9_ele_wait_rx(rx_channel) < 0) + { + return -ETIMEDOUT; + } msg_ptr->data[i - 1] = getreg32(ELE_MU_RR(rx_channel)); } + + return 0; +} + +/**************************************************************************** + * Name: imx9_ele_receivemsg + * + * Description: + * Wait for the reply to the command this side sent. ELE_RESP_TAG is a + * reply, ELE_CMD_TAG is the enclave asking something of its own, which is + * answered here because the reply does not come until it is. + * + ****************************************************************************/ + +static void imx9_ele_receivemsg(struct ele_msg *msg_ptr) +{ + for (; ; ) + { + if (imx9_ele_receivemsg_raw(msg_ptr) < 0) + { + /* Callers read the response word, so it must not be stale. */ + + _err("ELE did not answer command 0x%02x\n", + msg_ptr->header.command); + msg_ptr->data[0] = ELE_STORAGE_FAILURE; + return; + } + + if (msg_ptr->header.tag == ELE_RESP_TAG) + { + return; + } + + imx9_ele_service_request(msg_ptr); + } } /**************************************************************************** @@ -171,10 +383,205 @@ static uintptr_t imx9_ele_buffer_pa(void *va) #endif } +/**************************************************************************** + * Name: imx9_ele_service_request + * + * Description: + * Answer a request the enclave sent while a command of this side's was in + * flight. Having been asked to sync a key store it asks back where to put + * the blob, then whether it was kept, and the command that provoked those + * does not reply until they are answered. An unrecognised request is + * refused, so it fails rather than hangs. + * + ****************************************************************************/ + +static int imx9_ele_blob_slot(uint32_t id, uint32_t id_ext, bool allocate) +{ + int free_slot = -1; + int i; + + for (i = 0; i < ELE_BLOB_SLOTS; i++) + { + if (g_ele_blob[i].valid && + g_ele_blob[i].id == id && g_ele_blob[i].id_ext == id_ext) + { + return i; + } + + if (!g_ele_blob[i].valid && free_slot < 0) + { + free_slot = i; + } + } + + return allocate ? free_slot : -1; +} + +static void imx9_ele_service_request(struct ele_msg *req) +{ + /* A kilobyte does not belong on the caller's stack. */ + + static struct ele_msg rsp; + uint32_t handle = req->data[0]; + uintptr_t paddr; + int slot; + + memset(&rsp, 0, sizeof(rsp)); + rsp.header.version = req->header.version; + rsp.header.tag = ELE_RESP_TAG; + rsp.header.command = req->header.command; + + rsp.header.size = 2; + rsp.data[0] = ELE_STORAGE_FAILURE; + + switch (req->header.command) + { + case ELE_STORAGE_EXPORT_START: + + /* data[1] is the size it will write; a smaller buffer overruns. */ + + slot = imx9_ele_blob_slot(ELE_BLOB_MASTER_ID, 0, true); + + if (slot < 0 || req->data[1] > ELE_BLOB_SIZE) + { + break; + } + + paddr = imx9_ele_buffer_pa(g_ele_blob_data[slot]); + if (paddr == 0) + { + break; + } + + g_ele_blob[slot].id = ELE_BLOB_MASTER_ID; + g_ele_blob[slot].id_ext = 0; + g_ele_blob[slot].len = req->data[1]; + g_ele_blob[slot].pending = true; + + /* The enclave writes behind the cache. */ + + up_clean_dcache((uintptr_t)g_ele_blob_data[slot], + (uintptr_t)g_ele_blob_data[slot] + ELE_BLOB_SIZE); + + rsp.header.size = 4; + rsp.data[0] = handle; + rsp.data[1] = ELE_OK; + rsp.data[2] = (uint32_t)paddr; + break; + + case ELE_STORAGE_CHUNK_EXPORT: + + /* One key group. data[1] is its size, data[2] and data[3] name it. */ + + slot = imx9_ele_blob_slot(req->data[2], req->data[3], true); + + if (slot < 0 || req->data[1] > ELE_BLOB_SIZE) + { + break; + } + + paddr = imx9_ele_buffer_pa(g_ele_blob_data[slot]); + if (paddr == 0) + { + break; + } + + g_ele_blob[slot].id = req->data[2]; + g_ele_blob[slot].id_ext = req->data[3]; + g_ele_blob[slot].len = req->data[1]; + g_ele_blob[slot].pending = true; + + up_clean_dcache((uintptr_t)g_ele_blob_data[slot], + (uintptr_t)g_ele_blob_data[slot] + ELE_BLOB_SIZE); + + rsp.header.size = 3; + rsp.data[0] = ELE_OK; + rsp.data[1] = (uint32_t)paddr; + break; + + case ELE_STORAGE_EXPORT_FINISH: + + /* Only the pieces this export wrote are settled by it. */ + + for (slot = 0; slot < ELE_BLOB_SLOTS; slot++) + { + if (!g_ele_blob[slot].pending) + { + continue; + } + + g_ele_blob[slot].pending = false; + + if (req->data[1] != ELE_EXPORT_STATUS_SUCCESS) + { + g_ele_blob[slot].len = 0; + continue; + } + + up_invalidate_dcache((uintptr_t)g_ele_blob_data[slot], + (uintptr_t)g_ele_blob_data[slot] + + ELE_BLOB_SIZE); + + g_ele_blob[slot].valid = true; + } + + rsp.header.size = 3; + rsp.data[0] = handle; + rsp.data[1] = ELE_OK; + break; + + case ELE_STORAGE_CHUNK_GET: + + /* Not having it is the ordinary first boot, not a failure. */ + + slot = imx9_ele_blob_slot(req->data[1], req->data[2], false); + + if (slot < 0) + { + break; + } + + paddr = imx9_ele_buffer_pa(g_ele_blob_data[slot]); + if (paddr == 0) + { + break; + } + + up_clean_dcache((uintptr_t)g_ele_blob_data[slot], + (uintptr_t)g_ele_blob_data[slot] + ELE_BLOB_SIZE); + + rsp.header.size = 4; + rsp.data[0] = g_ele_blob[slot].len; + rsp.data[1] = (uint32_t)paddr; + rsp.data[2] = ELE_OK; + break; + + case ELE_STORAGE_CHUNK_GET_DONE: + rsp.header.size = 2; + rsp.data[0] = ELE_OK; + break; + + default: + break; + } + + imx9_ele_sendmsg(&rsp); +} + /**************************************************************************** * Public Functions ****************************************************************************/ +void imx9_ele_lock(void) +{ + nxrmutex_lock(&g_ele_lock); +} + +void imx9_ele_unlock(void) +{ + nxrmutex_unlock(&g_ele_lock); +} + void imx9_ele_init(void) { putreg32(0, ELE_MU_TCR); @@ -481,7 +888,7 @@ int imx9_ele_get_trng_state(void) return -EIO; } -int imx9_ele_get_random(void *buf, size_t len) +static int imx9_ele_get_random_locked(void *buf, size_t len) { uint16_t counter = 0; uint16_t max_tries = ELE_RNG_TIMEOUT_US / ELE_RNG_SLEEP_US; @@ -493,9 +900,7 @@ int imx9_ele_get_random(void *buf, size_t len) return -EINVAL; } - /* The buffer is invalidated after the transfer, so anything sharing its - * first or last cache line would lose whatever was written meanwhile. - */ + /* A neighbour sharing an end cache line would lose its contents. */ if (!IS_ALIGNED((uintptr_t)buf, ARMV8A_DCACHE_LINESIZE) || !IS_ALIGNED(len, ARMV8A_DCACHE_LINESIZE)) @@ -554,6 +959,16 @@ int imx9_ele_get_random(void *buf, size_t len) return -EIO; } +int imx9_ele_get_random(void *buf, size_t len) +{ + int ret; + + imx9_ele_lock(); + ret = imx9_ele_get_random_locked(buf, len); + imx9_ele_unlock(); + return ret; +} + int imx9_ele_commit(uint32_t info, uint32_t *response) { msg.header.version = ELE_VERSION; @@ -577,3 +992,817 @@ int imx9_ele_commit(uint32_t info, uint32_t *response) return -EIO; } + +/**************************************************************************** + * Name: imx9_ele_session_open + * + * Description: + * Open an ELE session. Every key store service hangs off one of these, and + * the enclave holds it until it is closed. + * + * Output Parameters: + * session - handle for the opened session + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_ele_sab_init + * + * Description: + * Start the enclave's security services. Every key store command answers + * "not ready" until this has been done once. + * + * Output Parameters: + * rsp - the raw ELE response word, or NULL + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_ele_update_crc + * + * Description: + * Fill the trailing crc word of a key store command. The enclave refuses + * these commands with rating 0xb9 without it. It is the exclusive or of + * every word of the message, the header included, except the crc word + * itself, which is the last one. + * + ****************************************************************************/ + +static void imx9_ele_update_crc(struct ele_msg *msg_ptr) +{ + uint32_t *words = (uint32_t *)msg_ptr; + uint32_t crc = 0; + unsigned int i; + + for (i = 0; i < msg_ptr->header.size - 1; i++) + { + crc ^= words[i]; + } + + msg_ptr->data[msg_ptr->header.size - 2] = crc; +} + +int imx9_ele_sab_init(uint32_t *rsp) +{ + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 1; + msg.header.command = ELE_SAB_INIT_REQ; + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + if (rsp != NULL) + { + *rsp = msg.data[0]; + } + + return ((msg.data[0] & 0xff) == ELE_OK) ? 0 : -EIO; +} + +int imx9_ele_session_open_rsp(uint32_t *session, uint32_t *rsp) +{ + struct ele_session_open_s cmd; + + if (session == NULL) + { + return -EINVAL; + } + + memset(&cmd, 0, sizeof(cmd)); + + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 1 + (sizeof(cmd) / sizeof(uint32_t)); + msg.header.command = ELE_SESSION_OPEN_REQ; + memcpy(msg.data, &cmd, sizeof(cmd)); + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + if (rsp != NULL) + { + *rsp = msg.data[0]; + } + + if ((msg.data[0] & 0xff) != ELE_OK) + { + return -EIO; + } + + *session = msg.data[1]; + return 0; +} + +int imx9_ele_session_open(uint32_t *session) +{ + return imx9_ele_session_open_rsp(session, NULL); +} + +/**************************************************************************** + * Name: imx9_ele_session_close + * + * Description: + * Close a session opened by imx9_ele_session_open(). + * + * Input Parameters: + * session - the session handle + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int imx9_ele_session_close(uint32_t session) +{ + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 2; + msg.header.command = ELE_SESSION_CLOSE_REQ; + msg.data[0] = session; + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + return ((msg.data[0] & 0xff) == ELE_OK) ? 0 : -EIO; +} + +/**************************************************************************** + * Name: imx9_ele_key_store_open + * + * Description: + * Open a key store, creating it if asked. A key store is where a generated + * key lives, and the private half has no command that returns it. + * + * Input Parameters: + * session - an open session + * id - caller-chosen key store identifier + * nonce - authentication nonce for the store + * flags - ELE_KEY_STORE_FLAG_*, none of them to load an existing store + * + * Output Parameters: + * store - handle for the opened key store + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int imx9_ele_key_store_open_rsp(uint32_t session, uint32_t id, + uint32_t nonce, uint8_t flags, + uint32_t *store, uint32_t *rsp) +{ + struct ele_key_store_open_s cmd; + + if (store == NULL) + { + return -EINVAL; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.session_handle = session; + cmd.key_store_id = id; + cmd.auth_nonce = nonce; + + /* Asking for SYNC is asking the enclave to hand the store back. */ + + cmd.flags = flags; + + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 1 + (sizeof(cmd) / sizeof(uint32_t)); + msg.header.command = ELE_KEY_STORE_OPEN_REQ; + memcpy(msg.data, &cmd, sizeof(cmd)); + imx9_ele_update_crc(&msg); + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + if (rsp != NULL) + { + *rsp = msg.data[0]; + } + + if ((msg.data[0] & 0xff) != ELE_OK) + { + return -EIO; + } + + *store = msg.data[1]; + return 0; +} + +int imx9_ele_key_store_open(uint32_t session, uint32_t id, uint32_t nonce, + uint8_t flags, uint32_t *store) +{ + return imx9_ele_key_store_open_rsp(session, id, nonce, flags, store, + NULL); +} + +/**************************************************************************** + * Name: imx9_ele_key_store_close + * + * Description: + * Close a key store opened by imx9_ele_key_store_open(). + * + ****************************************************************************/ + +int imx9_ele_key_store_close(uint32_t store) +{ + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 2; + msg.header.command = ELE_KEY_STORE_CLOSE_REQ; + msg.data[0] = store; + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + return ((msg.data[0] & 0xff) == ELE_OK) ? 0 : -EIO; +} + +/**************************************************************************** + * Name: imx9_ele_key_mgmt_open / imx9_ele_key_mgmt_close + * + * Description: + * Open a key management service on a key store. Generating a key needs one + * of these, and it is another handle to close. + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int imx9_ele_key_mgmt_open(uint32_t store, uint32_t *mgmt, uint32_t *rsp) +{ + struct ele_key_mgmt_open_s cmd; + + if (mgmt == NULL) + { + return -EINVAL; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.key_store_handle = store; + + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 1 + (sizeof(cmd) / sizeof(uint32_t)); + msg.header.command = ELE_KEY_MGMT_OPEN_REQ; + memcpy(msg.data, &cmd, sizeof(cmd)); + imx9_ele_update_crc(&msg); + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + if (rsp != NULL) + { + *rsp = msg.data[0]; + } + + if ((msg.data[0] & 0xff) != ELE_OK) + { + return -EIO; + } + + *mgmt = msg.data[1]; + return 0; +} + +int imx9_ele_key_mgmt_close(uint32_t mgmt) +{ + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 2; + msg.header.command = ELE_KEY_MGMT_CLOSE_REQ; + msg.data[0] = mgmt; + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + return ((msg.data[0] & 0xff) == ELE_OK) ? 0 : -EIO; +} + +/**************************************************************************** + * Name: imx9_ele_generate_key + * + * Description: + * Generate a key pair inside the enclave. The public half is written to + * the caller's buffer; the private half stays in the key store and there + * is no command that returns it. Withholding the export usage is what + * makes that true rather than merely unimplemented. + * + * Input Parameters: + * mgmt - an open key management handle + * key_type - ELE_KEY_TYPE_ECC_PAIR_SECP_R1 and friends + * key_bits - key size in bits + * algo - the one algorithm this key is permitted to perform + * lifecycle - the device lifecycle the key may be used in + * pubkey - buffer for the public half, cache line aligned and sized + * pubkey_len- its length + * + * Output Parameters: + * key_id - identifier of the generated key + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int imx9_ele_generate_key(uint32_t mgmt, uint16_t key_type, + uint16_t key_bits, uint32_t algo, + uint32_t lifecycle, + void *pubkey, size_t pubkey_len, + uint32_t *key_id, uint32_t *rsp) +{ + struct ele_generate_key_s cmd; + + uintptr_t paddr; + + if (pubkey == NULL || key_id == NULL) + { + return -EINVAL; + } + + /* A neighbour sharing an end cache line would lose its contents. */ + + if (!IS_ALIGNED((uintptr_t)pubkey, ARMV8A_DCACHE_LINESIZE) || + !IS_ALIGNED(pubkey_len, ARMV8A_DCACHE_LINESIZE)) + { + return -EINVAL; + } + + paddr = imx9_ele_buffer_pa(pubkey); + if (paddr == 0 || paddr > UINT32_MAX - pubkey_len) + { + return -EFAULT; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.key_mgmt_handle = mgmt; + cmd.public_key_size = (uint16_t)pubkey_len; + cmd.key_group = ELE_KEY_GROUP_PERSISTENT; + cmd.key_type = key_type; + cmd.key_size = key_bits; + cmd.key_lifetime = ELE_KEY_LIFETIME_PERSISTENT; + + /* Sign only, and no export: the private half has no way out. */ + + cmd.key_usage = ELE_KEY_USAGE_SIGN_HASH; + cmd.permitted_algo = algo; + cmd.key_lifecycle = lifecycle; + + /* The lifetime is intent; this is what writes the key to the store. */ + + cmd.flags = ELE_KEY_FLAG_STRICT; + cmd.public_key_addr = (uint32_t)paddr; + + up_flush_dcache((uintptr_t)pubkey, (uintptr_t)pubkey + pubkey_len); + + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 1 + (sizeof(cmd) / sizeof(uint32_t)); + msg.header.command = ELE_GENERATE_KEY_REQ; + memcpy(msg.data, &cmd, sizeof(cmd)); + imx9_ele_update_crc(&msg); + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + if (rsp != NULL) + { + *rsp = msg.data[0]; + } + + if ((msg.data[0] & 0xff) != ELE_OK) + { + return -EIO; + } + + up_invalidate_dcache((uintptr_t)pubkey, (uintptr_t)pubkey + pubkey_len); + + *key_id = msg.data[1]; + return 0; +} + +/**************************************************************************** + * Name: imx9_ele_sig_gen_open / imx9_ele_sig_gen_close + * + * Description: + * Open a signature generation service on a key store. Signing needs one of + * these, and it is another handle to close. + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int imx9_ele_sig_gen_open(uint32_t store, uint32_t *svc, uint32_t *rsp) +{ + struct ele_sig_gen_open_s cmd; + + if (svc == NULL) + { + return -EINVAL; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.key_store_handle = store; + + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 1 + (sizeof(cmd) / sizeof(uint32_t)); + msg.header.command = ELE_SIG_GEN_OPEN_REQ; + memcpy(msg.data, &cmd, sizeof(cmd)); + imx9_ele_update_crc(&msg); + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + if (rsp != NULL) + { + *rsp = msg.data[0]; + } + + if ((msg.data[0] & 0xff) != ELE_OK) + { + return -EIO; + } + + *svc = msg.data[1]; + return 0; +} + +int imx9_ele_sig_gen_close(uint32_t svc) +{ + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 2; + msg.header.command = ELE_SIG_GEN_CLOSE_REQ; + msg.data[0] = svc; + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + return ((msg.data[0] & 0xff) == ELE_OK) ? 0 : -EIO; +} + +/**************************************************************************** + * Name: imx9_ele_sign + * + * Description: + * Sign with a key held in the key store. The key is named by identifier, + * never handed over, so this is the only way to use it. + * + * Input Parameters: + * svc - an open signature generation handle + * key_id - identifier returned by imx9_ele_generate_key() + * algo - the algorithm, which must be the one the key permits + * digest - true if input is already hashed, false to let the enclave hash + * in - message or digest, cache line aligned + * inlen - its length + * out - buffer for the signature + * outlen - its length, 2 * key bytes + 1 for ECDSA + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int imx9_ele_sign(uint32_t svc, uint32_t key_id, uint32_t algo, bool digest, + void *in, size_t inlen, void *out, size_t outlen, + uint32_t *rsp) +{ + struct ele_sign_s cmd; + + uintptr_t in_pa; + uintptr_t out_pa; + size_t in_span; + + if (in == NULL || out == NULL || inlen == 0 || outlen == 0 || + outlen > ELE_SIG_SIZE) + { + return -EINVAL; + } + + if (!IS_ALIGNED((uintptr_t)in, ARMV8A_DCACHE_LINESIZE)) + { + return -EINVAL; + } + + in_span = ALIGN_UP(inlen, ARMV8A_DCACHE_LINESIZE); + + in_pa = imx9_ele_buffer_pa(in); + out_pa = imx9_ele_buffer_pa(g_ele_sig); + if (in_pa == 0 || out_pa == 0 || + in_pa > UINT32_MAX - inlen || out_pa > UINT32_MAX - outlen) + { + return -EFAULT; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.sig_gen_handle = svc; + cmd.key_identifier = key_id; + cmd.message_addr = (uint32_t)in_pa; + cmd.signature_addr = (uint32_t)out_pa; + cmd.message_size = (uint32_t)inlen; + cmd.signature_size = (uint16_t)outlen; + cmd.flags = digest ? ELE_SIG_FLAG_INPUT_DIGEST + : ELE_SIG_FLAG_INPUT_MESSAGE; + cmd.scheme_id = algo; + + up_clean_dcache((uintptr_t)in, (uintptr_t)in + in_span); + up_invalidate_dcache((uintptr_t)g_ele_sig, + (uintptr_t)g_ele_sig + ELE_SIG_SIZE); + + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 1 + (sizeof(cmd) / sizeof(uint32_t)); + msg.header.command = ELE_SIGNATURE_GEN_REQ; + memcpy(msg.data, &cmd, sizeof(cmd)); + imx9_ele_update_crc(&msg); + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + if (rsp != NULL) + { + *rsp = msg.data[0]; + } + + if ((msg.data[0] & 0xff) != ELE_OK) + { + return -EIO; + } + + up_invalidate_dcache((uintptr_t)g_ele_sig, + (uintptr_t)g_ele_sig + ELE_SIG_SIZE); + memcpy(out, g_ele_sig, outlen); + + return 0; +} + +/**************************************************************************** + * Name: imx9_ele_poll_msg + * + * Description: + * Receive a message the enclave sent on its own initiative, rather than a + * reply to something this side asked for. Persisting a key store works + * that way round: the enclave asks the host to store the blob. Unlike + * imx9_ele_receivemsg() this gives up instead of spinning forever, because + * a wrong guess about whether a message is coming would otherwise hang the + * caller. + * + * Input Parameters: + * timeout_us - how long to wait for the header + * + * Output Parameters: + * msg_ptr - the received message + * + * Returned Value: + * Zero (OK) on success, -ETIMEDOUT if nothing arrived. + * + ****************************************************************************/ + +int imx9_ele_poll_msg(struct ele_msg *msg_ptr, uint32_t timeout_us) +{ + uint32_t waited; + int i; + + if (msg_ptr == NULL) + { + return -EINVAL; + } + + for (waited = 0; !(getreg32(ELE_MU_RSR) & 1); waited += ELE_POLL_SLEEP_US) + { + if (waited >= timeout_us) + { + return -ETIMEDOUT; + } + + up_udelay(ELE_POLL_SLEEP_US); + } + + msg_ptr->header.data = getreg32(ELE_MU_RR(0)); + + for (i = 1; i < msg_ptr->header.size; i++) + { + int rx_channel = i % ELE_RR_NUM; + + for (waited = 0; !(getreg32(ELE_MU_RSR) & (1 << rx_channel)); + waited += ELE_POLL_SLEEP_US) + { + if (waited >= timeout_us) + { + return -ETIMEDOUT; + } + + up_udelay(ELE_POLL_SLEEP_US); + } + + msg_ptr->data[i - 1] = getreg32(ELE_MU_RR(rx_channel)); + } + + return 0; +} + +/**************************************************************************** + * Name: imx9_ele_storage_open / imx9_ele_storage_close + * + * Description: + * Open a storage session, without which the enclave will not sync a key + * store. It is the only part of that exchange this side initiates: the + * import and export that follow are requests the enclave sends. + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int imx9_ele_storage_open(uint32_t session, uint32_t *storage, uint32_t *rsp) +{ + struct ele_storage_open_s cmd; + + if (storage == NULL) + { + return -EINVAL; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.session_handle = session; + + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 1 + (sizeof(cmd) / sizeof(uint32_t)); + msg.header.command = ELE_STORAGE_OPEN_REQ; + memcpy(msg.data, &cmd, sizeof(cmd)); + imx9_ele_update_crc(&msg); + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + if (rsp != NULL) + { + *rsp = msg.data[0]; + } + + if ((msg.data[0] & 0xff) != ELE_OK) + { + return -EIO; + } + + *storage = msg.data[1]; + return 0; +} + +int imx9_ele_storage_close(uint32_t storage) +{ + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 2; + msg.header.command = ELE_STORAGE_CLOSE_REQ; + msg.data[0] = storage; + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + return ((msg.data[0] & 0xff) == ELE_OK) ? 0 : -EIO; +} + +/**************************************************************************** + * Name: imx9_ele_blob_get / imx9_ele_blob_put + * + * Description: + * The key store the enclave asked to have persisted, and the one to hand + * back on the next boot. Where it is kept is not this driver's business. + * A store is a master blob plus a chunk per key group, so it is a slot at + * a time. + * + ****************************************************************************/ + +uint32_t imx9_ele_blob_get(unsigned slot, uint32_t *id, uint32_t *id_ext, + const void **blob) +{ + if (slot >= ELE_BLOB_SLOTS || !g_ele_blob[slot].valid) + { + return 0; + } + + if (id != NULL) + { + *id = g_ele_blob[slot].id; + } + + if (id_ext != NULL) + { + *id_ext = g_ele_blob[slot].id_ext; + } + + if (blob != NULL) + { + *blob = g_ele_blob_data[slot]; + } + + return g_ele_blob[slot].len; +} + +int imx9_ele_blob_put(uint32_t id, uint32_t id_ext, const void *blob, + uint32_t len) +{ + int slot; + + if (blob == NULL || len == 0 || len > ELE_BLOB_SIZE) + { + return -EINVAL; + } + + slot = imx9_ele_blob_slot(id, id_ext, true); + if (slot < 0) + { + return -ENOSPC; + } + + memcpy(g_ele_blob_data[slot], blob, len); + g_ele_blob[slot].id = id; + g_ele_blob[slot].id_ext = id_ext; + g_ele_blob[slot].len = len; + g_ele_blob[slot].valid = true; + g_ele_blob[slot].pending = false; + return 0; +} + +/**************************************************************************** + * Name: imx9_ele_storage_master_import + * + * Description: + * Give the enclave back the master blob of a key store it exported before. + * Chunks it asks for by id; the master is the one piece this side pushes, + * and without it the open that follows answers UNKNOWN_ID. + * + * Input Parameters: + * storage - an open storage session + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure, -ENOENT if this side is holding no master blob. + * + ****************************************************************************/ + +int imx9_ele_storage_master_import(uint32_t storage, uint32_t *rsp) +{ + struct ele_master_import_s cmd; + + uintptr_t paddr; + int slot = imx9_ele_blob_slot(ELE_BLOB_MASTER_ID, 0, false); + + if (slot < 0) + { + return -ENOENT; + } + + paddr = imx9_ele_buffer_pa(g_ele_blob_data[slot]); + if (paddr == 0) + { + return -EFAULT; + } + + up_clean_dcache((uintptr_t)g_ele_blob_data[slot], + (uintptr_t)g_ele_blob_data[slot] + ELE_BLOB_SIZE); + + memset(&cmd, 0, sizeof(cmd)); + cmd.storage_handle = storage; + cmd.key_store_addr = (uint32_t)paddr; + cmd.key_store_size = g_ele_blob[slot].len; + + msg.header.version = ELE_VERSION_FW; + msg.header.tag = ELE_CMD_TAG; + msg.header.size = 1 + (sizeof(cmd) / sizeof(uint32_t)); + msg.header.command = ELE_STORAGE_IMPORT_REQ; + memcpy(msg.data, &cmd, sizeof(cmd)); + + imx9_ele_sendmsg(&msg); + imx9_ele_receivemsg(&msg); + + if (rsp != NULL) + { + *rsp = msg.data[0]; + } + + return ((msg.data[0] & 0xff) == ELE_OK) ? 0 : -EIO; +} diff --git a/arch/arm64/src/imx9/imx9_ele.h b/arch/arm64/src/imx9/imx9_ele.h index b28cdd0dcd0..e819316fe25 100644 --- a/arch/arm64/src/imx9/imx9_ele.h +++ b/arch/arm64/src/imx9/imx9_ele.h @@ -29,6 +29,7 @@ #include "hardware/imx9_ele.h" #include +#include #include /**************************************************************************** @@ -235,6 +236,19 @@ int imx9_ele_verify_image(uint32_t img_id, uint32_t *response); int imx9_ele_start_rng(void); +/**************************************************************************** + * Name: imx9_ele_lock / imx9_ele_unlock + * + * Description: + * Serialise use of the ELE mailbox, which every command shares. Recursive. + * imx9_ele_get_random() takes it itself; any other caller holds it across + * each command, or across a whole session that must not be interleaved. + * + ****************************************************************************/ + +void imx9_ele_lock(void); +void imx9_ele_unlock(void); + /**************************************************************************** * Name: imx9_ele_get_trng_state * @@ -287,4 +301,145 @@ int imx9_ele_get_random(void *buf, size_t len); ****************************************************************************/ int imx9_ele_commit(uint32_t info, uint32_t *response); + +/**************************************************************************** + * Name: imx9_ele_session_open / imx9_ele_session_close + * + * Description: + * Open and close an ELE session. The key store services hang off one, and + * the enclave holds the session until it is closed. + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int imx9_ele_sab_init(uint32_t *rsp); +int imx9_ele_session_open(uint32_t *session); +int imx9_ele_session_open_rsp(uint32_t *session, uint32_t *rsp); +int imx9_ele_session_close(uint32_t session); + +/**************************************************************************** + * Name: imx9_ele_key_store_open / imx9_ele_key_store_close + * + * Description: + * Open a key store on an ELE session, creating it if asked. A key + * generated into a store has no command that returns its private half. + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int imx9_ele_key_store_open(uint32_t session, uint32_t id, uint32_t nonce, + uint8_t flags, uint32_t *store); +int imx9_ele_key_store_open_rsp(uint32_t session, uint32_t id, + uint32_t nonce, uint8_t flags, + uint32_t *store, uint32_t *rsp); +int imx9_ele_key_store_close(uint32_t store); + +/**************************************************************************** + * Name: imx9_ele_key_mgmt_open / close, imx9_ele_generate_key + * + * Description: + * Generate a key pair inside the enclave. The public half is returned; the + * private half stays in the key store with no command that returns it. + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int imx9_ele_key_mgmt_open(uint32_t store, uint32_t *mgmt, uint32_t *rsp); +int imx9_ele_key_mgmt_close(uint32_t mgmt); +int imx9_ele_generate_key(uint32_t mgmt, uint16_t key_type, + uint16_t key_bits, uint32_t algo, + uint32_t lifecycle, + void *pubkey, size_t pubkey_len, + uint32_t *key_id, uint32_t *rsp); + +/**************************************************************************** + * Name: imx9_ele_sig_gen_open / close, imx9_ele_sign + * + * Description: + * Sign with a key held in the key store. The key is named by identifier, + * never handed over, so this is the only way to use it. + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int imx9_ele_sig_gen_open(uint32_t store, uint32_t *svc, uint32_t *rsp); +int imx9_ele_sig_gen_close(uint32_t svc); +int imx9_ele_sign(uint32_t svc, uint32_t key_id, uint32_t algo, bool digest, + void *in, size_t inlen, void *out, size_t outlen, + uint32_t *rsp); + +/**************************************************************************** + * Name: imx9_ele_poll_msg + * + * Description: + * Receive a message the enclave sent on its own initiative. + * + * Returned Value: + * Zero (OK) on success, -ETIMEDOUT if nothing arrived. + * + ****************************************************************************/ + +int imx9_ele_poll_msg(struct ele_msg *msg_ptr, uint32_t timeout_us); + +/**************************************************************************** + * Name: imx9_ele_storage_open / close + * + * Description: + * Open a storage session, without which the enclave will not sync a key + * store. + * + * Returned Value: + * Zero (OK) is returned for success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int imx9_ele_storage_open(uint32_t session, uint32_t *storage, + uint32_t *rsp); +int imx9_ele_storage_close(uint32_t storage); + +/**************************************************************************** + * Name: imx9_ele_blob_get / imx9_ele_blob_put + * + * Description: + * The key store the enclave asked to have persisted, a slot at a time. + * Where it is kept is not this driver's business. + * + * Returned Value: + * imx9_ele_blob_get() returns the size of that slot, zero if it holds + * nothing. imx9_ele_blob_put() returns zero (OK), or a negated errno. + * + ****************************************************************************/ + +uint32_t imx9_ele_blob_get(unsigned slot, uint32_t *id, uint32_t *id_ext, + const void **blob); +int imx9_ele_blob_put(uint32_t id, uint32_t id_ext, const void *blob, + uint32_t len); + +/**************************************************************************** + * Name: imx9_ele_storage_master_import + * + * Description: + * Give the enclave back the master blob of a key store it exported. + * Without it the open that follows answers UNKNOWN_ID. + * + * Returned Value: + * Zero (OK), -ENOENT if no master blob is held, or a negated errno. + * + ****************************************************************************/ + +int imx9_ele_storage_master_import(uint32_t storage, uint32_t *rsp); + #endif /* __ARCH_ARM64_SRC_IMX9_IMX9_ELE_H */