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