Path: blob/21.2-virgl/src/gallium/winsys/svga/drm/vmw_msg.c
4573 views
/*1* Copyright © 2016 VMware, Inc., Palo Alto, CA., USA2* All Rights Reserved.3*4* Permission is hereby granted, free of charge, to any person obtaining a5* copy of this software and associated documentation files (the6* "Software"), to deal in the Software without restriction, including7* without limitation the rights to use, copy, modify, merge, publish,8* distribute, sub license, and/or sell copies of the Software, and to9* permit persons to whom the Software is furnished to do so, subject to10* the following conditions:11*12* The above copyright notice and this permission notice (including the13* next paragraph) shall be included in all copies or substantial portions14* of the Software.15*16* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR17* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,18* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL19* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,20* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR21* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE22* USE OR OTHER DEALINGS IN THE SOFTWARE.23*24*/2526#include "util/u_math.h" /* for MAX2/MIN2 */27#include "util/u_debug.h"28#include "util/u_memory.h"29#include "util/u_string.h"30#include "pipe/p_defines.h"31#include "svga_winsys.h"32#include "vmw_msg.h"33#include "vmwgfx_drm.h"34#include "vmw_screen.h"35#include "xf86drm.h"363738#define MESSAGE_STATUS_SUCCESS 0x000139#define MESSAGE_STATUS_DORECV 0x000240#define MESSAGE_STATUS_CPT 0x001041#define MESSAGE_STATUS_HB 0x00804243#define RPCI_PROTOCOL_NUM 0x4943505244#define GUESTMSG_FLAG_COOKIE 0x800000004546#define RETRIES 34748#define VMW_HYPERVISOR_MAGIC 0x564D586849#define VMW_HYPERVISOR_PORT 0x565850#define VMW_HYPERVISOR_HB_PORT 0x56595152#define VMW_PORT_CMD_MSG 3053#define VMW_PORT_CMD_HB_MSG 054#define VMW_PORT_CMD_OPEN_CHANNEL (MSG_TYPE_OPEN << 16 | VMW_PORT_CMD_MSG)55#define VMW_PORT_CMD_CLOSE_CHANNEL (MSG_TYPE_CLOSE << 16 | VMW_PORT_CMD_MSG)56#define VMW_PORT_CMD_SENDSIZE (MSG_TYPE_SENDSIZE << 16 | VMW_PORT_CMD_MSG)57#define VMW_PORT_CMD_RECVSIZE (MSG_TYPE_RECVSIZE << 16 | VMW_PORT_CMD_MSG)58#define VMW_PORT_CMD_RECVSTATUS (MSG_TYPE_RECVSTATUS << 16 | VMW_PORT_CMD_MSG)5960#define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16)616263#if defined(PIPE_CC_GCC) && (PIPE_CC_GCC_VERSION > 502)6465/**66* Hypervisor-specific bi-directional communication channel. Should never67* execute on bare metal hardware. The caller must make sure to check for68* supported hypervisor before using these macros.69*70* The last two parameters are both input and output and must be initialized.71*72* @cmd: [IN] Message Cmd73* @in_bx: [IN] Message Len, through BX74* @in_si: [IN] Input argument through SI, set to 0 if not used75* @in_di: [IN] Input argument through DI, set ot 0 if not used76* @port_num: [IN] port number + [channel id]77* @magic: [IN] hypervisor magic value78* @ax: [OUT] value of AX register79* @bx: [OUT] e.g. status from an HB message status command80* @cx: [OUT] e.g. status from a non-HB message status command81* @dx: [OUT] e.g. channel id82* @si: [OUT]83* @di: [OUT]84*/85#define VMW_PORT(cmd, in_bx, in_si, in_di, \86port_num, magic, \87ax, bx, cx, dx, si, di) \88({ \89__asm__ volatile ("inl %%dx, %%eax;" : \90"=a"(ax), \91"=b"(bx), \92"=c"(cx), \93"=d"(dx), \94"=S"(si), \95"=D"(di) : \96"a"(magic), \97"b"(in_bx), \98"c"(cmd), \99"d"(port_num), \100"S"(in_si), \101"D"(in_di) : \102"memory"); \103})104105106107/**108* Hypervisor-specific bi-directional communication channel. Should never109* execute on bare metal hardware. The caller must make sure to check for110* supported hypervisor before using these macros.111*112* @cmd: [IN] Message Cmd113* @in_cx: [IN] Message Len, through CX114* @in_si: [IN] Input argument through SI, set to 0 if not used115* @in_di: [IN] Input argument through DI, set to 0 if not used116* @port_num: [IN] port number + [channel id]117* @magic: [IN] hypervisor magic value118* @bp: [IN]119* @ax: [OUT] value of AX register120* @bx: [OUT] e.g. status from an HB message status command121* @cx: [OUT] e.g. status from a non-HB message status command122* @dx: [OUT] e.g. channel id123* @si: [OUT]124* @di: [OUT]125*/126#if defined(PIPE_ARCH_X86_64)127128typedef uint64_t VMW_REG;129130#define VMW_PORT_HB_OUT(cmd, in_cx, in_si, in_di, \131port_num, magic, bp, \132ax, bx, cx, dx, si, di) \133({ \134__asm__ volatile ("push %%rbp;" \135"movq %12, %%rbp;" \136"rep outsb;" \137"pop %%rbp;" : \138"=a"(ax), \139"=b"(bx), \140"=c"(cx), \141"=d"(dx), \142"=S"(si), \143"=D"(di) : \144"a"(magic), \145"b"(cmd), \146"c"(in_cx), \147"d"(port_num), \148"S"(in_si), \149"D"(in_di), \150"r"(bp) : \151"memory", "cc"); \152})153154#define VMW_PORT_HB_IN(cmd, in_cx, in_si, in_di, \155port_num, magic, bp, \156ax, bx, cx, dx, si, di) \157({ \158__asm__ volatile ("push %%rbp;" \159"movq %12, %%rbp;" \160"rep insb;" \161"pop %%rbp" : \162"=a"(ax), \163"=b"(bx), \164"=c"(cx), \165"=d"(dx), \166"=S"(si), \167"=D"(di) : \168"a"(magic), \169"b"(cmd), \170"c"(in_cx), \171"d"(port_num), \172"S"(in_si), \173"D"(in_di), \174"r"(bp) : \175"memory", "cc"); \176})177178#else179180typedef uint32_t VMW_REG;181182/* In the 32-bit version of this macro, we store bp in a memory location183* because we've ran out of registers.184* Now we can't reference that memory location while we've modified185* %esp or %ebp, so we first push it on the stack, just before we push186* %ebp, and then when we need it we read it from the stack where we187* just pushed it.188*/189#define VMW_PORT_HB_OUT(cmd, in_cx, in_si, in_di, \190port_num, magic, bp, \191ax, bx, cx, dx, si, di) \192({ \193__asm__ volatile ("push %12;" \194"push %%ebp;" \195"mov 0x04(%%esp), %%ebp;" \196"rep outsb;" \197"pop %%ebp;" \198"add $0x04, %%esp;" : \199"=a"(ax), \200"=b"(bx), \201"=c"(cx), \202"=d"(dx), \203"=S"(si), \204"=D"(di) : \205"a"(magic), \206"b"(cmd), \207"c"(in_cx), \208"d"(port_num), \209"S"(in_si), \210"D"(in_di), \211"m"(bp) : \212"memory", "cc"); \213})214215216#define VMW_PORT_HB_IN(cmd, in_cx, in_si, in_di, \217port_num, magic, bp, \218ax, bx, cx, dx, si, di) \219({ \220__asm__ volatile ("push %12;" \221"push %%ebp;" \222"mov 0x04(%%esp), %%ebp;" \223"rep insb;" \224"pop %%ebp;" \225"add $0x04, %%esp;" : \226"=a"(ax), \227"=b"(bx), \228"=c"(cx), \229"=d"(dx), \230"=S"(si), \231"=D"(di) : \232"a"(magic), \233"b"(cmd), \234"c"(in_cx), \235"d"(port_num), \236"S"(in_si), \237"D"(in_di), \238"m"(bp) : \239"memory", "cc"); \240})241242#endif243244#else245246#define MSG_NOT_IMPLEMENTED 1247248/* not implemented */249250typedef uint32_t VMW_REG;251252253#define VMW_PORT(cmd, in_bx, in_si, in_di, \254port_num, magic, \255ax, bx, cx, dx, si, di) \256(void) in_bx; \257(void) ax; (void) bx; (void) cx; \258(void) dx; (void) si; (void) di;259260#define VMW_PORT_HB_OUT(cmd, in_cx, in_si, in_di, \261port_num, magic, bp, \262ax, bx, cx, dx, si, di) \263(void) in_cx; (void) bp; \264(void) ax; (void) bx; (void) cx; \265(void) dx; (void) si; (void) di;266267268#define VMW_PORT_HB_IN(cmd, in_cx, in_si, in_di, \269port_num, magic, bp, \270ax, bx, cx, dx, si, di) \271(void) bp; \272(void) ax; (void) bx; (void) cx; \273(void) dx; (void) si; (void) di;274275#endif /* #if PIPE_CC_GCC */276277278enum rpc_msg_type {279MSG_TYPE_OPEN,280MSG_TYPE_SENDSIZE,281MSG_TYPE_SENDPAYLOAD,282MSG_TYPE_RECVSIZE,283MSG_TYPE_RECVPAYLOAD,284MSG_TYPE_RECVSTATUS,285MSG_TYPE_CLOSE,286};287288struct rpc_channel {289uint16_t channel_id;290uint32_t cookie_high;291uint32_t cookie_low;292};293294295296/**297* vmw_open_channel298*299* @channel: RPC channel300* @protocol:301*302* Returns: PIPE_OK on success, PIPE_ERROR otherwise303*/304static enum pipe_error305vmw_open_channel(struct rpc_channel *channel, unsigned protocol)306{307VMW_REG ax = 0, bx = 0, cx = 0, dx = 0, si = 0, di = 0;308309VMW_PORT(VMW_PORT_CMD_OPEN_CHANNEL,310(protocol | GUESTMSG_FLAG_COOKIE), si, di,311VMW_HYPERVISOR_PORT,312VMW_HYPERVISOR_MAGIC,313ax, bx, cx, dx, si, di);314315if ((HIGH_WORD(cx) & MESSAGE_STATUS_SUCCESS) == 0)316return PIPE_ERROR;317318channel->channel_id = HIGH_WORD(dx);319channel->cookie_high = si;320channel->cookie_low = di;321322return PIPE_OK;323}324325326327/**328* svga_close_channel329*330* @channel: RPC channel331*332* Returns: PIPE_OK on success, PIPE_ERROR otherwises333*/334static enum pipe_error335vmw_close_channel(struct rpc_channel *channel)336{337VMW_REG ax = 0, bx = 0, cx = 0, dx = 0, si, di;338339/* Set up additional parameters */340si = channel->cookie_high;341di = channel->cookie_low;342343VMW_PORT(VMW_PORT_CMD_CLOSE_CHANNEL,3440, si, di,345(VMW_HYPERVISOR_PORT | (channel->channel_id << 16)),346VMW_HYPERVISOR_MAGIC,347ax, bx, cx, dx, si, di);348349if ((HIGH_WORD(cx) & MESSAGE_STATUS_SUCCESS) == 0)350return PIPE_ERROR;351352return PIPE_OK;353}354355356357/**358* vmw_send_msg: Sends a message to the host359*360* @channel: RPC channel361* @logmsg: NULL terminated string362*363* Returns: PIPE_OK on success364*/365static enum pipe_error366vmw_send_msg(struct rpc_channel *channel, const char *msg)367{368VMW_REG ax = 0, bx = 0, cx = 0, dx = 0, si, di, bp;369size_t msg_len = strlen(msg);370int retries = 0;371372373while (retries < RETRIES) {374retries++;375376/* Set up additional parameters */377si = channel->cookie_high;378di = channel->cookie_low;379380VMW_PORT(VMW_PORT_CMD_SENDSIZE,381msg_len, si, di,382VMW_HYPERVISOR_PORT | (channel->channel_id << 16),383VMW_HYPERVISOR_MAGIC,384ax, bx, cx, dx, si, di);385386if ((HIGH_WORD(cx) & MESSAGE_STATUS_SUCCESS) == 0 ||387(HIGH_WORD(cx) & MESSAGE_STATUS_HB) == 0) {388/* Expected success + high-bandwidth. Give up. */389return PIPE_ERROR;390}391392/* Send msg */393si = (uintptr_t) msg;394di = channel->cookie_low;395bp = channel->cookie_high;396397VMW_PORT_HB_OUT(398(MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,399msg_len, si, di,400VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16),401VMW_HYPERVISOR_MAGIC, bp,402ax, bx, cx, dx, si, di);403404if ((HIGH_WORD(bx) & MESSAGE_STATUS_SUCCESS) != 0) {405return PIPE_OK;406} else if ((HIGH_WORD(bx) & MESSAGE_STATUS_CPT) != 0) {407/* A checkpoint occurred. Retry. */408continue;409} else {410break;411}412}413414return PIPE_ERROR;415}416417418419/**420* vmw_svga_winsys_host_log: Sends a log message to the host421*422* @log: NULL terminated string423*424*/425void426vmw_svga_winsys_host_log(struct svga_winsys_screen *sws, const char *log)427{428struct rpc_channel channel;429struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);430char *msg;431int msg_len;432int ret;433434#ifdef MSG_NOT_IMPLEMENTED435return;436#endif437438if (!log)439return;440441msg_len = strlen(log) + strlen("log ") + 1;442msg = CALLOC(1, msg_len);443if (msg == NULL) {444debug_printf("Cannot allocate memory for log message\n");445return;446}447448sprintf(msg, "log %s", log);449450if (vws->ioctl.have_drm_2_17) {451struct drm_vmw_msg_arg msg_arg;452453memset(&msg_arg, 0, sizeof(msg_arg));454msg_arg.send = (uint64_t) (unsigned long) (msg);455msg_arg.send_only = 1;456457ret = drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_MSG,458&msg_arg, sizeof(msg_arg));459460} else {461if (!(ret = vmw_open_channel(&channel, RPCI_PROTOCOL_NUM))) {462ret = vmw_send_msg(&channel, msg);463vmw_close_channel(&channel);464}465}466467if (ret)468debug_printf("Failed to send log\n");469470FREE(msg);471472return;473}474475476477