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>