feat(runtime/rmi): implement RMI_PDEV_CREATE

Define pdev data type and implement RMI_PDEV_CREATE. pdev_create calls
device specific init routine.

This implementation supports off-chip PCIe device with pdev created with
flags SPDM, IDE supported.

This patch adds files to device_assignment app folder to handle
device specific security protocols like CMA SPDM, IDE_KM and TDISP.

Note that this commit will not build but it adds the necessary code
changes for RMI_PDEV_CREATE.

Signed-off-by: Soby Mathew <soby.mathew@arm.com>
Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
Change-Id: Ie55666b5bef9e5d797100bb769ae3f4ba51f1974
diff --git a/app/common/framework/include/app.h b/app/common/framework/include/app.h
index eb53fa6..1e3eed1 100644
--- a/app/common/framework/include/app.h
+++ b/app/common/framework/include/app.h
@@ -21,6 +21,7 @@
  */
 #define RMM_RANDOM_APP_ID			(103U)
 #define ATTESTATION_APP_ID			(211U)
+#define RMM_DEV_ASSIGN_APP_ID			(110U)
 
 #define GRANULE_COUNT(size)	(round_up(size, GRANULE_SIZE) / GRANULE_SIZE)
 
diff --git a/app/device_assignment/CMakeLists.txt b/app/device_assignment/CMakeLists.txt
index 99718fb..5192aec 100644
--- a/app/device_assignment/CMakeLists.txt
+++ b/app/device_assignment/CMakeLists.txt
@@ -4,5 +4,7 @@
 #
 
 add_subdirectory("el0_app")
+add_subdirectory("rmm_stub")
 
 set(EL0_APP_BIN_LIST ${EL0_APP_BIN_LIST} PARENT_SCOPE)
+set(RMM_EL2_STUB_LIBRARIES ${RMM_EL2_STUB_LIBRARIES} PARENT_SCOPE)
diff --git a/app/device_assignment/el0_app/mbedtls_da/rmm_mbedtls_config_da.h b/app/device_assignment/el0_app/mbedtls_da/rmm_mbedtls_config_da.h
index 8babcc0..48253f9 100644
--- a/app/device_assignment/el0_app/mbedtls_da/rmm_mbedtls_config_da.h
+++ b/app/device_assignment/el0_app/mbedtls_da/rmm_mbedtls_config_da.h
@@ -33,6 +33,10 @@
 /* Enable Mbed TLS's built in allocator */
 #define MBEDTLS_MEMORY_BUFFER_ALLOC_C
 
+/* RMM defines its own mbedtls_exit */
+#define MBEDTLS_PLATFORM_EXIT_MACRO mbedtls_exit_panic
+void mbedtls_exit_panic(unsigned int reason);
+
 #define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS
 
 #define MBEDTLS_CIPHER_C
