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