1/* 2 * Copyright © 2016 VMware, Inc., Palo Alto, CA., USA 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sub license, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial portions 15 * of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 20 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 23 * USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 */ 26 27#include "util/u_math.h" /* for MAX2/MIN2 */ 28#include "util/u_debug.h" 29#include "util/u_memory.h" 30#include "util/u_string.h" 31#include "pipe/p_defines.h" 32#include "svga_winsys.h" 33#include "vmw_msg.h" 34#include "vmwgfx_drm.h" 35#include "vmw_screen.h" 36#include "xf86drm.h" 37 38 39#define MESSAGE_STATUS_SUCCESS 0x0001 40#define MESSAGE_STATUS_DORECV 0x0002 41#define MESSAGE_STATUS_CPT 0x0010 42#define MESSAGE_STATUS_HB 0x0080 43 44#define RPCI_PROTOCOL_NUM 0x49435052 45#define GUESTMSG_FLAG_COOKIE 0x80000000 46 47#define RETRIES 3 48 49#define VMW_HYPERVISOR_MAGIC 0x564D5868 50#define VMW_HYPERVISOR_PORT 0x5658 51#define VMW_HYPERVISOR_HB_PORT 0x5659 52 53#define VMW_PORT_CMD_MSG 30 54#define VMW_PORT_CMD_HB_MSG 0 55#define VMW_PORT_CMD_OPEN_CHANNEL (MSG_TYPE_OPEN << 16 | VMW_PORT_CMD_MSG) 56#define VMW_PORT_CMD_CLOSE_CHANNEL (MSG_TYPE_CLOSE << 16 | VMW_PORT_CMD_MSG) 57#define VMW_PORT_CMD_SENDSIZE (MSG_TYPE_SENDSIZE << 16 | VMW_PORT_CMD_MSG) 58#define VMW_PORT_CMD_RECVSIZE (MSG_TYPE_RECVSIZE << 16 | VMW_PORT_CMD_MSG) 59#define VMW_PORT_CMD_RECVSTATUS (MSG_TYPE_RECVSTATUS << 16 | VMW_PORT_CMD_MSG) 60 61#define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16) 62 63 64#if defined(PIPE_CC_GCC) && (PIPE_CC_GCC_VERSION > 502) 65 66/** 67 * Hypervisor-specific bi-directional communication channel. Should never 68 * execute on bare metal hardware. The caller must make sure to check for 69 * supported hypervisor before using these macros. 70 * 71 * The last two parameters are both input and output and must be initialized. 72 * 73 * @cmd: [IN] Message Cmd 74 * @in_bx: [IN] Message Len, through BX 75 * @in_si: [IN] Input argument through SI, set to 0 if not used 76 * @in_di: [IN] Input argument through DI, set ot 0 if not used 77 * @port_num: [IN] port number + [channel id] 78 * @magic: [IN] hypervisor magic value 79 * @ax: [OUT] value of AX register 80 * @bx: [OUT] e.g. status from an HB message status command 81 * @cx: [OUT] e.g. status from a non-HB message status command 82 * @dx: [OUT] e.g. channel id 83 * @si: [OUT] 84 * @di: [OUT] 85 */ 86#define VMW_PORT(cmd, in_bx, in_si, in_di, \ 87 port_num, magic, \ 88 ax, bx, cx, dx, si, di) \ 89({ \ 90 __asm__ volatile ("inl %%dx, %%eax;" : \ 91 "=a"(ax), \ 92 "=b"(bx), \ 93 "=c"(cx), \ 94 "=d"(dx), \ 95 "=S"(si), \ 96 "=D"(di) : \ 97 "a"(magic), \ 98 "b"(in_bx), \ 99 "c"(cmd), \ 100 "d"(port_num), \ 101 "S"(in_si), \ 102 "D"(in_di) : \ 103 "memory"); \ 104}) 105 106 107 108/** 109 * Hypervisor-specific bi-directional communication channel. Should never 110 * execute on bare metal hardware. The caller must make sure to check for 111 * supported hypervisor before using these macros. 112 * 113 * @cmd: [IN] Message Cmd 114 * @in_cx: [IN] Message Len, through CX 115 * @in_si: [IN] Input argument through SI, set to 0 if not used 116 * @in_di: [IN] Input argument through DI, set to 0 if not used 117 * @port_num: [IN] port number + [channel id] 118 * @magic: [IN] hypervisor magic value 119 * @bp: [IN] 120 * @ax: [OUT] value of AX register 121 * @bx: [OUT] e.g. status from an HB message status command 122 * @cx: [OUT] e.g. status from a non-HB message status command 123 * @dx: [OUT] e.g. channel id 124 * @si: [OUT] 125 * @di: [OUT] 126 */ 127#if defined(PIPE_ARCH_X86_64) 128 129typedef uint64_t VMW_REG; 130 131#define VMW_PORT_HB_OUT(cmd, in_cx, in_si, in_di, \ 132 port_num, magic, bp, \ 133 ax, bx, cx, dx, si, di) \ 134({ \ 135 __asm__ volatile ("push %%rbp;" \ 136 "movq %12, %%rbp;" \ 137 "rep outsb;" \ 138 "pop %%rbp;" : \ 139 "=a"(ax), \ 140 "=b"(bx), \ 141 "=c"(cx), \ 142 "=d"(dx), \ 143 "=S"(si), \ 144 "=D"(di) : \ 145 "a"(magic), \ 146 "b"(cmd), \ 147 "c"(in_cx), \ 148 "d"(port_num), \ 149 "S"(in_si), \ 150 "D"(in_di), \ 151 "r"(bp) : \ 152 "memory", "cc"); \ 153}) 154 155#define VMW_PORT_HB_IN(cmd, in_cx, in_si, in_di, \ 156 port_num, magic, bp, \ 157 ax, bx, cx, dx, si, di) \ 158({ \ 159 __asm__ volatile ("push %%rbp;" \ 160 "movq %12, %%rbp;" \ 161 "rep insb;" \ 162 "pop %%rbp" : \ 163 "=a"(ax), \ 164 "=b"(bx), \ 165 "=c"(cx), \ 166 "=d"(dx), \ 167 "=S"(si), \ 168 "=D"(di) : \ 169 "a"(magic), \ 170 "b"(cmd), \ 171 "c"(in_cx), \ 172 "d"(port_num), \ 173 "S"(in_si), \ 174 "D"(in_di), \ 175 "r"(bp) : \ 176 "memory", "cc"); \ 177}) 178 179#else 180 181typedef uint32_t VMW_REG; 182 183/* In the 32-bit version of this macro, we store bp in a memory location 184 * because we've ran out of registers. 185 * Now we can't reference that memory location while we've modified 186 * %esp or %ebp, so we first push it on the stack, just before we push 187 * %ebp, and then when we need it we read it from the stack where we 188 * just pushed it. 189 */ 190#define VMW_PORT_HB_OUT(cmd, in_cx, in_si, in_di, \ 191 port_num, magic, bp, \ 192 ax, bx, cx, dx, si, di) \ 193({ \ 194 __asm__ volatile ("push %12;" \ 195 "push %%ebp;" \ 196 "mov 0x04(%%esp), %%ebp;" \ 197 "rep outsb;" \ 198 "pop %%ebp;" \ 199 "add $0x04, %%esp;" : \ 200 "=a"(ax), \ 201 "=b"(bx), \ 202 "=c"(cx), \ 203 "=d"(dx), \ 204 "=S"(si), \ 205 "=D"(di) : \ 206 "a"(magic), \ 207 "b"(cmd), \ 208 "c"(in_cx), \ 209 "d"(port_num), \ 210 "S"(in_si), \ 211 "D"(in_di), \ 212 "m"(bp) : \ 213 "memory", "cc"); \ 214}) 215 216 217#define VMW_PORT_HB_IN(cmd, in_cx, in_si, in_di, \ 218 port_num, magic, bp, \ 219 ax, bx, cx, dx, si, di) \ 220({ \ 221 __asm__ volatile ("push %12;" \ 222 "push %%ebp;" \ 223 "mov 0x04(%%esp), %%ebp;" \ 224 "rep insb;" \ 225 "pop %%ebp;" \ 226 "add $0x04, %%esp;" : \ 227 "=a"(ax), \ 228 "=b"(bx), \ 229 "=c"(cx), \ 230 "=d"(dx), \ 231 "=S"(si), \ 232 "=D"(di) : \ 233 "a"(magic), \ 234 "b"(cmd), \ 235 "c"(in_cx), \ 236 "d"(port_num), \ 237 "S"(in_si), \ 238 "D"(in_di), \ 239 "m"(bp) : \ 240 "memory", "cc"); \ 241}) 242 243#endif 244 245#else 246 247#define MSG_NOT_IMPLEMENTED 1 248 249/* not implemented */ 250 251typedef uint32_t VMW_REG; 252 253 254#define VMW_PORT(cmd, in_bx, in_si, in_di, \ 255 port_num, magic, \ 256 ax, bx, cx, dx, si, di) \ 257 (void) in_bx; \ 258 (void) ax; (void) bx; (void) cx; \ 259 (void) dx; (void) si; (void) di; 260 261#define VMW_PORT_HB_OUT(cmd, in_cx, in_si, in_di, \ 262 port_num, magic, bp, \ 263 ax, bx, cx, dx, si, di) \ 264 (void) in_cx; (void) bp; \ 265 (void) ax; (void) bx; (void) cx; \ 266 (void) dx; (void) si; (void) di; 267 268 269#define VMW_PORT_HB_IN(cmd, in_cx, in_si, in_di, \ 270 port_num, magic, bp, \ 271 ax, bx, cx, dx, si, di) \ 272 (void) bp; \ 273 (void) ax; (void) bx; (void) cx; \ 274 (void) dx; (void) si; (void) di; 275 276#endif /* #if PIPE_CC_GCC */ 277 278 279enum rpc_msg_type { 280 MSG_TYPE_OPEN, 281 MSG_TYPE_SENDSIZE, 282 MSG_TYPE_SENDPAYLOAD, 283 MSG_TYPE_RECVSIZE, 284 MSG_TYPE_RECVPAYLOAD, 285 MSG_TYPE_RECVSTATUS, 286 MSG_TYPE_CLOSE, 287}; 288 289struct rpc_channel { 290 uint16_t channel_id; 291 uint32_t cookie_high; 292 uint32_t cookie_low; 293}; 294 295 296 297/** 298 * vmw_open_channel 299 * 300 * @channel: RPC channel 301 * @protocol: 302 * 303 * Returns: PIPE_OK on success, PIPE_ERROR otherwise 304 */ 305static enum pipe_error 306vmw_open_channel(struct rpc_channel *channel, unsigned protocol) 307{ 308 VMW_REG ax = 0, bx = 0, cx = 0, dx = 0, si = 0, di = 0; 309 310 VMW_PORT(VMW_PORT_CMD_OPEN_CHANNEL, 311 (protocol | GUESTMSG_FLAG_COOKIE), si, di, 312 VMW_HYPERVISOR_PORT, 313 VMW_HYPERVISOR_MAGIC, 314 ax, bx, cx, dx, si, di); 315 316 if ((HIGH_WORD(cx) & MESSAGE_STATUS_SUCCESS) == 0) 317 return PIPE_ERROR; 318 319 channel->channel_id = HIGH_WORD(dx); 320 channel->cookie_high = si; 321 channel->cookie_low = di; 322 323 return PIPE_OK; 324} 325 326 327 328/** 329 * svga_close_channel 330 * 331 * @channel: RPC channel 332 * 333 * Returns: PIPE_OK on success, PIPE_ERROR otherwises 334 */ 335static enum pipe_error 336vmw_close_channel(struct rpc_channel *channel) 337{ 338 VMW_REG ax = 0, bx = 0, cx = 0, dx = 0, si, di; 339 340 /* Set up additional parameters */ 341 si = channel->cookie_high; 342 di = channel->cookie_low; 343 344 VMW_PORT(VMW_PORT_CMD_CLOSE_CHANNEL, 345 0, si, di, 346 (VMW_HYPERVISOR_PORT | (channel->channel_id << 16)), 347 VMW_HYPERVISOR_MAGIC, 348 ax, bx, cx, dx, si, di); 349 350 if ((HIGH_WORD(cx) & MESSAGE_STATUS_SUCCESS) == 0) 351 return PIPE_ERROR; 352 353 return PIPE_OK; 354} 355 356 357 358/** 359 * vmw_send_msg: Sends a message to the host 360 * 361 * @channel: RPC channel 362 * @logmsg: NULL terminated string 363 * 364 * Returns: PIPE_OK on success 365 */ 366static enum pipe_error 367vmw_send_msg(struct rpc_channel *channel, const char *msg) 368{ 369 VMW_REG ax = 0, bx = 0, cx = 0, dx = 0, si, di, bp; 370 size_t msg_len = strlen(msg); 371 int retries = 0; 372 373 374 while (retries < RETRIES) { 375 retries++; 376 377 /* Set up additional parameters */ 378 si = channel->cookie_high; 379 di = channel->cookie_low; 380 381 VMW_PORT(VMW_PORT_CMD_SENDSIZE, 382 msg_len, si, di, 383 VMW_HYPERVISOR_PORT | (channel->channel_id << 16), 384 VMW_HYPERVISOR_MAGIC, 385 ax, bx, cx, dx, si, di); 386 387 if ((HIGH_WORD(cx) & MESSAGE_STATUS_SUCCESS) == 0 || 388 (HIGH_WORD(cx) & MESSAGE_STATUS_HB) == 0) { 389 /* Expected success + high-bandwidth. Give up. */ 390 return PIPE_ERROR; 391 } 392 393 /* Send msg */ 394 si = (uintptr_t) msg; 395 di = channel->cookie_low; 396 bp = channel->cookie_high; 397 398 VMW_PORT_HB_OUT( 399 (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, 400 msg_len, si, di, 401 VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16), 402 VMW_HYPERVISOR_MAGIC, bp, 403 ax, bx, cx, dx, si, di); 404 405 if ((HIGH_WORD(bx) & MESSAGE_STATUS_SUCCESS) != 0) { 406 return PIPE_OK; 407 } else if ((HIGH_WORD(bx) & MESSAGE_STATUS_CPT) != 0) { 408 /* A checkpoint occurred. Retry. */ 409 continue; 410 } else { 411 break; 412 } 413 } 414 415 return PIPE_ERROR; 416} 417 418 419 420/** 421 * vmw_svga_winsys_host_log: Sends a log message to the host 422 * 423 * @log: NULL terminated string 424 * 425 */ 426void 427vmw_svga_winsys_host_log(struct svga_winsys_screen *sws, const char *log) 428{ 429 struct rpc_channel channel; 430 struct vmw_winsys_screen *vws = vmw_winsys_screen(sws); 431 char *msg; 432 int msg_len; 433 int ret; 434 435#ifdef MSG_NOT_IMPLEMENTED 436 return; 437#endif 438 439 if (!log) 440 return; 441 442 msg_len = strlen(log) + strlen("log ") + 1; 443 msg = CALLOC(1, msg_len); 444 if (msg == NULL) { 445 debug_printf("Cannot allocate memory for log message\n"); 446 return; 447 } 448 449 sprintf(msg, "log %s", log); 450 451 if (vws->ioctl.have_drm_2_17) { 452 struct drm_vmw_msg_arg msg_arg; 453 454 memset(&msg_arg, 0, sizeof(msg_arg)); 455 msg_arg.send = (uint64_t) (unsigned long) (msg); 456 msg_arg.send_only = 1; 457 458 ret = drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_MSG, 459 &msg_arg, sizeof(msg_arg)); 460 461 } else { 462 if (!(ret = vmw_open_channel(&channel, RPCI_PROTOCOL_NUM))) { 463 ret = vmw_send_msg(&channel, msg); 464 vmw_close_channel(&channel); 465 } 466 } 467 468 if (ret) 469 debug_printf("Failed to send log\n"); 470 471 FREE(msg); 472 473 return; 474} 475 476