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 
2789da76fdSSinclair Yeh 
2889da76fdSSinclair Yeh #include <linux/slab.h>
2989da76fdSSinclair Yeh #include <linux/module.h>
3089da76fdSSinclair Yeh #include <linux/kernel.h>
310b0d81e3SJosh Poimboeuf #include <linux/frame.h>
3289da76fdSSinclair Yeh #include <asm/hypervisor.h>
33008be682SMasahiro Yamada #include <drm/drmP.h>
346ff67ae7SThomas Hellstrom #include "vmwgfx_drv.h"
3589da76fdSSinclair Yeh #include "vmwgfx_msg.h"
3689da76fdSSinclair Yeh 
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 #define VMW_HYPERVISOR_PORT     0x5658
5089da76fdSSinclair Yeh #define VMW_HYPERVISOR_HB_PORT  0x5659
5189da76fdSSinclair Yeh 
5289da76fdSSinclair Yeh #define VMW_PORT_CMD_MSG        30
5389da76fdSSinclair Yeh #define VMW_PORT_CMD_HB_MSG     0
5489da76fdSSinclair Yeh #define VMW_PORT_CMD_OPEN_CHANNEL  (MSG_TYPE_OPEN << 16 | VMW_PORT_CMD_MSG)
5589da76fdSSinclair Yeh #define VMW_PORT_CMD_CLOSE_CHANNEL (MSG_TYPE_CLOSE << 16 | VMW_PORT_CMD_MSG)
5689da76fdSSinclair Yeh #define VMW_PORT_CMD_SENDSIZE   (MSG_TYPE_SENDSIZE << 16 | VMW_PORT_CMD_MSG)
5789da76fdSSinclair Yeh #define VMW_PORT_CMD_RECVSIZE   (MSG_TYPE_RECVSIZE << 16 | VMW_PORT_CMD_MSG)
5889da76fdSSinclair Yeh #define VMW_PORT_CMD_RECVSTATUS (MSG_TYPE_RECVSTATUS << 16 | VMW_PORT_CMD_MSG)
5989da76fdSSinclair Yeh 
6089da76fdSSinclair Yeh #define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16)
6189da76fdSSinclair Yeh 
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,
9689da76fdSSinclair Yeh 		VMW_HYPERVISOR_PORT,
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,
12989da76fdSSinclair Yeh 		(VMW_HYPERVISOR_PORT | (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 
154cc0ba0d8SThomas Hellstrom 	if (hb) {
155cc0ba0d8SThomas Hellstrom 		unsigned long bp = channel->cookie_high;
156cc0ba0d8SThomas Hellstrom 
157cc0ba0d8SThomas Hellstrom 		si = (uintptr_t) msg;
158cc0ba0d8SThomas Hellstrom 		di = channel->cookie_low;
159cc0ba0d8SThomas Hellstrom 
160cc0ba0d8SThomas Hellstrom 		VMW_PORT_HB_OUT(
161cc0ba0d8SThomas Hellstrom 			(MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
162cc0ba0d8SThomas Hellstrom 			msg_len, si, di,
163cc0ba0d8SThomas Hellstrom 			VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16),
164cc0ba0d8SThomas Hellstrom 			VMW_HYPERVISOR_MAGIC, bp,
165cc0ba0d8SThomas Hellstrom 			eax, ebx, ecx, edx, si, di);
166cc0ba0d8SThomas Hellstrom 
167cc0ba0d8SThomas Hellstrom 		return ebx;
168cc0ba0d8SThomas Hellstrom 	}
169cc0ba0d8SThomas Hellstrom 
170cc0ba0d8SThomas Hellstrom 	/* HB port not available. Send the message 4 bytes at a time. */
171cc0ba0d8SThomas Hellstrom 	ecx = MESSAGE_STATUS_SUCCESS << 16;
172cc0ba0d8SThomas Hellstrom 	while (msg_len && (HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS)) {
173cc0ba0d8SThomas Hellstrom 		unsigned int bytes = min_t(size_t, msg_len, 4);
174cc0ba0d8SThomas Hellstrom 		unsigned long word = 0;
175cc0ba0d8SThomas Hellstrom 
176cc0ba0d8SThomas Hellstrom 		memcpy(&word, msg, bytes);
177cc0ba0d8SThomas Hellstrom 		msg_len -= bytes;
178cc0ba0d8SThomas Hellstrom 		msg += bytes;
179cc0ba0d8SThomas Hellstrom 		si = channel->cookie_high;
180cc0ba0d8SThomas Hellstrom 		di = channel->cookie_low;
181cc0ba0d8SThomas Hellstrom 
182cc0ba0d8SThomas Hellstrom 		VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_SENDPAYLOAD << 16),
183cc0ba0d8SThomas Hellstrom 			 word, si, di,
184cc0ba0d8SThomas Hellstrom 			 VMW_HYPERVISOR_PORT | (channel->channel_id << 16),
185cc0ba0d8SThomas Hellstrom 			 VMW_HYPERVISOR_MAGIC,
186cc0ba0d8SThomas Hellstrom 			 eax, ebx, ecx, edx, si, di);
187cc0ba0d8SThomas Hellstrom 	}
188cc0ba0d8SThomas Hellstrom 
189cc0ba0d8SThomas Hellstrom 	return ecx;
190cc0ba0d8SThomas Hellstrom }
191cc0ba0d8SThomas Hellstrom 
192cc0ba0d8SThomas Hellstrom /**
193cc0ba0d8SThomas Hellstrom  * vmw_port_hb_in - Receive the message payload either through the
194cc0ba0d8SThomas Hellstrom  * high-bandwidth port if available, or through the backdoor otherwise.
195cc0ba0d8SThomas Hellstrom  * @channel: The rpc channel.
196cc0ba0d8SThomas Hellstrom  * @reply: Pointer to buffer holding reply.
197cc0ba0d8SThomas Hellstrom  * @reply_len: Length of the reply.
198cc0ba0d8SThomas Hellstrom  * @hb: Whether the high-bandwidth port is available.
199cc0ba0d8SThomas Hellstrom  *
200cc0ba0d8SThomas Hellstrom  * Return: The port status.
201cc0ba0d8SThomas Hellstrom  */
202cc0ba0d8SThomas Hellstrom static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply,
203cc0ba0d8SThomas Hellstrom 				    unsigned long reply_len, bool hb)
204cc0ba0d8SThomas Hellstrom {
205cc0ba0d8SThomas Hellstrom 	unsigned long si, di, eax, ebx, ecx, edx;
206cc0ba0d8SThomas Hellstrom 
207cc0ba0d8SThomas Hellstrom 	if (hb) {
208cc0ba0d8SThomas Hellstrom 		unsigned long bp = channel->cookie_low;
209cc0ba0d8SThomas Hellstrom 
210cc0ba0d8SThomas Hellstrom 		si = channel->cookie_high;
211cc0ba0d8SThomas Hellstrom 		di = (uintptr_t) reply;
212cc0ba0d8SThomas Hellstrom 
213cc0ba0d8SThomas Hellstrom 		VMW_PORT_HB_IN(
214cc0ba0d8SThomas Hellstrom 			(MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
215cc0ba0d8SThomas Hellstrom 			reply_len, si, di,
216cc0ba0d8SThomas Hellstrom 			VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16),
217cc0ba0d8SThomas Hellstrom 			VMW_HYPERVISOR_MAGIC, bp,
218cc0ba0d8SThomas Hellstrom 			eax, ebx, ecx, edx, si, di);
219cc0ba0d8SThomas Hellstrom 
220cc0ba0d8SThomas Hellstrom 		return ebx;
221cc0ba0d8SThomas Hellstrom 	}
222cc0ba0d8SThomas Hellstrom 
223cc0ba0d8SThomas Hellstrom 	/* HB port not available. Retrieve the message 4 bytes at a time. */
224cc0ba0d8SThomas Hellstrom 	ecx = MESSAGE_STATUS_SUCCESS << 16;
225cc0ba0d8SThomas Hellstrom 	while (reply_len) {
226cc0ba0d8SThomas Hellstrom 		unsigned int bytes = min_t(unsigned long, reply_len, 4);
227cc0ba0d8SThomas Hellstrom 
228cc0ba0d8SThomas Hellstrom 		si = channel->cookie_high;
229cc0ba0d8SThomas Hellstrom 		di = channel->cookie_low;
230cc0ba0d8SThomas Hellstrom 
231cc0ba0d8SThomas Hellstrom 		VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_RECVPAYLOAD << 16),
232cc0ba0d8SThomas Hellstrom 			 MESSAGE_STATUS_SUCCESS, si, di,
233cc0ba0d8SThomas Hellstrom 			 VMW_HYPERVISOR_PORT | (channel->channel_id << 16),
234cc0ba0d8SThomas Hellstrom 			 VMW_HYPERVISOR_MAGIC,
235cc0ba0d8SThomas Hellstrom 			 eax, ebx, ecx, edx, si, di);
236cc0ba0d8SThomas Hellstrom 
237cc0ba0d8SThomas Hellstrom 		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
238cc0ba0d8SThomas Hellstrom 			break;
239cc0ba0d8SThomas Hellstrom 
240cc0ba0d8SThomas Hellstrom 		memcpy(reply, &ebx, bytes);
241cc0ba0d8SThomas Hellstrom 		reply_len -= bytes;
242cc0ba0d8SThomas Hellstrom 		reply += bytes;
243cc0ba0d8SThomas Hellstrom 	}
244cc0ba0d8SThomas Hellstrom 
245cc0ba0d8SThomas Hellstrom 	return ecx;
246cc0ba0d8SThomas Hellstrom }
24789da76fdSSinclair Yeh 
24889da76fdSSinclair Yeh 
24989da76fdSSinclair Yeh /**
25089da76fdSSinclair Yeh  * vmw_send_msg: Sends a message to the host
25189da76fdSSinclair Yeh  *
25289da76fdSSinclair Yeh  * @channel: RPC channel
25389da76fdSSinclair Yeh  * @logmsg: NULL terminated string
25489da76fdSSinclair Yeh  *
25589da76fdSSinclair Yeh  * Returns: 0 on success
25689da76fdSSinclair Yeh  */
25789da76fdSSinclair Yeh static int vmw_send_msg(struct rpc_channel *channel, const char *msg)
25889da76fdSSinclair Yeh {
259cc0ba0d8SThomas Hellstrom 	unsigned long eax, ebx, ecx, edx, si, di;
26089da76fdSSinclair Yeh 	size_t msg_len = strlen(msg);
26189da76fdSSinclair Yeh 	int retries = 0;
26289da76fdSSinclair Yeh 
26389da76fdSSinclair Yeh 	while (retries < RETRIES) {
26489da76fdSSinclair Yeh 		retries++;
26589da76fdSSinclair Yeh 
26689da76fdSSinclair Yeh 		/* Set up additional parameters */
26789da76fdSSinclair Yeh 		si  = channel->cookie_high;
26889da76fdSSinclair Yeh 		di  = channel->cookie_low;
26989da76fdSSinclair Yeh 
27089da76fdSSinclair Yeh 		VMW_PORT(VMW_PORT_CMD_SENDSIZE,
27189da76fdSSinclair Yeh 			msg_len, si, di,
27289da76fdSSinclair Yeh 			VMW_HYPERVISOR_PORT | (channel->channel_id << 16),
27389da76fdSSinclair Yeh 			VMW_HYPERVISOR_MAGIC,
27489da76fdSSinclair Yeh 			eax, ebx, ecx, edx, si, di);
27589da76fdSSinclair Yeh 
276cc0ba0d8SThomas Hellstrom 		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
277cc0ba0d8SThomas Hellstrom 			/* Expected success. Give up. */
27889da76fdSSinclair Yeh 			return -EINVAL;
27989da76fdSSinclair Yeh 		}
28089da76fdSSinclair Yeh 
28189da76fdSSinclair Yeh 		/* Send msg */
282cc0ba0d8SThomas Hellstrom 		ebx = vmw_port_hb_out(channel, msg,
283cc0ba0d8SThomas Hellstrom 				      !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB));
28489da76fdSSinclair Yeh 
28589da76fdSSinclair Yeh 		if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) != 0) {
28689da76fdSSinclair Yeh 			return 0;
28789da76fdSSinclair Yeh 		} else if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) {
28889da76fdSSinclair Yeh 			/* A checkpoint occurred. Retry. */
28989da76fdSSinclair Yeh 			continue;
29089da76fdSSinclair Yeh 		} else {
29189da76fdSSinclair Yeh 			break;
29289da76fdSSinclair Yeh 		}
29389da76fdSSinclair Yeh 	}
29489da76fdSSinclair Yeh 
29589da76fdSSinclair Yeh 	return -EINVAL;
29689da76fdSSinclair Yeh }
2970b0d81e3SJosh Poimboeuf STACK_FRAME_NON_STANDARD(vmw_send_msg);
29889da76fdSSinclair Yeh 
29989da76fdSSinclair Yeh 
30089da76fdSSinclair Yeh /**
30189da76fdSSinclair Yeh  * vmw_recv_msg: Receives a message from the host
30289da76fdSSinclair Yeh  *
30389da76fdSSinclair Yeh  * Note:  It is the caller's responsibility to call kfree() on msg.
30489da76fdSSinclair Yeh  *
30589da76fdSSinclair Yeh  * @channel:  channel opened by vmw_open_channel
30689da76fdSSinclair Yeh  * @msg:  [OUT] message received from the host
30789da76fdSSinclair Yeh  * @msg_len: message length
30889da76fdSSinclair Yeh  */
30989da76fdSSinclair Yeh static int vmw_recv_msg(struct rpc_channel *channel, void **msg,
31089da76fdSSinclair Yeh 			size_t *msg_len)
31189da76fdSSinclair Yeh {
312cc0ba0d8SThomas Hellstrom 	unsigned long eax, ebx, ecx, edx, si, di;
31389da76fdSSinclair Yeh 	char *reply;
31489da76fdSSinclair Yeh 	size_t reply_len;
31589da76fdSSinclair Yeh 	int retries = 0;
31689da76fdSSinclair Yeh 
31789da76fdSSinclair Yeh 
31889da76fdSSinclair Yeh 	*msg_len = 0;
31989da76fdSSinclair Yeh 	*msg = NULL;
32089da76fdSSinclair Yeh 
32189da76fdSSinclair Yeh 	while (retries < RETRIES) {
32289da76fdSSinclair Yeh 		retries++;
32389da76fdSSinclair Yeh 
32489da76fdSSinclair Yeh 		/* Set up additional parameters */
32589da76fdSSinclair Yeh 		si  = channel->cookie_high;
32689da76fdSSinclair Yeh 		di  = channel->cookie_low;
32789da76fdSSinclair Yeh 
32889da76fdSSinclair Yeh 		VMW_PORT(VMW_PORT_CMD_RECVSIZE,
32989da76fdSSinclair Yeh 			0, si, di,
33089da76fdSSinclair Yeh 			(VMW_HYPERVISOR_PORT | (channel->channel_id << 16)),
33189da76fdSSinclair Yeh 			VMW_HYPERVISOR_MAGIC,
33289da76fdSSinclair Yeh 			eax, ebx, ecx, edx, si, di);
33389da76fdSSinclair Yeh 
334cc0ba0d8SThomas Hellstrom 		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
3353fbeccf8SThomas Hellstrom 			DRM_ERROR("Failed to get reply size for host message.\n");
33689da76fdSSinclair Yeh 			return -EINVAL;
33789da76fdSSinclair Yeh 		}
33889da76fdSSinclair Yeh 
33989da76fdSSinclair Yeh 		/* No reply available.  This is okay. */
34089da76fdSSinclair Yeh 		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_DORECV) == 0)
34189da76fdSSinclair Yeh 			return 0;
34289da76fdSSinclair Yeh 
34389da76fdSSinclair Yeh 		reply_len = ebx;
34489da76fdSSinclair Yeh 		reply     = kzalloc(reply_len + 1, GFP_KERNEL);
3451a4adb05SRavikant B Sharma 		if (!reply) {
3463fbeccf8SThomas Hellstrom 			DRM_ERROR("Cannot allocate memory for host message reply.\n");
34789da76fdSSinclair Yeh 			return -ENOMEM;
34889da76fdSSinclair Yeh 		}
34989da76fdSSinclair Yeh 
35089da76fdSSinclair Yeh 
35189da76fdSSinclair Yeh 		/* Receive buffer */
352cc0ba0d8SThomas Hellstrom 		ebx = vmw_port_hb_in(channel, reply, reply_len,
353cc0ba0d8SThomas Hellstrom 				     !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB));
35489da76fdSSinclair Yeh 		if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) == 0) {
35589da76fdSSinclair Yeh 			kfree(reply);
35689da76fdSSinclair Yeh 
35789da76fdSSinclair Yeh 			if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) {
35889da76fdSSinclair Yeh 				/* A checkpoint occurred. Retry. */
35989da76fdSSinclair Yeh 				continue;
36089da76fdSSinclair Yeh 			}
36189da76fdSSinclair Yeh 
36289da76fdSSinclair Yeh 			return -EINVAL;
36389da76fdSSinclair Yeh 		}
36489da76fdSSinclair Yeh 
36589da76fdSSinclair Yeh 		reply[reply_len] = '\0';
36689da76fdSSinclair Yeh 
36789da76fdSSinclair Yeh 
36889da76fdSSinclair Yeh 		/* Ack buffer */
36989da76fdSSinclair Yeh 		si  = channel->cookie_high;
37089da76fdSSinclair Yeh 		di  = channel->cookie_low;
37189da76fdSSinclair Yeh 
37289da76fdSSinclair Yeh 		VMW_PORT(VMW_PORT_CMD_RECVSTATUS,
37389da76fdSSinclair Yeh 			MESSAGE_STATUS_SUCCESS, si, di,
37489da76fdSSinclair Yeh 			(VMW_HYPERVISOR_PORT | (channel->channel_id << 16)),
37589da76fdSSinclair Yeh 			VMW_HYPERVISOR_MAGIC,
37689da76fdSSinclair Yeh 			eax, ebx, ecx, edx, si, di);
37789da76fdSSinclair Yeh 
37889da76fdSSinclair Yeh 		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
37989da76fdSSinclair Yeh 			kfree(reply);
38089da76fdSSinclair Yeh 
38189da76fdSSinclair Yeh 			if ((HIGH_WORD(ecx) & MESSAGE_STATUS_CPT) != 0) {
38289da76fdSSinclair Yeh 				/* A checkpoint occurred. Retry. */
38389da76fdSSinclair Yeh 				continue;
38489da76fdSSinclair Yeh 			}
38589da76fdSSinclair Yeh 
38689da76fdSSinclair Yeh 			return -EINVAL;
38789da76fdSSinclair Yeh 		}
38889da76fdSSinclair Yeh 
38989da76fdSSinclair Yeh 		break;
39089da76fdSSinclair Yeh 	}
39189da76fdSSinclair Yeh 
392a9cd9c04SSinclair Yeh 	if (retries == RETRIES)
393a9cd9c04SSinclair Yeh 		return -EINVAL;
394a9cd9c04SSinclair Yeh 
39589da76fdSSinclair Yeh 	*msg_len = reply_len;
39689da76fdSSinclair Yeh 	*msg     = reply;
39789da76fdSSinclair Yeh 
39889da76fdSSinclair Yeh 	return 0;
39989da76fdSSinclair Yeh }
4000b0d81e3SJosh Poimboeuf STACK_FRAME_NON_STANDARD(vmw_recv_msg);
40189da76fdSSinclair Yeh 
40289da76fdSSinclair Yeh 
40389da76fdSSinclair Yeh /**
40489da76fdSSinclair Yeh  * vmw_host_get_guestinfo: Gets a GuestInfo parameter
40589da76fdSSinclair Yeh  *
40689da76fdSSinclair Yeh  * Gets the value of a  GuestInfo.* parameter.  The value returned will be in
40789da76fdSSinclair Yeh  * a string, and it is up to the caller to post-process.
40889da76fdSSinclair Yeh  *
40989da76fdSSinclair Yeh  * @guest_info_param:  Parameter to get, e.g. GuestInfo.svga.gl3
41089da76fdSSinclair Yeh  * @buffer: if NULL, *reply_len will contain reply size.
41189da76fdSSinclair Yeh  * @length: size of the reply_buf.  Set to size of reply upon return
41289da76fdSSinclair Yeh  *
41389da76fdSSinclair Yeh  * Returns: 0 on success
41489da76fdSSinclair Yeh  */
41589da76fdSSinclair Yeh int vmw_host_get_guestinfo(const char *guest_info_param,
41689da76fdSSinclair Yeh 			   char *buffer, size_t *length)
41789da76fdSSinclair Yeh {
41889da76fdSSinclair Yeh 	struct rpc_channel channel;
41989da76fdSSinclair Yeh 	char *msg, *reply = NULL;
4206073a092SHimanshu Jha 	size_t reply_len = 0;
42189da76fdSSinclair Yeh 
42289da76fdSSinclair Yeh 	if (!vmw_msg_enabled)
42389da76fdSSinclair Yeh 		return -ENODEV;
42489da76fdSSinclair Yeh 
42589da76fdSSinclair Yeh 	if (!guest_info_param || !length)
42689da76fdSSinclair Yeh 		return -EINVAL;
42789da76fdSSinclair Yeh 
4286073a092SHimanshu Jha 	msg = kasprintf(GFP_KERNEL, "info-get %s", guest_info_param);
4291a4adb05SRavikant B Sharma 	if (!msg) {
4303fbeccf8SThomas Hellstrom 		DRM_ERROR("Cannot allocate memory to get guest info \"%s\".",
4313fbeccf8SThomas Hellstrom 			  guest_info_param);
43289da76fdSSinclair Yeh 		return -ENOMEM;
43389da76fdSSinclair Yeh 	}
43489da76fdSSinclair Yeh 
435f37230c0SThomas Hellstrom 	if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM))
436f37230c0SThomas Hellstrom 		goto out_open;
43789da76fdSSinclair Yeh 
438f37230c0SThomas Hellstrom 	if (vmw_send_msg(&channel, msg) ||
439f37230c0SThomas Hellstrom 	    vmw_recv_msg(&channel, (void *) &reply, &reply_len))
440f37230c0SThomas Hellstrom 		goto out_msg;
44189da76fdSSinclair Yeh 
442f37230c0SThomas Hellstrom 	vmw_close_channel(&channel);
44389da76fdSSinclair Yeh 	if (buffer && reply && reply_len > 0) {
44489da76fdSSinclair Yeh 		/* Remove reply code, which are the first 2 characters of
44589da76fdSSinclair Yeh 		 * the reply
44689da76fdSSinclair Yeh 		 */
44789da76fdSSinclair Yeh 		reply_len = max(reply_len - 2, (size_t) 0);
44889da76fdSSinclair Yeh 		reply_len = min(reply_len, *length);
44989da76fdSSinclair Yeh 
45089da76fdSSinclair Yeh 		if (reply_len > 0)
45189da76fdSSinclair Yeh 			memcpy(buffer, reply + 2, reply_len);
45289da76fdSSinclair Yeh 	}
45389da76fdSSinclair Yeh 
45489da76fdSSinclair Yeh 	*length = reply_len;
45589da76fdSSinclair Yeh 
45689da76fdSSinclair Yeh 	kfree(reply);
45789da76fdSSinclair Yeh 	kfree(msg);
45889da76fdSSinclair Yeh 
459f37230c0SThomas Hellstrom 	return 0;
460f37230c0SThomas Hellstrom 
461f37230c0SThomas Hellstrom out_msg:
462f37230c0SThomas Hellstrom 	vmw_close_channel(&channel);
463f37230c0SThomas Hellstrom 	kfree(reply);
464f37230c0SThomas Hellstrom out_open:
465f37230c0SThomas Hellstrom 	*length = 0;
466f37230c0SThomas Hellstrom 	kfree(msg);
4673fbeccf8SThomas Hellstrom 	DRM_ERROR("Failed to get guest info \"%s\".", guest_info_param);
468f37230c0SThomas Hellstrom 
469f37230c0SThomas Hellstrom 	return -EINVAL;
47089da76fdSSinclair Yeh }
47189da76fdSSinclair Yeh 
47289da76fdSSinclair Yeh 
47389da76fdSSinclair Yeh 
47489da76fdSSinclair Yeh /**
47589da76fdSSinclair Yeh  * vmw_host_log: Sends a log message to the host
47689da76fdSSinclair Yeh  *
47789da76fdSSinclair Yeh  * @log: NULL terminated string
47889da76fdSSinclair Yeh  *
47989da76fdSSinclair Yeh  * Returns: 0 on success
48089da76fdSSinclair Yeh  */
48189da76fdSSinclair Yeh int vmw_host_log(const char *log)
48289da76fdSSinclair Yeh {
48389da76fdSSinclair Yeh 	struct rpc_channel channel;
48489da76fdSSinclair Yeh 	char *msg;
48589da76fdSSinclair Yeh 	int ret = 0;
48689da76fdSSinclair Yeh 
48789da76fdSSinclair Yeh 
48889da76fdSSinclair Yeh 	if (!vmw_msg_enabled)
48989da76fdSSinclair Yeh 		return -ENODEV;
49089da76fdSSinclair Yeh 
49189da76fdSSinclair Yeh 	if (!log)
49289da76fdSSinclair Yeh 		return ret;
49389da76fdSSinclair Yeh 
4946073a092SHimanshu Jha 	msg = kasprintf(GFP_KERNEL, "log %s", log);
4951a4adb05SRavikant B Sharma 	if (!msg) {
4963fbeccf8SThomas Hellstrom 		DRM_ERROR("Cannot allocate memory for host log message.\n");
49789da76fdSSinclair Yeh 		return -ENOMEM;
49889da76fdSSinclair Yeh 	}
49989da76fdSSinclair Yeh 
500f37230c0SThomas Hellstrom 	if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM))
501f37230c0SThomas Hellstrom 		goto out_open;
50289da76fdSSinclair Yeh 
503f37230c0SThomas Hellstrom 	if (vmw_send_msg(&channel, msg))
504f37230c0SThomas Hellstrom 		goto out_msg;
50589da76fdSSinclair Yeh 
506f37230c0SThomas Hellstrom 	vmw_close_channel(&channel);
50789da76fdSSinclair Yeh 	kfree(msg);
50889da76fdSSinclair Yeh 
509f37230c0SThomas Hellstrom 	return 0;
510f37230c0SThomas Hellstrom 
511f37230c0SThomas Hellstrom out_msg:
512f37230c0SThomas Hellstrom 	vmw_close_channel(&channel);
513f37230c0SThomas Hellstrom out_open:
514f37230c0SThomas Hellstrom 	kfree(msg);
5153fbeccf8SThomas Hellstrom 	DRM_ERROR("Failed to send host log message.\n");
516f37230c0SThomas Hellstrom 
517f37230c0SThomas Hellstrom 	return -EINVAL;
51889da76fdSSinclair Yeh }
519