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 */