DPE: Add Destroy Context command

Signed-off-by: Maulik Patel <maulik.patel@arm.com>
Change-Id: Ibd70e6d7c61672c86a39f3d026d8e083f0b96bc8
diff --git a/partitions/dice_protection_environment/dpe_cmd_decode.c b/partitions/dice_protection_environment/dpe_cmd_decode.c
index 0c4215f..ff2595e 100644
--- a/partitions/dice_protection_environment/dpe_cmd_decode.c
+++ b/partitions/dice_protection_environment/dpe_cmd_decode.c
@@ -181,6 +181,52 @@
     return DPE_NO_ERROR;
 }
 
+static dpe_error_t decode_destroy_context(QCBORDecodeContext *decode_ctx,
+                                          QCBOREncodeContext *encode_ctx)
+{
+    dpe_error_t dpe_err;
+    QCBORError qcbor_err;
+    UsefulBufC out;
+    int context_handle;
+    bool destroy_recursively;
+
+    /* Decode Destroy context command */
+    QCBORDecode_EnterMap(decode_ctx, NULL);
+
+    QCBORDecode_GetByteStringInMapN(decode_ctx, DPE_DESTROY_CONTEXT_HANDLE,
+                                    &out);
+    if (out.len != sizeof(context_handle)) {
+        return DPE_INVALID_COMMAND;
+    }
+    memcpy(&context_handle, out.ptr, out.len);
+
+    QCBORDecode_GetBoolInMapN(decode_ctx, DPE_DESTROY_CONTEXT_RECURSIVELY,
+                              &destroy_recursively);
+
+    QCBORDecode_ExitMap(decode_ctx);
+
+    /* Exit top level array */
+    QCBORDecode_ExitArray(decode_ctx);
+
+    /* Finish and check for errors before using decoded values */
+    qcbor_err = QCBORDecode_Finish(decode_ctx);
+    if (qcbor_err != QCBOR_SUCCESS) {
+        return DPE_INVALID_COMMAND;
+    }
+
+    dpe_err = destroy_context_request(context_handle, destroy_recursively);
+    if (dpe_err != DPE_NO_ERROR) {
+        return dpe_err;
+    }
+
+    /* Encode response */
+    QCBOREncode_OpenArray(encode_ctx);
+    QCBOREncode_AddInt64(encode_ctx, DPE_NO_ERROR);
+    QCBOREncode_CloseArray(encode_ctx);
+
+    return DPE_NO_ERROR;
+}
+
 static dpe_error_t decode_certify_key(QCBORDecodeContext *decode_ctx,
                                       QCBOREncodeContext *encode_ctx)
 {
@@ -313,6 +359,9 @@
         case DPE_CERTIFY_KEY:
             dpe_err = decode_certify_key(&decode_ctx, &encode_ctx);
             break;
+        case DPE_DESTROY_CONTEXT:
+            dpe_err = decode_destroy_context(&decode_ctx, &encode_ctx);
+            break;
         default:
             dpe_err = DPE_INVALID_COMMAND;
             break;
diff --git a/partitions/dice_protection_environment/dpe_context_mngr.c b/partitions/dice_protection_environment/dpe_context_mngr.c
index d2bc2ab..8750922 100644
--- a/partitions/dice_protection_environment/dpe_context_mngr.c
+++ b/partitions/dice_protection_environment/dpe_context_mngr.c
@@ -410,3 +410,62 @@
 
     return DPE_NO_ERROR;
 }
