// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* Copyright 2016 VMware, Inc., Palo Alto, CA., USA
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <linux/objtool.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/cc_platform.h>
#include <asm/hypervisor.h>
#include <drm/drm_ioctl.h>
#include "vmwgfx_drv.h"
#include "vmwgfx_msg_x86.h"
#include "vmwgfx_msg_arm64.h"
#include "vmwgfx_mksstat.h"
#define MESSAGE_STATUS_SUCCESS 0x0001
#define MESSAGE_STATUS_DORECV 0x0002
#define MESSAGE_STATUS_CPT 0x0010
#define MESSAGE_STATUS_HB 0x0080
#define RPCI_PROTOCOL_NUM 0x49435052
#define GUESTMSG_FLAG_COOKIE 0x80000000
#define RETRIES 3
#define VMW_PORT_CMD_MSG 30
#define VMW_PORT_CMD_HB_MSG 0
#define VMW_PORT_CMD_OPEN_CHANNEL (MSG_TYPE_OPEN << 16 | VMW_PORT_CMD_MSG)
#define VMW_PORT_CMD_CLOSE_CHANNEL (MSG_TYPE_CLOSE << 16 | VMW_PORT_CMD_MSG)
#define VMW_PORT_CMD_SENDSIZE (MSG_TYPE_SENDSIZE << 16 | VMW_PORT_CMD_MSG)
#define VMW_PORT_CMD_RECVSIZE (MSG_TYPE_RECVSIZE << 16 | VMW_PORT_CMD_MSG)
#define VMW_PORT_CMD_RECVSTATUS (MSG_TYPE_RECVSTATUS << 16 | VMW_PORT_CMD_MSG)
#define VMW_PORT_CMD_MKS_GUEST_STATS 85
#define VMW_PORT_CMD_MKSGS_RESET (0 << 16 | VMW_PORT_CMD_MKS_GUEST_STATS)
#define VMW_PORT_CMD_MKSGS_ADD_PPN (1 << 16 | VMW_PORT_CMD_MKS_GUEST_STATS)
#define VMW_PORT_CMD_MKSGS_REMOVE_PPN (2 << 16 | VMW_PORT_CMD_MKS_GUEST_STATS)
#define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16)
#define MAX_USER_MSG_LENGTH PAGE_SIZE
static u32 vmw_msg_enabled = 1;
enum rpc_msg_type {
MSG_TYPE_OPEN,
MSG_TYPE_SENDSIZE,
MSG_TYPE_SENDPAYLOAD,
MSG_TYPE_RECVSIZE,
MSG_TYPE_RECVPAYLOAD,
MSG_TYPE_RECVSTATUS,
MSG_TYPE_CLOSE,
};
struct rpc_channel {
u16 channel_id;
u32 cookie_high;
u32 cookie_low;
};
#if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS)
/* Kernel mksGuestStats counter names and desciptions; same order as enum mksstat_kern_stats_t */
static const char* const mksstat_kern_name_desc[MKSSTAT_KERN_COUNT][2] =
{
{ "vmw_execbuf_ioctl", "vmw_execbuf_ioctl" },
{ "vmw_cotable_resize", "vmw_cotable_resize" },
};
#endif
/**
* vmw_open_channel
*
* @channel: RPC channel
* @protocol:
*
* Returns: 0 on success
*/
static int vmw_open_channel(struct rpc_channel *channel, unsigned int protocol)
{
u32 ecx, edx, esi, edi;
vmware_hypercall6(VMW_PORT_CMD_OPEN_CHANNEL,
(protocol | GUESTMSG_FLAG_COOKIE), 0,
&ecx, &edx, &esi, &edi);
if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
return -EINVAL;
channel->channel_id = HIGH_WORD(edx);
channel->cookie_high = esi;
channel->cookie_low = edi;
return 0;
}
/**
* vmw_close_channel
*
* @channel: RPC channel
*
* Returns: 0 on success
*/
static int vmw_close_channel(struct rpc_channel *channel)
{
u32 ecx;
vmware_hypercall5(VMW_PORT_CMD_CLOSE_CHANNEL,
0, channel->channel_id << 16,
channel->cookie_high,
channel->cookie_low,
&ecx);
if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
return -EINVAL;
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)
{
u32 ebx, ecx;
unsigned long msg_len = strlen(msg);
/* HB port can't access encrypted memory. */
if (hb && !cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {
vmware_hypercall_hb_out(
(MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
msg_len,
channel->channel_id << 16,
(uintptr_t) msg, channel->cookie_low,
channel->cookie_high,
&ebx);
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;
vmware_hypercall5(VMW_PORT_CMD_MSG |
(MSG_TYPE_SENDPAYLOAD << 16),
word, channel->channel_id << 16,
channel->cookie_high,
channel->cookie_low,
&ecx);
}
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 *<