1dff96888SDirk Hohndel (VMware) // SPDX-License-Identifier: GPL-2.0 OR MIT
289da76fdSSinclair Yeh /*
3dff96888SDirk Hohndel (VMware)  * Copyright 2016 VMware, Inc., Palo Alto, CA., USA
489da76fdSSinclair Yeh  *
589da76fdSSinclair Yeh  * Permission is hereby granted, free of charge, to any person obtaining a
689da76fdSSinclair Yeh  * copy of this software and associated documentation files (the
789da76fdSSinclair Yeh  * "Software"), to deal in the Software without restriction, including
889da76fdSSinclair Yeh  * without limitation the rights to use, copy, modify, merge, publish,
989da76fdSSinclair Yeh  * distribute, sub license, and/or sell copies of the Software, and to
1089da76fdSSinclair Yeh  * permit persons to whom the Software is furnished to do so, subject to
1189da76fdSSinclair Yeh  * the following conditions:
1289da76fdSSinclair Yeh  *
1389da76fdSSinclair Yeh  * The above copyright notice and this permission notice (including the
1489da76fdSSinclair Yeh  * next paragraph) shall be included in all copies or substantial portions
1589da76fdSSinclair Yeh  * of the Software.
1689da76fdSSinclair Yeh  *
1789da76fdSSinclair Yeh  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1889da76fdSSinclair Yeh  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1989da76fdSSinclair Yeh  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
2089da76fdSSinclair Yeh  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
2189da76fdSSinclair Yeh  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
2289da76fdSSinclair Yeh  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2389da76fdSSinclair Yeh  * USE OR OTHER DEALINGS IN THE SOFTWARE.
2489da76fdSSinclair Yeh  *
2589da76fdSSinclair Yeh  */
2689da76fdSSinclair Yeh 
270b0d81e3SJosh Poimboeuf #include <linux/frame.h>
286ae8748bSSam Ravnborg #include <linux/kernel.h>
296ae8748bSSam Ravnborg #include <linux/module.h>
306ae8748bSSam Ravnborg #include <linux/slab.h>
31af4eaf10SThomas Hellstrom #include <linux/mem_encrypt.h>
326ae8748bSSam Ravnborg 
3389da76fdSSinclair Yeh #include <asm/hypervisor.h>
346ae8748bSSam Ravnborg 
356ff67ae7SThomas Hellstrom #include "vmwgfx_drv.h"
3689da76fdSSinclair Yeh #include "vmwgfx_msg.h"
3789da76fdSSinclair Yeh 
3889da76fdSSinclair Yeh #define MESSAGE_STATUS_SUCCESS  0x0001
3989da76fdSSinclair Yeh #define MESSAGE_STATUS_DORECV   0x0002
4089da76fdSSinclair Yeh #define MESSAGE_STATUS_CPT      0x0010
4189da76fdSSinclair Yeh #define MESSAGE_STATUS_HB       0x0080
4289da76fdSSinclair Yeh 
4389da76fdSSinclair Yeh #define RPCI_PROTOCOL_NUM       0x49435052
4489da76fdSSinclair Yeh #define GUESTMSG_FLAG_COOKIE    0x80000000
4589da76fdSSinclair Yeh 
4689da76fdSSinclair Yeh #define RETRIES                 3
4789da76fdSSinclair Yeh 
4889da76fdSSinclair Yeh #define VMW_HYPERVISOR_MAGIC    0x564D5868
4989da76fdSSinclair Yeh 
5089da76fdSSinclair Yeh #define VMW_PORT_CMD_MSG        30
5189da76fdSSinclair Yeh #define VMW_PORT_CMD_HB_MSG     0
5289da76fdSSinclair Yeh #define VMW_PORT_CMD_OPEN_CHANNEL  (MSG_TYPE_OPEN << 16 | VMW_PORT_CMD_MSG)
5389da76fdSSinclair Yeh #define VMW_PORT_CMD_CLOSE_CHANNEL (MSG_TYPE_CLOSE << 16 | VMW_PORT_CMD_MSG)
5489da76fdSSinclair Yeh #define VMW_PORT_CMD_SENDSIZE   (MSG_TYPE_SENDSIZE << 16 | VMW_PORT_CMD_MSG)
5589da76fdSSinclair Yeh #define VMW_PORT_CMD_RECVSIZE   (MSG_TYPE_RECVSIZE << 16 | VMW_PORT_CMD_MSG)
5689da76fdSSinclair Yeh #define VMW_PORT_CMD_RECVSTATUS (MSG_TYPE_RECVSTATUS << 16 | VMW_PORT_CMD_MSG)
5789da76fdSSinclair Yeh 
5889da76fdSSinclair Yeh #define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16)
5989da76fdSSinclair Yeh 
60cb92a323SRoland Scheidegger #define MAX_USER_MSG_LENGTH	PAGE_SIZE
61cb92a323SRoland Scheidegger 
6289da76fdSSinclair Yeh static u32 vmw_msg_enabled = 1;
6389da76fdSSinclair Yeh 
6489da76fdSSinclair Yeh enum rpc_msg_type {
6589da76fdSSinclair Yeh 	MSG_TYPE_OPEN,
6689da76fdSSinclair Yeh 	MSG_TYPE_SENDSIZE,
6789da76fdSSinclair Yeh 	MSG_TYPE_SENDPAYLOAD,
6889da76fdSSinclair Yeh 	MSG_TYPE_RECVSIZE,
6989da76fdSSinclair Yeh 	MSG_TYPE_RECVPAYLOAD,
7089da76fdSSinclair Yeh 	MSG_TYPE_RECVSTATUS,
7189da76fdSSinclair Yeh 	MSG_TYPE_CLOSE,
7289da76fdSSinclair Yeh };
7389da76fdSSinclair Yeh 
7489da76fdSSinclair Yeh struct rpc_channel {
7589da76fdSSinclair Yeh 	u16 channel_id;
7689da76fdSSinclair Yeh 	u32 cookie_high;
7789da76fdSSinclair Yeh 	u32 cookie_low;
7889da76fdSSinclair Yeh };
7989da76fdSSinclair Yeh 
8089da76fdSSinclair Yeh 
8189da76fdSSinclair Yeh 
8289da76fdSSinclair Yeh /**
8389da76fdSSinclair Yeh  * vmw_open_channel
8489da76fdSSinclair Yeh  *
8589da76fdSSinclair Yeh  * @channel: RPC channel
8689da76fdSSinclair Yeh  * @protocol:
8789da76fdSSinclair Yeh  *
8889da76fdSSinclair Yeh  * Returns: 0 on success
8989da76fdSSinclair Yeh  */
9089da76fdSSinclair Yeh static int vmw_open_channel(struct rpc_channel *channel, unsigned int protocol)
9189da76fdSSinclair Yeh {
9289da76fdSSinclair Yeh 	unsigned long eax, ebx, ecx, edx, si = 0, di = 0;
9389da76fdSSinclair Yeh 
9489da76fdSSinclair Yeh 	VMW_PORT(VMW_PORT_CMD_OPEN_CHANNEL,
9589da76fdSSinclair Yeh 		(protocol | GUESTMSG_FLAG_COOKIE), si, di,
966abe3778SThomas Hellstrom 		0,
9789da76fdSSinclair Yeh 		VMW_HYPERVISOR_MAGIC,
9889da76fdSSinclair Yeh 		eax, ebx, ecx, edx, si, di);
9989da76fdSSinclair Yeh 
10089da76fdSSinclair Yeh 	if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
10189da76fdSSinclair Yeh 		return -EINVAL;
10289da76fdSSinclair Yeh 
10389da76fdSSinclair Yeh 	channel->channel_id  = HIGH_WORD(edx);
10489da76fdSSinclair Yeh 	channel->cookie_high = si;
10589da76fdSSinclair Yeh 	channel->cookie_low  = di;
10689da76fdSSinclair Yeh 
10789da76fdSSinclair Yeh 	return 0;
10889da76fdSSinclair Yeh }
10989da76fdSSinclair Yeh 
11089da76fdSSinclair Yeh 
11189da76fdSSinclair Yeh 
11289da76fdSSinclair Yeh /**
11389da76fdSSinclair Yeh  * vmw_close_channel
11489da76fdSSinclair Yeh  *
11589da76fdSSinclair Yeh  * @channel: RPC channel
11689da76fdSSinclair Yeh  *
11789da76fdSSinclair Yeh  * Returns: 0 on success
11889da76fdSSinclair Yeh  */
11989da76fdSSinclair Yeh static int vmw_close_channel(struct rpc_channel *channel)
12089da76fdSSinclair Yeh {
12189da76fdSSinclair Yeh 	unsigned long eax, ebx, ecx, edx, si, di;
12289da76fdSSinclair Yeh 
12389da76fdSSinclair Yeh 	/* Set up additional parameters */
12489da76fdSSinclair Yeh 	si  = channel->cookie_high;
12589da76fdSSinclair Yeh 	di  = channel->cookie_low;
12689da76fdSSinclair Yeh 
12789da76fdSSinclair Yeh 	VMW_PORT(VMW_PORT_CMD_CLOSE_CHANNEL,
12889da76fdSSinclair Yeh 		0, si, di,
1296abe3778SThomas Hellstrom 		channel->channel_id << 16,
13089da76fdSSinclair Yeh 		VMW_HYPERVISOR_MAGIC,
13189da76fdSSinclair Yeh 		eax, ebx, ecx, edx, si, di);
13289da76fdSSinclair Yeh 
13389da76fdSSinclair Yeh 	if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
13489da76fdSSinclair Yeh 		return -EINVAL;
13589da76fdSSinclair Yeh 
13689da76fdSSinclair Yeh 	return 0;
13789da76fdSSinclair Yeh }
13889da76fdSSinclair Yeh 
139cc0ba0d8SThomas Hellstrom /**
140cc0ba0d8SThomas Hellstrom  * vmw_port_hb_out - Send the message payload either through the
141cc0ba0d8SThomas Hellstrom  * high-bandwidth port if available, or through the backdoor otherwise.
142cc0ba0d8SThomas Hellstrom  * @channel: The rpc channel.
143cc0ba0d8SThomas Hellstrom  * @msg: NULL-terminated message.
144cc0ba0d8SThomas Hellstrom  * @hb: Whether the high-bandwidth port is available.
145cc0ba0d8SThomas Hellstrom  *
146cc0ba0d8SThomas Hellstrom  * Return: The port status.
147cc0ba0d8SThomas Hellstrom  */
148cc0ba0d8SThomas Hellstrom static unsigned long vmw_port_hb_out(struct rpc_channel *channel,
149cc0ba0d8SThomas Hellstrom 				     const char *msg, bool hb)
150cc0ba0d8SThomas Hellstrom {
151cc0ba0d8SThomas Hellstrom 	unsigned long si, di, eax, ebx, ecx, edx;
152cc0ba0d8SThomas Hellstrom 	unsigned long msg_len = strlen(msg);
153cc0ba0d8SThomas Hellstrom 
154af4eaf10SThomas Hellstrom 	/* HB port can't access encrypted memory. */
155af4eaf10SThomas Hellstrom 	if (hb && !mem_encrypt_active()) {
156cc0ba0d8SThomas Hellstrom 		unsigned long bp = channel->cookie_high;
157cc0ba0d8SThomas Hellstrom 
158cc0ba0d8SThomas Hellstrom 		si = (uintptr_t) msg;
159cc0ba0d8SThomas Hellstrom 		di = channel->cookie_low;
160cc0ba0d8SThomas Hellstrom 
161cc0ba0d8SThomas Hellstrom 		VMW_PORT_HB_OUT(
162cc0ba0d8SThomas Hellstrom 			(MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
163cc0ba0d8SThomas Hellstrom 			msg_len, si, di,
1646abe3778SThomas Hellstrom 			VMWARE_HYPERVISOR_HB | (channel->channel_id << 16) |
1656abe3778SThomas Hellstrom 			VMWARE_HYPERVISOR_OUT,
166cc0ba0d8SThomas Hellstrom 			VMW_HYPERVISOR_MAGIC, bp,
167cc0ba0d8SThomas Hellstrom 			eax, ebx, ecx, edx, si, di);
168cc0ba0d8SThomas Hellstrom 
169cc0ba0d8SThomas Hellstrom 		return ebx;
170cc0ba0d8SThomas Hellstrom 	}
171cc0ba0d8SThomas Hellstrom 
172cc0ba0d8SThomas Hellstrom 	/* HB port not available. Send the message 4 bytes at a time. */
173cc0ba0d8SThomas Hellstrom 	ecx = MESSAGE_STATUS_SUCCESS << 16;
174cc0ba0d8SThomas Hellstrom 	while (msg_len && (HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS)) {
175cc0ba0d8SThomas Hellstrom 		unsigned int bytes = min_t(size_t, msg_len, 4);
176cc0ba0d8SThomas Hellstrom 		unsigned long word = 0;
177cc0ba0d8SThomas Hellstrom 
178cc0ba0d8SThomas Hellstrom 		memcpy(&word, msg, bytes);
179cc0ba0d8SThomas Hellstrom 		msg_len -= bytes;
180cc0ba0d8SThomas Hellstrom 		msg += bytes;
181cc0ba0d8SThomas Hellstrom 		si = channel->cookie_high;
182cc0ba0d8SThomas Hellstrom 		di = channel->cookie_low;
183cc0ba0d8SThomas Hellstrom 
184cc0ba0d8SThomas Hellstrom 		VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_SENDPAYLOAD << 16),
185cc0ba0d8SThomas Hellstrom 			 word, si, di,
1866abe3778SThomas Hellstrom 			 channel->channel_id << 16,
187cc0ba0d8SThomas Hellstrom 			 VMW_HYPERVISOR_MAGIC,
188cc0ba0d8SThomas Hellstrom 			 eax, ebx, ecx, edx, si, di);
189cc0ba0d8SThomas Hellstrom 	}
190cc0ba0d8SThomas Hellstrom 
191cc0ba0d8SThomas Hellstrom 	return ecx;
192cc0ba0d8SThomas Hellstrom }
193cc0ba0d8SThomas Hellstrom 
194cc0ba0d8SThomas Hellstrom /**
195cc0ba0d8SThomas Hellstrom  * vmw_port_hb_in - Receive the message payload either through the
196cc0ba0d8SThomas Hellstrom  * high-bandwidth port if available, or through the backdoor otherwise.
197cc0ba0d8SThomas Hellstrom  * @channel: The rpc channel.
198cc0ba0d8SThomas Hellstrom  * @reply: Pointer to buffer holding reply.
199cc0ba0d8SThomas Hellstrom  * @reply_len: Length of the reply.
200cc0ba0d8SThomas Hellstrom  * @hb: Whether the high-bandwidth port is available.
201cc0ba0d8SThomas Hellstrom  *
202cc0ba0d8SThomas Hellstrom  * Return: The port status.
203cc0ba0d8SThomas Hellstrom  */
204cc0ba0d8SThomas Hellstrom static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply,
205cc0ba0d8SThomas Hellstrom 				    unsigned long reply_len, bool hb)
206cc0ba0d8SThomas Hellstrom {
207cc0ba0d8SThomas Hellstrom 	unsigned long si, di, eax, ebx, ecx, edx;
208cc0ba0d8SThomas Hellstrom 
209af4eaf10SThomas Hellstrom 	/* HB port can't access encrypted memory */
210af4eaf10SThomas Hellstrom 	if (hb && !mem_encrypt_active()) {
211cc0ba0d8SThomas Hellstrom 		unsigned long bp = channel->cookie_low;
212cc0ba0d8SThomas Hellstrom 
213cc0ba0d8SThomas Hellstrom 		si = channel->cookie_high;
214cc0ba0d8SThomas Hellstrom 		di = (uintptr_t) reply;
215cc0ba0d8SThomas Hellstrom 
216cc0ba0d8SThomas Hellstrom 		VMW_PORT_HB_IN(
217cc0ba0d8SThomas Hellstrom 			(MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
218cc0ba0d8SThomas Hellstrom 			reply_len, si, di,
2196abe3778SThomas Hellstrom 			VMWARE_HYPERVISOR_HB | (channel->channel_id << 16),
220cc0ba0d8SThomas Hellstrom 			VMW_HYPERVISOR_MAGIC, bp,
221cc0ba0d8SThomas Hellstrom 			eax, ebx, ecx, edx, si, di);
222cc0ba0d8SThomas Hellstrom 
223cc0ba0d8SThomas Hellstrom 		return ebx;
224cc0ba0d8SThomas Hellstrom 	}
225cc0ba0d8SThomas Hellstrom 
226cc0ba0d8SThomas Hellstrom 	/* HB port not available. Retrieve the message 4 bytes at a time. */
227cc0ba0d8SThomas Hellstrom 	ecx = MESSAGE_STATUS_SUCCESS << 16;
228cc0ba0d8SThomas Hellstrom 	while (reply_len) {
229cc0ba0d8SThomas Hellstrom 		unsigned int bytes = min_t(unsigned long, reply_len, 4);
230cc0ba0d8SThomas Hellstrom 
231cc0ba0d8SThomas Hellstrom 		si = channel->cookie_high;
232cc0ba0d8SThomas Hellstrom 		di = channel->cookie_low;
233cc0ba0d8SThomas Hellstrom 
234cc0ba0d8SThomas Hellstrom 		VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_RECVPAYLOAD << 16),
235cc0ba0d8SThomas Hellstrom 			 MESSAGE_STATUS_SUCCESS, si, di,
2366abe3778SThomas Hellstrom 			 channel->channel_id << 16,
237cc0ba0d8SThomas Hellstrom 			 VMW_HYPERVISOR_MAGIC,
238cc0ba0d8SThomas Hellstrom 			 eax, ebx, ecx, edx, si, di);
239cc0ba0d8SThomas Hellstrom 
240cc0ba0d8SThomas Hellstrom 		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
241cc0ba0d8SThomas Hellstrom 			break;
242cc0ba0d8SThomas Hellstrom 
243cc0ba0d8SThomas Hellstrom 		memcpy(reply, &ebx, bytes);
244cc0ba0d8SThomas Hellstrom 		reply_len -= bytes;
245cc0ba0d8SThomas Hellstrom 		reply += bytes;
246cc0ba0d8SThomas Hellstrom 	}
247cc0ba0d8SThomas Hellstrom 
248cc0ba0d8SThomas Hellstrom 	return ecx;
249cc0ba0d8SThomas Hellstrom }
25089da76fdSSinclair Yeh 
25189da76fdSSinclair Yeh 
25289da76fdSSinclair Yeh /**
25389da76fdSSinclair Yeh  * vmw_send_msg: Sends a message to the host
25489da76fdSSinclair Yeh  *
25589da76fdSSinclair Yeh  * @channel: RPC channel
25689da76fdSSinclair Yeh  * @logmsg: NULL terminated string
25789da76fdSSinclair Yeh  *
25889da76fdSSinclair Yeh  * Returns: 0 on success
25989da76fdSSinclair Yeh  */
26089da76fdSSinclair Yeh static int vmw_send_msg(struct rpc_channel *channel, const char *msg)
26189da76fdSSinclair Yeh {
262cc0ba0d8SThomas Hellstrom 	unsigned long eax, ebx, ecx, edx, si, di;
26389da76fdSSinclair Yeh 	size_t msg_len = strlen(msg);
26489da76fdSSinclair Yeh 	int retries = 0;
26589da76fdSSinclair Yeh 
26689da76fdSSinclair Yeh 	while (retries < RETRIES) {
26789da76fdSSinclair Yeh 		retries++;
26889da76fdSSinclair Yeh 
26989da76fdSSinclair Yeh 		/* Set up additional parameters */
27089da76fdSSinclair Yeh 		si  = channel->cookie_high;
27189da76fdSSinclair Yeh 		di  = channel->cookie_low;
27289da76fdSSinclair Yeh 
27389da76fdSSinclair Yeh 		VMW_PORT(VMW_PORT_CMD_SENDSIZE,
27489da76fdSSinclair Yeh 			msg_len, si, di,
2756abe3778SThomas Hellstrom 			channel->channel_id << 16,
27689da76fdSSinclair Yeh 			VMW_HYPERVISOR_MAGIC,
27789da76fdSSinclair Yeh 			eax, ebx, ecx, edx, si, di);
27889da76fdSSinclair Yeh 
279cc0ba0d8SThomas Hellstrom 		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
280cc0ba0d8SThomas Hellstrom 			/* Expected success. Give up. */
28189da76fdSSinclair Yeh 			return -EINVAL;
28289da76fdSSinclair Yeh 		}
28389da76fdSSinclair Yeh 
28489da76fdSSinclair Yeh 		/* Send msg */
285cc0ba0d8SThomas Hellstrom 		ebx = vmw_port_hb_out(channel, msg,
286cc0ba0d8SThomas Hellstrom 				      !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB));
28789da76fdSSinclair Yeh 
28889da76fdSSinclair Yeh 		if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) != 0) {
28989da76fdSSinclair Yeh 			return 0;
29089da76fdSSinclair Yeh 		} else if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) {
29189da76fdSSinclair Yeh 			/* A checkpoint occurred. Retry. */
29289da76fdSSinclair Yeh 			continue;
29389da76fdSSinclair Yeh 		} else {
29489da76fdSSinclair Yeh 			break;
29589da76fdSSinclair Yeh 		}
29689da76fdSSinclair Yeh 	}
29789da76fdSSinclair Yeh 
29889da76fdSSinclair Yeh 	return -EINVAL;
29989da76fdSSinclair Yeh }
3000b0d81e3SJosh Poimboeuf STACK_FRAME_NON_STANDARD(vmw_send_msg);
30189da76fdSSinclair Yeh 
30289da76fdSSinclair Yeh 
30389da76fdSSinclair Yeh /**
30489da76fdSSinclair Yeh  * vmw_recv_msg: Receives a message from the host
30589da76fdSSinclair Yeh  *
30689da76fdSSinclair Yeh  * Note:  It is the caller's responsibility to call kfree() on msg.
30789da76fdSSinclair Yeh  *
30889da76fdSSinclair Yeh  * @channel:  channel opened by vmw_open_channel
30989da76fdSSinclair Yeh  * @msg:  [OUT] message received from the host
31089da76fdSSinclair Yeh  * @msg_len: message length
31189da76fdSSinclair Yeh  */
31289da76fdSSinclair Yeh static int vmw_recv_msg(struct rpc_channel *channel, void **msg,
31389da76fdSSinclair Yeh 			size_t *msg_len)
31489da76fdSSinclair Yeh {
315cc0ba0d8SThomas Hellstrom 	unsigned long eax, ebx, ecx, edx, si, di;
31689da76fdSSinclair Yeh 	char *reply;
31789da76fdSSinclair Yeh 	size_t reply_len;
31889da76fdSSinclair Yeh 	int retries = 0;
31989da76fdSSinclair Yeh 
32089da76fdSSinclair Yeh 
32189da76fdSSinclair Yeh 	*msg_len = 0;
32289da76fdSSinclair Yeh 	*msg = NULL;
32389da76fdSSinclair Yeh 
32489da76fdSSinclair Yeh 	while (retries < RETRIES) {
32589da76fdSSinclair Yeh 		retries++;
32689da76fdSSinclair Yeh 
32789da76fdSSinclair Yeh 		/* Set up additional parameters */
32889da76fdSSinclair Yeh 		si  = channel->cookie_high;
32989da76fdSSinclair Yeh 		di  = channel->cookie_low;
33089da76fdSSinclair Yeh 
33189da76fdSSinclair Yeh 		VMW_PORT(VMW_PORT_CMD_RECVSIZE,
33289da76fdSSinclair Yeh 			0, si, di,
3336abe3778SThomas Hellstrom 			channel->channel_id << 16,
33489da76fdSSinclair Yeh 			VMW_HYPERVISOR_MAGIC,
33589da76fdSSinclair Yeh 			eax, ebx, ecx, edx, si, di);
33689da76fdSSinclair Yeh 
337cc0ba0d8SThomas Hellstrom 		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
3383fbeccf8SThomas Hellstrom 			DRM_ERROR("Failed to get reply size for host message.\n");
33989da76fdSSinclair Yeh 			return -EINVAL;
34089da76fdSSinclair Yeh 		}
34189da76fdSSinclair Yeh 
34289da76fdSSinclair Yeh 		/* No reply available.  This is okay. */
34389da76fdSSinclair Yeh 		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_DORECV) == 0)
34489da76fdSSinclair Yeh 			return 0;
34589da76fdSSinclair Yeh 
34689da76fdSSinclair Yeh 		reply_len = ebx;
34789da76fdSSinclair Yeh 		reply     = kzalloc(reply_len + 1, GFP_KERNEL);
3481a4adb05SRavikant B Sharma 		if (!reply) {
3493fbeccf8SThomas Hellstrom 			DRM_ERROR("Cannot allocate memory for host message reply.\n");
35089da76fdSSinclair Yeh 			return -ENOMEM;
35189da76fdSSinclair Yeh 		}
35289da76fdSSinclair Yeh 
35389da76fdSSinclair Yeh 
35489da76fdSSinclair Yeh 		/* Receive buffer */
355cc0ba0d8SThomas Hellstrom 		ebx = vmw_port_hb_in(channel, reply, reply_len,
356cc0ba0d8SThomas Hellstrom 				     !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB));
35789da76fdSSinclair Yeh 		if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) == 0) {
35889da76fdSSinclair Yeh 			kfree(reply);
35908b0c891SDan Carpenter 			reply = NULL;
36089da76fdSSinclair Yeh 			if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) {
36189da76fdSSinclair Yeh 				/* A checkpoint occurred. Retry. */
36289da76fdSSinclair Yeh 				continue;
36389da76fdSSinclair Yeh 			}
36489da76fdSSinclair Yeh 
36589da76fdSSinclair Yeh 			return -EINVAL;
36689da76fdSSinclair Yeh 		}
36789da76fdSSinclair Yeh 
36889da76fdSSinclair Yeh 		reply[reply_len] = '\0';
36989da76fdSSinclair Yeh 
37089da76fdSSinclair Yeh 
37189da76fdSSinclair Yeh 		/* Ack buffer */
37289da76fdSSinclair Yeh 		si  = channel->cookie_high;
37389da76fdSSinclair Yeh 		di  = channel->cookie_low;
37489da76fdSSinclair Yeh 
37589da76fdSSinclair Yeh 		VMW_PORT(VMW_PORT_CMD_RECVSTATUS,
37689da76fdSSinclair Yeh 			MESSAGE_STATUS_SUCCESS, si, di,
3776abe3778SThomas Hellstrom 			channel->channel_id << 16,
37889da76fdSSinclair Yeh 			VMW_HYPERVISOR_MAGIC,
37989da76fdSSinclair Yeh 			eax, ebx, ecx, edx, si, di);
38089da76fdSSinclair Yeh 
38189da76fdSSinclair Yeh 		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
38289da76fdSSinclair Yeh 			kfree(reply);
38308b0c891SDan Carpenter 			reply = NULL;
38489da76fdSSinclair Yeh 			if ((HIGH_WORD(ecx) & MESSAGE_STATUS_CPT) != 0) {
38589da76fdSSinclair Yeh 				/* A checkpoint occurred. Retry. */
38689da76fdSSinclair Yeh 				continue;
38789da76fdSSinclair Yeh 			}
38889da76fdSSinclair Yeh 
38989da76fdSSinclair Yeh 			return -EINVAL;
39089da76fdSSinclair Yeh 		}
39189da76fdSSinclair Yeh 
39289da76fdSSinclair Yeh 		break;
39389da76fdSSinclair Yeh 	}
39489da76fdSSinclair Yeh 
39508b0c891SDan Carpenter 	if (!reply)
396a9cd9c04SSinclair Yeh 		return -EINVAL;
397a9cd9c04SSinclair Yeh 
39889da76fdSSinclair Yeh 	*msg_len = reply_len;
39989da76fdSSinclair Yeh 	*msg     = reply;
40089da76fdSSinclair Yeh 
40189da76fdSSinclair Yeh 	return 0;
40289da76fdSSinclair Yeh }
4030b0d81e3SJosh Poimboeuf STACK_FRAME_NON_STANDARD(vmw_recv_msg);
40489da76fdSSinclair Yeh 
40589da76fdSSinclair Yeh 
40689da76fdSSinclair Yeh /**
40789da76fdSSinclair Yeh  * vmw_host_get_guestinfo: Gets a GuestInfo parameter
40889da76fdSSinclair Yeh  *
40989da76fdSSinclair Yeh  * Gets the value of a  GuestInfo.* parameter.  The value returned will be in
41089da76fdSSinclair Yeh  * a string, and it is up to the caller to post-process.
41189da76fdSSinclair Yeh  *
41289da76fdSSinclair Yeh  * @guest_info_param:  Parameter to get, e.g. GuestInfo.svga.gl3
41389da76fdSSinclair Yeh  * @buffer: if NULL, *reply_len will contain reply size.
41489da76fdSSinclair Yeh  * @length: size of the reply_buf.  Set to size of reply upon return
41589da76fdSSinclair Yeh  *
41689da76fdSSinclair Yeh  * Returns: 0 on success
41789da76fdSSinclair Yeh  */
41889da76fdSSinclair Yeh int vmw_host_get_guestinfo(const char *guest_info_param,
41989da76fdSSinclair Yeh 			   char *buffer, size_t *length)
42089da76fdSSinclair Yeh {
42189da76fdSSinclair Yeh 	struct rpc_channel channel;
42289da76fdSSinclair Yeh 	char *msg, *reply = NULL;
4236073a092SHimanshu Jha 	size_t reply_len = 0;
42489da76fdSSinclair Yeh 
42589da76fdSSinclair Yeh 	if (!vmw_msg_enabled)
42689da76fdSSinclair Yeh 		return -ENODEV;
42789da76fdSSinclair Yeh 
42889da76fdSSinclair Yeh 	if (!guest_info_param || !length)
42989da76fdSSinclair Yeh 		return -EINVAL;
43089da76fdSSinclair Yeh 
4316073a092SHimanshu Jha 	msg = kasprintf(GFP_KERNEL, "info-get %s", guest_info_param);
4321a4adb05SRavikant B Sharma 	if (!msg) {
4333fbeccf8SThomas Hellstrom 		DRM_ERROR("Cannot allocate memory to get guest info \"%s\".",
4343fbeccf8SThomas Hellstrom 			  guest_info_param);
43589da76fdSSinclair Yeh 		return -ENOMEM;
43689da76fdSSinclair Yeh 	}
43789da76fdSSinclair Yeh 
438f37230c0SThomas Hellstrom 	if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM))
439f37230c0SThomas Hellstrom 		goto out_open;
44089da76fdSSinclair Yeh 
441f37230c0SThomas Hellstrom 	if (vmw_send_msg(&channel, msg) ||
442f37230c0SThomas Hellstrom 	    vmw_recv_msg(&channel, (void *) &reply, &reply_len))
443f37230c0SThomas Hellstrom 		goto out_msg;
44489da76fdSSinclair Yeh 
445f37230c0SThomas Hellstrom 	vmw_close_channel(&channel);
44689da76fdSSinclair Yeh 	if (buffer && reply && reply_len > 0) {
44789da76fdSSinclair Yeh 		/* Remove reply code, which are the first 2 characters of
44889da76fdSSinclair Yeh 		 * the reply
44989da76fdSSinclair Yeh 		 */
45089da76fdSSinclair Yeh 		reply_len = max(reply_len - 2, (size_t) 0);
45189da76fdSSinclair Yeh 		reply_len = min(reply_len, *length);
45289da76fdSSinclair Yeh 
45389da76fdSSinclair Yeh 		if (reply_len > 0)
45489da76fdSSinclair Yeh 			memcpy(buffer, reply + 2, reply_len);
45589da76fdSSinclair Yeh 	}
45689da76fdSSinclair Yeh 
45789da76fdSSinclair Yeh 	*length = reply_len;
45889da76fdSSinclair Yeh 
45989da76fdSSinclair Yeh 	kfree(reply);
46089da76fdSSinclair Yeh 	kfree(msg);
46189da76fdSSinclair Yeh 
462f37230c0SThomas Hellstrom 	return 0;
463f37230c0SThomas Hellstrom 
464f37230c0SThomas Hellstrom out_msg:
465f37230c0SThomas Hellstrom 	vmw_close_channel(&channel);
466f37230c0SThomas Hellstrom 	kfree(reply);
467f37230c0SThomas Hellstrom out_open:
468f37230c0SThomas Hellstrom 	*length = 0;
469f37230c0SThomas Hellstrom 	kfree(msg);
4703fbeccf8SThomas Hellstrom 	DRM_ERROR("Failed to get guest info \"%s\".", guest_info_param);
471f37230c0SThomas Hellstrom 
472f37230c0SThomas Hellstrom 	return -EINVAL;
47389da76fdSSinclair Yeh }
47489da76fdSSinclair Yeh 
47589da76fdSSinclair Yeh 
47689da76fdSSinclair Yeh 
47789da76fdSSinclair Yeh /**
47889da76fdSSinclair Yeh  * vmw_host_log: Sends a log message to the host
47989da76fdSSinclair Yeh  *
48089da76fdSSinclair Yeh  * @log: NULL terminated string
48189da76fdSSinclair Yeh  *
48289da76fdSSinclair Yeh  * Returns: 0 on success
48389da76fdSSinclair Yeh  */
48489da76fdSSinclair Yeh int vmw_host_log(const char *log)
48589da76fdSSinclair Yeh {
48689da76fdSSinclair Yeh 	struct rpc_channel channel;
48789da76fdSSinclair Yeh 	char *msg;
48889da76fdSSinclair Yeh 	int ret = 0;
48989da76fdSSinclair Yeh 
49089da76fdSSinclair Yeh 
49189da76fdSSinclair Yeh 	if (!vmw_msg_enabled)
49289da76fdSSinclair Yeh 		return -ENODEV;
49389da76fdSSinclair Yeh 
49489da76fdSSinclair Yeh 	if (!log)
49589da76fdSSinclair Yeh 		return ret;
49689da76fdSSinclair Yeh 
4976073a092SHimanshu Jha 	msg = kasprintf(GFP_KERNEL, "log %s", log);
4981a4adb05SRavikant B Sharma 	if (!msg) {
4993fbeccf8SThomas Hellstrom 		DRM_ERROR("Cannot allocate memory for host log message.\n");
50089da76fdSSinclair Yeh 		return -ENOMEM;
50189da76fdSSinclair Yeh 	}
50289da76fdSSinclair Yeh 
503f37230c0SThomas Hellstrom 	if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM))
504f37230c0SThomas Hellstrom 		goto out_open;
50589da76fdSSinclair Yeh 
506f37230c0SThomas Hellstrom 	if (vmw_send_msg(&channel, msg))
507f37230c0SThomas Hellstrom 		goto out_msg;
50889da76fdSSinclair Yeh 
509f37230c0SThomas Hellstrom 	vmw_close_channel(&channel);
51089da76fdSSinclair Yeh 	kfree(msg);
51189da76fdSSinclair Yeh 
512f37230c0SThomas Hellstrom 	return 0;
513f37230c0SThomas Hellstrom 
514f37230c0SThomas Hellstrom out_msg:
515f37230c0SThomas Hellstrom 	vmw_close_channel(&channel);
516f37230c0SThomas Hellstrom out_open:
517f37230c0SThomas Hellstrom 	kfree(msg);
5183fbeccf8SThomas Hellstrom 	DRM_ERROR("Failed to send host log message.\n");
519f37230c0SThomas Hellstrom 
520f37230c0SThomas Hellstrom 	return -EINVAL;
52189da76fdSSinclair Yeh }
522cb92a323SRoland Scheidegger 
523cb92a323SRoland Scheidegger 
524cb92a323SRoland Scheidegger /**
525cb92a323SRoland Scheidegger  * vmw_msg_ioctl: Sends and receveives a message to/from host from/to user-space
526cb92a323SRoland Scheidegger  *
527cb92a323SRoland Scheidegger  * Sends a message from user-space to host.
528cb92a323SRoland Scheidegger  * Can also receive a result from host and return that to user-space.
529cb92a323SRoland Scheidegger  *
530cb92a323SRoland Scheidegger  * @dev: Identifies the drm device.
531cb92a323SRoland Scheidegger  * @data: Pointer to the ioctl argument.
532cb92a323SRoland Scheidegger  * @file_priv: Identifies the caller.
533cb92a323SRoland Scheidegger  * Return: Zero on success, negative error code on error.
534cb92a323SRoland Scheidegger  */
535cb92a323SRoland Scheidegger 
536cb92a323SRoland Scheidegger int vmw_msg_ioctl(struct drm_device *dev, void *data,
537cb92a323SRoland Scheidegger 		  struct drm_file *file_priv)
538cb92a323SRoland Scheidegger {
539cb92a323SRoland Scheidegger 	struct drm_vmw_msg_arg *arg =
540cb92a323SRoland Scheidegger 		(struct drm_vmw_msg_arg *) data;
541cb92a323SRoland Scheidegger 	struct rpc_channel channel;
542cb92a323SRoland Scheidegger 	char *msg;
543cb92a323SRoland Scheidegger 	int length;
544cb92a323SRoland Scheidegger 
545cb92a323SRoland Scheidegger 	msg = kmalloc(MAX_USER_MSG_LENGTH, GFP_KERNEL);
546cb92a323SRoland Scheidegger 	if (!msg) {
547cb92a323SRoland Scheidegger 		DRM_ERROR("Cannot allocate memory for log message.\n");
548cb92a323SRoland Scheidegger 		return -ENOMEM;
549cb92a323SRoland Scheidegger 	}
550cb92a323SRoland Scheidegger 
551cb92a323SRoland Scheidegger 	length = strncpy_from_user(msg, (void __user *)((unsigned long)arg->send),
552cb92a323SRoland Scheidegger 				   MAX_USER_MSG_LENGTH);
553cb92a323SRoland Scheidegger 	if (length < 0 || length >= MAX_USER_MSG_LENGTH) {
554cb92a323SRoland Scheidegger 		DRM_ERROR("Userspace message access failure.\n");
555cb92a323SRoland Scheidegger 		kfree(msg);
556cb92a323SRoland Scheidegger 		return -EINVAL;
557cb92a323SRoland Scheidegger 	}
558cb92a323SRoland Scheidegger 
559cb92a323SRoland Scheidegger 
560cb92a323SRoland Scheidegger 	if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM)) {
561cb92a323SRoland Scheidegger 		DRM_ERROR("Failed to open channel.\n");
562cb92a323SRoland Scheidegger 		goto out_open;
563cb92a323SRoland Scheidegger 	}
564cb92a323SRoland Scheidegger 
565cb92a323SRoland Scheidegger 	if (vmw_send_msg(&channel, msg)) {
566cb92a323SRoland Scheidegger 		DRM_ERROR("Failed to send message to host.\n");
567cb92a323SRoland Scheidegger 		goto out_msg;
568cb92a323SRoland Scheidegger 	}
569cb92a323SRoland Scheidegger 
570cb92a323SRoland Scheidegger 	if (!arg->send_only) {
571cb92a323SRoland Scheidegger 		char *reply = NULL;
572cb92a323SRoland Scheidegger 		size_t reply_len = 0;
573cb92a323SRoland Scheidegger 
574cb92a323SRoland Scheidegger 		if (vmw_recv_msg(&channel, (void *) &reply, &reply_len)) {
575cb92a323SRoland Scheidegger 			DRM_ERROR("Failed to receive message from host.\n");
576cb92a323SRoland Scheidegger 			goto out_msg;
577cb92a323SRoland Scheidegger 		}
578cb92a323SRoland Scheidegger 		if (reply && reply_len > 0) {
579cb92a323SRoland Scheidegger 			if (copy_to_user((void __user *)((unsigned long)arg->receive),
580cb92a323SRoland Scheidegger 							 reply, reply_len)) {
581cb92a323SRoland Scheidegger 				DRM_ERROR("Failed to copy message to userspace.\n");
582cb92a323SRoland Scheidegger 				kfree(reply);
583cb92a323SRoland Scheidegger 				goto out_msg;
584cb92a323SRoland Scheidegger 			}
585cb92a323SRoland Scheidegger 			arg->receive_len = (__u32)reply_len;
586cb92a323SRoland Scheidegger 		}
587cb92a323SRoland Scheidegger 		kfree(reply);
588cb92a323SRoland Scheidegger 	}
589cb92a323SRoland Scheidegger 
590cb92a323SRoland Scheidegger 	vmw_close_channel(&channel);
591cb92a323SRoland Scheidegger 	kfree(msg);
592cb92a323SRoland Scheidegger 
593cb92a323SRoland Scheidegger 	return 0;
594cb92a323SRoland Scheidegger 
595cb92a323SRoland Scheidegger out_msg:
596cb92a323SRoland Scheidegger 	vmw_close_channel(&channel);
597cb92a323SRoland Scheidegger out_open:
598cb92a323SRoland Scheidegger 	kfree(msg);
599cb92a323SRoland Scheidegger 
600cb92a323SRoland Scheidegger 	return -EINVAL;
601cb92a323SRoland Scheidegger }
602cb92a323SRoland Scheidegger 
603