Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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 <drm/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) {
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 reply_len = 0;
332
333	if (!vmw_msg_enabled)
334		return -ENODEV;
335
336	if (!guest_info_param || !length)
337		return -EINVAL;
338
339	msg = kasprintf(GFP_KERNEL, "info-get %s", guest_info_param);
340	if (!msg) {
341		DRM_ERROR("Cannot allocate memory to get %s", guest_info_param);
342		return -ENOMEM;
343	}
344
345	if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM))
346		goto out_open;
347
348	if (vmw_send_msg(&channel, msg) ||
349	    vmw_recv_msg(&channel, (void *) &reply, &reply_len))
350		goto out_msg;
351
352	vmw_close_channel(&channel);
353	if (buffer && reply && reply_len > 0) {
354		/* Remove reply code, which are the first 2 characters of
355		 * the reply
356		 */
357		reply_len = max(reply_len - 2, (size_t) 0);
358		reply_len = min(reply_len, *length);
359
360		if (reply_len > 0)
361			memcpy(buffer, reply + 2, reply_len);
362	}
363
364	*length = reply_len;
365
366	kfree(reply);
367	kfree(msg);
368
369	return 0;
370
371out_msg:
372	vmw_close_channel(&channel);
373	kfree(reply);
374out_open:
375	*length = 0;
376	kfree(msg);
377	DRM_ERROR("Failed to get %s", guest_info_param);
378
379	return -EINVAL;
380}
381
382
383
384/**
385 * vmw_host_log: Sends a log message to the host
386 *
387 * @log: NULL terminated string
388 *
389 * Returns: 0 on success
390 */
391int vmw_host_log(const char *log)
392{
393	struct rpc_channel channel;
394	char *msg;
395	int ret = 0;
396
397
398	if (!vmw_msg_enabled)
399		return -ENODEV;
400
401	if (!log)
402		return ret;
403
404	msg = kasprintf(GFP_KERNEL, "log %s", log);
405	if (!msg) {
406		DRM_ERROR("Cannot allocate memory for log message\n");
407		return -ENOMEM;
408	}
409
410	if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM))
411		goto out_open;
412
413	if (vmw_send_msg(&channel, msg))
414		goto out_msg;
415
416	vmw_close_channel(&channel);
417	kfree(msg);
418
419	return 0;
420
421out_msg:
422	vmw_close_channel(&channel);
423out_open:
424	kfree(msg);
425	DRM_ERROR("Failed to send log\n");
426
427	return -EINVAL;
428}