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/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;
+}