aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorThomas Hellstrom <thellstrom@vmware.com>2019-05-29 08:15:19 +0200
committerThomas Hellstrom <thellstrom@vmware.com>2019-06-11 16:59:54 +0200
commitcc0ba0d8624f210995924bb57a8b181ce8976606 (patch)
treed9f31efec66c045ee2bb70dc00b75b4784be9984 /drivers/gpu
parentdrm/vmwgfx: integer underflow in vmw_cmd_dx_set_shader() leading to an invalid read (diff)
downloadlinux-dev-cc0ba0d8624f210995924bb57a8b181ce8976606.tar.xz
linux-dev-cc0ba0d8624f210995924bb57a8b181ce8976606.zip
drm/vmwgfx: Use the backdoor port if the HB port is not available
The HB port may not be available for various reasons. Either it has been disabled by a config option or by the hypervisor for other reasons. In that case, make sure we have a backup plan and use the backdoor port instead with a performance penalty. Cc: stable@vger.kernel.org Fixes: 89da76fde68d ("drm/vmwgfx: Add VMWare host messaging capability") Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com> Reviewed-by: Deepak Rawat <drawat@vmware.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_msg.c146
1 files changed, 117 insertions, 29 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c
index 8b9270f31409..e4e09d47c5c0 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c
@@ -136,6 +136,114 @@ static int vmw_close_channel(struct rpc_channel *channel)
return 0;
}
+/**
+ * vmw_port_hb_out - Send the message payload either through the
+ * high-bandwidth port if available, or through the backdoor otherwise.
+ * @channel: The rpc channel.
+ * @msg: NULL-terminated message.
+ * @hb: Whether the high-bandwidth port is available.
+ *
+ * Return: The port status.
+ */
+static unsigned long vmw_port_hb_out(struct rpc_channel *channel,
+ const char *msg, bool hb)
+{
+ unsigned long si, di, eax, ebx, ecx, edx;
+ unsigned long msg_len = strlen(msg);
+
+ if (hb) {
+ unsigned long bp = channel->cookie_high;
+
+ si = (uintptr_t) msg;
+ di = channel->cookie_low;
+
+ 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),
+ VMW_HYPERVISOR_MAGIC, bp,
+ eax, ebx, ecx, edx, si, di);
+
+ return ebx;
+ }
+
+ /* HB port not available. Send the message 4 bytes at a time. */
+ ecx = MESSAGE_STATUS_SUCCESS << 16;
+ while (msg_len && (HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS)) {
+ unsigned int bytes = min_t(size_t, msg_len, 4);
+ unsigned long word = 0;
+
+ memcpy(&word, msg, bytes);
+ msg_len -= bytes;
+ msg += bytes;
+ si = channel->cookie_high;
+ di = channel->cookie_low;
+
+ VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_SENDPAYLOAD << 16),
+ word, si, di,
+ VMW_HYPERVISOR_PORT | (channel->channel_id << 16),
+ VMW_HYPERVISOR_MAGIC,
+ eax, ebx, ecx, edx, si, di);
+ }
+
+ return ecx;
+}
+
+/**
+ * vmw_port_hb_in - Receive the message payload either through the
+ * high-bandwidth port if available, or through the backdoor otherwise.
+ * @channel: The rpc channel.
+ * @reply: Pointer to buffer holding reply.
+ * @reply_len: Length of the reply.
+ * @hb: Whether the high-bandwidth port is available.
+ *
+ * Return: The port status.
+ */
+static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply,
+ unsigned long reply_len, bool hb)
+{
+ unsigned long si, di, eax, ebx, ecx, edx;
+
+ if (hb) {
+ unsigned long bp = channel->cookie_low;
+
+ si = channel->cookie_high;
+ di = (uintptr_t) 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),
+ VMW_HYPERVISOR_MAGIC, bp,
+ eax, ebx, ecx, edx, si, di);
+
+ return ebx;
+ }
+
+ /* HB port not available. Retrieve the message 4 bytes at a time. */
+ ecx = MESSAGE_STATUS_SUCCESS << 16;
+ while (reply_len) {
+ unsigned int bytes = min_t(unsigned long, reply_len, 4);
+
+ si = channel->cookie_high;
+ di = channel->cookie_low;
+
+ VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_RECVPAYLOAD << 16),
+ MESSAGE_STATUS_SUCCESS, si, di,
+ VMW_HYPERVISOR_PORT | (channel->channel_id << 16),
+ VMW_HYPERVISOR_MAGIC,
+ eax, ebx, ecx, edx, si, di);
+
+ if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
+ break;
+
+ memcpy(reply, &ebx, bytes);
+ reply_len -= bytes;
+ reply += bytes;
+ }
+
+ return ecx;
+}
/**
@@ -148,11 +256,10 @@ static int vmw_close_channel(struct rpc_channel *channel)
*/
static int vmw_send_msg(struct rpc_channel *channel, const char *msg)
{
- unsigned long eax, ebx, ecx, edx, si, di, bp;
+ unsigned long eax, ebx, ecx, edx, si, di;
size_t msg_len = strlen(msg);
int retries = 0;
-
while (retries < RETRIES) {
retries++;
@@ -166,23 +273,14 @@ static int vmw_send_msg(struct rpc_channel *channel, const char *msg)
VMW_HYPERVISOR_MAGIC,
eax, ebx, ecx, edx, si, di);
- if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0 ||
- (HIGH_WORD(ecx) & MESSAGE_STATUS_HB) == 0) {
- /* Expected success + high-bandwidth. Give up. */
+ if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
+ /* Expected success. Give up. */
return -EINVAL;
}
/* Send msg */
- si = (uintptr_t) msg;
- di = channel->cookie_low;
- bp = channel->cookie_high;
-
- 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),
- VMW_HYPERVISOR_MAGIC, bp,
- eax, ebx, ecx, edx, si, di);
+ ebx = vmw_port_hb_out(channel, msg,
+ !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB));
if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) != 0) {
return 0;
@@ -211,7 +309,7 @@ STACK_FRAME_NON_STANDARD(vmw_send_msg);
static int vmw_recv_msg(struct rpc_channel *channel, void **msg,
size_t *msg_len)
{
- unsigned long eax, ebx, ecx, edx, si, di, bp;
+ unsigned long eax, ebx, ecx, edx, si, di;
char *reply;
size_t reply_len;
int retries = 0;
@@ -233,8 +331,7 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg,
VMW_HYPERVISOR_MAGIC,
eax, ebx, ecx, edx, si, di);
- if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0 ||
- (HIGH_WORD(ecx) & MESSAGE_STATUS_HB) == 0) {
+ if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
DRM_ERROR("Failed to get reply size for host message.\n");
return -EINVAL;
}
@@ -252,17 +349,8 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg,
/* Receive buffer */
- si = channel->cookie_high;
- di = (uintptr_t) reply;
- bp = channel->cookie_low;
-
- 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),
- VMW_HYPERVISOR_MAGIC, bp,
- eax, ebx, ecx, edx, si, di);
-
+ ebx = vmw_port_hb_in(channel, reply, reply_len,
+ !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB));
if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) == 0) {
kfree(reply);