diff --git a/app/device_assignment/el0_app/src/dev_assign_el0_app.c b/app/device_assignment/el0_app/src/dev_assign_el0_app.c
new file mode 100644
index 0000000..6e09607
--- /dev/null
+++ b/app/device_assignment/el0_app/src/dev_assign_el0_app.c
@@ -0,0 +1,410 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <app_common.h>
+#include <debug.h>
+#include <dev_assign_private.h>
+#include <el0_app_helpers.h>
+#include <mbedtls/memory_buffer_alloc.h>
+#include <psa/crypto.h>
+#include <psa/crypto_struct.h>
+#include <string.h>
+
+static libspdm_return_t spdm_send_message(void *spdm_context,
+					      size_t request_size,
+					      const void *request,
+					      uint64_t timeout)
+{
+	(void)spdm_context;
+	(void)request_size;
+	(void)request;
+	(void)timeout;
+	return LIBSPDM_STATUS_SUCCESS;
+}
+
+static libspdm_return_t spdm_receive_message(void *spdm_context,
+						 size_t *response_size,
+						 void **response,
+						 uint64_t timeout)
+{
+	(void)spdm_context;
+	(void)response_size;
+	(void)response;
+	(void)timeout;
+	return LIBSPDM_STATUS_SUCCESS;
+}
+
+static libspdm_return_t
+spdm_transport_encode_message(void *spdm_context, const uint32_t *session_id,
+				  bool is_app_message, bool is_request_message,
+				  size_t message_size, void *message,
+				  size_t *transport_message_size,
+				  void **transport_message)
+{
+	(void)spdm_context;
+	(void)session_id;
+	(void)is_app_message;
+	(void)is_request_message;
+	(void)message_size;
+	(void)message;
+	(void)transport_message_size;
+	(void)transport_message;
+	return LIBSPDM_STATUS_SUCCESS;
+}
+
+static libspdm_return_t
+spdm_transport_decode_message(void *spdm_context, uint32_t **session_id,
+				  bool *is_app_message, bool is_request_message,
+				  size_t transport_message_size,
+				  void *transport_message,
+				  size_t *message_size, void **message)
+{
+	(void)spdm_context;
+	(void)session_id;
+	(void)is_app_message;
+	(void)is_request_message;
+	(void)transport_message_size;
+	(void)transport_message;
+	(void)message_size;
+	(void)message;
+	return LIBSPDM_STATUS_SUCCESS;
+}
+
+static libspdm_return_t spdm_acquire_sender_buffer(void *spdm_context,
+						       void **msg_buf_ptr)
+{
+	struct dev_assign_info *info __unused;
+
+	info = spdm_to_dev_assign_info(spdm_context);
+	*msg_buf_ptr = info->send_recv_buffer;
+
+	return LIBSPDM_STATUS_SUCCESS;
+}
+
+static void spdm_release_sender_buffer(void *spdm_context,
+					   const void *msg_buf_ptr)
+{
+	struct dev_assign_info *info __unused;
+
+	(void)msg_buf_ptr;
+	info = spdm_to_dev_assign_info(spdm_context);
+	assert(info->send_recv_buffer == msg_buf_ptr);
+}
+
+static libspdm_return_t spdm_acquire_receiver_buffer(void *spdm_context,
+							 void **msg_buf_ptr)
+{
+	struct dev_assign_info *info __unused;
+
+	info = spdm_to_dev_assign_info(spdm_context);
+	*msg_buf_ptr = info->send_recv_buffer;
+
+	return LIBSPDM_STATUS_SUCCESS;
+}
+
+static void spdm_release_receiver_buffer(void *spdm_context,
+					     const void *msg_buf_ptr)
+{
+	struct dev_assign_info *info __unused;
+
+	(void)msg_buf_ptr;
+	info = spdm_to_dev_assign_info(spdm_context);
+	assert(info->send_recv_buffer == msg_buf_ptr);
+}
+
+/*
+ * Returns the min heap size. This include libspdm context, libspdm secured
+ * message context, libspdm scratch space, libspdm send recv buffer and
+ * MbedTLS heap.
+ */
+static size_t get_min_heap_size(void)
+{
+	size_t total;
+
+	/*
+	 * As libspdm public headers do not export the type of libsdpm_context.
+	 * RMM reserves 8192 bytes and check at runtime if the size is enough.
+	 */
+	assert(libspdm_get_context_size() <= PRIV_LIBSPDM_CONTEXT_SIZE);
+
+	total = sizeof(struct dev_assign_info) +
+		PRIV_LIBSPDM_SEND_RECV_BUF_SIZE +
+		PRIV_LIBSPDM_CONTEXT_SIZE +
+		PRIV_LIBSPDM_SCRATCH_BUF_SIZE +
+		PRIV_LIBSPDM_MBEDTLS_HEAP_SIZE;
+
+	return total;
+}
+
+static inline psa_algorithm_t rmi_to_psa_hash_algo(uint8_t rmi_hash_algo)
+{
+	if (rmi_hash_algo == RMI_HASH_SHA_256) {
+		return PSA_ALG_SHA_256;
+	} else if (rmi_hash_algo == RMI_HASH_SHA_512) {
+		return PSA_ALG_SHA_512;
+	}
+
+	return PSA_ALG_NONE;
+}
+
+/* coverity[misra_c_2012_rule_5_8_violation:SUPPRESS] */
+void *mbedtls_app_get_heap(void)
+{
+	struct dev_assign_info *info;
+
+	info = heap_start_to_dev_assign_info((uintptr_t)get_heap_start());
+	return &(info->mbedtls_heap_ctx);
+}
+
+/*
+ * Assigns buffers to various objects as mentioned in the below mapping starting
+ * from start of EL0 heap. Note that send_recv_buffer must be first and
+ * libspdm_context must be just before struct dsm as this is assumed in
+ * spdm_to_dev_assign_info() macro.
+ *
+ *       --------------------------------
+ *      |      send_recv_buffer          | PRIV_LIBSPDM_SEND_RECV_BUF_SIZE
+ *      |--------------------------------|
+ *      |    libspdm scratch_buffer      | PRIV_LIBSPDM_SCRATCH_BUF_SIZE
+ *      |--------------------------------|
+ *      |      MbedTLS heap buffer       | PRIV_LIBSPDM_MBEDTLS_HEAP_SIZE
+ *      |--------------------------------|
+ *      |  |     libspdm_context     |   | PRIV_DATA_LIBSPMD_CONTEXT_SIZE
+ *      |--|                         |---|
+ *      |  | struct dev_assign_info  |   | sizeof(struct dev_assign_info)
+ *       --------------------------------
+ *
+ * Inits libspdm context using libspdm helper routines and registers send/recv
+ * buffer acquire/release routines. Registers device send/recv callback handlers.
+ *
+ * This function suppresses few MISRA rule 10.1 violations, as the macros that
+ * causes these violations are not declared as unsigned type and these macros
+ * are from libspdm header files.
+ */
+static int dev_assign_init(uintptr_t el0_heap, size_t heap_size, struct dev_assign_params *params)
+{
+	libspdm_return_t status __unused;
+	spdm_version_number_t cma_spdm_version;
+	spdm_version_number_t cma_sspdm_version;
+	libspdm_data_parameter_t parameter;
+	struct dev_assign_info *info;
+	void *spdm_ctx;
+	uint32_t data32;
+	uint16_t data16;
+	uint8_t data8;
+	size_t sb_size;
+
+	if (heap_size < get_min_heap_size()) {
+		ERROR("Min heap size 0x%lx expected. Current heap size = 0x%lx\n",
+			get_min_heap_size(), heap_size);
+		return DEV_ASSIGN_STATUS_ERROR;
+	}
+
+	if (params->cert_slot_id >= (uint8_t)SPDM_MAX_SLOT_COUNT) {
+		return DEV_ASSIGN_STATUS_ERROR;
+	}
+
+	info = heap_start_to_dev_assign_info(el0_heap);
+
+	info->send_recv_buffer = (void *)el0_heap;
+	info->scratch_buffer =  (void *)((uintptr_t)info->send_recv_buffer +
+			PRIV_LIBSPDM_SEND_RECV_BUF_SIZE);
+	info->mbedtls_heap_buf = (void *)((uintptr_t)info->scratch_buffer +
+			PRIV_LIBSPDM_SCRATCH_BUF_SIZE);
+	info->libspdm_ctx = (void *)((uintptr_t)info->mbedtls_heap_buf +
+			PRIV_LIBSPDM_MBEDTLS_HEAP_SIZE);
+
+	assert((uintptr_t)spdm_to_dev_assign_info(info->libspdm_ctx) == (uintptr_t)info);
+
+	VERBOSE("dev assign send_recv buf: 0x%p\n", info->send_recv_buffer);
+	VERBOSE("dev assign scratch_buffer: 0x%p\n", info->scratch_buffer);
+	VERBOSE("dev assign libspdm_ctx: 0x%p\n", info->libspdm_ctx);
+	VERBOSE("dev assign info: 0x%p\n", (void *)info);
+
+
+	/* Initialize the mbedTLS heap */
+	mbedtls_memory_buffer_alloc_init(info->mbedtls_heap_buf, PRIV_LIBSPDM_MBEDTLS_HEAP_SIZE);
+
+	/* Initialize DSM */
+	info->dev_handle = params->dev_handle;
+	info->cert_slot_id = params->cert_slot_id;
+	info->has_ide = params->has_ide;
+	if (info->has_ide) {
+		info->ecam_addr = params->ecam_addr;
+		info->rp_id = params->rp_id;
+		info->ide_sid = params->ide_sid;
+	}
+
+	info->psa_hash_algo = rmi_to_psa_hash_algo(params->rmi_hash_algo);
+
+	/*
+	 * Initialize SPDM and Secure SPDM context. 'spdm_ctx' is a combination
+	 * of both SPDM context and secured message context.
+	 */
+	spdm_ctx = info->libspdm_ctx;
+	status = libspdm_init_context(spdm_ctx);
+	assert(status == LIBSPDM_STATUS_SUCCESS);
+
+	/* Register device send/recv handlers */
+	libspdm_register_device_io_func(spdm_ctx, spdm_send_message,
+					spdm_receive_message);
+
+	/*
+	 * No transport encodings used as this is handled by NS host. So the
+	 * transport_header_size and transport_tail_size are passed as 0.
+	 */
+	libspdm_register_transport_layer_func(spdm_ctx,
+					      (uint32_t)CMA_MAX_SPDM_MSG_SIZE,
+					      0U, /* transport_header_size */
+					      0U, /* transport_tail_size */
+					      spdm_transport_encode_message,
+					      spdm_transport_decode_message);
+
+	/* Register send/recv buffer acquire/release functions */
+	libspdm_register_device_buffer_func(spdm_ctx,
+					    (uint32_t)CMA_SENDER_BUFFER_SIZE,
+					    (uint32_t)CMA_RECEIVER_BUFFER_SIZE,
+					    spdm_acquire_sender_buffer,
+					    spdm_release_sender_buffer,
+					    spdm_acquire_receiver_buffer,
+					    spdm_release_receiver_buffer);
+
+	/* Set scratch buffer size */
+	sb_size = libspdm_get_sizeof_required_scratch_buffer(spdm_ctx);
+
+	VERBOSE("libspdm_context_size: 0x%lx\n", libspdm_get_context_size());
+	VERBOSE("libspdm_scratch_buffer_size: 0x%lx\n", sb_size);
+	VERBOSE("struct dev_assign_info size: 0x%lx\n", sizeof(struct dev_assign_info));
+
+	assert(sb_size <= PRIV_LIBSPDM_SCRATCH_BUF_SIZE);
+	libspdm_set_scratch_buffer(spdm_ctx, info->scratch_buffer, sb_size);
+
+	/* Check libspdm context */
+	if (!libspdm_check_context(spdm_ctx)) {
+		assert(false);
+	}
+
+	/* Set SPDM version */
+	(void)memset(&parameter, 0, sizeof(parameter));
+	parameter.location = LIBSPDM_DATA_LOCATION_LOCAL;
+	cma_spdm_version = (spdm_version_number_t)CMA_SPDM_VERSION <<
+		SPDM_VERSION_NUMBER_SHIFT_BIT;
+	status = libspdm_set_data(spdm_ctx, LIBSPDM_DATA_SPDM_VERSION,
+				  &parameter, &cma_spdm_version,
+				  sizeof(cma_spdm_version));
+	assert(status == LIBSPDM_STATUS_SUCCESS);
+
+	/* Set secured message version */
+	(void)memset(&parameter, 0, sizeof(parameter));
+	parameter.location = LIBSPDM_DATA_LOCATION_LOCAL;
+	cma_sspdm_version = (spdm_version_number_t)CMA_SECURED_SPDM_VERSION <<
+		SPDM_VERSION_NUMBER_SHIFT_BIT;
+	status = libspdm_set_data(spdm_ctx, LIBSPDM_DATA_SECURED_MESSAGE_VERSION,
+				  &parameter, &cma_sspdm_version,
+				  sizeof(cma_sspdm_version));
+	assert(status == LIBSPDM_STATUS_SUCCESS);
+
+	/*
+	 * Set GET_CAPABILITY fields
+	 * Note: DataTransferSize and MaxSPDMmsgSize is automatically set by
+	 * libspdm during init connection based on CMA_SPDM_SENDER_BUFFER_SIZE
+	 * and CMA_SPDM_MSG_SIZE_MAX respectivelky.
+	 */
+	(void)memset(&parameter, 0, sizeof(parameter));
+	parameter.location = LIBSPDM_DATA_LOCATION_LOCAL;
+	data8 = CMA_SPDM_GET_CAPABILITY_CT_EXPONENT;
+	status = libspdm_set_data(spdm_ctx, LIBSPDM_DATA_CAPABILITY_CT_EXPONENT,
+				  &parameter, &data8, sizeof(data8));
+	assert(status == LIBSPDM_STATUS_SUCCESS);
+
+	/* coverity[misra_c_2012_rule_10_1_violation:SUPPRESS] */
+	data32 = CMA_SPDM_GET_CAPABILITIES_REQUEST_FLAGS;
+	status = libspdm_set_data(spdm_ctx, LIBSPDM_DATA_CAPABILITY_FLAGS,
+				  &parameter, &data32, sizeof(data32));
+	assert(status == LIBSPDM_STATUS_SUCCESS);
+
+	/* Set NEGOTIATE_ALGORITHMS fields */
+	/* coverity[misra_c_2012_rule_10_1_violation:SUPPRESS] */
+	data8 = CMA_SPDM_ALGORITHMS_MEASUREMENT_SPEC;
+	status = libspdm_set_data(spdm_ctx, LIBSPDM_DATA_MEASUREMENT_SPEC,
+				  &parameter, &data8, sizeof(data8));
+	assert(status == LIBSPDM_STATUS_SUCCESS);
+
+	/* coverity[misra_c_2012_rule_10_1_violation:SUPPRESS] */
+	data8 = CMA_SPDM_ALGORITHMS_OTHER_PARAMS_SUPPORT;
+	status = libspdm_set_data(spdm_ctx, LIBSPDM_DATA_OTHER_PARAMS_SUPPORT,
+				  &parameter, &data8, sizeof(data8));
+	assert(status == LIBSPDM_STATUS_SUCCESS);
+
+	/* coverity[misra_c_2012_rule_10_1_violation:SUPPRESS] */
+	data32 = CMA_SPDM_ALGORITHMS_BASE_ASYM_ALGOS;
+	status = libspdm_set_data(spdm_ctx, LIBSPDM_DATA_BASE_ASYM_ALGO,
+				  &parameter, &data32, sizeof(data32));
+	assert(status == LIBSPDM_STATUS_SUCCESS);
+
+	/* coverity[misra_c_2012_rule_10_1_violation:SUPPRESS] */
+	data32 = CMA_SPDM_ALGORITHMS_BASE_HASH_ALGOS;
+	status = libspdm_set_data(spdm_ctx, LIBSPDM_DATA_BASE_HASH_ALGO,
+				  &parameter, &data32, sizeof(data32));
+	assert(status == LIBSPDM_STATUS_SUCCESS);
+
+	/* coverity[misra_c_2012_rule_10_1_violation:SUPPRESS] */
+	data16 = CMA_SPDM_ALGORITHMS_DHE_GROUPS;
+	status = libspdm_set_data(spdm_ctx, LIBSPDM_DATA_DHE_NAME_GROUP,
+				  &parameter, &data16, sizeof(data16));
+	assert(status == LIBSPDM_STATUS_SUCCESS);
+
+	/* coverity[misra_c_2012_rule_10_1_violation:SUPPRESS] */
+	data16 = CMA_SPDM_ALGORITHMS_AEAD_CIPHER_SUITES;
+	status = libspdm_set_data(spdm_ctx, LIBSPDM_DATA_AEAD_CIPHER_SUITE,
+				  &parameter, &data16, sizeof(data16));
+	assert(status == LIBSPDM_STATUS_SUCCESS);
+
+	/* coverity[misra_c_2012_rule_10_1_violation:SUPPRESS] */
+	data16 = CMA_SPDM_ALGORITHMS_KEY_SCHEDULE;
+	status = libspdm_set_data(spdm_ctx, LIBSPDM_DATA_KEY_SCHEDULE,
+				  &parameter, &data16, sizeof(data16));
+	assert(status == LIBSPDM_STATUS_SUCCESS);
+
+	/* coverity[misra_c_2012_rule_10_1_violation:SUPPRESS] */
+	data16 = CMA_SPDM_ALGORITHMS_REQ_BASE_ASYM_ALGOS;
+	status = libspdm_set_data(spdm_ctx, LIBSPDM_DATA_REQ_BASE_ASYM_ALG,
+				  &parameter, &data16, sizeof(data16));
+	assert(status == LIBSPDM_STATUS_SUCCESS);
+
+	/* Assign the shared_buf. This serves as a marker that init is done. */
+	info->shared_buf = (void *)params;
+
+	return DEV_ASSIGN_STATUS_SUCCESS;
+}
+
+
+/* coverity[misra_c_2012_rule_5_8_violation:SUPPRESS] */
+unsigned long el0_app_entry_func(
+	unsigned long func_id,
+	unsigned long arg_0,
+	unsigned long arg_1,
+	unsigned long arg_2,
+	unsigned long arg_3)
+{
+	uintptr_t heap = (uintptr_t)get_heap_start();
+
+	(void)arg_1;
+	(void)arg_2;
+	(void)arg_3;
+
+	switch (func_id) {
+	case DEVICE_ASSIGN_APP_FUNC_ID_INIT:
+	{
+		uintptr_t shared = (uintptr_t)get_shared_mem_start();
+
+		return (unsigned long)dev_assign_init(heap, arg_0,
+			(struct dev_assign_params *)shared);
+	}
+	default:
+		assert(false);
+		return (unsigned long)DEV_ASSIGN_STATUS_ERROR;
+	}
+}
diff --git a/app/device_assignment/el0_app/src/dev_assign_private.h b/app/device_assignment/el0_app/src/dev_assign_private.h
new file mode 100644
index 0000000..cf9dd41
--- /dev/null
+++ b/app/device_assignment/el0_app/src/dev_assign_private.h
@@ -0,0 +1,259 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef DEV_ASSIGN_PRIVATE_H
+#define DEV_ASSIGN_PRIVATE_H
+
+#include <assert.h>
+#include <dev_assign_structs.h>
+#include <industry_standard/spdm.h>
+#include <industry_standard/spdm_secured_message.h>
+#include <library/spdm_requester_lib.h>
+#include <library/spdm_secured_message_lib.h>
+#include <mbedtls/memory_buffer_alloc.h>
+#include <sizes.h>
+#include <utils_def.h>
+
+/*
+ * Requester SPDM version is 1.2. This is the minimum SPDM version required to
+ * support TDISP.
+ *
+ * From PCIe spec: CMA requires SPDM Version 1.0 or above. IDE requires SPDM
+ * Version 1.1 or above. TDISP requires version 1.2 or above.
+ */
+#define CMA_SPDM_VERSION		SPDM_MESSAGE_VERSION_12
+
+/*
+ * Secured Messages using SPDM Specification (IDE requires version 1.0 or above)
+ * Set this to 1.1. DSM supports 1.1
+ */
+#define CMA_SECURED_SPDM_VERSION	SECURED_SPDM_VERSION_11
+
+/*
+ * Responders must implement a Cryptographic Timeout (CT), as defined by SPDM
+ * specification, of not more than 2^23 μs.
+ */
+#define CMA_SPDM_GET_CAPABILITY_CT_EXPONENT		20
+
+/*
+ * List of capabilities enabled and supported by SPDM requester. These flags are
+ * passed to GET_CAPABILITIES request message. Currently these flags are specific
+ * to PCIe TDI off-chip device.
+ *
+ * CERT_CAP	- Supports DIGESTS and CERTIFICATE response messages.
+ * ENCRYPT_CAP	- Supports message encryption in a secure session.
+ * KEY_EX_CAP	- Support KEY_EXCHANGE request message. Needs ENCRYPT_CAP and
+ *		  MAC_CAP.
+ * MAC_CAP	- Supports message authentication in a secure session.
+ * CHUNK_CAP	- Supports large SPDM message transfer mechanism messages.
+ *		  Note: Add SPDM_GET_CAPABILITIES_REQUEST_FLAGS_CHUNK_CAP.
+ *			CHUNK retrieval is not verified.
+ */
+#define CMA_SPDM_GET_CAPABILITIES_REQUEST_FLAGS				( \
+	SPDM_GET_CAPABILITIES_REQUEST_FLAGS_CERT_CAP			| \
+	SPDM_GET_CAPABILITIES_REQUEST_FLAGS_ENCRYPT_CAP			| \
+	SPDM_GET_CAPABILITIES_REQUEST_FLAGS_MAC_CAP			| \
+	SPDM_GET_CAPABILITIES_REQUEST_FLAGS_KEY_EX_CAP			| \
+	SPDM_GET_CAPABILITIES_REQUEST_FLAGS_HANDSHAKE_IN_THE_CLEAR_CAP)
+
+/*
+ * List of minimum set of capabilities required to be supported by the responder.
+ * These flags are checked against CAPABILITIES response message. Currently these
+ * flags are specific to PCIe TDI off-chip device.
+ */
+#define CMA_SPDM_GET_CAPABILITIES_RESPONSE_FLAGS			( \
+	SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP			| \
+	SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ENCRYPT_CAP		| \
+	SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_MAC_CAP			| \
+	SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_MEAS_CAP			| \
+	SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_KEY_EX_CAP)
+
+/*
+ * Optional set of capabilities that can be supported by the responder. These
+ * flags are checked against CAPABILITIES response message.
+ */
+#define CMA_SPDM_GET_CAPABILITIES_RESPONSE_OPTIONAL_FLAGS		( \
+	SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_MEAS_FRESH_CAP		| \
+	SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CHUNK_CAP)
+
+/*
+ * The measurement specification is used in the MEASUREMENTS response. This is
+ * not explicitly mentioned in PCIe CMA-SPDM.
+ */
+#define CMA_SPDM_ALGORITHMS_MEASUREMENT_SPEC				\
+	SPDM_MEASUREMENT_SPECIFICATION_DMTF
+
+/*
+ * OtherParamsSupport: Opaque data format used is DMTF. This is not explicitly
+ * mentioned in PCIe CMA-SPDM.
+ */
+#define CMA_SPDM_ALGORITHMS_OTHER_PARAMS_SUPPORT			\
+	SPDM_ALGORITHMS_OPAQUE_DATA_FORMAT_1
+
+/*
+ * Requesters are required to support responders that implement any of these
+ * choices of BaseAsymAlgo:
+ *	TPM_ALG_RSASSA_3072
+ *	TPM_ALG_ECDSA_ECC_NIST_P256
+ *	TPM_ALG_ECDSA_ECC_NIST_P384
+ */
+#define CMA_SPDM_ALGORITHMS_BASE_ASYM_ALGOS				( \
+	SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_RSASSA_3072		| \
+	SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_ECDSA_ECC_NIST_P256	| \
+	SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_ECDSA_ECC_NIST_P384)
+
+/*
+ * Requesters and responders must, for MeasurementHashAlgo, support one or both
+ * of the following:
+ *	TPM_ALG_SHA_256
+ *	TPM_ALG_SHA_384
+ */
+#define CMA_SPDM_ALGORITHMS_BASE_HASH_ALGOS				( \
+	SPDM_ALGORITHMS_BASE_HASH_ALGO_TPM_ALG_SHA_256			| \
+	SPDM_ALGORITHMS_BASE_HASH_ALGO_TPM_ALG_SHA_384)
+
+/*
+ * Requester are required to responders that implement any of these DHE groups
+ *	SECP_256_R1
+ *	SECP_384_R1
+ */
+#define CMA_SPDM_ALGORITHMS_DHE_GROUPS					( \
+	SPDM_ALGORITHMS_DHE_NAMED_GROUP_SECP_256_R1			| \
+	SPDM_ALGORITHMS_DHE_NAMED_GROUP_SECP_384_R1)
+
+/*
+ * Requester are required to responders that implement any of these AEAD Cipher
+ * Suite
+ *	AES-128-GCM
+ *	AES-256-GCM
+ */
+#define CMA_SPDM_ALGORITHMS_AEAD_CIPHER_SUITES				( \
+	SPDM_ALGORITHMS_AEAD_CIPHER_SUITE_AES_128_GCM			| \
+	SPDM_ALGORITHMS_AEAD_CIPHER_SUITE_AES_256_GCM)
+
+/* Requester-supported SPDM-enumerated Key Schedule algorithms. */
+#define CMA_SPDM_ALGORITHMS_KEY_SCHEDULE				\
+	SPDM_ALGORITHMS_KEY_SCHEDULE_HMAC_HASH
+
+/*
+ * Requesters supported asym algorithm.
+ *	TPM_ALG_RSAPSS_3072
+ *	TPM_ALG_RSAPSS_2048
+ *	TPM_ALG_RSASSA_3072
+ *	TPM_ALG_RSASSA_2048
+ */
+#define CMA_SPDM_ALGORITHMS_REQ_BASE_ASYM_ALGOS				( \
+	SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_RSAPSS_3072		| \
+	SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_RSAPSS_2048		| \
+	SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_RSASSA_3072		| \
+	SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_RSASSA_2048)
+
+/*
+ * SPDM GET_CAPABILITIES.DataTransferSize
+ *
+ * This is the size of send/receive buffer that is registered with libspdm to
+ * write device request and read device response. RMM uses the same buffer for
+ * both send and receive as only one device request can be active at a time.
+ *
+ * RMM spec limits the response (recv) length to GRANULE_SIZE. And so the send
+ * buffer size is also limited to GRANULE_SIZE. This value is set as SPDM
+ * requester GET_CAPABILITIES.DataTransferSize as part of connection init.
+ *
+ * Note: Increasing CMA_DATA_TRANSFER_SIZE increases
+ * PRIV_DATA_LIBSPMD_SCRATCH_BUFFER_SIZE.
+ */
+#define CMA_DATA_TRANSFER_SIZE		GRANULE_SIZE
+#define CMA_SENDER_BUFFER_SIZE		CMA_DATA_TRANSFER_SIZE
+#define CMA_RECEIVER_BUFFER_SIZE	CMA_DATA_TRANSFER_SIZE
+
+/*
+ * SPDM GET_CAPABILITIES.MaxSPDMmsgSize
+ *
+ * If the Requester supports the Large SPDM message transfer mechanism this
+ * field shall indicate the maximum size, in bytes, of the internal buffer used
+ * to reassemble a single and complete Large SPDM message.
+ *
+ * Currently this is set as the same value of CMA_DATA_TRANSFER_SIZE. This value
+ * is set as SPDM requester GET_CAPABILITIES.MaxSPDMmsgSize as part of connection
+ * init.
+ *
+ * This could be later incremented once SPDM CHUNK support is enabled and tested.
+ */
+#define CMA_MAX_SPDM_MSG_SIZE		CMA_DATA_TRANSFER_SIZE
+
+/*
+ * List of data objects mapped in heap prefixed by PRIV_
+ *
+ * PRIV_LIBSPDM_SEND_RECV_BUF_SIZE:
+ *	This is buffer to send and receive SPDM data. Must be allocated first on
+ *	heap as this shared with RMM stub and accessed by RMM.
+ *
+ * PRIV_LIBSPDM_CONTEXT_SIZE:
+ *	Libspdm public headers do not export the type of libsdpm_context. Reserve
+ *	2*4K pages and check at runtime if context size are within limit using
+ *	libspdm_get_context_size() api.
+ *
+ * PRIV_LIBSPDM_SCRATCH_BUF_SIZE:
+ *	This is an internal buffer used by libspdm as scratch space. This is
+ *	approx 6 times of CMA_DATA_TRANSFER_SIZE. As part of init connection
+ *	there is a run time check to verify if enough scratch space is reserved.
+ *
+ * PRIV_LIBSPDM_MBEDTLS_HEAP_SIZE
+ *	This is the heap used by Libspdm MbedTLS library.
+ */
+#define PRIV_LIBSPDM_SEND_RECV_BUF_SIZE		sizeof(struct dev_assign_spdm_shared)
+#define PRIV_LIBSPDM_CONTEXT_SIZE		(2U * SZ_4K)
+#define PRIV_LIBSPDM_SCRATCH_BUF_SIZE		(6U * CMA_DATA_TRANSFER_SIZE)
+#define PRIV_LIBSPDM_MBEDTLS_HEAP_SIZE		(3U * SZ_4K)
+
+/* Custom libspdm status code. Wait for device response  */
+#define LIBSPDM_STATUS_DEV_COMM_BLOCKED		((libspdm_return_t)0x80008000U)
+
+#define spdm_to_dev_assign_info(spdm)					\
+		((struct dev_assign_info *)((unsigned long)(spdm) +	\
+		PRIV_LIBSPDM_CONTEXT_SIZE))
+
+#define heap_start_to_dev_assign_info(heap)				\
+				((struct dev_assign_info *)((heap) +	\
+				PRIV_LIBSPDM_SEND_RECV_BUF_SIZE +	\
+				PRIV_LIBSPDM_SCRATCH_BUF_SIZE +		\
+				PRIV_LIBSPDM_MBEDTLS_HEAP_SIZE +	\
+				PRIV_LIBSPDM_CONTEXT_SIZE))
+
+struct dev_assign_info {
+	/* RMI device handle */
+	void *dev_handle;
+
+	/* SPDM certificate slot ID */
+	uint8_t cert_slot_id;
+
+	bool has_ide;
+
+	/* Identify the root complex (RC). */
+	uint64_t ecam_addr;
+
+	/* Identify the RP within the RC. RootPort PCI BDF */
+	uint16_t rp_id;
+
+	/* IDE stream ID */
+	uint64_t ide_sid;
+
+	buffer_alloc_ctx mbedtls_heap_ctx;
+
+	/* Exit and Entry args for dev_communicate cmds */
+	struct rmi_dev_comm_enter enter_args;
+	struct rmi_dev_comm_exit exit_args;
+
+	void *send_recv_buffer;
+	void *scratch_buffer;
+	void *mbedtls_heap_buf;
+	void *libspdm_ctx;
+
+	void *shared_buf;
+};
+
+int dev_assign_cmd_init_connection_main(struct dev_assign_info *info);
+
+#endif /* DEV_ASSIGN_PRIVATE_H */
diff --git a/app/device_assignment/rmm_stub/CMakeLists.txt b/app/device_assignment/rmm_stub/CMakeLists.txt
new file mode 100644
index 0000000..d5ad243
--- /dev/null
+++ b/app/device_assignment/rmm_stub/CMakeLists.txt
@@ -0,0 +1,23 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-app-dev-assign-stub)
+
+target_link_libraries(rmm-app-dev-assign-stub
+    PRIVATE
+    rmm-el2-stub
+    rmm-lib-arch
+    rmm-lib-common
+    rmm-lib-debug)
+
+target_include_directories(rmm-app-dev-assign-stub
+    PUBLIC "include"
+    PRIVATE "../../common/include")
+
+target_sources(rmm-app-dev-assign-stub
+        PRIVATE "src/dev_assign_app_stub.c")
+
+list(APPEND RMM_EL2_STUB_LIBRARIES "rmm-app-dev-assign-stub")
+set(RMM_EL2_STUB_LIBRARIES ${RMM_EL2_STUB_LIBRARIES} PARENT_SCOPE)
diff --git a/app/device_assignment/rmm_stub/include/dev_assign_app.h b/app/device_assignment/rmm_stub/include/dev_assign_app.h
new file mode 100644
index 0000000..8f124af
--- /dev/null
+++ b/app/device_assignment/rmm_stub/include/dev_assign_app.h
@@ -0,0 +1,29 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef DEV_ASSIGN_APP_H
+#define DEV_ASSIGN_APP_H
+
+#include <dev_assign_structs.h>
+
+
+/*
+ * Initialize an instance of device_assignment app.
+ *
+ * Arguments:
+ * app_data - Pointer to app_data_cfg. This is uninitialized and opaque to caller
+ * granule_pas - Array of contiguous granule addresses to be used for the app.
+ * granule_pa_count - Num of elements in `granule_pas` array.
+ * granule_va_start - Start VA address of the `granule_pas` array.
+ * params - Pointer to the dev_assign_params populated by the caller.
+ *
+ * Returns DEV_ASSIGN_STATUS_SUCCESS on success, DEV_ASSIGN_STATUS_ERROR
+ * on error.
+ */
+int dev_assign_app_init(struct app_data_cfg *app_data, uintptr_t granule_pas[],
+	size_t granule_pa_count, void *granule_va_start,
+	struct dev_assign_params *params);
+
+#endif /* DEV_ASSIGN_APP_H */
diff --git a/app/device_assignment/rmm_stub/include/dev_assign_structs.h b/app/device_assignment/rmm_stub/include/dev_assign_structs.h
new file mode 100644
index 0000000..480b01d
--- /dev/null
+++ b/app/device_assignment/rmm_stub/include/dev_assign_structs.h
@@ -0,0 +1,52 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef DEV_ASSIGN_STRUCTS_H
+#define DEV_ASSIGN_STRUCTS_H
+
+#include <smc-rmi.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#define DEV_ASSIGN_STATUS_SUCCESS	(0)
+#define DEV_ASSIGN_STATUS_ERROR		(-1)
+#define DEV_ASSIGN_STATUS_COMM_BLOCKED	(1)
+
+/*
+ * App function for initialization. This needs to be invoked for every
+ * new instance of the app. App uses heap available via tpidrro_el0.
+ *
+ * arg0 == Size of Heap in num of 4K pages.
+ *
+ * Shared app buf == `struct dev_assign_params`
+ *
+ * ret0 == DEV_ASSIGN_STATUS_SUCCESS if initialization is successful.
+ *         DEV_ASSIGN_STATUS_ERROR if error on initialization.
+ */
+#define DEVICE_ASSIGN_APP_FUNC_ID_INIT			1
+
+struct dev_assign_params {
+	/* RMI device handle */
+	void *dev_handle;
+	/* Algorithm used to generate device digests. */
+	uint8_t rmi_hash_algo;
+	/* SPDM certificate slot ID */
+	uint8_t cert_slot_id;
+	bool has_ide;
+	/* Identify the root complex (RC). */
+	uint64_t ecam_addr;
+	/* Identify the RP within the RC. RootPort PCI BDF */
+	uint16_t rp_id;
+	/* IDE stream ID */
+	uint64_t ide_sid;
+};
+
+/* Shared structure on the app heap for SPDM comms */
+struct dev_assign_spdm_shared {
+	uint8_t sendrecv_buf[GRANULE_SIZE];
+};
+
+#endif /* DEV_ASSIGN_STRUCTS_H */
diff --git a/app/device_assignment/rmm_stub/src/dev_assign_app_stub.c b/app/device_assignment/rmm_stub/src/dev_assign_app_stub.c
new file mode 100644
index 0000000..fb8a4d0
--- /dev/null
+++ b/app/device_assignment/rmm_stub/src/dev_assign_app_stub.c
@@ -0,0 +1,46 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+ #include <app.h>
+ #include <assert.h>
+ #include <dev_assign_app.h>
+ #include <dev_assign_structs.h>
+
+/* Add declaration to prevent static checker error */
+void dev_assign_app_get_bss(uintptr_t *bss_pa, size_t *bss_size);
+
+void dev_assign_app_get_bss(uintptr_t *bss_pa, size_t *bss_size)
+{
+	static char dev_assign_app_bss[GRANULE_SIZE] __aligned(GRANULE_SIZE);
+	*bss_pa = (uintptr_t)dev_assign_app_bss;
+	*bss_size = sizeof(dev_assign_app_bss);
+}
+
+int dev_assign_app_init(struct app_data_cfg *app_data, uintptr_t granule_pas[],
+	size_t granule_pa_count, void *granule_va_start,
+	struct dev_assign_params *params)
+{
+	int rc;
+	struct dev_assign_params *shared;
+
+	rc = app_init_data(app_data,
+				  RMM_DEV_ASSIGN_APP_ID,
+				  granule_pas,
+				  granule_pa_count,
+				  granule_va_start);
+	if (rc != 0) {
+		return DEV_ASSIGN_STATUS_ERROR;
+	}
+
+	app_map_shared_page(app_data);
+	shared = app_data->el2_shared_page;
+	(void)memcpy(shared, params, sizeof(*shared));
+
+	rc = (int)app_run(app_data, DEVICE_ASSIGN_APP_FUNC_ID_INIT,
+			app_data->heap_size, 0, 0, 0);
+	app_unmap_shared_page(app_data);
+
+	return rc;
+}
diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt
index c3f36de..907ff8a 100644
--- a/runtime/CMakeLists.txt
+++ b/runtime/CMakeLists.txt
@@ -84,8 +84,8 @@
             "rmi/rec.c"
             "rmi/rtt.c"
             "rmi/run.c"
