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 35 36#define MESSAGE_STATUS_SUCCESS 0x0001 37#define MESSAGE_STATUS_DORECV 0x0002 38#define MESSAGE_STATUS_CPT 0x0010 39#define MESSAGE_STATUS_HB 0x0080 40 41#define RPCI_PROTOCOL_NUM 0x49435052 42#define GUESTMSG_FLAG_COOKIE 0x80000000 43 44#define RETRIES 3 45 46#define VMW_HYPERVISOR_MAGIC 0x564D5868 47#define VMW_HYPERVISOR_PORT 0x5658 48#define VMW_HYPERVISOR_HB_PORT 0x5659 49 50#define VMW_PORT_CMD_MSG 30 51#define VMW_PORT_CMD_HB_MSG 0 52#define VMW_PORT_CMD_OPEN_CHANNEL (MSG_TYPE_OPEN << 16 | VMW_PORT_CMD_MSG) 53#define VMW_PORT_CMD_CLOSE_CHANNEL (MSG_TYPE_CLOSE << 16 | VMW_PORT_CMD_MSG) 54#define VMW_PORT_CMD_SENDSIZE (MSG_TYPE_SENDSIZE << 16 | VMW_PORT_CMD_MSG) 55#define VMW_PORT_CMD_RECVSIZE (MSG_TYPE_RECVSIZE << 16 | VMW_PORT_CMD_MSG) 56#define VMW_PORT_CMD_RECVSTATUS (MSG_TYPE_RECVSTATUS << 16 | VMW_PORT_CMD_MSG) 57 58#define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16) 59 60 61#if defined(PIPE_CC_GCC) && (PIPE_CC_GCC_VERSION > 502) 62 63/** 64 * Hypervisor-specific bi-directional communication channel. Should never 65 * execute on bare metal hardware. The caller must make sure to check for 66 * supported hypervisor before using these macros. 67 * 68 * The last two parameters are both input and output and must be initialized. 69 * 70 * @cmd: [IN] Message Cmd 71 * @in_bx: [IN] Message Len, through BX 72 * @in_si: [IN] Input argument through SI, set to 0 if not used 73 * @in_di: [IN] Input argument through DI, set ot 0 if not used 74 * @port_num: [IN] port number + [channel id] 75 * @magic: [IN] hypervisor magic value 76 * @ax: [OUT] value of AX register 77 * @bx: [OUT] e.g. status from an HB message status command 78 * @cx: [OUT] e.g. status from a non-HB message status command 79 * @dx: [OUT] e.g. channel id 80 * @si: [OUT] 81 * @di: [OUT] 82 */ 83#define VMW_PORT(cmd, in_bx, in_si, in_di, \ 84 port_num, magic, \ 85 ax, bx, cx, dx, si, di) \ 86({ \ 87 __asm__ volatile ("inl %%dx, %%eax;" : \ 88 "=a"(ax), \ 89 "=b"(bx), \ 90 "=c"(cx), \ 91 "=d"(dx), \ 92 "=S"(si), \ 93 "=D"(di) : \ 94 "a"(magic), \ 95 "b"(in_bx), \ 96 "c"(cmd), \ 97 "d"(port_num), \ 98 "S"(in_si), \ 99 "D"(in_di) : \ 100 "memory"); \ 101}) 102 103 104 105/** 106 * Hypervisor-specific bi-directional communication channel. Should never 107 * execute on bare metal hardware. The caller must make sure to check for 108 * supported hypervisor before using these macros. 109 * 110 * @cmd: [IN] Message Cmd 111 * @in_cx: [IN] Message Len, through CX 112 * @in_si: [IN] Input argument through SI, set to 0 if not used 113 * @in_di: [IN] Input argument through DI, set to 0 if not used 114 * @port_num: [IN] port number + [channel id] 115 * @magic: [IN] hypervisor magic value 116 * @bp: [IN] 117 * @ax: [OUT] value of AX register 118 * @bx: [OUT] e.g. status from an HB message status command 119 * @cx: [OUT] e.g. status from a non-HB message status command 120 * @dx: [OUT] e.g. channel id 121 * @si: [OUT] 122 * @di: [OUT] 123 */ 124#if defined(PIPE_ARCH_X86_64) 125 126typedef uint64_t VMW_REG; 127 128#define VMW_PORT_HB_OUT(cmd, in_cx, in_si, in_di, \ 129 port_num, magic, bp, \ 130 ax, bx, cx, dx, si, di) \ 131({ \ 132 __asm__ volatile ("push %%rbp;" \ 133 "movq %12, %%rbp;" \ 134 "rep outsb;" \ 135 "pop %%rbp;" : \ 136 "=a"(ax), \ 137 "=b"(bx), \ 138 "=c"(cx), \ 139 "=d"(dx), \ 140 "=S"(si), \ 141 "=D"(di) : \ 142 "a"(magic), \ 143 "b"(cmd), \ 144 "c"(in_cx), \ 145 "d"(port_num), \ 146 "S"(in_si), \ 147 "D"(in_di), \ 148 "r"(bp) : \ 149 "memory", "cc"); \ 150}) 151 152#define VMW_PORT_HB_IN(cmd, in_cx, in_si, in_di, \ 153 port_num, magic, bp, \ 154 ax, bx, cx, dx, si, di) \ 155({ \ 156 __asm__ volatile ("push %%rbp;" \ 157 "movq %12, %%rbp;" \ 158 "rep insb;" \ 159 "pop %%rbp" : \ 160 "=a"(ax), \ 161 "=b"(bx), \ 162 "=c"(cx), \ 163 "=d"(dx), \ 164 "=S"(si), \ 165 "=D"(di) : \ 166 "a"(magic), \ 167 "b"(cmd), \ 168 "c"(in_cx), \ 169 "d"(port_num), \ 170 "S"(in_si), \ 171 "D"(in_di), \ 172 "r"(bp) : \ 173 "memory", "cc"); \ 174}) 175 176#else 177 178typedef uint32_t VMW_REG; 179 180/* In the 32-bit version of this macro, we store bp in a memory location 181 * because we've ran out of registers. 182 * Now we can't reference that memory location while we've modified 183 * %esp or %ebp, so we first push it on the stack, just before we push 184 * %ebp, and then when we need it we read it from the stack where we 185 * just pushed it. 186 */ 187#define VMW_PORT_HB_OUT(cmd, in_cx, in_si, in_di, \ 188 port_num, magic, bp, \ 189 ax, bx, cx, dx, si, di) \ 190({ \ 191 __asm__ volatile ("push %12;" \ 192 "push %%ebp;" \ 193 "mov 0x04(%%esp), %%ebp;" \ 194 "rep outsb;" \ 195 "pop %%ebp;" \ 196 "add $0x04, %%esp;" : \ 197 "=a"(ax), \ 198 "=b"(bx), \ 199 "=c"(cx), \ 200 "=d"(dx), \ 201 "=S"(si), \ 202 "=D"(di) : \ 203 "a"(magic), \ 204 "b"(cmd), \ 205 "c"(in_cx), \ 206 "d"(port_num), \ 207 "S"(in_si), \ 208 "D"(in_di), \ 209 "m"(bp) : \ 210 "memory", "cc"); \ 211}) 212 213 214#define VMW_PORT_HB_IN(cmd, in_cx, in_si, in_di, \ 215 port_num, magic, bp, \ 216 ax, bx, cx, dx, si, di) \ 217({ \ 218 __asm__ volatile ("push %12;" \ 219 "push %%ebp;" \ 220 "mov 0x04(%%esp), %%ebp;" \ 221 "rep insb;" \ 222 "pop %%ebp;" \ 223 "add $0x04, %%esp;" : \ 224 "=a"(ax), \ 225 "=b"(bx), \ 226 "=c"(cx), \ 227 "=d"(dx), \ 228 "=S"(si), \ 229 "=D"(di) : \ 230 "a"(magic), \ 231 "b"(cmd), \ 232 "c"(in_cx), \ 233 "d"(port_num), \ 234 "S"(in_si), \ 235 "D"(in_di), \ 236 "m"(bp) : \ 237 "memory", "cc"); \ 238}) 239 240#endif 241 242#else 243 244#define MSG_NOT_IMPLEMENTED 1 245 246/* not implemented */ 247 248typedef uint32_t VMW_REG; 249 250 251#define VMW_PORT(cmd, in_bx, in_si, in_di, \ 252 port_num, magic, \ 253 ax, bx, cx, dx, si, di) \ 254 (void) in_bx; \ 255 (void) ax; (void) bx; (void) cx; \ 256 (void) dx; (void) si; (void) di; 257 258#define VMW_PORT_HB_OUT(cmd, in_cx, in_si, in_di, \ 259 port_num, magic, bp, \ 260 ax, bx, cx, dx, si, di) \ 261 (void) in_cx; (void) bp; \ 262 (void) ax; (void) bx; (void) cx; \ 263 (void) dx; (void) si; (void) di; 264 265 266#define VMW_PORT_HB_IN(cmd, in_cx, in_si, in_di, \ 267 port_num, magic, bp, \ 268 ax, bx, cx, dx, si, di) \ 269 (void) bp; \ 270 (void) ax; (void) bx; (void) cx; \ 271 (void) dx; (void) si; (void) di; 272 273#endif /* #if PIPE_CC_GCC */ 274 275 276enum rpc_msg_type { 277 MSG_TYPE_OPEN, 278 MSG_TYPE_SENDSIZE, 279 MSG_TYPE_SENDPAYLOAD, 280 MSG_TYPE_RECVSIZE, 281 MSG_TYPE_RECVPAYLOAD, 282 MSG_TYPE_RECVSTATUS, 283 MSG_TYPE_CLOSE, 284}; 285 286struct rpc_channel { 287 uint16_t channel_id; 288 uint32_t cookie_high; 289 uint32_t cookie_low; 290}; 291 292 293 294/** 295 * vmw_open_channel 296 * 297 * @channel: RPC channel 298 * @protocol: 299 * 300 * Returns: PIPE_OK on success, PIPE_ERROR otherwise 301 */ 302static enum pipe_error 303vmw_open_channel(struct rpc_channel *channel, unsigned protocol) 304{ 305 VMW_REG ax = 0, bx = 0, cx = 0, dx = 0, si = 0, di = 0; 306 307 VMW_PORT(VMW_PORT_CMD_OPEN_CHANNEL, 308 (protocol | GUESTMSG_FLAG_COOKIE), si, di, 309 VMW_HYPERVISOR_PORT, 310 VMW_HYPERVISOR_MAGIC, 311 ax, bx, cx, dx, si, di); 312 313 if ((HIGH_WORD(cx) & MESSAGE_STATUS_SUCCESS) == 0) 314 return PIPE_ERROR; 315 316 channel->channel_id = HIGH_WORD(dx); 317 channel->cookie_high = si; 318 channel->cookie_low = di; 319 320 return PIPE_OK; 321} 322 323 324 325/** 326 * svga_close_channel 327 * 328 * @channel: RPC channel 329 * 330 * Returns: PIPE_OK on success, PIPE_ERROR otherwises 331 */ 332static enum pipe_error 333vmw_close_channel(struct rpc_channel *channel) 334{ 335 VMW_REG ax = 0, bx = 0, cx = 0, dx = 0, si, di; 336 337 /* Set up additional parameters */ 338 si = channel->cookie_high; 339 di = channel->cookie_low; 340 341 VMW_PORT(VMW_PORT_CMD_CLOSE_CHANNEL, 342 0, si, di, 343 (VMW_HYPERVISOR_PORT | (channel->channel_id << 16)), 344 VMW_HYPERVISOR_MAGIC, 345 ax, bx, cx, dx, si, di); 346 347 if ((HIGH_WORD(cx) & MESSAGE_STATUS_SUCCESS) == 0) 348 return PIPE_ERROR; 349 350 return PIPE_OK; 351} 352 353 354 355/** 356 * vmw_send_msg: Sends a message to the host 357 * 358 * @channel: RPC channel 359 * @logmsg: NULL terminated string 360 * 361 * Returns: PIPE_OK on success 362 */ 363static enum pipe_error 364vmw_send_msg(struct rpc_channel *channel, const char *msg) 365{ 366 VMW_REG ax = 0, bx = 0, cx = 0, dx = 0, si, di, bp; 367 size_t msg_len = strlen(msg); 368 int retries = 0; 369 370 371 while (retries < RETRIES) { 372 retries++; 373 374 /* Set up additional parameters */ 375 si = channel->cookie_high; 376 di = channel->cookie_low; 377 378 VMW_PORT(VMW_PORT_CMD_SENDSIZE, 379 msg_len, si, di, 380 VMW_HYPERVISOR_PORT | (channel->channel_id << 16), 381 VMW_HYPERVISOR_MAGIC, 382 ax, bx, cx, dx, si, di); 383 384 if ((HIGH_WORD(cx) & MESSAGE_STATUS_SUCCESS) == 0 || 385 (HIGH_WORD(cx) & MESSAGE_STATUS_HB) == 0) { 386 /* Expected success + high-bandwidth. Give up. */ 387 return PIPE_ERROR; 388 } 389 390 /* Send msg */ 391 si = (uintptr_t) msg; 392 di = channel->cookie_low; 393 bp = channel->cookie_high; 394 395 VMW_PORT_HB_OUT( 396 (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, 397 msg_len, si, di, 398 VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16), 399 VMW_HYPERVISOR_MAGIC, bp, 400 ax, bx, cx, dx, si, di); 401 402 if ((HIGH_WORD(bx) & MESSAGE_STATUS_SUCCESS) != 0) { 403 return PIPE_OK; 404 } else if ((HIGH_WORD(bx) & MESSAGE_STATUS_CPT) != 0) { 405 /* A checkpoint occurred. Retry. */ 406 continue; 407 } else { 408 break; 409 } 410 } 411 412 return PIPE_ERROR; 413} 414 415 416 417/** 418 * vmw_svga_winsys_host_log: Sends a log message to the host 419 * 420 * @log: NULL terminated string 421 * 422 */ 423void 424vmw_svga_winsys_host_log(struct svga_winsys_screen *sws, const char *log) 425{ 426 struct rpc_channel channel; 427 char *msg; 428 int msg_len; 429 int ret; 430 431#ifdef MSG_NOT_IMPLEMENTED 432 return; 433#endif 434 435 if (!log) 436 return; 437 438 msg_len = strlen(log) + strlen("log ") + 1; 439 msg = CALLOC(1, msg_len); 440 if (msg == NULL) { 441 debug_printf("Cannot allocate memory for log message\n"); 442 return; 443 } 444 445 util_sprintf(msg, "log %s", log); 446 447 if (!(ret = vmw_open_channel(&channel, RPCI_PROTOCOL_NUM))) { 448 ret = vmw_send_msg(&channel, msg); 449 vmw_close_channel(&channel); 450 } 451 452 if (ret) 453 debug_printf("Failed to send log\n"); 454 455 FREE(msg); 456 457 return; 458} 459 460