diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_msg.c')
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_msg.c | 130 |
1 files changed, 106 insertions, 24 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c index 59e9d05ab928..e9f448a5ebb3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c @@ -24,17 +24,17 @@ * */ - -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/kernel.h> #include <linux/frame.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/mem_encrypt.h> + #include <asm/hypervisor.h> -#include <drm/drmP.h> + #include "vmwgfx_drv.h" #include "vmwgfx_msg.h" - #define MESSAGE_STATUS_SUCCESS 0x0001 #define MESSAGE_STATUS_DORECV 0x0002 #define MESSAGE_STATUS_CPT 0x0010 @@ -46,8 +46,6 @@ #define RETRIES 3 #define VMW_HYPERVISOR_MAGIC 0x564D5868 -#define VMW_HYPERVISOR_PORT 0x5658 -#define VMW_HYPERVISOR_HB_PORT 0x5659 #define VMW_PORT_CMD_MSG 30 #define VMW_PORT_CMD_HB_MSG 0 @@ -59,6 +57,8 @@ #define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16) +#define MAX_USER_MSG_LENGTH PAGE_SIZE + static u32 vmw_msg_enabled = 1; enum rpc_msg_type { @@ -93,7 +93,7 @@ static int vmw_open_channel(struct rpc_channel *channel, unsigned int protocol) VMW_PORT(VMW_PORT_CMD_OPEN_CHANNEL, (protocol | GUESTMSG_FLAG_COOKIE), si, di, - VMW_HYPERVISOR_PORT, + 0, VMW_HYPERVISOR_MAGIC, eax, ebx, ecx, edx, si, di); @@ -126,7 +126,7 @@ static int vmw_close_channel(struct rpc_channel *channel) VMW_PORT(VMW_PORT_CMD_CLOSE_CHANNEL, 0, si, di, - (VMW_HYPERVISOR_PORT | (channel->channel_id << 16)), + channel->channel_id << 16, VMW_HYPERVISOR_MAGIC, eax, ebx, ecx, edx, si, di); @@ -151,7 +151,8 @@ static unsigned long vmw_port_hb_out(struct rpc_channel *channel, unsigned long si, di, eax, ebx, ecx, edx; unsigned long msg_len = strlen(msg); - if (hb) { + /* HB port can't access encrypted memory. */ + if (hb && !mem_encrypt_active()) { unsigned long bp = channel->cookie_high; si = (uintptr_t) msg; @@ -160,7 +161,8 @@ static unsigned long vmw_port_hb_out(struct rpc_channel *channel, VMW_PORT_HB_OUT( (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, msg_len, si, di, - VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16), + VMWARE_HYPERVISOR_HB | (channel->channel_id << 16) | + VMWARE_HYPERVISOR_OUT, VMW_HYPERVISOR_MAGIC, bp, eax, ebx, ecx, edx, si, di); @@ -181,7 +183,7 @@ static unsigned long vmw_port_hb_out(struct rpc_channel *channel, VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_SENDPAYLOAD << 16), word, si, di, - VMW_HYPERVISOR_PORT | (channel->channel_id << 16), + channel->channel_id << 16, VMW_HYPERVISOR_MAGIC, eax, ebx, ecx, edx, si, di); } @@ -204,7 +206,8 @@ static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply, { unsigned long si, di, eax, ebx, ecx, edx; - if (hb) { + /* HB port can't access encrypted memory */ + if (hb && !mem_encrypt_active()) { unsigned long bp = channel->cookie_low; si = channel->cookie_high; @@ -213,7 +216,7 @@ static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply, VMW_PORT_HB_IN( (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, reply_len, si, di, - VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16), + VMWARE_HYPERVISOR_HB | (channel->channel_id << 16), VMW_HYPERVISOR_MAGIC, bp, eax, ebx, ecx, edx, si, di); @@ -230,7 +233,7 @@ static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply, VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_RECVPAYLOAD << 16), MESSAGE_STATUS_SUCCESS, si, di, - VMW_HYPERVISOR_PORT | (channel->channel_id << 16), + channel->channel_id << 16, VMW_HYPERVISOR_MAGIC, eax, ebx, ecx, edx, si, di); @@ -269,7 +272,7 @@ static int vmw_send_msg(struct rpc_channel *channel, const char *msg) VMW_PORT(VMW_PORT_CMD_SENDSIZE, msg_len, si, di, - VMW_HYPERVISOR_PORT | (channel->channel_id << 16), + channel->channel_id << 16, VMW_HYPERVISOR_MAGIC, eax, ebx, ecx, edx, si, di); @@ -327,7 +330,7 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg, VMW_PORT(VMW_PORT_CMD_RECVSIZE, 0, si, di, - (VMW_HYPERVISOR_PORT | (channel->channel_id << 16)), + channel->channel_id << 16, VMW_HYPERVISOR_MAGIC, eax, ebx, ecx, edx, si, di); @@ -353,7 +356,7 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg, !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB)); if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) == 0) { kfree(reply); - + reply = NULL; if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) { /* A checkpoint occurred. Retry. */ continue; @@ -371,13 +374,13 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg, VMW_PORT(VMW_PORT_CMD_RECVSTATUS, MESSAGE_STATUS_SUCCESS, si, di, - (VMW_HYPERVISOR_PORT | (channel->channel_id << 16)), + channel->channel_id << 16, VMW_HYPERVISOR_MAGIC, eax, ebx, ecx, edx, si, di); if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) { kfree(reply); - + reply = NULL; if ((HIGH_WORD(ecx) & MESSAGE_STATUS_CPT) != 0) { /* A checkpoint occurred. Retry. */ continue; @@ -389,10 +392,8 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg, break; } - if (retries == RETRIES) { - kfree(reply); + if (!reply) return -EINVAL; - } *msg_len = reply_len; *msg = reply; @@ -518,3 +519,84 @@ out_open: return -EINVAL; } + + +/** + * vmw_msg_ioctl: Sends and receveives a message to/from host from/to user-space + * + * Sends a message from user-space to host. + * Can also receive a result from host and return that to user-space. + * + * @dev: Identifies the drm device. + * @data: Pointer to the ioctl argument. + * @file_priv: Identifies the caller. + * Return: Zero on success, negative error code on error. + */ + +int vmw_msg_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_msg_arg *arg = + (struct drm_vmw_msg_arg *) data; + struct rpc_channel channel; + char *msg; + int length; + + msg = kmalloc(MAX_USER_MSG_LENGTH, GFP_KERNEL); + if (!msg) { + DRM_ERROR("Cannot allocate memory for log message.\n"); + return -ENOMEM; + } + + length = strncpy_from_user(msg, (void __user *)((unsigned long)arg->send), + MAX_USER_MSG_LENGTH); + if (length < 0 || length >= MAX_USER_MSG_LENGTH) { + DRM_ERROR("Userspace message access failure.\n"); + kfree(msg); + return -EINVAL; + } + + + if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM)) { + DRM_ERROR("Failed to open channel.\n"); + goto out_open; + } + + if (vmw_send_msg(&channel, msg)) { + DRM_ERROR("Failed to send message to host.\n"); + goto out_msg; + } + + if (!arg->send_only) { + char *reply = NULL; + size_t reply_len = 0; + + if (vmw_recv_msg(&channel, (void *) &reply, &reply_len)) { + DRM_ERROR("Failed to receive message from host.\n"); + goto out_msg; + } + if (reply && reply_len > 0) { + if (copy_to_user((void __user *)((unsigned long)arg->receive), + reply, reply_len)) { + DRM_ERROR("Failed to copy message to userspace.\n"); + kfree(reply); + goto out_msg; + } + arg->receive_len = (__u32)reply_len; + } + kfree(reply); + } + + vmw_close_channel(&channel); + kfree(msg); + + return 0; + +out_msg: + vmw_close_channel(&channel); +out_open: + kfree(msg); + + return -EINVAL; +} + |