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/include/runtime_services/host_realm_managment/host_crypto_utils.h b/include/runtime_services/host_realm_managment/host_crypto_utils.h
new file mode 100644
index 0000000..2f865c0
--- /dev/null
+++ b/include/runtime_services/host_realm_managment/host_crypto_utils.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2024, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef HOST_CRYPTO_UTILS_H
+#define HOST_CRYPTO_UTILS_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define PUBLIC_KEY_ALGO_ECDSA_ECC_NIST_P256 0x10
+#define PUBLIC_KEY_ALGO_ECDSA_ECC_NIST_P384 0x20
+#define PUBLIC_KEY_ALGO_RSASSA_3072 0x30
+
+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);
+
+#endif /* HOST_CRYPTO_UTILS_H */
diff --git a/include/runtime_services/host_realm_managment/host_realm_rmi.h b/include/runtime_services/host_realm_managment/host_realm_rmi.h
index 484deeb..8f487d0 100644
--- a/include/runtime_services/host_realm_managment/host_realm_rmi.h
+++ b/include/runtime_services/host_realm_managment/host_realm_rmi.h
@@ -18,6 +18,12 @@
#define RMI_FNUM_MIN_VALUE U(0x150)
#define RMI_FNUM_MAX_VALUE U(0x18F)
+/*
+ * Defines member of structure and reserves space
+ * for the next member with specified offset.
+ */
+#define SET_MEMBER_RMI SET_MEMBER
+
/* Get RMI fastcall std FID from offset */
#define SMC64_RMI_FID(_offset) \
((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT) | \
@@ -92,6 +98,11 @@
#define RMI_DATA_DESTROY SMC64_RMI_FID(U(0x5))
/*
+ * FID: 0xC4000156
+ */
+#define SMC_RMI_PDEV_AUX_COUNT SMC64_RMI_FID(U(0x6))
+
+/*
* arg0 == RD address
*/
#define RMI_REALM_ACTIVATE SMC64_RMI_FID(U(0x7))
@@ -219,6 +230,156 @@
*/
#define RMI_RTT_SET_RIPAS SMC64_RMI_FID(U(0x19))
+/*
+ * FID: 0xC4000170
+ */
+#define SMC_RMI_GRANULE_DEV_DELEGATE SMC64_RMI_FID(U(0x20))
+
+/*
+ * FID: 0xC4000171
+ */
+#define SMC_RMI_GRANULE_DEV_UNDELEGATE SMC64_RMI_FID(U(0x21))
+
+/*
+ * FID: 0xC4000172
+ */
+#define SMC_RMI_DEV_MAP SMC64_RMI_FID(U(0x22))
+
+/*
+ * FID: 0xC4000173
+ */
+#define SMC_RMI_DEV_UNMAP SMC64_RMI_FID(U(0x23))
+
+/*
+ * FID: 0xC4000174
+ */
+#define SMC_RMI_PDEV_ABORT SMC64_RMI_FID(U(0x24))
+
+/*
+ * FID: 0xC4000175
+ */
+#define SMC_RMI_PDEV_COMMUNICATE SMC64_RMI_FID(U(0x25))
+
+/*
+ * FID: 0xC4000176
+ */
+#define SMC_RMI_PDEV_CREATE SMC64_RMI_FID(U(0x26))
+
+/*
+ * FID: 0xC4000177
+ */
+#define SMC_RMI_PDEV_DESTROY SMC64_RMI_FID(U(0x27))
+
+/*
+ * FID: 0xC4000178
+ */
+#define SMC_RMI_PDEV_GET_STATE SMC64_RMI_FID(U(0x28))
+
+/*
+ * FID: 0xC4000179
+ */
+#define SMC_RMI_PDEV_IDE_RESET SMC64_RMI_FID(U(0x29))
+
+/*
+ * FID: 0xC400017A
+ */
+#define SMC_RMI_PDEV_NOTIFY SMC64_RMI_FID(U(0x2A))
+
+/*
+ * FID: 0xC400017B
+ */
+#define SMC_RMI_PDEV_SET_PUBKEY SMC64_RMI_FID(U(0x2B))
+
+/*
+ * FID: 0xC400017C
+ */
+#define SMC_RMI_PDEV_STOP SMC64_RMI_FID(U(0x2C))
+
+/*
+ * FID: 0xC400017D
+ */
+#define SMC_RMI_RTT_AUX_CREATE SMC64_RMI_FID(U(0x2D))
+
+/*
+ * FID: 0xC400017E
+ */
+#define SMC_RMI_RTT_AUX_DESTROY SMC64_RMI_FID(U(0x2E))
+
+/*
+ * FID: 0xC400017F
+ */
+#define SMC_RMI_RTT_AUX_FOLD SMC64_RMI_FID(U(0x2F))
+
+/*
+ * FID: 0xC4000180
+ */
+#define SMC_RMI_RTT_AUX_MAP_PROTECTED SMC64_RMI_FID(U(0x30))
+
+/*
+ * FID: 0xC4000181
+ */
+#define SMC_RMI_RTT_AUX_MAP_UNPROTECTED SMC64_RMI_FID(U(0x31))
+
+/*
+ * FID: 0xC4000183
+ */
+#define SMC_RMI_RTT_AUX_UNMAP_PROTECTED SMC64_RMI_FID(U(0x33))
+
+/*
+ * FID: 0xC4000184
+ */
+#define SMC_RMI_RTT_AUX_UNMAP_UNPROTECTED SMC64_RMI_FID(U(0x34))
+
+/*
+ * FID: 0xC4000185
+ */
+#define SMC_RMI_VDEV_ABORT SMC64_RMI_FID(U(0x35))
+
+/*
+ * FID: 0xC4000186
+ */
+#define SMC_RMI_VDEV_COMMUNICATE SMC64_RMI_FID(U(0x36))
+
+/*
+ * FID: 0xC4000187
+ */
+#define SMC_RMI_VDEV_CREATE SMC64_RMI_FID(U(0x37))
+
+/*
+ * FID: 0xC4000188
+ */
+#define SMC_RMI_VDEV_DESTROY SMC64_RMI_FID(U(0x38))
+
+/*
+ * FID: 0xC4000189
+ */
+#define SMC_RMI_VDEV_GET_STATE SMC64_RMI_FID(U(0x39))
+
+/*
+ * FID: 0xC400018A
+ */
+#define SMC_RMI_VDEV_STOP SMC64_RMI_FID(U(0x3A))
+
+/*
+ * FID: 0xC400018B
+ */
+#define SMC_RMI_RTT_SET_S2AP SMC64_RMI_FID(U(0x3B))
+
+/*
+ * FID: 0xC400018C
+ */
+#define SMC_RMI_MEC_SET_SHARED SMC64_RMI_FID(U(0x3C))
+
+/*
+ * FID: 0xC400018D
+ */
+#define SMC_RMI_MEC_SET_PRIVATE SMC64_RMI_FID(U(0x3D))
+
+/*
+ * FID: 0xC400018E
+ */
+#define SMC_RMI_VDEV_COMPLETE SMC64_RMI_FID(U(0x3E))
+
#define GRANULE_SIZE PAGE_SIZE_4KB
/* Maximum number of auxiliary granules required for a REC */
@@ -305,6 +466,7 @@
#define RMI_FEATURE_REGISTER_0_GICV3_NUM_LRS_WIDTH 4UL
#define RMI_FEATURE_REGISTER_0_MAX_RECS_ORDER_SHIFT 38UL
#define RMI_FEATURE_REGISTER_0_MAX_RECS_ORDER_WIDTH 4UL
+#define RMI_FEATURE_REGISTER_0_DA_EN BIT(42)
/*
* Format of feature_flag[63:32].
@@ -537,6 +699,249 @@
SET_MEMBER(struct rmi_rec_exit exit, 0x800, 0x1000); /* 0x800 */
};
+/*
+ * RmiPdevProtConfig
+ * Represents the protection between system and device.
+ * Width: 2 bits
+ */
+#define RMI_PDEV_IOCOH_E2E_IDE U(0)
+#define RMI_PDEV_IOCOH_E2E_SYS U(1)
+#define RMI_PDEV_FCOH_E2E_IDE U(2)
+#define RMI_PDEV_FCOH_E2E_SYS U(3)
+
+/*
+ * RmiPdevFlags
+ * Fieldset contains flags provided by the Host during PDEV creation
+ * Width: 64 bits
+ */
+/* RmiPdevProtConfig Bits 1:0 */
+#define RMI_PDEV_FLAGS_PROT_CONFIG_SHIFT UL(0)
+#define RMI_PDEV_FLAGS_PROT_CONFIG_WIDTH UL(2)
+
+/*
+ * RmiPdevEvent
+ * Represents physical device event.
+ * Width: 8 bits
+ */
+#define RMI_PDEV_EVENT_IDE_KEY_REFRESH U(0)
+
+/*
+ * RmiPdevState
+ * Represents the state of a PDEV
+ * Width: 8 bits
+ */
+#define RMI_PDEV_STATE_NEW U(0)
+#define RMI_PDEV_STATE_NEEDS_KEY U(1)
+#define RMI_PDEV_STATE_HAS_KEY U(2)
+#define RMI_PDEV_STATE_READY U(3)
+#define RMI_PDEV_STATE_COMMUNICATING U(4)
+#define RMI_PDEV_STATE_STOPPING U(5)
+#define RMI_PDEV_STATE_STOPPED U(6)
+#define RMI_PDEV_STATE_ERROR U(7)
+
+/*
+ * RmiSignatureAlgorithm
+ * Represents signature algorithm used in PDEV set key RMI call.
+ * Width: 8 bits
+ */
+#define RMI_SIGNATURE_ALGORITHM_RSASSA_3072 U(0)
+#define RMI_SIGNATURE_ALGORITHM_ECDSA_P256 U(1)
+#define RMI_SIGNATURE_ALGORITHM_ECDSA_P384 U(2)
+
+/*
+ * RmiDevMemShared
+ * Represents whether device memory Granule should be shared
+ * Width: 1 bit
+ */
+#define RMI_DEV_MEM_PRIVATE U(0)
+#define RMI_DEV_MEM_SHARED U(1)
+
+/*
+ * RmiDevDelegateFlags
+ * Fieldset contains flags provided by the Host during device memory granule
+ * delegation.
+ * Width: 64 bits
+ */
+/* RmiDevMemShared: Bit 0 */
+#define RMI_DEV_DELEGATE_FLAGS_SHARE_SHIFT U(0)
+#define RMI_DEV_DELEGATE_FLAGS_SHARE_WIDTH U(1)
+
+/*
+ * RmiDevCommEnterStatus (Name in Spec RmiDevCommStatus)
+ * Represents status passed from the Host to the RMM during device communication.
+ * Width: 8 bits
+ */
+#define RMI_DEV_COMM_ENTER_STATUS_SUCCESS U(0)
+#define RMI_DEV_COMM_ENTER_STATUS_ERROR U(1)
+#define RMI_DEV_COMM_ENTER_STATUS_NONE U(2)
+
+/*
+ * RmiDevCommEnter
+ * This structure contains data passed from the Host to the RMM during device
+ * communication.
+ * Width: 256 (0x100) bytes
+ */
+struct rmi_dev_comm_enter {
+ /* RmiDevCommEnterStatus: Status of device transaction */
+ SET_MEMBER_RMI(unsigned char status, 0, 0x8);
+ /* Address: Address of request buffer */
+ SET_MEMBER_RMI(unsigned long req_addr, 0x8, 0x10);
+ /* Address: Address of response buffer */
+ SET_MEMBER_RMI(unsigned long resp_addr, 0x10, 0x18);
+ /* UInt64: Amount of valid data in response buffer in bytes */
+ SET_MEMBER_RMI(unsigned long resp_len, 0x18, 0x100);
+};
+
+/*
+ * RmiDevCommExitFlags
+ * Fieldset contains flags provided by the RMM during a device transaction.
+ * Width: 64 bits
+ */
+#define RMI_DEV_COMM_EXIT_FLAGS_CACHE_SHIFT UL(0)
+#define RMI_DEV_COMM_EXIT_FLAGS_CACHE_WIDTH UL(1)
+#define RMI_DEV_COMM_EXIT_FLAGS_SEND_SHIFT UL(1)
+#define RMI_DEV_COMM_EXIT_FLAGS_SEND_WIDTH UL(1)
+#define RMI_DEV_COMM_EXIT_FLAGS_WAIT_SHIFT UL(2)
+#define RMI_DEV_COMM_EXIT_FLAGS_WAIT_WIDTH UL(1)
+#define RMI_DEV_COMM_EXIT_FLAGS_MULTI_SHIFT UL(3)
+#define RMI_DEV_COMM_EXIT_FLAGS_MULTI_WIDTH UL(1)
+
+/*
+ * RmiDevCommProtocol
+ * Represents the protocol used for device communication.
+ * Width: 8 bits
+ */
+#define RMI_DEV_COMM_PROTOCOL_SPDM U(0)
+#define RMI_DEV_COMM_PROTOCOL_SECURE_SPDM U(1)
+
+/*
+ * RmiDevCommExit
+ * This structure contains data passed from the RMM to the Host during device
+ * communication.
+ * Width: 256 (0x100) bytes.
+ */
+struct rmi_dev_comm_exit {
+ /*
+ * RmiDevCommExitFlags: Flags indicating the actions the host is
+ * requested to perform
+ */
+ SET_MEMBER_RMI(unsigned long flags, 0, 0x8);
+ /*
+ * UInt64: If flags.cache is true, offset in the device response buffer
+ * to the start of data to be cached in bytes.
+ */
+ SET_MEMBER_RMI(unsigned long cache_offset, 0x8, 0x10);
+ /*
+ * UInt64: If flags.cache is true, amount of data to be cached in
+ * bytes.
+ */
+ SET_MEMBER_RMI(unsigned long cache_len, 0x10, 0x18);
+ /* RmiDevCommProtocol: If flags.send is true, type of request */
+ SET_MEMBER_RMI(unsigned char protocol, 0x18, 0x20);
+ /*
+ * UInt64: If flags.send is true, amount of valid data in request buffer
+ * in bytes
+ */
+ SET_MEMBER_RMI(unsigned long req_len, 0x20, 0x28);
+ /*
+ * UInt64: If flags.wait is true, amount of time to wait for device
+ * response in milliseconds
+ */
+ SET_MEMBER_RMI(unsigned long timeout, 0x28, 0x100);
+};
+
+/*
+ * RmiDevCommData
+ * This structure contains data structure shared between Host and RMM for
+ * device communication.
+ * Width: 4096 (0x1000) bytes.
+ */
+#define RMI_DEV_COMM_ENTER_OFFSET 0x0
+#define RMI_DEV_COMM_EXIT_OFFSET 0x800
+#define RMI_DEV_COMM_DATA_SIZE 0x1000
+struct rmi_dev_comm_data {
+ /* RmiDevCommEnter: Entry information */
+ SET_MEMBER_RMI(struct rmi_dev_comm_enter enter,
+ RMI_DEV_COMM_ENTER_OFFSET, RMI_DEV_COMM_EXIT_OFFSET);
+ /* RmiDevCommExit: Exit information */
+ SET_MEMBER_RMI(struct rmi_dev_comm_exit exit,
+ RMI_DEV_COMM_EXIT_OFFSET, RMI_DEV_COMM_DATA_SIZE);
+};
+
+/*
+ * RmiAddressRange
+ * This structure contains base and top value of an address range.
+ * Width: 16 (0x10) bytes.
+ */
+struct rmi_address_range {
+ /* Address: Base of the address range (inclusive) */
+ SET_MEMBER_RMI(unsigned long base, 0, 0x8);
+ /* Address: Top of the address range (exclusive) */
+ SET_MEMBER_RMI(unsigned long top, 0x8, 0x10);
+};
+
+/*
+ * Maximum number of aux granules paramenter passed in rmi_pdev_params during
+ * PDEV createto PDEV create
+ */
+#define PDEV_PARAM_AUX_GRANULES_MAX U(32)
+
+/*
+ * Maximum number of IO coherent RmiAddressRange parameter passed in
+ * rmi_pdev_params during PDEV create
+ */
+#define PDEV_PARAM_IOCOH_ADDR_RANGE_MAX U(16)
+
+/*
+ * Maximum number of fully coherent RmiAddressRange parameter passed in
+ * rmi_pdev_params during PDEV create
+ */
+#define PDEV_PARAM_FCOH_ADDR_RANGE_MAX U(4)
+
+/*
+ * RmiPdevParams
+ * This structure contains parameters provided by Host during PDEV creation.
+ * Width: 4096 (0x1000) bytes.
+ */
+struct rmi_pdev_params {
+ /* RmiPdevFlags: Flags */
+ SET_MEMBER_RMI(unsigned long flags, 0, 0x8);
+ /* Bits64: Physical device identifier */
+ SET_MEMBER_RMI(unsigned long pdev_id, 0x8, 0x10);
+ /* Bits16: Segment ID */
+ SET_MEMBER_RMI(unsigned short segment_id, 0x10, 0x18);
+ /* Bits16: Root Port identifier */
+ SET_MEMBER_RMI(unsigned short root_id, 0x18, 0x20);
+ /* UInt64: Certificate identifier */
+ SET_MEMBER_RMI(unsigned long cert_id, 0x20, 0x28);
+ /* UInt64: Base requester ID range (inclusive) */
+ SET_MEMBER_RMI(unsigned long rid_base, 0x28, 0x30);
+ /* UInt64: Top of requester ID range (exclusive) */
+ SET_MEMBER_RMI(unsigned long rid_top, 0x30, 0x38);
+ /* RmiHashAlgorithm: Algorithm used to generate device digests */
+ SET_MEMBER_RMI(unsigned char hash_algo, 0x38, 0x40);
+ /* UInt64: Number of auxiliary granules */
+ SET_MEMBER_RMI(unsigned long num_aux, 0x40, 0x48);
+ /* UInt64: IDE stream identifier */
+ SET_MEMBER_RMI(unsigned long ide_sid, 0x48, 0x50);
+ /* UInt64: Number of IO-coherent address ranges */
+ SET_MEMBER_RMI(unsigned long iocoh_num_addr_range, 0x50, 0x58);
+ /* UInt64: Number of fully-coherent address ranges */
+ SET_MEMBER_RMI(unsigned long fcoh_num_addr_range, 0x58, 0x100);
+
+ /* Address: Addresses of auxiliary granules */
+ SET_MEMBER_RMI(unsigned long aux[PDEV_PARAM_AUX_GRANULES_MAX], 0x100,
+ 0x200);
+ /* RmiAddressRange: IO-coherent address range */
+ SET_MEMBER_RMI(struct rmi_address_range
+ iocoh_addr_range[PDEV_PARAM_IOCOH_ADDR_RANGE_MAX],
+ 0x200, 0x300);
+ /* RmiAddressRange: Fully coherent address range */
+ SET_MEMBER_RMI(struct rmi_address_range
+ fcoh_addr_range[PDEV_PARAM_FCOH_ADDR_RANGE_MAX],
+ 0x300, 0x1000);
+};
+
struct rtt_entry {
long walk_level;
uint64_t out_addr;
@@ -646,4 +1051,15 @@
u_register_t map_size);
u_register_t host_realm_fold_rtt(u_register_t rd, u_register_t addr, long level);
+u_register_t host_rmi_pdev_aux_count(u_register_t pdev_ptr, u_register_t *count);
+u_register_t host_rmi_pdev_create(u_register_t pdev_ptr,
+ u_register_t params_ptr);
+u_register_t host_rmi_pdev_get_state(u_register_t pdev_ptr, u_register_t *state);
+u_register_t host_rmi_pdev_communicate(u_register_t pdev_ptr,
+ u_register_t data_ptr);
+u_register_t host_rmi_pdev_set_pubkey(u_register_t pdev_ptr, u_register_t key,
+ u_register_t len, uint8_t algo);
+u_register_t host_rmi_pdev_stop(u_register_t pdev_ptr);
+u_register_t host_rmi_pdev_destroy(u_register_t pdev_ptr);
+
#endif /* HOST_REALM_RMI_H */
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>