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