-            "rmi/version.c")
-
+            "rmi/version.c"
+            "rmi/pdev.c")
 
 if(HOST_VARIANT STREQUAL "host_cbmc")
     target_sources(rmm-runtime
diff --git a/runtime/core/handler.c b/runtime/core/handler.c
index 5cda469..3f7594f 100644
--- a/runtime/core/handler.c
+++ b/runtime/core/handler.c
@@ -163,7 +163,7 @@
 	HANDLER(DEV_MEM_UNMAP,		3, 2, smc_dev_mem_unmap,	 false, true),
 	HANDLER(PDEV_ABORT,		0, 0, NULL,			 true, true),
 	HANDLER(PDEV_COMMUNICATE,	2, 0, NULL,			 true, true),
-	HANDLER(PDEV_CREATE,		2, 0, NULL,			 true, true),
+	HANDLER(PDEV_CREATE,		2, 0, smc_pdev_create,		 true, true),
 	HANDLER(PDEV_DESTROY,		0, 0, NULL,			 true, true),
 	HANDLER(PDEV_GET_STATE,		1, 1, NULL,			 true, true),
 	HANDLER(PDEV_IDE_RESET,		0, 0, NULL,			 true, true),
diff --git a/runtime/include/dev.h b/runtime/include/dev.h
new file mode 100644
index 0000000..61f90ed
--- /dev/null
+++ b/runtime/include/dev.h
@@ -0,0 +1,101 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef DEV_H
+#define DEV_H
+
+#include <app_fw_structures.h>
+#include <arch.h>
+#include <arch_features.h>
+#include <granule.h>
+#include <sizes.h>
+#include <smc-handler.h>
+#include <smc-rmi.h>
+#include <utils_def.h>
+
+/*
+ * Represents the state of communication between an RMM device object and a
+ * device. The device object could be PDEV or VDEV.
+ */
+#define DEV_COMM_ACTIVE			U(0)
+#define DEV_COMM_ERROR			U(1)
+#define DEV_COMM_IDLE			U(2)
+#define DEV_COMM_PENDING		U(3)
+
+/* PCIe device specific details */
+struct pcie_dev {
+	/* Device identifier */
+	uint64_t bdf;
+
+	/* PCIe Segment identifier of the Root Port and endpoint. */
+	uint16_t segment_id;
+
+	/*
+	 * Physical PCIe routing identifier of the Root Port to which the
+	 * endpoint is connected.
+	 */
+	uint16_t root_id;
+
+	/* ECAM base address of the PCIe configuration space */
+	uint64_t ecam_addr;
+
+	/* Certificate slot identifier */
+	uint64_t cert_slot_id;
+
+	/* IDE stream ID */
+	uint64_t ide_sid;
+
+	/*
+	 * Base and top of requester ID range (inclusive). The value is in
+	 * PCI BDF format.
+	 */
+	uint64_t rid_base;
+	uint64_t rid_top;
+
+	/* Device non-coherent address range and its range */
+	struct rmi_address_range
+			ncoh_addr_range[PDEV_PARAM_NCOH_ADDR_RANGE_MAX];
+	uint64_t ncoh_num_addr_range;
+};
+
+/*
+ * PDEV object. Represents a communication channel between the RMM and a
+ * physical device, for example a PCIe device.
+ */
+struct pdev {
+	/* Pointer to this granule */
+	struct granule *g_pdev;
+
+	/* State of this PDEV. RmiPdevState */
+	unsigned long rmi_state;
+
+	/* Flags provided by the Host during PDEV creation. RmiPdevFlags */
+	unsigned long rmi_flags;
+
+	/* Number of VDEVs associated with this PDEV */
+	uint32_t num_vdevs;
+
+	/* Number and addresses of PDEV auxiliary granules */
+	struct granule *g_aux[PDEV_PARAM_AUX_GRANULES_MAX];
+	unsigned int num_aux;
+
+	/*
+	 * Algorithm used to generate device digests. This value is returned to
+	 * Realm as part of RDEV_GET_INFO call
+	 */
+	uint8_t rmi_hash_algo;
+
+	/* Device communiction state */
+	unsigned int dev_comm_state;
+
+	/* The associated device */
+	struct pcie_dev dev;
+
+	/* DA app cfg */
+	struct app_data_cfg da_app_data;
+};
+COMPILER_ASSERT(sizeof(struct pdev) <= GRANULE_SIZE);
+
+#endif /* DEV_H */
diff --git a/runtime/include/smc-handler.h b/runtime/include/smc-handler.h
index c24099b..82508cb 100644
--- a/runtime/include/smc-handler.h
+++ b/runtime/include/smc-handler.h
@@ -106,4 +106,7 @@
 			unsigned long ulevel,
 			struct smc_result *res);
 
