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 28 #include <linux/slab.h> 29 #include <linux/module.h> 30 #include <linux/kernel.h> 31 #include <linux/frame.h> 32 #include <asm/hypervisor.h> 33 #include "drmP.h" 34 #include "vmwgfx_msg.h" 35 36 37 #define MESSAGE_STATUS_SUCCESS 0x0001 38 #define MESSAGE_STATUS_DORECV 0x0002 39 #define MESSAGE_STATUS_CPT 0x0010 40 #define MESSAGE_STATUS_HB 0x0080 41 42 #define RPCI_PROTOCOL_NUM 0x49435052 43 #define GUESTMSG_FLAG_COOKIE 0x80000000 44 45 #define RETRIES 3 46 47 #define VMW_HYPERVISOR_MAGIC 0x564D5868 48 #define VMW_HYPERVISOR_PORT 0x5658 49 #define VMW_HYPERVISOR_HB_PORT 0x5659 50 51 #define VMW_PORT_CMD_MSG 30 52 #define VMW_PORT_CMD_HB_MSG 0 53 #define VMW_PORT_CMD_OPEN_CHANNEL (MSG_TYPE_OPEN << 16 | VMW_PORT_CMD_MSG) 54 #define VMW_PORT_CMD_CLOSE_CHANNEL (MSG_TYPE_CLOSE << 16 | VMW_PORT_CMD_MSG) 55 #define VMW_PORT_CMD_SENDSIZE (MSG_TYPE_SENDSIZE << 16 | VMW_PORT_CMD_MSG) 56 #define VMW_PORT_CMD_RECVSIZE (MSG_TYPE_RECVSIZE << 16 | VMW_PORT_CMD_MSG) 57 #define VMW_PORT_CMD_RECVSTATUS (MSG_TYPE_RECVSTATUS << 16 | VMW_PORT_CMD_MSG) 58 59 #define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16) 60 61 static u32 vmw_msg_enabled = 1; 62 63 enum rpc_msg_type { 64 MSG_TYPE_OPEN, 65 MSG_TYPE_SENDSIZE, 66 MSG_TYPE_SENDPAYLOAD, 67 MSG_TYPE_RECVSIZE, 68 MSG_TYPE_RECVPAYLOAD, 69 MSG_TYPE_RECVSTATUS, 70 MSG_TYPE_CLOSE, 71 }; 72 73 struct rpc_channel { 74 u16 channel_id; 75 u32 cookie_high; 76 u32 cookie_low; 77 }; 78 79 80 81 /** 82 * vmw_open_channel 83 * 84 * @channel: RPC channel 85 * @protocol: 86 * 87 * Returns: 0 on success 88 */ 89 static int vmw_open_channel(struct rpc_channel *channel, unsigned int protocol) 90 { 91 unsigned long eax, ebx, ecx, edx, si = 0, di = 0; 92 93 VMW_PORT(VMW_PORT_CMD_OPEN_CHANNEL, 94 (protocol | GUESTMSG_FLAG_COOKIE), si, di, 95 VMW_HYPERVISOR_PORT, 96 VMW_HYPERVISOR_MAGIC, 97 eax, ebx, ecx, edx, si, di); 98 99 if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) 100 return -EINVAL; 101 102 channel->channel_id = HIGH_WORD(edx); 103 channel->cookie_high = si; 104 channel->cookie_low = di; 105 106 return 0; 107 } 108 109 110 111 /** 112 * vmw_close_channel 113 * 114 * @channel: RPC channel 115 * 116 * Returns: 0 on success 117 */ 118 static int vmw_close_channel(struct rpc_channel *channel) 119 { 120 unsigned long eax, ebx, ecx, edx, si, di; 121 122 /* Set up additional parameters */ 123 si = channel->cookie_high; 124 di = channel->cookie_low; 125 126 VMW_PORT(VMW_PORT_CMD_CLOSE_CHANNEL, 127 0, si, di, 128 (VMW_HYPERVISOR_PORT | (channel->channel_id << 16)), 129 VMW_HYPERVISOR_MAGIC, 130 eax, ebx, ecx, edx, si, di); 131 132 if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) 133 return -EINVAL; 134 135 return 0; 136 } 137 138 139 140 /** 141 * vmw_send_msg: Sends a message to the host 142 * 143 * @channel: RPC channel 144 * @logmsg: NULL terminated string 145 * 146 * Returns: 0 on success 147 */ 148 static int vmw_send_msg(struct rpc_channel *channel, const char *msg) 149 { 150 unsigned long eax, ebx, ecx, edx, si, di, bp; 151 size_t msg_len = strlen(msg); 152 int retries = 0; 153 154 155 while (retries < RETRIES) { 156 retries++; 157 158 /* Set up additional parameters */ 159 si = channel->cookie_high; 160 di = channel->cookie_low; 161 162 VMW_PORT(VMW_PORT_CMD_SENDSIZE, 163 msg_len, si, di, 164 VMW_HYPERVISOR_PORT | (channel->channel_id << 16), 165 VMW_HYPERVISOR_MAGIC, 166 eax, ebx, ecx, edx, si, di); 167 168 if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0 || 169 (HIGH_WORD(ecx) & MESSAGE_STATUS_HB) == 0) { 170 /* Expected success + high-bandwidth. Give up. */ 171 return -EINVAL; 172 } 173 174 /* Send msg */ 175 si = (uintptr_t) msg; 176 di = channel->cookie_low; 177 bp = channel->cookie_high; 178 179 VMW_PORT_HB_OUT( 180 (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, 181 msg_len, si, di, 182 VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16), 183 VMW_HYPERVISOR_MAGIC, bp, 184 eax, ebx, ecx, edx, si, di); 185 186 if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) != 0) { 187 return 0; 188 } else if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) { 189 /* A checkpoint occurred. Retry. */ 190 continue; 191 } else { 192 break; 193 } 194 } 195 196 return -EINVAL; 197 } 198 STACK_FRAME_NON_STANDARD(vmw_send_msg); 199 200 201 /** 202 * vmw_recv_msg: Receives a message from the host 203 * 204 * Note: It is the caller's responsibility to call kfree() on msg. 205 * 206 * @channel: channel opened by vmw_open_channel 207 * @msg: [OUT] message received from the host 208 * @msg_len: message length 209 */ 210 static int vmw_recv_msg(struct rpc_channel *channel, void **msg, 211 size_t *msg_len) 212 { 213 unsigned long eax, ebx, ecx, edx, si, di, bp; 214 char *reply; 215 size_t reply_len; 216 int retries = 0; 217 218 219 *msg_len = 0; 220 *msg = NULL; 221 222 while (retries < RETRIES) { 223 retries++; 224 225 /* Set up additional parameters */ 226 si = channel->cookie_high; 227 di = channel->cookie_low; 228 229 VMW_PORT(VMW_PORT_CMD_RECVSIZE, 230 0, si, di, 231 (VMW_HYPERVISOR_PORT | (channel->channel_id << 16)), 232 VMW_HYPERVISOR_MAGIC, 233 eax, ebx, ecx, edx, si, di); 234 235 if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0 || 236 (HIGH_WORD(ecx) & MESSAGE_STATUS_HB) == 0) { 237 DRM_ERROR("Failed to get reply size\n"); 238 return -EINVAL; 239 } 240 241 /* No reply available. This is okay. */ 242 if ((HIGH_WORD(ecx) & MESSAGE_STATUS_DORECV) == 0) 243 return 0; 244 245 reply_len = ebx; 246 reply = kzalloc(reply_len + 1, GFP_KERNEL); 247 if (reply == NULL) { 248 DRM_ERROR("Cannot allocate memory for reply\n"); 249 return -ENOMEM; 250 } 251 252 253 /* Receive buffer */ 254 si = channel->cookie_high; 255 di = (uintptr_t) reply; 256 bp = channel->cookie_low; 257 258 VMW_PORT_HB_IN( 259 (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, 260 reply_len, si, di, 261 VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16), 262 VMW_HYPERVISOR_MAGIC, bp, 263 eax, ebx, ecx, edx, si, di); 264 265 if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) == 0) { 266 kfree(reply); 267 268 if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) { 269 /* A checkpoint occurred. Retry. */ 270 continue; 271 } 272 273 return -EINVAL; 274 } 275 276 reply[reply_len] = '\0'; 277 278 279 /* Ack buffer */ 280 si = channel->cookie_high; 281 di = channel->cookie_low; 282 283 VMW_PORT(VMW_PORT_CMD_RECVSTATUS, 284 MESSAGE_STATUS_SUCCESS, si, di, 285 (VMW_HYPERVISOR_PORT | (channel->channel_id << 16)), 286 VMW_HYPERVISOR_MAGIC, 287 eax, ebx, ecx, edx, si, di); 288 289 if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) { 290 kfree(reply); 291 292 if ((HIGH_WORD(ecx) & MESSAGE_STATUS_CPT) != 0) { 293 /* A checkpoint occurred. Retry. */ 294 continue; 295 } 296 297 return -EINVAL; 298 } 299 300 break; 301 } 302 303 if (retries == RETRIES) 304 return -EINVAL; 305 306 *msg_len = reply_len; 307 *msg = reply; 308 309 return 0; 310 } 311 STACK_FRAME_NON_STANDARD(vmw_recv_msg); 312 313 314 /** 315 * vmw_host_get_guestinfo: Gets a GuestInfo parameter 316 * 317 * Gets the value of a GuestInfo.* parameter. The value returned will be in 318 * a string, and it is up to the caller to post-process. 319 * 320 * @guest_info_param: Parameter to get, e.g. GuestInfo.svga.gl3 321 * @buffer: if NULL, *reply_len will contain reply size. 322 * @length: size of the reply_buf. Set to size of reply upon return 323 * 324 * Returns: 0 on success 325 */ 326 int vmw_host_get_guestinfo(const char *guest_info_param, 327 char *buffer, size_t *length) 328 { 329 struct rpc_channel channel; 330 char *msg, *reply = NULL; 331 size_t msg_len, reply_len = 0; 332 int ret = 0; 333 334 335 if (!vmw_msg_enabled) 336 return -ENODEV; 337 338 if (!guest_info_param || !length) 339 return -EINVAL; 340 341 msg_len = strlen(guest_info_param) + strlen("info-get ") + 1; 342 msg = kzalloc(msg_len, GFP_KERNEL); 343 if (msg == NULL) { 344 DRM_ERROR("Cannot allocate memory to get %s", guest_info_param); 345 return -ENOMEM; 346 } 347 348 sprintf(msg, "info-get %s", guest_info_param); 349 350 if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM) || 351 vmw_send_msg(&channel, msg) || 352 vmw_recv_msg(&channel, (void *) &reply, &reply_len) || 353 vmw_close_channel(&channel)) { 354 DRM_ERROR("Failed to get %s", guest_info_param); 355 356 ret = -EINVAL; 357 } 358 359 if (buffer && reply && reply_len > 0) { 360 /* Remove reply code, which are the first 2 characters of 361 * the reply 362 */ 363 reply_len = max(reply_len - 2, (size_t) 0); 364 reply_len = min(reply_len, *length); 365 366 if (reply_len > 0) 367 memcpy(buffer, reply + 2, reply_len); 368 } 369 370 *length = reply_len; 371 372 kfree(reply); 373 kfree(msg); 374 375 return ret; 376 } 377 378 379 380 /** 381 * vmw_host_log: Sends a log message to the host 382 * 383 * @log: NULL terminated string 384 * 385 * Returns: 0 on success 386 */ 387 int vmw_host_log(const char *log) 388 { 389 struct rpc_channel channel; 390 char *msg; 391 int msg_len; 392 int ret = 0; 393 394 395 if (!vmw_msg_enabled) 396 return -ENODEV; 397 398 if (!log) 399 return ret; 400 401 msg_len = strlen(log) + strlen("log ") + 1; 402 msg = kzalloc(msg_len, GFP_KERNEL); 403 if (msg == NULL) { 404 DRM_ERROR("Cannot allocate memory for log message\n"); 405 return -ENOMEM; 406 } 407 408 sprintf(msg, "log %s", log); 409 410 if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM) || 411 vmw_send_msg(&channel, msg) || 412 vmw_close_channel(&channel)) { 413 DRM_ERROR("Failed to send log\n"); 414 415 ret = -EINVAL; 416 } 417 418 kfree(msg); 419 420 return ret; 421 } 422