linux/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c
<<
>>
Prefs
   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
  61static u32 vmw_msg_enabled = 1;
  62
  63enum 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
  73struct 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 */
  89static 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 */
 118static 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 */
 148static 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}
 198STACK_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 */
 210static 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}
 311STACK_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 */
 326int 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 */
 387int 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