xref: /aosp_15_r20/external/mesa3d/src/gallium/winsys/svga/drm/vmw_msg.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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