feat(realm): call RMI_PDEV ABIs from host realm mgmt
Initial support to invoke RMI PDEV ABIs from host realm management. The
PDEV management testcase host_test_rmi_pdev_calls does PDEV create, get
state, get aux count, communicate, set public key, stop, destroy calls.
These calls internally retrieve device certificate, extract public key,
and establish a secure session with the PCIe device.
Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
Change-Id: If007b5170cbaa932c4f18a01a825a8dc0b752f26
diff --git a/tftf/tests/runtime_services/host_realm_managment/host_crypto_utils.c b/tftf/tests/runtime_services/host_realm_managment/host_crypto_utils.c
new file mode 100644
index 0000000..80834fa
--- /dev/null
+++ b/tftf/tests/runtime_services/host_realm_managment/host_crypto_utils.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2024, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <string.h>
+#include <stdbool.h>
+
+#ifdef MBEDTLS_CONFIG_FILE
+#include <mbedtls/asn1.h>
+#include <mbedtls/bignum.h>
+#include <mbedtls/ecdh.h>
+#include <mbedtls/memory_buffer_alloc.h>
+#include <mbedtls/x509.h>
+#include <mbedtls/x509_crt.h>
+#endif
+
+#include <host_crypto_utils.h>
+#include <test_helpers.h>
+
+#ifdef MBEDTLS_CONFIG_FILE
+#define HOST_MBEDTLS_HEAP_SIZE (2U * SZ_4K)
+static unsigned char host_mbedtls_heap[HOST_MBEDTLS_HEAP_SIZE];
+
+static int host_get_leaf_cert_from_cert_chain(uint8_t *cert_chain,
+ size_t cert_chain_len,
+ uint8_t **leaf_cert,
+ size_t *leaf_cert_len)
+{
+ size_t asn1_len;
+ unsigned char *tag_ptr;
+ uint8_t *cert_chain_end;
+ uint8_t *cur_cert;
+ size_t cur_cert_len;
+ int cur_cert_idx;
+ int rc;
+
+ cur_cert = cert_chain;
+ cur_cert_idx = -1;
+ cert_chain_end = cert_chain + cert_chain_len;
+
+ /* Get leaf certificate from cert_chain */
+ while (true) {
+ tag_ptr = cur_cert;
+ rc = mbedtls_asn1_get_tag(&tag_ptr, cert_chain_end, &asn1_len,
+ MBEDTLS_ASN1_CONSTRUCTED |
+ MBEDTLS_ASN1_SEQUENCE);
+ if (rc != 0) {
+ break;
+ }
+
+ cur_cert_len = asn1_len + (tag_ptr - cur_cert);
+ if (cur_cert + cur_cert_len > cert_chain_end) {
+ cur_cert_idx = -1;
+ break;
+ }
+
+ cur_cert = cur_cert + cur_cert_len;
+ cur_cert_idx++;
+ }
+
+ if (cur_cert_idx == -1) {
+ return -1;
+ }
+
+ *leaf_cert = cur_cert - cur_cert_len;
+ *leaf_cert_len = cur_cert_len;
+ INFO("leaf_cert_len: 0x%lx\n", *leaf_cert_len);
+
+ return 0;
+}
+
+/*
+ * @In:
+ * cert_chain - Certificate chain
+ * cert_chain_len - Length of certificate
+ * public_key_len - Max length of 'public_key' buffer
+ * metadata_len - Max length of 'metadata' buffer
+ *
+ * On success below variables are set:
+ * @Out:
+ * public_key - Public key content
+ * public_key_len - length of 'public_key'
+ * metadata - Public key metadata content
+ * metadata_len - length of 'metadata' if metadata exists, else set to 0
+ * public_key_algo - Public key algorithm
+ */
+static int _host_get_public_key_from_cert_chain(uint8_t *cert_chain,
+ size_t cert_chain_len,
+ void *public_key,
+ size_t *public_key_len,
+ void *public_key_metadata,
+ size_t *public_key_metadata_len,
+ uint8_t *public_key_algo)
+{
+ uint8_t *leaf_cert;
+ size_t leaf_cert_len;
+ mbedtls_x509_crt crt;
+ mbedtls_pk_type_t pk_type;
+ int rc;
+
+ /* Get leaf cert and its length */
+ rc = host_get_leaf_cert_from_cert_chain(cert_chain, cert_chain_len,
+ &leaf_cert, &leaf_cert_len);
+ if (rc != 0) {
+ return -1;
+ }
+
+ /* Get public key from leaf certificate */
+ mbedtls_x509_crt_init(&crt);
+
+ if (mbedtls_x509_crt_parse_der(&crt, leaf_cert, leaf_cert_len) != 0) {
+ return -1;
+ }
+
+ pk_type = mbedtls_pk_get_type(&crt.pk);
+ if (pk_type != MBEDTLS_PK_ECKEY && pk_type != MBEDTLS_PK_RSA) {
+ rc = -1;
+ goto out_crt_free;
+ }
+
+ if (pk_type == MBEDTLS_PK_ECKEY) {
+ mbedtls_ecp_keypair *ecp;
+ mbedtls_ecp_group grp;
+ mbedtls_ecp_point pt;
+
+ ecp = mbedtls_pk_ec(crt.pk);
+ mbedtls_ecp_group_init(&grp);
+ mbedtls_ecp_point_init(&pt);
+ rc = mbedtls_ecp_export(ecp, &grp, NULL, &pt);
+
+ if (rc != 0 || (grp.id != MBEDTLS_ECP_DP_SECP256R1 &&
+ grp.id != MBEDTLS_ECP_DP_SECP384R1)) {
+ mbedtls_ecp_point_free(&pt);
+ mbedtls_ecp_group_free(&grp);
+ rc = -1;
+ goto out_crt_free;
+ }
+
+ rc = mbedtls_ecp_point_write_binary(&grp, &pt,
+ MBEDTLS_ECP_PF_UNCOMPRESSED,
+ public_key_len, public_key,
+ MBEDTLS_ECP_MAX_PT_LEN);
+
+ if (rc == 0) {
+ if (grp.id == MBEDTLS_ECP_DP_SECP256R1) {
+ *public_key_algo =
+ PUBLIC_KEY_ALGO_ECDSA_ECC_NIST_P256;
+ } else {
+ *public_key_algo =
+ PUBLIC_KEY_ALGO_ECDSA_ECC_NIST_P384;
+ }
+
+ /* No metadata for PK_ECKEY type */
+ *public_key_metadata_len = 0;
+ }
+
+ mbedtls_ecp_point_free(&pt);
+ mbedtls_ecp_group_free(&grp);
+ } else {
+ mbedtls_rsa_context *rsa;
+ mbedtls_mpi N;
+ mbedtls_mpi E;
+ size_t N_sz, E_sz;
+
+ rsa = mbedtls_pk_rsa(crt.pk);
+ mbedtls_mpi_init(&N);
+ mbedtls_mpi_init(&E);
+
+ rc = mbedtls_rsa_export(rsa, &N, NULL, NULL, NULL, &E);
+ N_sz = mbedtls_mpi_size(&N);
+ E_sz = mbedtls_mpi_size(&E);
+ INFO("RSA public key len: %ld, metadata len:%ld\n", N_sz, E_sz);
+
+ /* Supported ALGO type RSASSA_3072 (384 bytes) */
+ if (rc == 0 && N_sz == 384) {
+ rc = mbedtls_mpi_write_binary(&N, public_key, N_sz);
+ rc |= mbedtls_mpi_write_binary(&E, public_key_metadata,
+ E_sz);
+ if (rc == 0) {
+ *public_key_algo = PUBLIC_KEY_ALGO_RSASSA_3072;
+ *public_key_len = N_sz;
+ *public_key_metadata_len = E_sz;
+ }
+ } else {
+ rc = -1;
+ }
+
+ mbedtls_mpi_free(&N);
+ mbedtls_mpi_free(&E);
+ }
+
+out_crt_free:
+ mbedtls_x509_crt_free(&crt);
+
+ return rc;
+}
+#endif
+
+int host_get_public_key_from_cert_chain(uint8_t *cert_chain,
+ size_t cert_chain_len,
+ void *public_key,
+ size_t *public_key_len,
+ void *public_key_metadata,
+ size_t *public_key_metadata_len,
+ uint8_t *public_key_algo)
+{
+ int rc;
+
+#ifdef MBEDTLS_CONFIG_FILE
+ mbedtls_memory_buffer_alloc_init(host_mbedtls_heap,
+ sizeof(host_mbedtls_heap));
+
+ rc = _host_get_public_key_from_cert_chain(cert_chain, cert_chain_len,
+ public_key, public_key_len,
+ public_key_metadata,
+ public_key_metadata_len,
+ public_key_algo);
+#else
+ ERROR("MbedTLS not found.\n");
+ rc = -1;
+#endif
+ return rc;
+}
diff --git a/tftf/tests/runtime_services/host_realm_managment/host_realm_rmi.c b/tftf/tests/runtime_services/host_realm_managment/host_realm_rmi.c
index fdb1d4a..e6b6ad4 100644
--- a/tftf/tests/runtime_services/host_realm_managment/host_realm_rmi.c
+++ b/tftf/tests/runtime_services/host_realm_managment/host_realm_rmi.c
@@ -1299,3 +1299,55 @@
*exit_reason = run->exit.exit_reason;
return ret;
}
+
+u_register_t host_rmi_pdev_aux_count(u_register_t pdev_ptr, u_register_t *count)
+{
+ smc_ret_values rets;
+
+ rets = host_rmi_handler(&(smc_args) {SMC_RMI_PDEV_AUX_COUNT, pdev_ptr},
+ 2U);
+ *count = rets.ret1;
+ return rets.ret0;
+}
+
+u_register_t host_rmi_pdev_create(u_register_t pdev_ptr, u_register_t params_ptr)
+{
+ return host_rmi_handler(&(smc_args) {SMC_RMI_PDEV_CREATE, pdev_ptr,
+ params_ptr}, 3U).ret0;
+}
+
+u_register_t host_rmi_pdev_get_state(u_register_t pdev_ptr, u_register_t *state)
+{
+ smc_ret_values rets;
+
+ rets = host_rmi_handler(&(smc_args) {SMC_RMI_PDEV_GET_STATE, pdev_ptr},
+ 2U);
+ *state = rets.ret1;
+ return rets.ret0;
+}
+
+u_register_t host_rmi_pdev_communicate(u_register_t pdev_ptr,
+ u_register_t data_ptr)
+{
+ return host_rmi_handler(&(smc_args) {SMC_RMI_PDEV_COMMUNICATE, pdev_ptr,
+ data_ptr}, 3U).ret0;
+}
+
+u_register_t host_rmi_pdev_set_pubkey(u_register_t pdev_ptr, u_register_t key,
+ u_register_t len, uint8_t algo)
+{
+ return host_rmi_handler(&(smc_args) {SMC_RMI_PDEV_SET_PUBKEY, pdev_ptr,
+ key, len, algo}, 5U).ret0;
+}
+
+u_register_t host_rmi_pdev_stop(u_register_t pdev_ptr)
+{
+ return host_rmi_handler(&(smc_args) {SMC_RMI_PDEV_STOP, pdev_ptr},
+ 2U).ret0;
+}
+
+u_register_t host_rmi_pdev_destroy(u_register_t pdev_ptr)
+{
+ return host_rmi_handler(&(smc_args) {SMC_RMI_PDEV_DESTROY, pdev_ptr},
+ 2U).ret0;
+}
diff --git a/tftf/tests/runtime_services/host_realm_managment/host_rmi_pdev.c b/tftf/tests/runtime_services/host_realm_managment/host_rmi_pdev.c
new file mode 100644
index 0000000..c43c374
--- /dev/null
+++ b/tftf/tests/runtime_services/host_realm_managment/host_rmi_pdev.c
@@ -0,0 +1,580 @@
+/*
+ * Copyright (c) 2024, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <string.h>
+
+#include <heap/page_alloc.h>
+#include <host_crypto_utils.h>
+#include <host_realm_helper.h>
+#include <host_realm_mem_layout.h>
+#include <host_shared_data.h>
+#include <mmio.h>
+#include <pcie.h>
+#include <pcie_doe.h>
+#include <pcie_spec.h>
+#include <platform.h>
+#include <spdm.h>
+#include <test_helpers.h>
+
+/* SPDM_MAX_CERTIFICATE_CHAIN_SIZE is 64KB */
+#define HOST_PDEV_CERT_LEN_MAX (64 * 1024)
+
+/*
+ * Measurement max supported is 4KB.
+ * todo: This will be increased if device supports returning more measurements
+ */
+#define HOST_PDEV_MEAS_LEN_MAX (4 * 1024)
+
+#define DEV_OBJ_CERT 0U
+#define DEV_OBJ_MEASUREMENTS 2U
+#define DEV_OBJ_INTERFACE_REPORT 3U
+
+struct host_tdi {
+ /* PDEV related fields */
+ void *pdev;
+ unsigned long pdev_flags;
+ void *pdev_aux[PDEV_PARAM_AUX_GRANULES_MAX];
+ uint32_t pdev_aux_num;
+ struct rmi_dev_comm_data *dev_comm_data;
+
+ /* Algorithm used to generate device digests */
+ uint8_t pdev_hash_algo;
+
+ /* Certificate, public key fields */
+ uint8_t cert_slot_id;
+ uint8_t *cert_chain;
+ size_t cert_chain_len;
+ void *public_key;
+ size_t public_key_len;
+ void *public_key_metadata;
+ size_t public_key_metadata_len;
+ unsigned char public_key_sig_algo;
+
+ /*
+ * Fields related to cached device measurements.
+ * todo: This will be moved to vdev scope
+ */
+ uint8_t *meas;
+ size_t meas_len;
+
+ /* PCIe details: bdf, DOE, Stream id, IO range */
+ uint32_t bdf;
+ uint32_t doe_cap_base;
+};
+
+static struct host_tdi g_tdi;
+
+static const char * const pdev_state_str[] = {
+ "PDEV_STATE_NEW",
+ "PDEV_STATE_NEEDS_KEY",
+ "PDEV_STATE_HAS_KEY",
+ "PDEV_STATE_READY",
+ "PDEV_STATE_COMMUNICATING",
+ "PDEV_STATE_STOPPED",
+ "RMI_PDEV_STATE_ERROR"
+};
+
+static int host_tdi_pdev_get_state(struct host_tdi *tdi, u_register_t *state)
+{
+ u_register_t ret;
+
+ ret = host_rmi_pdev_get_state((u_register_t)tdi->pdev, state);
+ if (ret != RMI_SUCCESS) {
+ return -1;
+ }
+ return 0;
+}
+
+static bool is_host_tdi_pdev_state(struct host_tdi *tdi, u_register_t exp_state)
+{
+ u_register_t cur_state;
+
+ if (host_tdi_pdev_get_state(tdi, &cur_state) != 0) {
+ return false;
+ }
+
+ if (cur_state != exp_state) {
+ return false;
+ }
+
+ return true;
+}
+
+static int host_tdi_pdev_create(struct host_tdi *tdi)
+{
+ struct rmi_pdev_params *pdev_params;
+ u_register_t ret;
+ uint32_t i;
+
+ pdev_params = (struct rmi_pdev_params *)page_alloc(PAGE_SIZE);
+ memset(pdev_params, 0, GRANULE_SIZE);
+
+ pdev_params->flags = tdi->pdev_flags;
+ pdev_params->cert_id = tdi->cert_slot_id;
+ pdev_params->pdev_id = tdi->bdf;
+ pdev_params->num_aux = tdi->pdev_aux_num;
+ pdev_params->hash_algo = tdi->pdev_hash_algo;
+ for (i = 0; i < tdi->pdev_aux_num; i++) {
+ pdev_params->aux[i] = (uintptr_t)tdi->pdev_aux[i];
+ }
+
+ ret = host_rmi_pdev_create((u_register_t)tdi->pdev,
+ (u_register_t)pdev_params);
+ if (ret != RMI_SUCCESS) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int host_tdi_pdev_set_pubkey(struct host_tdi *tdi)
+{
+ u_register_t ret;
+
+ ret = host_rmi_pdev_set_pubkey((u_register_t)tdi->pdev,
+ (u_register_t)tdi->public_key,
+ (u_register_t)tdi->public_key_len,
+ (u_register_t)tdi->public_key_sig_algo);
+ if (ret != RMI_SUCCESS) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int host_tdi_pdev_stop(struct host_tdi *tdi)
+{
+ u_register_t ret;
+
+ ret = host_rmi_pdev_stop((u_register_t)tdi->pdev);
+ if (ret != RMI_SUCCESS) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int host_tdi_pdev_destroy(struct host_tdi *tdi)
+{
+ u_register_t ret;
+
+ ret = host_rmi_pdev_destroy((u_register_t)tdi->pdev);
+ if (ret != RMI_SUCCESS) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int host_tdi_pdev_cache_device_object(struct host_tdi *tdi,
+ uint8_t obj_type,
+ const uint8_t *obj_buf,
+ size_t obj_len)
+{
+ int rc = -1;
+
+ if (obj_type == DEV_OBJ_CERT) {
+ if ((tdi->cert_chain_len + obj_len) > HOST_PDEV_CERT_LEN_MAX) {
+ return -1;
+ }
+
+ INFO("%s: cache_cert: offset: 0x%lx, len: 0x%lx\n",
+ __func__, tdi->cert_chain_len, obj_len);
+
+ memcpy((void *)(tdi->cert_chain + tdi->cert_chain_len),
+ obj_buf, obj_len);
+ tdi->cert_chain_len += obj_len;
+ rc = 0;
+ } else if (obj_type == DEV_OBJ_MEASUREMENTS) {
+ if ((tdi->meas_len + obj_len) > HOST_PDEV_MEAS_LEN_MAX) {
+ return -1;
+ }
+
+ INFO("%s: cache_meas: offset: 0x%lx, len: 0x%lx\n",
+ __func__, tdi->meas_len, obj_len);
+
+ memcpy((void *)(tdi->meas + tdi->meas_len), obj_buf, obj_len);
+ tdi->meas_len += obj_len;
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/* Call RMI PDEV communicate until the target state is reached */
+static int host_tdi_pdev_communicate(struct host_tdi *tdi,
+ unsigned char target_state)
+{
+ int rc;
+ u_register_t state;
+ u_register_t ret;
+ struct rmi_dev_comm_enter *dev_comm_enter;
+ struct rmi_dev_comm_exit *dev_comm_exit;
+ size_t resp_len;
+
+ dev_comm_enter = &tdi->dev_comm_data->enter;
+ dev_comm_exit = &tdi->dev_comm_data->exit;
+
+ dev_comm_enter->status = RMI_DEV_COMM_ENTER_STATUS_NONE;
+ dev_comm_enter->resp_len = 0;
+
+ if (host_tdi_pdev_get_state(tdi, &state) != 0) {
+ return -1;
+ }
+
+ do {
+ ret = host_rmi_pdev_communicate((u_register_t)tdi->pdev,
+ (u_register_t)tdi->dev_comm_data);
+ if (ret != RMI_SUCCESS) {
+ INFO("rmi_pdev_communicate failed\n");
+ rc = -1;
+ break;
+ }
+
+ /*
+ * If cache is set, then response buffer has the device object
+ * to be cached.
+ */
+ if (EXTRACT(RMI_DEV_COMM_EXIT_FLAGS_CACHE,
+ dev_comm_exit->flags)) {
+ uint8_t *obj_buf;
+ uint8_t obj_type;
+
+ if (dev_comm_exit->cache_len == 0 ||
+ (dev_comm_exit->cache_offset +
+ dev_comm_exit->cache_len) >
+ GRANULE_SIZE) {
+ INFO("Invalid cache offset/length\n");
+ rc = -1;
+ break;
+ }
+
+ if (state == RMI_PDEV_STATE_NEW) {
+ obj_type = DEV_OBJ_CERT;
+ } else if (state == RMI_PDEV_STATE_HAS_KEY) {
+ /* todo: replace with RMI_PDEV_STATE_READY */
+ obj_type = DEV_OBJ_MEASUREMENTS;
+ } else {
+ rc = -1;
+ break;
+ }
+
+ obj_buf = (uint8_t *)dev_comm_enter->resp_addr +
+ dev_comm_exit->cache_offset;
+ rc = host_tdi_pdev_cache_device_object(tdi, obj_type,
+ obj_buf,
+ dev_comm_exit->cache_len);
+ if (rc != 0) {
+ INFO("host_pdev_cache_device_object failed\n");
+ rc = -1;
+ break;
+ }
+ }
+
+ /* Send request to spdm responder */
+ if (EXTRACT(RMI_DEV_COMM_EXIT_FLAGS_SEND,
+ dev_comm_exit->flags)) {
+ uint32_t doe_header;
+
+ /* todo: validate DevCommExit flags */
+ if (dev_comm_exit->protocol ==
+ RMI_DEV_COMM_PROTOCOL_SPDM) {
+ doe_header = DOE_HEADER_1;
+ } else if (dev_comm_exit->protocol ==
+ RMI_DEV_COMM_PROTOCOL_SECURE_SPDM) {
+ doe_header = DOE_HEADER_2;
+ } else {
+ INFO("Invalid dev_comm_exit.protocol\n");
+ rc = -1;
+ break;
+ }
+
+ rc = pcie_doe_communicate(doe_header, tdi->bdf, tdi->doe_cap_base,
+ (void *)dev_comm_enter->req_addr,
+ dev_comm_exit->req_len,
+ (void *)dev_comm_enter->resp_addr,
+ &resp_len);
+
+ /*
+ * Set IoEnter args for next pdev_communicate. Upon
+ * success or error call pdev_communicate
+ */
+ if (rc == 0) {
+ dev_comm_enter->status =
+ RMI_DEV_COMM_ENTER_STATUS_SUCCESS;
+ dev_comm_enter->resp_len = resp_len;
+ } else {
+ dev_comm_enter->status =
+ RMI_DEV_COMM_ENTER_STATUS_ERROR;
+ dev_comm_enter->resp_len = 0;
+ }
+ }
+
+ rc = host_tdi_pdev_get_state(tdi, &state);
+ if (rc != 0) {
+ break;
+ }
+ } while ((state != target_state) && (state != RMI_PDEV_STATE_ERROR));
+
+ return rc;
+}
+
+/*
+ * Invoke RMI handler to transition PDEV state to 'to_state'
+ */
+static int host_tdi_pdev_transition(struct host_tdi *tdi, unsigned char to_state)
+{
+ int rc;
+
+ switch (to_state) {
+ case RMI_PDEV_STATE_NEW:
+ rc = host_tdi_pdev_create(tdi);
+ break;
+ case RMI_PDEV_STATE_NEEDS_KEY:
+ rc = host_tdi_pdev_communicate(tdi, RMI_PDEV_STATE_NEEDS_KEY);
+ break;
+ case RMI_PDEV_STATE_HAS_KEY:
+ rc = host_tdi_pdev_set_pubkey(tdi);
+ break;
+ case RMI_PDEV_STATE_READY:
+ rc = host_tdi_pdev_communicate(tdi, RMI_PDEV_STATE_READY);
+ break;
+ case RMI_PDEV_STATE_STOPPING:
+ rc = host_tdi_pdev_stop(tdi);
+ break;
+ case RMI_PDEV_STATE_STOPPED:
+ rc = host_tdi_pdev_communicate(tdi, RMI_PDEV_STATE_STOPPED);
+ break;
+ default:
+ rc = -1;
+ }
+
+ if (rc != 0) {
+ INFO("RMI command failed\n");
+ return -1;
+ }
+
+ if (!is_host_tdi_pdev_state(tdi, to_state)) {
+ ERROR("PDEV state not [%s]\n", pdev_state_str[to_state]);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Allocate granules needed for a PDEV object like device communication data,
+ * response buffer, PDEV AUX granules and memory required to store cert_chain
+ */
+static int host_tdi_pdev_setup(struct host_tdi *tdi)
+{
+ u_register_t ret, count;
+ int i;
+
+ memset(tdi, 0, sizeof(struct host_tdi));
+
+ /* Allocate granule for PDEV and delegate */
+ tdi->pdev = page_alloc(PAGE_SIZE);
+ memset(tdi->pdev, 0, GRANULE_SIZE);
+ ret = host_rmi_granule_delegate((u_register_t)tdi->pdev);
+ if (ret != RMI_SUCCESS) {
+ ERROR("PDEV delegate failed 0x%lx\n", ret);
+ return -1;
+ }
+
+ /* Set flags as IO coherent device protected by end to end IDE. */
+ tdi->pdev_flags = INPLACE(RMI_PDEV_FLAGS_PROT_CONFIG,
+ RMI_PDEV_IOCOH_E2E_IDE);
+
+ /* Get num of aux granules required for this PDEV */
+ ret = host_rmi_pdev_aux_count(tdi->pdev_flags, &count);
+ if (ret != RMI_SUCCESS) {
+ ERROR("host_rmi_pdev_aux_count() failed 0x%lx\n", ret);
+ return -1;
+ }
+ tdi->pdev_aux_num = count;
+
+ /* Allocate aux granules for PDEV and delegate */
+ INFO("PDEV create requires %u aux pages\n", tdi->pdev_aux_num);
+ for (i = 0; i < tdi->pdev_aux_num; i++) {
+ tdi->pdev_aux[i] = page_alloc(PAGE_SIZE);
+ ret = host_rmi_granule_delegate((u_register_t)tdi->pdev_aux[i]);
+ if (ret != RMI_SUCCESS) {
+ ERROR("Aux granule delegate failed 0x%lx\n", ret);
+ return -1;
+ }
+ }
+
+ /* Allocate dev_comm_data and send/recv buffer for Dev communication */
+ tdi->dev_comm_data = (struct rmi_dev_comm_data *)page_alloc(PAGE_SIZE);
+ memset(tdi->dev_comm_data, 0, sizeof(struct rmi_dev_comm_data));
+ tdi->dev_comm_data->enter.req_addr = (unsigned long)
+ page_alloc(PAGE_SIZE);
+ tdi->dev_comm_data->enter.resp_addr = (unsigned long)
+ page_alloc(PAGE_SIZE);
+
+ /* Allocate buffer to cache device certificate */
+ tdi->cert_slot_id = 0;
+ tdi->cert_chain = (uint8_t *)page_alloc(HOST_PDEV_CERT_LEN_MAX);
+ tdi->cert_chain_len = 0;
+ if (tdi->cert_chain == NULL) {
+ return -1;
+ }
+
+ /* Allocate buffer to store extracted public key */
+ tdi->public_key = (void *)page_alloc(PAGE_SIZE);
+ if (tdi->public_key == NULL) {
+ return -1;
+ }
+ tdi->public_key_len = PAGE_SIZE;
+
+ /* Allocate buffer to store public key metadata */
+ tdi->public_key_metadata = (void *)page_alloc(PAGE_SIZE);
+ if (tdi->public_key_metadata == NULL) {
+ return -1;
+ }
+ tdi->public_key_metadata_len = PAGE_SIZE;
+
+ /* Allocate buffer to cache device measurements */
+ tdi->meas = (uint8_t *)page_alloc(HOST_PDEV_MEAS_LEN_MAX);
+ tdi->meas_len = 0;
+ if (tdi->meas == NULL) {
+ return -1;
+ }
+
+ /* Set algorithm to use for device digests */
+ tdi->pdev_hash_algo = RMI_HASH_SHA_512;
+
+ return 0;
+}
+
+/*
+ * Stop PDEV and ternimate secure session and call PDEV destroy
+ */
+static int host_tdi_pdev_reclaim(struct host_tdi *tdi)
+{
+ int rc;
+
+ /* Move the device to STOPPING state */
+ rc = host_tdi_pdev_transition(tdi, RMI_PDEV_STATE_STOPPING);
+ if (rc != 0) {
+ INFO("PDEV transition: to PDEV_STATE_STOPPING failed\n");
+ return -1;
+ }
+
+ /* Do pdev_communicate to terminate secure session */
+ rc = host_tdi_pdev_transition(tdi, RMI_PDEV_STATE_STOPPED);
+ if (rc != 0) {
+ INFO("PDEV transition: to PDEV_STATE_STOPPED failed\n");
+ return -1;
+ }
+
+ rc = host_tdi_pdev_destroy(tdi);
+ if (rc != 0) {
+ INFO("PDEV transition: to STATE_NULL failed\n");
+ return -1;
+ }
+
+ /* Undelegate all the delegated pages */
+ for (int i = 0; i < tdi->pdev_aux_num; i++) {
+ host_rmi_granule_undelegate((u_register_t)tdi->pdev_aux[i]);
+ }
+ host_rmi_granule_undelegate((u_register_t)tdi->pdev);
+
+ return rc;
+}
+
+/*
+ * This invokes various RMI calls related to PDEV management that does
+ * PDEV create/communicate/set_key/abort/stop/destroy on a device.
+ */
+test_result_t host_test_rmi_pdev_calls(void)
+{
+ u_register_t rmi_feat_reg0;
+ uint32_t pdev_bdf, doe_cap_base;
+ struct host_tdi *tdi;
+ uint8_t public_key_algo;
+ int ret, rc;
+
+ CHECK_DA_SUPPORT_IN_RMI(rmi_feat_reg0);
+ SKIP_TEST_IF_DOE_NOT_SUPPORTED(pdev_bdf, doe_cap_base);
+
+ /* Initialize Host NS heap memory */
+ ret = page_pool_init((u_register_t)PAGE_POOL_BASE,
+ (u_register_t)PAGE_POOL_MAX_SIZE);
+ if (ret != HEAP_INIT_SUCCESS) {
+ ERROR("Failed to init heap pool %d\n", ret);
+ return TEST_RESULT_FAIL;
+ }
+
+ tdi = &g_tdi;
+
+ /* Allocate granules. Skip DA ABIs if host_pdev_setup fails */
+ rc = host_tdi_pdev_setup(tdi);
+ if (rc == -1) {
+ INFO("host_pdev_setup failed. skipping DA ABIs...\n");
+ return TEST_RESULT_SKIPPED;
+ }
+
+ /* todo: move to tdi_pdev_setup */
+ tdi->bdf = pdev_bdf;
+ tdi->doe_cap_base = doe_cap_base;
+
+ /* Call rmi_pdev_create to transition PDEV to STATE_NEW */
+ rc = host_tdi_pdev_transition(tdi, RMI_PDEV_STATE_NEW);
+ if (rc != 0) {
+ ERROR("PDEV transition: NULL -> STATE_NEW failed\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Call rmi_pdev_communicate to transition PDEV to NEEDS_KEY */
+ rc = host_tdi_pdev_transition(tdi, RMI_PDEV_STATE_NEEDS_KEY);
+ if (rc != 0) {
+ ERROR("PDEV transition: PDEV_NEW -> PDEV_NEEDS_KEY failed\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Get public key. Verifying cert_chain not done by host but by Realm? */
+ rc = host_get_public_key_from_cert_chain(tdi->cert_chain,
+ tdi->cert_chain_len,
+ tdi->public_key,
+ &tdi->public_key_len,
+ tdi->public_key_metadata,
+ &tdi->public_key_metadata_len,
+ &public_key_algo);
+ if (rc != 0) {
+ ERROR("Get public key failed\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ if (public_key_algo == PUBLIC_KEY_ALGO_ECDSA_ECC_NIST_P256) {
+ tdi->public_key_sig_algo = RMI_SIGNATURE_ALGORITHM_ECDSA_P256;
+ } else if (public_key_algo == PUBLIC_KEY_ALGO_ECDSA_ECC_NIST_P384) {
+ tdi->public_key_sig_algo = RMI_SIGNATURE_ALGORITHM_ECDSA_P384;
+ } else {
+ tdi->public_key_sig_algo = RMI_SIGNATURE_ALGORITHM_RSASSA_3072;
+ }
+ INFO("DEV public key len/sig_algo: %ld/%d\n", tdi->public_key_len,
+ tdi->public_key_sig_algo);
+
+ /* Call rmi_pdev_set_key transition PDEV to HAS_KEY */
+ rc = host_tdi_pdev_transition(tdi, RMI_PDEV_STATE_HAS_KEY);
+ if (rc != 0) {
+ INFO("PDEV transition: PDEV_NEEDS_KEY -> PDEV_HAS_KEY failed\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Call rmi_pdev_comminucate to transition PDEV to READY state */
+ rc = host_tdi_pdev_transition(tdi, RMI_PDEV_STATE_READY);
+ if (rc != 0) {
+ INFO("PDEV transition: PDEV_HAS_KEY -> PDEV_READY failed\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ host_tdi_pdev_reclaim(tdi);
+
+ return TEST_RESULT_SUCCESS;
+}
diff --git a/tftf/tests/tests-realm-payload.mk b/tftf/tests/tests-realm-payload.mk
index 4da8e3e..b6833e1 100644
--- a/tftf/tests/tests-realm-payload.mk
+++ b/tftf/tests/tests-realm-payload.mk
@@ -25,6 +25,8 @@
host_realm_helper.c \
host_shared_data.c \
rmi_delegate_tests.c \
+ host_rmi_pdev.c \
+ host_crypto_utils.c \
)
TESTS_SOURCES += \
@@ -56,4 +58,7 @@
pcie.c \
pcie_doe.c \
)
+
+include lib/ext_mbedtls/mbedtls.mk
+
endif
diff --git a/tftf/tests/tests-realm-payload.xml b/tftf/tests/tests-realm-payload.xml
index d95c474..da88701 100644
--- a/tftf/tests/tests-realm-payload.xml
+++ b/tftf/tests/tests-realm-payload.xml
@@ -137,5 +137,8 @@
function="doe_discovery_test" />
<testcase name="SPDM Get Version"
function="spdm_version_test" />
+ <!-- Invoke RMI calls related to PDEV management -->
+ <testcase name="Invoke RMI PDEV calls "
+ function="host_test_rmi_pdev_calls" />
</testsuite>
</testsuites>