+
+dpe_error_t destroy_context_request(int input_ctx_handle,
+                                    bool destroy_recursively)
+{
+    uint16_t input_ctx_idx, linked_layer_idx;
+    int i;
+    bool is_layer_empty;
+
+    log_destroy_context(input_ctx_handle, destroy_recursively);
+
+    /* Get child component index and linked layer from the input handle */
+    input_ctx_idx = GET_IDX(input_ctx_handle);
+
+#ifdef TFM_S_REG_TEST
+    if (input_ctx_idx == 0) {
+        invalidate_layer(DPE_ROT_LAYER_IDX);
+        set_context_to_default(0);
+        return DPE_NO_ERROR;
+    }
+#endif /* TFM_S_REG_TEST */
+
+    /* Validate input handle */
+    if (!is_input_handle_valid(input_ctx_handle)) {
+        return DPE_INVALID_ARGUMENT;
+    }
+    linked_layer_idx = component_ctx_array[input_ctx_idx].linked_layer_idx;
+
+#ifndef TFM_S_REG_TEST
+    if (linked_layer_idx <= DPE_DESTROY_CONTEXT_THRESHOLD_LAYER_IDX) {
+        /* All layers till hypervisor cannot be destroyed dynamically */
+        return DPE_INVALID_ARGUMENT;
+    }
+#endif /* !TFM_S_REG_TEST */
+
+
+    if (!destroy_recursively) {
+        set_context_to_default(input_ctx_idx);
+    } else {
+        //TODO: To be implemented
+    }
+
+    assert(linked_layer_idx < MAX_NUM_OF_LAYERS);
+
+    /* Close the layer if all of its contexts are destroyed */
+    is_layer_empty = true;
+    for (i = 0; i < MAX_NUM_OF_COMPONENTS; i++) {
+        if (component_ctx_array[i].linked_layer_idx == linked_layer_idx) {
+            /* There are active component context in the layer */
+            is_layer_empty = false;
+            break;
+        }
+    }
+
+    if (is_layer_empty) {
+        invalidate_layer(linked_layer_idx);
+    }
+
+    return DPE_NO_ERROR;
+}
diff --git a/partitions/dice_protection_environment/dpe_context_mngr.h b/partitions/dice_protection_environment/dpe_context_mngr.h
index 8335263..8e679f6 100644
--- a/partitions/dice_protection_environment/dpe_context_mngr.h
+++ b/partitions/dice_protection_environment/dpe_context_mngr.h
@@ -23,10 +23,16 @@
 #define INVALID_HANDLE 0xFFFFFFFF
 #define INVALID_COMPONENT_IDX 0xFFFF
 #define INVALID_NONCE_VALUE  0xFFFF
-#define MAX_NUM_OF_COMPONENTS 30
-#define DPE_ROT_LAYER_IDX 0
-#define MAX_NUM_OF_LAYERS 10
 #define INVALID_LAYER_IDX 65535
+#define DPE_ROT_LAYER_IDX 0
+
+/* Below configuration defines are platform dependant */
+#define MAX_NUM_OF_COMPONENTS 30
+#define MAX_NUM_OF_LAYERS 10
+#define DPE_PLATFORM_LAYER_IDX 1
+#define DPE_SECURE_WORLD_AND_HYPERVISOR_LAYER_IDX 2
+/* Below threshold defines the threshold below which a context cannot be destroyed */
+#define DPE_DESTROY_CONTEXT_THRESHOLD_LAYER_IDX DPE_SECURE_WORLD_AND_HYPERVISOR_LAYER_IDX
 
 /* Most significant 16 bits represent nonce & remaining 16 bits represent component index */
 #define GET_IDX(handle) ((handle) & 0xffff)
@@ -123,6 +129,19 @@
                                  int *new_parent_context_handle);
 
 /**
+ * \brief Destroys a component context and optionally depending on argument
+ *        destroy_recursively, destroys all its child context too.
+ *
+ * \param[in]  input_context_handle      Input handle to child component context
+ * \param[in]  destroy_recursively       Flag to indicate if all derived contexts
+ *                                       should also be destroyed recursively.
+ *
+ * \return Returns error code of type dpe_error_t
+ */
+dpe_error_t destroy_context_request(int input_ctx_handle,
+                                    bool destroy_recursively);
+
+/**
  * \brief Initialise all DPE Layer and component contexts
  *
  */
diff --git a/partitions/dice_protection_environment/dpe_log.c b/partitions/dice_protection_environment/dpe_log.c
index 5a37706..5e4da40 100644
--- a/partitions/dice_protection_environment/dpe_log.c
+++ b/partitions/dice_protection_environment/dpe_log.c
@@ -74,6 +74,14 @@
     LOG_DBGFMT(" - client_id = %d\r\n", client_id);
 }
 
