18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2016 VMware, Inc., Palo Alto, CA., USA
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
68c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the
78c2ecf20Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
88c2ecf20Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
98c2ecf20Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
108c2ecf20Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
118c2ecf20Sopenharmony_ci * the following conditions:
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the
148c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
158c2ecf20Sopenharmony_ci * of the Software.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
188c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
198c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
208c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
218c2ecf20Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
228c2ecf20Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
238c2ecf20Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/objtool.h>
288c2ecf20Sopenharmony_ci#include <linux/kernel.h>
298c2ecf20Sopenharmony_ci#include <linux/module.h>
308c2ecf20Sopenharmony_ci#include <linux/slab.h>
318c2ecf20Sopenharmony_ci#include <linux/mem_encrypt.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <asm/hypervisor.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include "vmwgfx_drv.h"
368c2ecf20Sopenharmony_ci#include "vmwgfx_msg.h"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define MESSAGE_STATUS_SUCCESS  0x0001
398c2ecf20Sopenharmony_ci#define MESSAGE_STATUS_DORECV   0x0002
408c2ecf20Sopenharmony_ci#define MESSAGE_STATUS_CPT      0x0010
418c2ecf20Sopenharmony_ci#define MESSAGE_STATUS_HB       0x0080
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define RPCI_PROTOCOL_NUM       0x49435052
448c2ecf20Sopenharmony_ci#define GUESTMSG_FLAG_COOKIE    0x80000000
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define RETRIES                 3
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define VMW_HYPERVISOR_MAGIC    0x564D5868
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define VMW_PORT_CMD_MSG        30
518c2ecf20Sopenharmony_ci#define VMW_PORT_CMD_HB_MSG     0
528c2ecf20Sopenharmony_ci#define VMW_PORT_CMD_OPEN_CHANNEL  (MSG_TYPE_OPEN << 16 | VMW_PORT_CMD_MSG)
538c2ecf20Sopenharmony_ci#define VMW_PORT_CMD_CLOSE_CHANNEL (MSG_TYPE_CLOSE << 16 | VMW_PORT_CMD_MSG)
548c2ecf20Sopenharmony_ci#define VMW_PORT_CMD_SENDSIZE   (MSG_TYPE_SENDSIZE << 16 | VMW_PORT_CMD_MSG)
558c2ecf20Sopenharmony_ci#define VMW_PORT_CMD_RECVSIZE   (MSG_TYPE_RECVSIZE << 16 | VMW_PORT_CMD_MSG)
568c2ecf20Sopenharmony_ci#define VMW_PORT_CMD_RECVSTATUS (MSG_TYPE_RECVSTATUS << 16 | VMW_PORT_CMD_MSG)
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16)
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define MAX_USER_MSG_LENGTH	PAGE_SIZE
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic u32 vmw_msg_enabled = 1;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cienum rpc_msg_type {
658c2ecf20Sopenharmony_ci	MSG_TYPE_OPEN,
668c2ecf20Sopenharmony_ci	MSG_TYPE_SENDSIZE,
678c2ecf20Sopenharmony_ci	MSG_TYPE_SENDPAYLOAD,
688c2ecf20Sopenharmony_ci	MSG_TYPE_RECVSIZE,
698c2ecf20Sopenharmony_ci	MSG_TYPE_RECVPAYLOAD,
708c2ecf20Sopenharmony_ci	MSG_TYPE_RECVSTATUS,
718c2ecf20Sopenharmony_ci	MSG_TYPE_CLOSE,
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistruct rpc_channel {
758c2ecf20Sopenharmony_ci	u16 channel_id;
768c2ecf20Sopenharmony_ci	u32 cookie_high;
778c2ecf20Sopenharmony_ci	u32 cookie_low;
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/**
838c2ecf20Sopenharmony_ci * vmw_open_channel
848c2ecf20Sopenharmony_ci *
858c2ecf20Sopenharmony_ci * @channel: RPC channel
868c2ecf20Sopenharmony_ci * @protocol:
878c2ecf20Sopenharmony_ci *
888c2ecf20Sopenharmony_ci * Returns: 0 on success
898c2ecf20Sopenharmony_ci */
908c2ecf20Sopenharmony_cistatic int vmw_open_channel(struct rpc_channel *channel, unsigned int protocol)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	unsigned long eax, ebx, ecx, edx, si = 0, di = 0;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	VMW_PORT(VMW_PORT_CMD_OPEN_CHANNEL,
958c2ecf20Sopenharmony_ci		(protocol | GUESTMSG_FLAG_COOKIE), si, di,
968c2ecf20Sopenharmony_ci		0,
978c2ecf20Sopenharmony_ci		VMW_HYPERVISOR_MAGIC,
988c2ecf20Sopenharmony_ci		eax, ebx, ecx, edx, si, di);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
1018c2ecf20Sopenharmony_ci		return -EINVAL;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	channel->channel_id  = HIGH_WORD(edx);
1048c2ecf20Sopenharmony_ci	channel->cookie_high = si;
1058c2ecf20Sopenharmony_ci	channel->cookie_low  = di;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/**
1138c2ecf20Sopenharmony_ci * vmw_close_channel
1148c2ecf20Sopenharmony_ci *
1158c2ecf20Sopenharmony_ci * @channel: RPC channel
1168c2ecf20Sopenharmony_ci *
1178c2ecf20Sopenharmony_ci * Returns: 0 on success
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_cistatic int vmw_close_channel(struct rpc_channel *channel)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	unsigned long eax, ebx, ecx, edx, si, di;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/* Set up additional parameters */
1248c2ecf20Sopenharmony_ci	si  = channel->cookie_high;
1258c2ecf20Sopenharmony_ci	di  = channel->cookie_low;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	VMW_PORT(VMW_PORT_CMD_CLOSE_CHANNEL,
1288c2ecf20Sopenharmony_ci		0, si, di,
1298c2ecf20Sopenharmony_ci		channel->channel_id << 16,
1308c2ecf20Sopenharmony_ci		VMW_HYPERVISOR_MAGIC,
1318c2ecf20Sopenharmony_ci		eax, ebx, ecx, edx, si, di);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
1348c2ecf20Sopenharmony_ci		return -EINVAL;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	return 0;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/**
1408c2ecf20Sopenharmony_ci * vmw_port_hb_out - Send the message payload either through the
1418c2ecf20Sopenharmony_ci * high-bandwidth port if available, or through the backdoor otherwise.
1428c2ecf20Sopenharmony_ci * @channel: The rpc channel.
1438c2ecf20Sopenharmony_ci * @msg: NULL-terminated message.
1448c2ecf20Sopenharmony_ci * @hb: Whether the high-bandwidth port is available.
1458c2ecf20Sopenharmony_ci *
1468c2ecf20Sopenharmony_ci * Return: The port status.
1478c2ecf20Sopenharmony_ci */
1488c2ecf20Sopenharmony_cistatic unsigned long vmw_port_hb_out(struct rpc_channel *channel,
1498c2ecf20Sopenharmony_ci				     const char *msg, bool hb)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	unsigned long si, di, eax, ebx, ecx, edx;
1528c2ecf20Sopenharmony_ci	unsigned long msg_len = strlen(msg);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* HB port can't access encrypted memory. */
1558c2ecf20Sopenharmony_ci	if (hb && !mem_encrypt_active()) {
1568c2ecf20Sopenharmony_ci		unsigned long bp = channel->cookie_high;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		si = (uintptr_t) msg;
1598c2ecf20Sopenharmony_ci		di = channel->cookie_low;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		VMW_PORT_HB_OUT(
1628c2ecf20Sopenharmony_ci			(MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
1638c2ecf20Sopenharmony_ci			msg_len, si, di,
1648c2ecf20Sopenharmony_ci			VMWARE_HYPERVISOR_HB | (channel->channel_id << 16) |
1658c2ecf20Sopenharmony_ci			VMWARE_HYPERVISOR_OUT,
1668c2ecf20Sopenharmony_ci			VMW_HYPERVISOR_MAGIC, bp,
1678c2ecf20Sopenharmony_ci			eax, ebx, ecx, edx, si, di);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		return ebx;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/* HB port not available. Send the message 4 bytes at a time. */
1738c2ecf20Sopenharmony_ci	ecx = MESSAGE_STATUS_SUCCESS << 16;
1748c2ecf20Sopenharmony_ci	while (msg_len && (HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS)) {
1758c2ecf20Sopenharmony_ci		unsigned int bytes = min_t(size_t, msg_len, 4);
1768c2ecf20Sopenharmony_ci		unsigned long word = 0;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		memcpy(&word, msg, bytes);
1798c2ecf20Sopenharmony_ci		msg_len -= bytes;
1808c2ecf20Sopenharmony_ci		msg += bytes;
1818c2ecf20Sopenharmony_ci		si = channel->cookie_high;
1828c2ecf20Sopenharmony_ci		di = channel->cookie_low;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_SENDPAYLOAD << 16),
1858c2ecf20Sopenharmony_ci			 word, si, di,
1868c2ecf20Sopenharmony_ci			 channel->channel_id << 16,
1878c2ecf20Sopenharmony_ci			 VMW_HYPERVISOR_MAGIC,
1888c2ecf20Sopenharmony_ci			 eax, ebx, ecx, edx, si, di);
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return ecx;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/**
1958c2ecf20Sopenharmony_ci * vmw_port_hb_in - Receive the message payload either through the
1968c2ecf20Sopenharmony_ci * high-bandwidth port if available, or through the backdoor otherwise.
1978c2ecf20Sopenharmony_ci * @channel: The rpc channel.
1988c2ecf20Sopenharmony_ci * @reply: Pointer to buffer holding reply.
1998c2ecf20Sopenharmony_ci * @reply_len: Length of the reply.
2008c2ecf20Sopenharmony_ci * @hb: Whether the high-bandwidth port is available.
2018c2ecf20Sopenharmony_ci *
2028c2ecf20Sopenharmony_ci * Return: The port status.
2038c2ecf20Sopenharmony_ci */
2048c2ecf20Sopenharmony_cistatic unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply,
2058c2ecf20Sopenharmony_ci				    unsigned long reply_len, bool hb)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	unsigned long si, di, eax, ebx, ecx, edx;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* HB port can't access encrypted memory */
2108c2ecf20Sopenharmony_ci	if (hb && !mem_encrypt_active()) {
2118c2ecf20Sopenharmony_ci		unsigned long bp = channel->cookie_low;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci		si = channel->cookie_high;
2148c2ecf20Sopenharmony_ci		di = (uintptr_t) reply;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		VMW_PORT_HB_IN(
2178c2ecf20Sopenharmony_ci			(MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
2188c2ecf20Sopenharmony_ci			reply_len, si, di,
2198c2ecf20Sopenharmony_ci			VMWARE_HYPERVISOR_HB | (channel->channel_id << 16),
2208c2ecf20Sopenharmony_ci			VMW_HYPERVISOR_MAGIC, bp,
2218c2ecf20Sopenharmony_ci			eax, ebx, ecx, edx, si, di);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci		return ebx;
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* HB port not available. Retrieve the message 4 bytes at a time. */
2278c2ecf20Sopenharmony_ci	ecx = MESSAGE_STATUS_SUCCESS << 16;
2288c2ecf20Sopenharmony_ci	while (reply_len) {
2298c2ecf20Sopenharmony_ci		unsigned int bytes = min_t(unsigned long, reply_len, 4);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		si = channel->cookie_high;
2328c2ecf20Sopenharmony_ci		di = channel->cookie_low;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_RECVPAYLOAD << 16),
2358c2ecf20Sopenharmony_ci			 MESSAGE_STATUS_SUCCESS, si, di,
2368c2ecf20Sopenharmony_ci			 channel->channel_id << 16,
2378c2ecf20Sopenharmony_ci			 VMW_HYPERVISOR_MAGIC,
2388c2ecf20Sopenharmony_ci			 eax, ebx, ecx, edx, si, di);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
2418c2ecf20Sopenharmony_ci			break;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		memcpy(reply, &ebx, bytes);
2448c2ecf20Sopenharmony_ci		reply_len -= bytes;
2458c2ecf20Sopenharmony_ci		reply += bytes;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return ecx;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci/**
2538c2ecf20Sopenharmony_ci * vmw_send_msg: Sends a message to the host
2548c2ecf20Sopenharmony_ci *
2558c2ecf20Sopenharmony_ci * @channel: RPC channel
2568c2ecf20Sopenharmony_ci * @logmsg: NULL terminated string
2578c2ecf20Sopenharmony_ci *
2588c2ecf20Sopenharmony_ci * Returns: 0 on success
2598c2ecf20Sopenharmony_ci */
2608c2ecf20Sopenharmony_cistatic int vmw_send_msg(struct rpc_channel *channel, const char *msg)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	unsigned long eax, ebx, ecx, edx, si, di;
2638c2ecf20Sopenharmony_ci	size_t msg_len = strlen(msg);
2648c2ecf20Sopenharmony_ci	int retries = 0;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	while (retries < RETRIES) {
2678c2ecf20Sopenharmony_ci		retries++;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci		/* Set up additional parameters */
2708c2ecf20Sopenharmony_ci		si  = channel->cookie_high;
2718c2ecf20Sopenharmony_ci		di  = channel->cookie_low;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		VMW_PORT(VMW_PORT_CMD_SENDSIZE,
2748c2ecf20Sopenharmony_ci			msg_len, si, di,
2758c2ecf20Sopenharmony_ci			channel->channel_id << 16,
2768c2ecf20Sopenharmony_ci			VMW_HYPERVISOR_MAGIC,
2778c2ecf20Sopenharmony_ci			eax, ebx, ecx, edx, si, di);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
2808c2ecf20Sopenharmony_ci			/* Expected success. Give up. */
2818c2ecf20Sopenharmony_ci			return -EINVAL;
2828c2ecf20Sopenharmony_ci		}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		/* Send msg */
2858c2ecf20Sopenharmony_ci		ebx = vmw_port_hb_out(channel, msg,
2868c2ecf20Sopenharmony_ci				      !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB));
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) != 0) {
2898c2ecf20Sopenharmony_ci			return 0;
2908c2ecf20Sopenharmony_ci		} else if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) {
2918c2ecf20Sopenharmony_ci			/* A checkpoint occurred. Retry. */
2928c2ecf20Sopenharmony_ci			continue;
2938c2ecf20Sopenharmony_ci		} else {
2948c2ecf20Sopenharmony_ci			break;
2958c2ecf20Sopenharmony_ci		}
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return -EINVAL;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ciSTACK_FRAME_NON_STANDARD(vmw_send_msg);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci/**
3048c2ecf20Sopenharmony_ci * vmw_recv_msg: Receives a message from the host
3058c2ecf20Sopenharmony_ci *
3068c2ecf20Sopenharmony_ci * Note:  It is the caller's responsibility to call kfree() on msg.
3078c2ecf20Sopenharmony_ci *
3088c2ecf20Sopenharmony_ci * @channel:  channel opened by vmw_open_channel
3098c2ecf20Sopenharmony_ci * @msg:  [OUT] message received from the host
3108c2ecf20Sopenharmony_ci * @msg_len: message length
3118c2ecf20Sopenharmony_ci */
3128c2ecf20Sopenharmony_cistatic int vmw_recv_msg(struct rpc_channel *channel, void **msg,
3138c2ecf20Sopenharmony_ci			size_t *msg_len)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	unsigned long eax, ebx, ecx, edx, si, di;
3168c2ecf20Sopenharmony_ci	char *reply;
3178c2ecf20Sopenharmony_ci	size_t reply_len;
3188c2ecf20Sopenharmony_ci	int retries = 0;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	*msg_len = 0;
3228c2ecf20Sopenharmony_ci	*msg = NULL;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	while (retries < RETRIES) {
3258c2ecf20Sopenharmony_ci		retries++;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		/* Set up additional parameters */
3288c2ecf20Sopenharmony_ci		si  = channel->cookie_high;
3298c2ecf20Sopenharmony_ci		di  = channel->cookie_low;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		VMW_PORT(VMW_PORT_CMD_RECVSIZE,
3328c2ecf20Sopenharmony_ci			0, si, di,
3338c2ecf20Sopenharmony_ci			channel->channel_id << 16,
3348c2ecf20Sopenharmony_ci			VMW_HYPERVISOR_MAGIC,
3358c2ecf20Sopenharmony_ci			eax, ebx, ecx, edx, si, di);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
3388c2ecf20Sopenharmony_ci			DRM_ERROR("Failed to get reply size for host message.\n");
3398c2ecf20Sopenharmony_ci			return -EINVAL;
3408c2ecf20Sopenharmony_ci		}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci		/* No reply available.  This is okay. */
3438c2ecf20Sopenharmony_ci		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_DORECV) == 0)
3448c2ecf20Sopenharmony_ci			return 0;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		reply_len = ebx;
3478c2ecf20Sopenharmony_ci		reply     = kzalloc(reply_len + 1, GFP_KERNEL);
3488c2ecf20Sopenharmony_ci		if (!reply) {
3498c2ecf20Sopenharmony_ci			DRM_ERROR("Cannot allocate memory for host message reply.\n");
3508c2ecf20Sopenharmony_ci			return -ENOMEM;
3518c2ecf20Sopenharmony_ci		}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		/* Receive buffer */
3558c2ecf20Sopenharmony_ci		ebx = vmw_port_hb_in(channel, reply, reply_len,
3568c2ecf20Sopenharmony_ci				     !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB));
3578c2ecf20Sopenharmony_ci		if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) == 0) {
3588c2ecf20Sopenharmony_ci			kfree(reply);
3598c2ecf20Sopenharmony_ci			reply = NULL;
3608c2ecf20Sopenharmony_ci			if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) {
3618c2ecf20Sopenharmony_ci				/* A checkpoint occurred. Retry. */
3628c2ecf20Sopenharmony_ci				continue;
3638c2ecf20Sopenharmony_ci			}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci			return -EINVAL;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci		reply[reply_len] = '\0';
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		/* Ack buffer */
3728c2ecf20Sopenharmony_ci		si  = channel->cookie_high;
3738c2ecf20Sopenharmony_ci		di  = channel->cookie_low;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		VMW_PORT(VMW_PORT_CMD_RECVSTATUS,
3768c2ecf20Sopenharmony_ci			MESSAGE_STATUS_SUCCESS, si, di,
3778c2ecf20Sopenharmony_ci			channel->channel_id << 16,
3788c2ecf20Sopenharmony_ci			VMW_HYPERVISOR_MAGIC,
3798c2ecf20Sopenharmony_ci			eax, ebx, ecx, edx, si, di);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
3828c2ecf20Sopenharmony_ci			kfree(reply);
3838c2ecf20Sopenharmony_ci			reply = NULL;
3848c2ecf20Sopenharmony_ci			if ((HIGH_WORD(ecx) & MESSAGE_STATUS_CPT) != 0) {
3858c2ecf20Sopenharmony_ci				/* A checkpoint occurred. Retry. */
3868c2ecf20Sopenharmony_ci				continue;
3878c2ecf20Sopenharmony_ci			}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci			return -EINVAL;
3908c2ecf20Sopenharmony_ci		}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		break;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	if (!reply)
3968c2ecf20Sopenharmony_ci		return -EINVAL;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	*msg_len = reply_len;
3998c2ecf20Sopenharmony_ci	*msg     = reply;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ciSTACK_FRAME_NON_STANDARD(vmw_recv_msg);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci/**
4078c2ecf20Sopenharmony_ci * vmw_host_get_guestinfo: Gets a GuestInfo parameter
4088c2ecf20Sopenharmony_ci *
4098c2ecf20Sopenharmony_ci * Gets the value of a  GuestInfo.* parameter.  The value returned will be in
4108c2ecf20Sopenharmony_ci * a string, and it is up to the caller to post-process.
4118c2ecf20Sopenharmony_ci *
4128c2ecf20Sopenharmony_ci * @guest_info_param:  Parameter to get, e.g. GuestInfo.svga.gl3
4138c2ecf20Sopenharmony_ci * @buffer: if NULL, *reply_len will contain reply size.
4148c2ecf20Sopenharmony_ci * @length: size of the reply_buf.  Set to size of reply upon return
4158c2ecf20Sopenharmony_ci *
4168c2ecf20Sopenharmony_ci * Returns: 0 on success
4178c2ecf20Sopenharmony_ci */
4188c2ecf20Sopenharmony_ciint vmw_host_get_guestinfo(const char *guest_info_param,
4198c2ecf20Sopenharmony_ci			   char *buffer, size_t *length)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	struct rpc_channel channel;
4228c2ecf20Sopenharmony_ci	char *msg, *reply = NULL;
4238c2ecf20Sopenharmony_ci	size_t reply_len = 0;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (!vmw_msg_enabled)
4268c2ecf20Sopenharmony_ci		return -ENODEV;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (!guest_info_param || !length)
4298c2ecf20Sopenharmony_ci		return -EINVAL;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	msg = kasprintf(GFP_KERNEL, "info-get %s", guest_info_param);
4328c2ecf20Sopenharmony_ci	if (!msg) {
4338c2ecf20Sopenharmony_ci		DRM_ERROR("Cannot allocate memory to get guest info \"%s\".",
4348c2ecf20Sopenharmony_ci			  guest_info_param);
4358c2ecf20Sopenharmony_ci		return -ENOMEM;
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM))
4398c2ecf20Sopenharmony_ci		goto out_open;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (vmw_send_msg(&channel, msg) ||
4428c2ecf20Sopenharmony_ci	    vmw_recv_msg(&channel, (void *) &reply, &reply_len))
4438c2ecf20Sopenharmony_ci		goto out_msg;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	vmw_close_channel(&channel);
4468c2ecf20Sopenharmony_ci	if (buffer && reply && reply_len > 0) {
4478c2ecf20Sopenharmony_ci		/* Remove reply code, which are the first 2 characters of
4488c2ecf20Sopenharmony_ci		 * the reply
4498c2ecf20Sopenharmony_ci		 */
4508c2ecf20Sopenharmony_ci		reply_len = max(reply_len - 2, (size_t) 0);
4518c2ecf20Sopenharmony_ci		reply_len = min(reply_len, *length);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		if (reply_len > 0)
4548c2ecf20Sopenharmony_ci			memcpy(buffer, reply + 2, reply_len);
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	*length = reply_len;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	kfree(reply);
4608c2ecf20Sopenharmony_ci	kfree(msg);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	return 0;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ciout_msg:
4658c2ecf20Sopenharmony_ci	vmw_close_channel(&channel);
4668c2ecf20Sopenharmony_ci	kfree(reply);
4678c2ecf20Sopenharmony_ciout_open:
4688c2ecf20Sopenharmony_ci	*length = 0;
4698c2ecf20Sopenharmony_ci	kfree(msg);
4708c2ecf20Sopenharmony_ci	DRM_ERROR("Failed to get guest info \"%s\".", guest_info_param);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return -EINVAL;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci/**
4788c2ecf20Sopenharmony_ci * vmw_host_log: Sends a log message to the host
4798c2ecf20Sopenharmony_ci *
4808c2ecf20Sopenharmony_ci * @log: NULL terminated string
4818c2ecf20Sopenharmony_ci *
4828c2ecf20Sopenharmony_ci * Returns: 0 on success
4838c2ecf20Sopenharmony_ci */
4848c2ecf20Sopenharmony_ciint vmw_host_log(const char *log)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	struct rpc_channel channel;
4878c2ecf20Sopenharmony_ci	char *msg;
4888c2ecf20Sopenharmony_ci	int ret = 0;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (!vmw_msg_enabled)
4928c2ecf20Sopenharmony_ci		return -ENODEV;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (!log)
4958c2ecf20Sopenharmony_ci		return ret;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	msg = kasprintf(GFP_KERNEL, "log %s", log);
4988c2ecf20Sopenharmony_ci	if (!msg) {
4998c2ecf20Sopenharmony_ci		DRM_ERROR("Cannot allocate memory for host log message.\n");
5008c2ecf20Sopenharmony_ci		return -ENOMEM;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM))
5048c2ecf20Sopenharmony_ci		goto out_open;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (vmw_send_msg(&channel, msg))
5078c2ecf20Sopenharmony_ci		goto out_msg;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	vmw_close_channel(&channel);
5108c2ecf20Sopenharmony_ci	kfree(msg);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	return 0;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ciout_msg:
5158c2ecf20Sopenharmony_ci	vmw_close_channel(&channel);
5168c2ecf20Sopenharmony_ciout_open:
5178c2ecf20Sopenharmony_ci	kfree(msg);
5188c2ecf20Sopenharmony_ci	DRM_ERROR("Failed to send host log message.\n");
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	return -EINVAL;
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci/**
5258c2ecf20Sopenharmony_ci * vmw_msg_ioctl: Sends and receveives a message to/from host from/to user-space
5268c2ecf20Sopenharmony_ci *
5278c2ecf20Sopenharmony_ci * Sends a message from user-space to host.
5288c2ecf20Sopenharmony_ci * Can also receive a result from host and return that to user-space.
5298c2ecf20Sopenharmony_ci *
5308c2ecf20Sopenharmony_ci * @dev: Identifies the drm device.
5318c2ecf20Sopenharmony_ci * @data: Pointer to the ioctl argument.
5328c2ecf20Sopenharmony_ci * @file_priv: Identifies the caller.
5338c2ecf20Sopenharmony_ci * Return: Zero on success, negative error code on error.
5348c2ecf20Sopenharmony_ci */
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ciint vmw_msg_ioctl(struct drm_device *dev, void *data,
5378c2ecf20Sopenharmony_ci		  struct drm_file *file_priv)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	struct drm_vmw_msg_arg *arg =
5408c2ecf20Sopenharmony_ci		(struct drm_vmw_msg_arg *) data;
5418c2ecf20Sopenharmony_ci	struct rpc_channel channel;
5428c2ecf20Sopenharmony_ci	char *msg;
5438c2ecf20Sopenharmony_ci	int length;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	msg = kmalloc(MAX_USER_MSG_LENGTH, GFP_KERNEL);
5468c2ecf20Sopenharmony_ci	if (!msg) {
5478c2ecf20Sopenharmony_ci		DRM_ERROR("Cannot allocate memory for log message.\n");
5488c2ecf20Sopenharmony_ci		return -ENOMEM;
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	length = strncpy_from_user(msg, (void __user *)((unsigned long)arg->send),
5528c2ecf20Sopenharmony_ci				   MAX_USER_MSG_LENGTH);
5538c2ecf20Sopenharmony_ci	if (length < 0 || length >= MAX_USER_MSG_LENGTH) {
5548c2ecf20Sopenharmony_ci		DRM_ERROR("Userspace message access failure.\n");
5558c2ecf20Sopenharmony_ci		kfree(msg);
5568c2ecf20Sopenharmony_ci		return -EINVAL;
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM)) {
5618c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to open channel.\n");
5628c2ecf20Sopenharmony_ci		goto out_open;
5638c2ecf20Sopenharmony_ci	}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	if (vmw_send_msg(&channel, msg)) {
5668c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to send message to host.\n");
5678c2ecf20Sopenharmony_ci		goto out_msg;
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (!arg->send_only) {
5718c2ecf20Sopenharmony_ci		char *reply = NULL;
5728c2ecf20Sopenharmony_ci		size_t reply_len = 0;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci		if (vmw_recv_msg(&channel, (void *) &reply, &reply_len)) {
5758c2ecf20Sopenharmony_ci			DRM_ERROR("Failed to receive message from host.\n");
5768c2ecf20Sopenharmony_ci			goto out_msg;
5778c2ecf20Sopenharmony_ci		}
5788c2ecf20Sopenharmony_ci		if (reply && reply_len > 0) {
5798c2ecf20Sopenharmony_ci			if (copy_to_user((void __user *)((unsigned long)arg->receive),
5808c2ecf20Sopenharmony_ci							 reply, reply_len)) {
5818c2ecf20Sopenharmony_ci				DRM_ERROR("Failed to copy message to userspace.\n");
5828c2ecf20Sopenharmony_ci				kfree(reply);
5838c2ecf20Sopenharmony_ci				goto out_msg;
5848c2ecf20Sopenharmony_ci			}
5858c2ecf20Sopenharmony_ci			arg->receive_len = (__u32)reply_len;
5868c2ecf20Sopenharmony_ci		}
5878c2ecf20Sopenharmony_ci		kfree(reply);
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	vmw_close_channel(&channel);
5918c2ecf20Sopenharmony_ci	kfree(msg);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	return 0;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ciout_msg:
5968c2ecf20Sopenharmony_ci	vmw_close_channel(&channel);
5978c2ecf20Sopenharmony_ciout_open:
5988c2ecf20Sopenharmony_ci	kfree(msg);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	return -EINVAL;
6018c2ecf20Sopenharmony_ci}
602