feat(runtime/rmi): initial implementation of RMI_PDEV_COMMUNICATE
Implements RMI_PDEV_COMMUNICATE handler. This part of the implementation
covers
- Validates device communication data passed by NS host
- Maps PDEV aux granules, heap
- Dispatches command SPDM_INIT_CONNECTION. This includes sending SPDM
version, capabilities, algorithms to device and initializes the
connection based on PCI CMA SPDM specification.
Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
Signed-off-by: Soby Mathew <soby.mathew@arm.com>
Change-Id: Ic49a14c6c10fc9e15403ce29d1fbe2ff7ba6c83b
diff --git a/runtime/core/handler.c b/runtime/core/handler.c
index 2684d89..24947e1 100644
--- a/runtime/core/handler.c
+++ b/runtime/core/handler.c
@@ -162,7 +162,7 @@
HANDLER(DEV_MEM_MAP, 4, 0, smc_dev_mem_map, false, true),
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_COMMUNICATE, 2, 0, smc_pdev_communicate, true, true),
HANDLER(PDEV_CREATE, 2, 0, smc_pdev_create, true, true),
HANDLER(PDEV_DESTROY, 1, 0, smc_pdev_destroy, true, true),
HANDLER(PDEV_GET_STATE, 1, 1, smc_pdev_get_state, true, true),
diff --git a/runtime/include/smc-handler.h b/runtime/include/smc-handler.h
index 222c5bf..fc919ff 100644
--- a/runtime/include/smc-handler.h
+++ b/runtime/include/smc-handler.h
@@ -111,6 +111,9 @@
void smc_pdev_aux_count(unsigned long flags, struct smc_result *res);
+unsigned long smc_pdev_communicate(unsigned long pdev_ptr,
+ unsigned long dev_comm_data_ptr);
+
void smc_pdev_get_state(unsigned long pdev_ptr, struct smc_result *res);
unsigned long smc_pdev_destroy(unsigned long pdev_ptr);
diff --git a/runtime/rmi/pdev.c b/runtime/rmi/pdev.c
index 6284e7e..210eb76 100644
--- a/runtime/rmi/pdev.c
+++ b/runtime/rmi/pdev.c
@@ -331,6 +331,244 @@
return smc_rc;
}
+
+/* Validate RmiDevCommData.RmiDevCommEnter argument passed by Host */
+static unsigned long copyin_and_validate_dev_comm_enter(
+ unsigned long dev_comm_data_ptr,
+ struct rmi_dev_comm_enter *enter_args,
+ unsigned int dev_comm_state)
+{
+ struct granule *g_dev_comm_data;
+ struct granule *g_buf;
+ bool ns_access_ok;
+
+ g_dev_comm_data = find_granule(dev_comm_data_ptr);
+ if ((g_dev_comm_data == NULL) ||
+ (granule_unlocked_state(g_dev_comm_data) != GRANULE_STATE_NS)) {
+ return RMI_ERROR_INPUT;
+ }
+
+ ns_access_ok = ns_buffer_read(SLOT_NS, g_dev_comm_data,
+ RMI_DEV_COMM_ENTER_OFFSET,
+ sizeof(struct rmi_dev_comm_enter),
+ enter_args);
+ if (!ns_access_ok) {
+ return RMI_ERROR_INPUT;
+ }
+
+ if (!GRANULE_ALIGNED(enter_args->req_addr) ||
+ !GRANULE_ALIGNED(enter_args->resp_addr) ||
+ (enter_args->resp_len > GRANULE_SIZE)) {
+ return RMI_ERROR_INPUT;
+ }
+
+ if ((enter_args->status == RMI_DEV_COMM_ENTER_STATUS_RESPONSE) &&
+ (enter_args->resp_len == 0U)) {
+ return RMI_ERROR_INPUT;
+ }
+
+ /* Check if request and response buffers are in NS PAS */
+ g_buf = find_granule(enter_args->req_addr);
+ if ((g_buf == NULL) ||
+ (granule_unlocked_state(g_buf) != GRANULE_STATE_NS)) {
+ return RMI_ERROR_INPUT;
+ }
+
+ g_buf = find_granule(enter_args->resp_addr);
+ if ((g_buf == NULL) ||
+ (granule_unlocked_state(g_buf) != GRANULE_STATE_NS)) {
+ return RMI_ERROR_INPUT;
+ }
+
+ if ((dev_comm_state == DEV_COMM_ACTIVE) &&
+ ((enter_args->status != RMI_DEV_COMM_ENTER_STATUS_RESPONSE) &&
+ (enter_args->status != RMI_DEV_COMM_ENTER_STATUS_ERROR))) {
+ return RMI_ERROR_DEVICE;
+ }
+
+ if ((dev_comm_state == DEV_COMM_PENDING) &&
+ (enter_args->status != RMI_DEV_COMM_ENTER_STATUS_NONE)) {
+ return RMI_ERROR_DEVICE;
+ }
+ return RMI_SUCCESS;
+}
+
+/*
+ * copyout DevCommExitArgs
+ */
+static unsigned long copyout_dev_comm_exit(unsigned long dev_comm_data_ptr,
+ struct rmi_dev_comm_exit *exit_args)
+{
+ struct granule *g_dev_comm_data;
+ bool ns_access_ok;
+
+ g_dev_comm_data = find_granule(dev_comm_data_ptr);
+ if ((g_dev_comm_data == NULL) ||
+ (granule_unlocked_state(g_dev_comm_data) != GRANULE_STATE_NS)) {
+ return RMI_ERROR_INPUT;
+ }
+
+ ns_access_ok = ns_buffer_write(SLOT_NS, g_dev_comm_data,
+ RMI_DEV_COMM_EXIT_OFFSET,
+ sizeof(struct rmi_dev_comm_exit),
+ exit_args);
+ if (!ns_access_ok) {
+ return RMI_ERROR_INPUT;
+ }
+
+ return RMI_SUCCESS;
+}
+
+static int pdev_dispatch_cmd(struct pdev *pd, struct rmi_dev_comm_enter *enter_args,
+ struct rmi_dev_comm_exit *exit_args)
+{
+ int rc;
+
+ if (pd->dev_comm_state == DEV_COMM_ACTIVE) {
+ return dev_assign_dev_communicate(&pd->da_app_data, enter_args,
+ exit_args, DEVICE_ASSIGN_APP_FUNC_ID_RESUME);
+ }
+
+ switch (pd->rmi_state) {
+ case RMI_PDEV_STATE_NEW:
+ rc = dev_assign_dev_communicate(&pd->da_app_data, enter_args,
+ exit_args, DEVICE_ASSIGN_APP_FUNC_ID_CONNECT_INIT);
+ break;
+ default:
+ assert(false);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static unsigned long dev_communicate(struct pdev *pd,
+ unsigned long dev_comm_data_ptr)
+{
+ struct rmi_dev_comm_enter enter_args;
+ struct rmi_dev_comm_exit exit_args;
+ void *aux_mapped_addr;
+ unsigned long comm_rc;
+ int rc;
+
+ assert(pd != NULL);
+
+ if ((pd->dev_comm_state == DEV_COMM_IDLE) ||
+ (pd->dev_comm_state == DEV_COMM_ERROR)) {
+ return RMI_ERROR_DEVICE;
+ }
+
+ /* Validate RmiDevCommEnter arguments in DevCommData */
+ /* coverity[uninit_use_in_call:SUPPRESS] */
+ comm_rc = copyin_and_validate_dev_comm_enter(dev_comm_data_ptr, &enter_args,
+ pd->dev_comm_state);
+ if (comm_rc != RMI_SUCCESS) {
+ return comm_rc;
+ }
+
+ /* Map PDEV aux granules */
+ aux_mapped_addr = buffer_pdev_aux_granules_map(pd->g_aux, pd->num_aux);
+ assert(aux_mapped_addr != NULL);
+
+ rc = pdev_dispatch_cmd(pd, &enter_args, &exit_args);
+
+ /* Unmap all PDEV aux granules */
+ buffer_pdev_aux_unmap(aux_mapped_addr, pd->num_aux);
+
+ comm_rc = copyout_dev_comm_exit(dev_comm_data_ptr,
+ &exit_args);
+ if (comm_rc != RMI_SUCCESS) {
+ /* todo: device status is updated but copyout data failed? */
+ return RMI_ERROR_INPUT;
+ }
+
+ /*
+ * Based on the device communication results update the device IO state
+ * and PDEV state.
+ */
+ switch (rc) {
+ case DEV_ASSIGN_STATUS_COMM_BLOCKED:
+ pd->dev_comm_state = DEV_COMM_ACTIVE;
+ break;
+ case DEV_ASSIGN_STATUS_ERROR:
+ pd->dev_comm_state = DEV_COMM_ERROR;
+ break;
+ case DEV_ASSIGN_STATUS_SUCCESS:
+ pd->dev_comm_state = DEV_COMM_IDLE;
+ break;
+ default:
+ assert(false);
+ }
+
+ return RMI_SUCCESS;
+}
+
+/*
+ * smc_pdev_communicate
+ *
+ * pdev_ptr - PA of the PDEV
+ * data_ptr - PA of the communication data structure
+ */
+unsigned long smc_pdev_communicate(unsigned long pdev_ptr,
+ unsigned long dev_comm_data_ptr)
+{
+ struct granule *g_pdev;
+ struct pdev *pd;
+ unsigned long rmi_rc;
+
+ if (!is_rmi_feat_da_enabled()) {
+ return SMC_NOT_SUPPORTED;
+ }
+
+ if (!GRANULE_ALIGNED(pdev_ptr) || !GRANULE_ALIGNED(dev_comm_data_ptr)) {
+ return RMI_ERROR_INPUT;
+ }
+
+ /* Lock pdev granule and map it */
+ g_pdev = find_lock_granule(pdev_ptr, GRANULE_STATE_PDEV);
+ if (g_pdev == NULL) {
+ return RMI_ERROR_INPUT;
+ }
+
+ pd = buffer_granule_map(g_pdev, SLOT_PDEV);
+ if (pd == NULL) {
+ granule_unlock(g_pdev);
+ return RMI_ERROR_INPUT;
+ }
+
+ assert(pd->g_pdev == g_pdev);
+
+ rmi_rc = dev_communicate(pd, dev_comm_data_ptr);
+
+ /*
+ * Based on the device communication results update the device IO state
+ * and PDEV state.
+ */
+ switch (pd->dev_comm_state) {
+ case DEV_COMM_ERROR:
+ pd->rmi_state = RMI_PDEV_STATE_ERROR;
+ break;
+ case DEV_COMM_IDLE:
+ if (pd->rmi_state == RMI_PDEV_STATE_NEW) {
+ pd->rmi_state = RMI_PDEV_STATE_NEEDS_KEY;
+ } else {
+ pd->rmi_state = RMI_PDEV_STATE_ERROR;
+ }
+ break;
+ case DEV_COMM_ACTIVE:
+ /* No state change required */
+ break;
+ case DEV_COMM_PENDING:
+ default:
+ assert(false);
+ }
+
+ buffer_unmap(pd);
+ granule_unlock(g_pdev);
+
+ return rmi_rc;
+}
+
/*
* smc_pdev_get_state
*