+unsigned long smc_pdev_create(unsigned long pdev_ptr,
+			      unsigned long pdev_params_ptr);
+
 #endif /* SMC_HANDLER_H */
diff --git a/runtime/rmi/pdev.c b/runtime/rmi/pdev.c
new file mode 100644
index 0000000..1e1f4e8
--- /dev/null
+++ b/runtime/rmi/pdev.c
@@ -0,0 +1,279 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <arch_features.h>
+#include <buffer.h>
+#include <debug.h>
+#include <dev.h>
+#include <dev_assign_app.h>
+#include <feature.h>
+#include <granule.h>
+#include <sizes.h>
+#include <smc-handler.h>
+#include <smc-rmi.h>
+#include <string.h>
+#include <utils_def.h>
+
+/*
+ * This function will only be invoked when the PDEV create fails or when PDEV is
+ * being destroyed. Hence the PDEV will not be in use when this function is
+ * called and therefore no lock is acquired before its invocation.
+ */
+static void pdev_restore_aux_granules_state(struct granule *pdev_aux[],
+					    unsigned int cnt, bool scrub)
+{
+	for (unsigned int i = 0U; i < cnt; i++) {
+		struct granule *g_pdev_aux = pdev_aux[i];
+
+		granule_lock(g_pdev_aux, GRANULE_STATE_PDEV_AUX);
+		if (scrub) {
+			buffer_granule_memzero(g_pdev_aux,
+			   (enum buffer_slot)((unsigned int)SLOT_PDEV_AUX0 + i));
+		}
+		granule_unlock_transition(g_pdev_aux, GRANULE_STATE_DELEGATED);
+	}
+}
+
+/*
+ * todo:
+ * Validate device specific PDEV parameters by traversing all previously created
+ * PDEVs and check against current PDEV parameters. This implements
+ * RmiPdevParamsIsValid of RMM specification.
+ */
+static int validate_rmi_pdev_params(struct rmi_pdev_params *pd_params)
+
+{
+	(void)pd_params;
+	/*
+	 * Check if device identifier, Root Port identifier, IDE stream
+	 * identifier, RID range are valid.
+	 */
+
+	/*
+	 * Check if device identifier is not equal to the device identifier of
+	 * another PDEV
+	 */
+
+	/* Whether RID range does not overlap the RID range of another PDEV */
+
+	/*
+	 * Every address range falls within an MMIO range permitted by the system
+	 */
+
+	/*
+	 * None of the address ranges overlaps another address range for this
+	 * PDEV
+	 */
+
+	return 0;
+}
+
+/*
+ * smc_pdev_create
+ *
+ * pdev_ptr		- PA of the PDEV
+ * pdev_params_ptr	- PA of PDEV parameters
+ */
+unsigned long smc_pdev_create(unsigned long pdev_ptr,
+			      unsigned long pdev_params_ptr)
+{
+	struct granule *g_pdev;
+	struct granule *g_pdev_params;
+	struct pdev *pd;
+	struct rmi_pdev_params pdev_params; /* this consumes 4k of stack */
+	struct granule *pdev_aux_granules[PDEV_PARAM_AUX_GRANULES_MAX];
+	bool ns_access_ok;
+	void *aux_mapped_addr;
+	struct dev_assign_params dparams;
+	unsigned long smc_rc;
+	int rc;
+
+	if (!is_rmi_feat_da_enabled()) {
+		return SMC_NOT_SUPPORTED;
+	}
+
+	if (!GRANULE_ALIGNED(pdev_ptr) ||
+	    !GRANULE_ALIGNED(pdev_params_ptr)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	/* Map and copy PDEV parameters */
+	g_pdev_params = find_granule(pdev_params_ptr);
+	if ((g_pdev_params == NULL) ||
+	    (granule_unlocked_state(g_pdev_params) != GRANULE_STATE_NS)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	ns_access_ok = ns_buffer_read(SLOT_NS, g_pdev_params, 0U,
+				      sizeof(struct rmi_pdev_params),
+				      &pdev_params);
+	if (!ns_access_ok) {
+		return RMI_ERROR_INPUT;
+	}
+
+	/*
+	 * Validate RmiPdevFlags. RMM supports PCIE off-chip device represented
+	 * by flags: SPDM=true, IDE=true, COHERENT=false, P2P= false.
+	 */
+	/* coverity[uninit_use:SUPPRESS] */
+	if ((EXTRACT(RMI_PDEV_FLAGS_SPDM, pdev_params.flags) !=
+	     RMI_PDEV_SPDM_TRUE) ||
+	    (EXTRACT(RMI_PDEV_FLAGS_IDE, pdev_params.flags) !=
+	     RMI_PDEV_IDE_TRUE) ||
+	    (EXTRACT(RMI_PDEV_FLAGS_COHERENT, pdev_params.flags) !=
+	     RMI_PDEV_COHERENT_FALSE) ||
+	    (EXTRACT(RMI_PDEV_FLAGS_P2P, pdev_params.flags) !=
+	     RMI_PDEV_COHERENT_FALSE)) {
+		return RMI_ERROR_NOT_SUPPORTED;
+	}
+
+	/* Validate PDEV parameters that are not specific to a device class. */
+	/* coverity[uninit_use:SUPPRESS] */
+	if ((pdev_params.num_aux > PDEV_PARAM_AUX_GRANULES_MAX) ||
+	    (pdev_params.ncoh_num_addr_range > PDEV_PARAM_NCOH_ADDR_RANGE_MAX)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	/* Validate hash algorithm */
+	/* coverity[uninit_use:SUPPRESS] */
+	if ((pdev_params.hash_algo != RMI_HASH_SHA_256) &&
+	    (pdev_params.hash_algo != RMI_HASH_SHA_512)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	/* cppcheck-suppress knownConditionTrueFalse */
+	if (validate_rmi_pdev_params(&pdev_params) != 0) {
+		return RMI_ERROR_INPUT;
+	}
+
+	/* Loop through pdev_aux_granules and transit them */
+	for (unsigned int i = 0U; i < pdev_params.num_aux; i++) {
+		struct granule *g_pdev_aux;
+
+		/* coverity[uninit_use_in_call:SUPPRESS] */
+		g_pdev_aux = find_lock_granule(pdev_params.aux[i],
+					       GRANULE_STATE_DELEGATED);
+		if (g_pdev_aux == NULL) {
+			pdev_restore_aux_granules_state(pdev_aux_granules, i,
+							false);
+			return RMI_ERROR_INPUT;
+		}
+		granule_unlock_transition(g_pdev_aux, GRANULE_STATE_PDEV_AUX);
+		pdev_aux_granules[i] = g_pdev_aux;
+	}
+
+	/* Lock pdev granule and map it */
+	g_pdev = find_lock_granule(pdev_ptr, GRANULE_STATE_DELEGATED);
+	if (g_pdev == NULL) {
+		smc_rc = RMI_ERROR_INPUT;
+		goto out_restore_pdev_aux_granule_state;
+	}
+
+	pd = buffer_granule_map(g_pdev, SLOT_PDEV);
+	if (pd == NULL) {
+		smc_rc = RMI_ERROR_INPUT;
+		granule_unlock_transition(g_pdev, GRANULE_STATE_DELEGATED);
+		goto out_restore_pdev_aux_granule_state;
+	}
+
+	/* Map all PDEV aux granules to slot starting SLOT_PDEV_AUX0 */
+	aux_mapped_addr = buffer_pdev_aux_granules_map(pdev_aux_granules,
+						       (unsigned int)pdev_params.num_aux);
+	if (aux_mapped_addr == NULL) {
+		smc_rc = RMI_ERROR_INPUT;
+		goto out_unmap_pdev_slot_buffer;
+	}
+
+	/* Call init routine to initialize device class specific state */
+	dparams.dev_handle = (void *)pd;
+	dparams.rmi_hash_algo = pdev_params.hash_algo;
+	dparams.cert_slot_id = (uint8_t)pdev_params.cert_id;
+
+	if (EXTRACT(RMI_PDEV_FLAGS_IDE, pdev_params.flags) ==
+	    RMI_PDEV_IDE_TRUE) {
+		dparams.has_ide = true;
+		dparams.ecam_addr = pdev_params.ecam_addr;
+		dparams.rp_id = pdev_params.root_id;
+		dparams.ide_sid = pdev_params.ide_sid;
+	} else {
+		dparams.has_ide = false;
+	}
+	/* Use the PDEV aux pages for the DA app */
+	uintptr_t granule_pas[PDEV_PARAM_AUX_GRANULES_MAX];
+
+	for (unsigned int i = 0; i < pdev_params.num_aux; ++i) {
+		granule_pas[i] = granule_addr(pdev_aux_granules[i]);
+	}
+
+	rc = dev_assign_app_init(&pd->da_app_data,
+		granule_pas,
+		pdev_params.num_aux,
+		aux_mapped_addr, &dparams);
+
+	if (rc == DEV_ASSIGN_STATUS_SUCCESS) {
+		/* Initialize PDEV */
+		pd->g_pdev = g_pdev;
+		pd->rmi_state = RMI_PDEV_STATE_NEW;
+		pd->rmi_flags = pdev_params.flags;
+		pd->num_vdevs = 0;
+		pd->rmi_hash_algo = pdev_params.hash_algo;
+		pd->num_aux = (unsigned int)pdev_params.num_aux;
+		(void)memcpy((void *)pd->g_aux, (void *)pdev_aux_granules, pdev_params.num_aux *
+			     sizeof(struct granule *));
+
+		/* Initialize PDEV communication state */
+		pd->dev_comm_state = DEV_COMM_PENDING;
+
+		/* Initialize PDEV pcie device */
+		pd->dev.bdf = pdev_params.pdev_id;
+		pd->dev.segment_id = pdev_params.segment_id;
+		pd->dev.ecam_addr = pdev_params.ecam_addr;
+		pd->dev.root_id = pdev_params.root_id;
+		pd->dev.cert_slot_id = pdev_params.cert_id;
+		pd->dev.ide_sid = pdev_params.ide_sid;
+		pd->dev.rid_base = pdev_params.rid_base;
+		pd->dev.rid_top = pdev_params.rid_top;
+		pd->dev.ncoh_num_addr_range = pdev_params.ncoh_num_addr_range;
+		(void)memcpy(&pd->dev.ncoh_addr_range,
+			     &pdev_params.ncoh_addr_range,
+			     (sizeof(struct rmi_address_range) *
+			      pdev_params.ncoh_num_addr_range));
+
+		smc_rc = RMI_SUCCESS;
+	} else {
+		smc_rc = RMI_ERROR_INPUT;
+	}
+
+	/* Unmap all PDEV aux granules */
+	buffer_pdev_aux_unmap(aux_mapped_addr, (unsigned int)pdev_params.num_aux);
+
+out_unmap_pdev_slot_buffer:
+	/* unmap PDEV buffer from slot PDEV */
+	buffer_unmap(pd);
+
+	/*
+	 * On success, unlock and transit the PDEV granule state to
+	 * GRANULE_STATE_PDEV else unlock and retain the state at
+	 * GRANULE_STATE_DELEGATED.
+	 */
+	if (smc_rc == RMI_SUCCESS) {
+		granule_unlock_transition(g_pdev, GRANULE_STATE_PDEV);
+	} else {
+		granule_unlock_transition(g_pdev, GRANULE_STATE_DELEGATED);
+	}
+
+out_restore_pdev_aux_granule_state:
+	if (smc_rc != RMI_SUCCESS) {
+		/*
+		 * Transit all PDEV AUX granule state back to
+		 * GRANULE_STATE_DELEGATED
+		 */
+		pdev_restore_aux_granules_state(pdev_aux_granules,
+				(unsigned int)pdev_params.num_aux, false);
+	}
+
+	return smc_rc;
+}