[virtio-magma] Add magma_execute_command_buffer_with_resources

Copies the user memory blocks into a single buffer.

Change-Id: I959ee174b19e92fde68220af9c420f21975f54f9
diff --git a/drivers/virtio/virtio_magma.c b/drivers/virtio/virtio_magma.c
index cd6da57..0a35ff8 100644
--- a/drivers/virtio/virtio_magma.c
+++ b/drivers/virtio/virtio_magma.c
@@ -1025,6 +1025,81 @@
 	return 0;
 }
 
+static int virtmagma_command_magma_execute_command_buffer_with_resources(
+	struct virtmagma_instance *instance,
+	struct virtmagma_virtio_command *command)
+{
+	struct virtio_magma_execute_command_buffer_with_resources_ctrl *request =
+		command->request_ptr;
+	struct virtio_magma_execute_command_buffer_with_resources_resp
+		*response = command->response_ptr;
+	struct virtmagma_connection *connection;
+	struct virtmagma_command_buffer virt_command_buffer;
+	char *dst_ptr;
+	int ret;
+
+	if (!COMMAND_OK(command, request, response))
+		return -EINVAL;
+
+	connection = get_connection(instance, request->connection);
+	if (!connection)
+		return -EINVAL;
+
+	if (!get_connection_object(connection, request->context_id,
+				   MAGMA_CONTEXT))
+		return -EINVAL;
+
+	/* The virtmagma_comand_buffer includes the resources and semaphore data
+	   and their lengths, so we ignore the corresponding members of the request. */
+	ret = copy_from_user(&virt_command_buffer,
+			     (void *)request->command_buffer,
+			     sizeof(virt_command_buffer));
+	if (ret)
+		return ret;
+
+	/* reallocate request buffer with enough space for the structures */
+	command->request_size = sizeof(*request) +
+				virt_command_buffer.command_buffer_size +
+				virt_command_buffer.resource_size +
+				virt_command_buffer.semaphore_size;
+	/* memory will be freed by the caller */
+	dst_ptr = kzalloc(command->request_size, GFP_KERNEL);
+	if (!dst_ptr)
+		return -ENOMEM;
+
+	memcpy(dst_ptr, request, sizeof(*request));
+	command->request_ptr = dst_ptr;
+
+	dst_ptr += sizeof(*request);
+	ret = copy_from_user(dst_ptr,
+			     (void *)virt_command_buffer.command_buffer,
+			     virt_command_buffer.command_buffer_size);
+	if (ret)
+		return ret;
+
+	dst_ptr += virt_command_buffer.command_buffer_size;
+	ret = copy_from_user(dst_ptr, (void *)virt_command_buffer.resources,
+			     virt_command_buffer.resource_size);
+	if (ret)
+		return ret;
+
+	dst_ptr += virt_command_buffer.resource_size;
+	ret = copy_from_user(dst_ptr, (void *)virt_command_buffer.semaphores,
+			     virt_command_buffer.semaphore_size);
+	if (ret)
+		return ret;
+
+	ret = vq_out_send_sync(instance->vi, command);
+	if (ret)
+		return ret;
+
+	ret = virtmagma_check_expected_response_type(request, response);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static int virtmagma_command_magma_create_semaphore(
 	struct virtmagma_instance *instance,
 	struct virtmagma_virtio_command *command)
@@ -1411,6 +1486,10 @@
 		ret = virtmagma_command_magma_submit_command_buffer(instance,
 								    &command);
 		break;
+	case VIRTIO_MAGMA_CMD_EXECUTE_COMMAND_BUFFER_WITH_RESOURCES:
+		ret = virtmagma_command_magma_execute_command_buffer_with_resources(
+			instance, &command);
+		break;
 	case VIRTIO_MAGMA_CMD_CREATE_SEMAPHORE:
 		ret = virtmagma_command_magma_create_semaphore(instance,
 							       &command);
diff --git a/include/uapi/linux/virtmagma.h b/include/uapi/linux/virtmagma.h
index 5da7a3a..01bf455 100644
--- a/include/uapi/linux/virtmagma.h
+++ b/include/uapi/linux/virtmagma.h
@@ -39,6 +39,15 @@
 	__u64 response_size;
 };
 
+struct virtmagma_command_buffer {
+	__u64 command_buffer_size;
+	__u64 command_buffer;
+	__u64 resource_size;
+	__u64 resources;
+	__u64 semaphore_size;
+	__u64 semaphores;
+};
+
 #define VIRTMAGMA_IOCTL_HANDSHAKE VIRTMAGMA_IOWR(0x00, struct virtmagma_ioctl_args_handshake)
 #define VIRTMAGMA_IOCTL_GET_MMFD VIRTMAGMA_IOWR(0x01, struct virtmagma_ioctl_args_get_mmfd)
 #define VIRTMAGMA_IOCTL_MAGMA_COMMAND VIRTMAGMA_IOWR(0x02, struct virtmagma_ioctl_args_magma_command)