+void log_destroy_context(int context_handle, bool destroy_recursively)
+{
+    LOG_DBGFMT("DPE DestroyContext:\r\n");
+    LOG_DBGFMT(" - context_handle index = %d\r\n", GET_IDX(context_handle));
+    LOG_DBGFMT(" - context_handle nonce = %d\r\n", GET_NONCE(context_handle));
+    LOG_DBGFMT(" - destroy_recursively = %d\r\n", destroy_recursively);
+}
+
 void log_certify_key(int context_handle,
                      bool retain_context,
                      const uint8_t *public_key,
diff --git a/partitions/dice_protection_environment/dpe_log.h b/partitions/dice_protection_environment/dpe_log.h
index 97eb752..8f2f5b8 100644
--- a/partitions/dice_protection_environment/dpe_log.h
+++ b/partitions/dice_protection_environment/dpe_log.h
@@ -33,6 +33,12 @@
                       int32_t client_id);
 
 /**
+ * \brief Log the destroy context command parameters.
+ */
+void log_destroy_context(int context_handle,
+                         bool destroy_recursively);
+
+/**
  * \brief Log the certify key command parameters.
  */
 void log_certify_key(int context_handle,
@@ -46,6 +52,7 @@
 
 #define log_derive_rot_context(...)
 #define log_derive_child(...)
+#define log_destroy_context(...)
 #define log_certify_key(...)
 
 #endif /* TFM_PARTITION_LOG_LEVEL */
diff --git a/partitions/dice_protection_environment/interface/include/dice_protection_environment.h b/partitions/dice_protection_environment/interface/include/dice_protection_environment.h
index 8aefec2..12082ad 100644
--- a/partitions/dice_protection_environment/interface/include/dice_protection_environment.h
+++ b/partitions/dice_protection_environment/interface/include/dice_protection_environment.h
@@ -67,6 +67,20 @@
                  int                   *new_parent_context_handle);
 
 /**
+ * \brief Destroys a DPE context.
+ *
+ * \param[in] context_handle       Input context handle for the DPE context to
+ *                                 be destroyed.
+ * \param[in] destroy_recursively  Flag to indicate whether all derived contexts
+ *                                 should also be destroyed recursively.
+ *
+ * \return Returns error code of type dpe_error_t
+ */
+dpe_error_t
+dpe_destroy_context(int context_handle,
+                    bool destroy_recursively);
+
+/**
  * \brief Certifies an attestation key with a new leaf certificate and returns
  *        the certificate chain containing all certificates up to and including
  *        the new leaf certificate.
diff --git a/partitions/dice_protection_environment/interface/include/dpe_client.h b/partitions/dice_protection_environment/interface/include/dpe_client.h
index 8a21d26..ef4e376 100644
--- a/partitions/dice_protection_environment/interface/include/dpe_client.h
+++ b/partitions/dice_protection_environment/interface/include/dpe_client.h
@@ -59,6 +59,11 @@
     DPE_DERIVE_CHILD_INTERNAL_INPUTS = 8,
 };
 
+enum dpe_destroy_context_input_labels_t {
+    DPE_DESTROY_CONTEXT_HANDLE = 1,
+    DPE_DESTROY_CONTEXT_RECURSIVELY = 2,
+};
+
 enum dpe_derive_child_output_labels_t {
     DPE_DERIVE_CHILD_NEW_CONTEXT_HANDLE = 1,
     DPE_DERIVE_CHILD_NEW_SESSION_RESPONDER_HANDSHAKE = 2,
diff --git a/partitions/dice_protection_environment/interface/src/dpe_cmd_encode.c b/partitions/dice_protection_environment/interface/src/dpe_cmd_encode.c
index 35c7631..1112560 100644
--- a/partitions/dice_protection_environment/interface/src/dpe_cmd_encode.c
+++ b/partitions/dice_protection_environment/interface/src/dpe_cmd_encode.c
@@ -25,6 +25,11 @@
     int new_parent_context_handle;
 };
 
+struct destroy_context_input_t {
+    int context_handle;
+    bool destroy_recursively;
+};
+
 struct certify_key_input_t {
     int context_handle;
     bool retain_context;
@@ -120,6 +125,31 @@
     return QCBOREncode_Finish(&encode_ctx, encoded_buf);
 }
 
+static QCBORError encode_destroy_context(const struct destroy_context_input_t *args,
+                                         UsefulBuf buf,
+                                         UsefulBufC *encoded_buf)
+{
+    QCBOREncodeContext encode_ctx;
+
+    QCBOREncode_Init(&encode_ctx, buf);
+
+    QCBOREncode_OpenArray(&encode_ctx);
+    QCBOREncode_AddUInt64(&encode_ctx, DPE_DESTROY_CONTEXT);
+
+    /* Encode DestroyContext command */
+    QCBOREncode_OpenMap(&encode_ctx);
+    QCBOREncode_AddBytesToMapN(&encode_ctx, DPE_DESTROY_CONTEXT_HANDLE,
+                               (UsefulBufC){ &args->context_handle,
+                                             sizeof(args->context_handle) });
+    QCBOREncode_AddBoolToMapN(&encode_ctx, DPE_DESTROY_CONTEXT_RECURSIVELY,
+                              args->destroy_recursively);
+    QCBOREncode_CloseMap(&encode_ctx);
+
+    QCBOREncode_CloseArray(&encode_ctx);
+
+    return QCBOREncode_Finish(&encode_ctx, encoded_buf);
+}
+
 static QCBORError decode_derive_child_response(UsefulBufC encoded_buf,
                                                struct derive_child_output_t *args,
                                                dpe_error_t *dpe_err)
@@ -164,6 +194,25 @@
     return QCBORDecode_Finish(&decode_ctx);
 }
 
+static QCBORError decode_destroy_context_response(UsefulBufC encoded_buf,
+                                                  dpe_error_t *dpe_err)
+{
+    QCBORDecodeContext decode_ctx;
+    int64_t response_dpe_err;
+
+    QCBORDecode_Init(&decode_ctx, encoded_buf, QCBOR_DECODE_MODE_NORMAL);
+
+    QCBORDecode_EnterArray(&decode_ctx, NULL);
+
+    /* Get the error code from the response */
+    QCBORDecode_GetInt64(&decode_ctx, &response_dpe_err);
+    *dpe_err = (dpe_error_t)response_dpe_err;
+
+    QCBORDecode_ExitArray(&decode_ctx);
+
+    return QCBORDecode_Finish(&decode_ctx);
+}
+
 static QCBORError encode_certify_key(const struct certify_key_input_t *args,
                                      UsefulBuf buf,
                                      UsefulBufC *encoded_buf)
@@ -291,6 +340,41 @@
     return DPE_NO_ERROR;
 }
 
+dpe_error_t dpe_destroy_context(int context_handle, bool destroy_recursively)
+{
+    int32_t service_err;
+    dpe_error_t dpe_err;
+    QCBORError qcbor_err;
+    UsefulBufC encoded_buf;
+    UsefulBuf_MAKE_STACK_UB(cmd_buf, 12);
+
+    const struct destroy_context_input_t in_args = {
+        context_handle,
+        destroy_recursively
+    };
+
+    qcbor_err = encode_destroy_context(&in_args, cmd_buf, &encoded_buf);
+    if (qcbor_err != QCBOR_SUCCESS) {
+        return DPE_INTERNAL_ERROR;
+    }
+
+    service_err = dpe_client_call(encoded_buf.ptr, encoded_buf.len,
+                                  cmd_buf.ptr, &cmd_buf.len);
+    if (service_err != 0) {
+        return DPE_INTERNAL_ERROR;
+    }
+
+    qcbor_err = decode_destroy_context_response(UsefulBuf_Const(cmd_buf),
+                                                &dpe_err);
+    if (qcbor_err != QCBOR_SUCCESS) {
+        return DPE_INTERNAL_ERROR;
+    } else if (dpe_err != DPE_NO_ERROR) {
+        return dpe_err;
+    }
+
+    return DPE_NO_ERROR;
+}
+
 dpe_error_t dpe_certify_key(int context_handle,
                             bool retain_context,
                             const uint8_t *public_key,