• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**************************************************************************
2  *
3  * Copyright (C) 2015 Red Hat Inc.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21  * OTHER DEALINGS IN THE SOFTWARE.
22  *
23  **************************************************************************/
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <limits.h>
31 
32 #include "virgl_hw.h"
33 #include "virglrenderer.h"
34 
35 #include <sys/uio.h>
36 #include <sys/socket.h>
37 #include <sys/mman.h>
38 
39 #include "vtest.h"
40 #include "vtest_shm.h"
41 #include "vtest_protocol.h"
42 
43 #include "util.h"
44 #include "util/u_debug.h"
45 #include "util/u_double_list.h"
46 #include "util/u_math.h"
47 #include "util/u_memory.h"
48 #include "util/u_hash_table.h"
49 
50 struct vtest_resource {
51    uint32_t res_id;
52 
53    struct iovec iov;
54 };
55 
56 struct vtest_context {
57    struct list_head head;
58 
59    int ctx_id;
60 
61    struct vtest_input *input;
62    int out_fd;
63 
64    char *debug_name;
65 
66    unsigned protocol_version;
67    unsigned capset_id;
68    bool context_initialized;
69 
70    struct util_hash_table *resource_table;
71 };
72 
73 struct vtest_renderer {
74    const char *rendernode_name;
75 
76    uint32_t max_length;
77 
78    int fence_id;
79    int last_fence;
80 
81    struct list_head active_contexts;
82    struct list_head free_contexts;
83    int next_context_id;
84 
85    struct vtest_context *current_context;
86 };
87 
vtest_write_fence(UNUSED void * cookie,uint32_t fence_id_in)88 static void vtest_write_fence(UNUSED void *cookie, uint32_t fence_id_in)
89 {
90    struct vtest_renderer *renderer = (struct vtest_renderer*)cookie;
91    renderer->last_fence = fence_id_in;
92 }
93 
vtest_get_drm_fd(void * cookie)94 static int vtest_get_drm_fd(void *cookie)
95 {
96    int fd = -1;
97    struct vtest_renderer *renderer = (struct vtest_renderer*)cookie;
98    if (!renderer->rendernode_name)
99       return -1;
100    fd = open(renderer->rendernode_name, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
101    if (fd == -1)
102       fprintf(stderr, "Unable to open rendernode '%s' falling back to default search\n",
103               renderer->rendernode_name);
104    return fd;
105 }
106 
107 static struct virgl_renderer_callbacks renderer_cbs = {
108    .version = 2,
109    .write_fence = vtest_write_fence,
110    .get_drm_fd = vtest_get_drm_fd
111 };
112 
113 
114 static struct vtest_renderer renderer = {
115    .max_length = UINT_MAX,
116    .fence_id = 1,
117    .next_context_id = 1,
118 };
119 
120 static unsigned
resource_hash_func(void * key)121 resource_hash_func(void *key)
122 {
123    intptr_t ip = pointer_to_intptr(key);
124    return (unsigned)(ip & 0xffffffff);
125 }
126 
127 static int
resource_compare_func(void * key1,void * key2)128 resource_compare_func(void *key1, void *key2)
129 {
130    if (key1 < key2) {
131       return -1;
132    } else if (key1 > key2) {
133       return 1;
134    } else {
135       return 0;
136    }
137 }
138 
139 static void
resource_destroy_func(void * value)140 resource_destroy_func(void *value)
141 {
142    struct vtest_resource *res = value;
143 
144    /* virgl_renderer_ctx_detach_resource and virgl_renderer_resource_detach_iov
145     * are implied
146     */
147    virgl_renderer_resource_unref(res->res_id);
148 
149    if (res->iov.iov_base)
150       munmap(res->iov.iov_base, res->iov.iov_len);
151    free(res);
152 }
153 
vtest_block_write(int fd,void * buf,int size)154 static int vtest_block_write(int fd, void *buf, int size)
155 {
156    char *ptr = buf;
157    int left;
158    int ret;
159    left = size;
160 
161    do {
162       ret = write(fd, ptr, left);
163       if (ret < 0) {
164          return -errno;
165       }
166 
167       left -= ret;
168       ptr += ret;
169    } while (left);
170 
171    return size;
172 }
173 
vtest_block_read(struct vtest_input * input,void * buf,int size)174 int vtest_block_read(struct vtest_input *input, void *buf, int size)
175 {
176    int fd = input->data.fd;
177    char *ptr = buf;
178    int left;
179    int ret;
180    static int savefd = -1;
181 
182    left = size;
183    do {
184       ret = read(fd, ptr, left);
185       if (ret <= 0) {
186          return ret == -1 ? -errno : 0;
187       }
188 
189       left -= ret;
190       ptr += ret;
191    } while (left);
192 
193    if (getenv("VTEST_SAVE")) {
194       if (savefd == -1) {
195          savefd = open(getenv("VTEST_SAVE"),
196                        O_CLOEXEC|O_CREAT|O_WRONLY|O_TRUNC|O_DSYNC, S_IRUSR|S_IWUSR);
197          if (savefd == -1) {
198             perror("error opening save file");
199             exit(1);
200          }
201       }
202       if (write(savefd, buf, size) != size) {
203          perror("failed to save");
204          exit(1);
205       }
206    }
207 
208    return size;
209 }
210 
vtest_send_fd(int socket_fd,int fd)211 static int vtest_send_fd(int socket_fd, int fd)
212 {
213     struct iovec iovec;
214     char buf[CMSG_SPACE(sizeof(int))];
215     char c = 0;
216     struct msghdr msgh = { 0 };
217     memset(buf, 0, sizeof(buf));
218 
219     iovec.iov_base = &c;
220     iovec.iov_len = sizeof(char);
221 
222     msgh.msg_name = NULL;
223     msgh.msg_namelen = 0;
224     msgh.msg_iov = &iovec;
225     msgh.msg_iovlen = 1;
226     msgh.msg_control = buf;
227     msgh.msg_controllen = sizeof(buf);
228     msgh.msg_flags = 0;
229 
230     struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh);
231     cmsg->cmsg_level = SOL_SOCKET;
232     cmsg->cmsg_type = SCM_RIGHTS;
233     cmsg->cmsg_len = CMSG_LEN(sizeof(int));
234 
235     *((int *) CMSG_DATA(cmsg)) = fd;
236 
237     int size = sendmsg(socket_fd, &msgh, 0);
238     if (size < 0) {
239       return report_failure("Failed to send fd", -EINVAL);
240     }
241 
242     return 0;
243 }
244 
vtest_buf_read(struct vtest_input * input,void * buf,int size)245 int vtest_buf_read(struct vtest_input *input, void *buf, int size)
246 {
247    struct vtest_buffer *inbuf = input->data.buffer;
248    if (size > inbuf->size) {
249       return 0;
250    }
251 
252    memcpy(buf, inbuf->buffer, size);
253    inbuf->buffer += size;
254    inbuf->size -= size;
255 
256    return size;
257 }
258 
vtest_init_renderer(int ctx_flags,const char * render_device)259 int vtest_init_renderer(int ctx_flags, const char *render_device)
260 {
261    int ret;
262 
263    renderer.rendernode_name = render_device;
264    list_inithead(&renderer.active_contexts);
265    list_inithead(&renderer.free_contexts);
266 
267    ret = virgl_renderer_init(&renderer,
268          ctx_flags | VIRGL_RENDERER_THREAD_SYNC, &renderer_cbs);
269    if (ret) {
270       fprintf(stderr, "failed to initialise renderer.\n");
271       return -1;
272    }
273 
274    return 0;
275 }
276 
277 static void vtest_free_context(struct vtest_context *ctx, bool cleanup);
278 
vtest_cleanup_renderer(void)279 void vtest_cleanup_renderer(void)
280 {
281    if (renderer.next_context_id > 1) {
282       struct vtest_context *ctx, *tmp;
283 
284       LIST_FOR_EACH_ENTRY_SAFE(ctx, tmp, &renderer.active_contexts, head) {
285          vtest_destroy_context(ctx);
286       }
287       LIST_FOR_EACH_ENTRY_SAFE(ctx, tmp, &renderer.free_contexts, head) {
288          vtest_free_context(ctx, true);
289       }
290       list_inithead(&renderer.active_contexts);
291       list_inithead(&renderer.free_contexts);
292 
293       renderer.next_context_id = 1;
294       renderer.current_context = NULL;
295    }
296 
297    virgl_renderer_cleanup(&renderer);
298 }
299 
vtest_new_context(struct vtest_input * input,int out_fd)300 static struct vtest_context *vtest_new_context(struct vtest_input *input,
301                                                int out_fd)
302 {
303    struct vtest_context *ctx;
304 
305    if (LIST_IS_EMPTY(&renderer.free_contexts)) {
306       ctx = malloc(sizeof(*ctx));
307       if (!ctx) {
308          return NULL;
309       }
310 
311       ctx->resource_table = util_hash_table_create(resource_hash_func,
312                                                    resource_compare_func,
313                                                    resource_destroy_func);
314       if (!ctx->resource_table) {
315          free(ctx);
316          return NULL;
317       }
318 
319       ctx->ctx_id = renderer.next_context_id++;
320    } else {
321       ctx = LIST_ENTRY(struct vtest_context, renderer.free_contexts.next, head);
322       list_del(&ctx->head);
323    }
324 
325    ctx->input = input;
326    ctx->out_fd = out_fd;
327 
328    ctx->debug_name = NULL;
329    /* By default we support version 0 unless VCMD_PROTOCOL_VERSION is sent */
330    ctx->protocol_version = 0;
331    ctx->capset_id = 0;
332    ctx->context_initialized = false;
333 
334    return ctx;
335 }
336 
vtest_free_context(struct vtest_context * ctx,bool cleanup)337 static void vtest_free_context(struct vtest_context *ctx, bool cleanup)
338 {
339    if (cleanup) {
340       util_hash_table_destroy(ctx->resource_table);
341       free(ctx);
342    } else {
343       list_add(&ctx->head, &renderer.free_contexts);
344    }
345 }
346 
vtest_create_context(struct vtest_input * input,int out_fd,uint32_t length,struct vtest_context ** out_ctx)347 int vtest_create_context(struct vtest_input *input, int out_fd,
348                          uint32_t length, struct vtest_context **out_ctx)
349 {
350    struct vtest_context *ctx;
351    char *vtestname;
352    int ret;
353 
354    if (length > 1024 * 1024) {
355       return -1;
356    }
357 
358    ctx = vtest_new_context(input, out_fd);
359    if (!ctx) {
360       return -1;
361    }
362 
363    vtestname = calloc(1, length + 1);
364    if (!vtestname) {
365       ret = -1;
366       goto err;
367    }
368 
369    ret = ctx->input->read(ctx->input, vtestname, length);
370    if (ret != (int)length) {
371       ret = -1;
372       goto err;
373    }
374 
375    ctx->debug_name = vtestname;
376 
377    list_addtail(&ctx->head, &renderer.active_contexts);
378    *out_ctx = ctx;
379 
380    return 0;
381 
382 err:
383    free(vtestname);
384    vtest_free_context(ctx, false);
385    return ret;
386 }
387 
vtest_lazy_init_context(struct vtest_context * ctx)388 int vtest_lazy_init_context(struct vtest_context *ctx)
389 {
390    int ret;
391 
392    if (ctx->context_initialized)
393       return 0;
394 
395    if (ctx->capset_id) {
396       ret = virgl_renderer_context_create_with_flags(ctx->ctx_id,
397                                                      ctx->capset_id,
398                                                      strlen(ctx->debug_name),
399                                                      ctx->debug_name);
400    } else {
401       ret = virgl_renderer_context_create(ctx->ctx_id,
402                                           strlen(ctx->debug_name),
403                                           ctx->debug_name);
404    }
405    ctx->context_initialized = (ret == 0);
406 
407    return ret;
408 }
409 
vtest_destroy_context(struct vtest_context * ctx)410 void vtest_destroy_context(struct vtest_context *ctx)
411 {
412    if (renderer.current_context == ctx) {
413       renderer.current_context = NULL;
414    }
415    list_del(&ctx->head);
416 
417    free(ctx->debug_name);
418    if (ctx->context_initialized)
419       virgl_renderer_context_destroy(ctx->ctx_id);
420    util_hash_table_clear(ctx->resource_table);
421    vtest_free_context(ctx, false);
422 }
423 
vtest_set_current_context(struct vtest_context * ctx)424 void vtest_set_current_context(struct vtest_context *ctx)
425 {
426    renderer.current_context = ctx;
427 }
428 
vtest_get_current_context(void)429 static struct vtest_context *vtest_get_current_context(void)
430 {
431    return renderer.current_context;
432 }
433 
vtest_ping_protocol_version(UNUSED uint32_t length_dw)434 int vtest_ping_protocol_version(UNUSED uint32_t length_dw)
435 {
436    struct vtest_context *ctx = vtest_get_current_context();
437    uint32_t hdr_buf[VTEST_HDR_SIZE];
438    int ret;
439 
440    hdr_buf[VTEST_CMD_LEN] = VCMD_PING_PROTOCOL_VERSION_SIZE;
441    hdr_buf[VTEST_CMD_ID] = VCMD_PING_PROTOCOL_VERSION;
442    ret = vtest_block_write(ctx->out_fd, hdr_buf, sizeof(hdr_buf));
443    if (ret < 0) {
444       return ret;
445    }
446 
447    return 0;
448 }
449 
vtest_protocol_version(UNUSED uint32_t length_dw)450 int vtest_protocol_version(UNUSED uint32_t length_dw)
451 {
452    struct vtest_context *ctx = vtest_get_current_context();
453    uint32_t hdr_buf[VTEST_HDR_SIZE];
454    uint32_t version_buf[VCMD_PROTOCOL_VERSION_SIZE];
455    unsigned version;
456    int ret;
457 
458    ret = ctx->input->read(ctx->input, &version_buf, sizeof(version_buf));
459    if (ret != sizeof(version_buf))
460       return -1;
461 
462    version = MIN2(version_buf[VCMD_PROTOCOL_VERSION_VERSION],
463                   VTEST_PROTOCOL_VERSION);
464 
465    /*
466     * We've deprecated protocol version 1. All of it's called sites are being
467     * moved protocol version 2. If the server supports version 2 and the guest
468     * supports verison 1, fall back to version 0.
469     */
470    if (version == 1) {
471       printf("Older guest Mesa detected, fallbacking to protocol version 0\n");
472       version = 0;
473    }
474 
475    /* Protocol version 2 requires shm support. */
476    if (!vtest_shm_check()) {
477       printf("Shared memory not supported, fallbacking to protocol version 0\n");
478       version = 0;
479    }
480 
481    ctx->protocol_version = version;
482 
483    hdr_buf[VTEST_CMD_LEN] = VCMD_PROTOCOL_VERSION_SIZE;
484    hdr_buf[VTEST_CMD_ID] = VCMD_PROTOCOL_VERSION;
485 
486    version_buf[VCMD_PROTOCOL_VERSION_VERSION] = ctx->protocol_version;
487 
488    ret = vtest_block_write(ctx->out_fd, hdr_buf, sizeof(hdr_buf));
489    if (ret < 0) {
490       return ret;
491    }
492 
493    ret = vtest_block_write(ctx->out_fd, version_buf, sizeof(version_buf));
494    if (ret < 0) {
495       return ret;
496    }
497 
498    return 0;
499 }
500 
vtest_get_param(UNUSED uint32_t length_dw)501 int vtest_get_param(UNUSED uint32_t length_dw)
502 {
503    struct vtest_context *ctx = vtest_get_current_context();
504    uint32_t get_param_buf[VCMD_GET_PARAM_SIZE];
505    uint32_t resp_buf[VTEST_HDR_SIZE + 2];
506    uint32_t param;
507    uint32_t *resp;
508    int ret;
509 
510    ret = ctx->input->read(ctx->input, get_param_buf, sizeof(get_param_buf));
511    if (ret != sizeof(get_param_buf))
512       return -1;
513 
514    param = get_param_buf[VCMD_GET_PARAM_PARAM];
515 
516    resp_buf[VTEST_CMD_LEN] = 2;
517    resp_buf[VTEST_CMD_ID] = VCMD_GET_PARAM;
518    resp = &resp_buf[VTEST_CMD_DATA_START];
519    switch (param) {
520    default:
521       resp[0] = false;
522       resp[1] = 0;
523       break;
524    }
525 
526    ret = vtest_block_write(ctx->out_fd, resp_buf, sizeof(resp_buf));
527    if (ret < 0)
528       return -1;
529 
530    return 0;
531 }
532 
vtest_get_capset(UNUSED uint32_t length_dw)533 int vtest_get_capset(UNUSED uint32_t length_dw)
534 {
535    struct vtest_context *ctx = vtest_get_current_context();
536    uint32_t get_capset_buf[VCMD_GET_CAPSET_SIZE];
537    uint32_t resp_buf[VTEST_HDR_SIZE + 1];
538    uint32_t id;
539    uint32_t version;
540    uint32_t max_version;
541    uint32_t max_size;
542    void *caps;
543    int ret;
544 
545    ret = ctx->input->read(ctx->input, get_capset_buf, sizeof(get_capset_buf));
546    if (ret != sizeof(get_capset_buf))
547       return -1;
548 
549    id = get_capset_buf[VCMD_GET_CAPSET_ID];
550    version = get_capset_buf[VCMD_GET_CAPSET_VERSION];
551 
552    virgl_renderer_get_cap_set(id, &max_version, &max_size);
553 
554    /* unsupported id or version */
555    if ((!max_version && !max_size) || version > max_version) {
556       resp_buf[VTEST_CMD_LEN] = 1;
557       resp_buf[VTEST_CMD_ID] = VCMD_GET_CAPSET;
558       resp_buf[VTEST_CMD_DATA_START] = false;
559       return vtest_block_write(ctx->out_fd, resp_buf, sizeof(resp_buf));
560    }
561 
562    if (max_size % 4)
563       return -EINVAL;
564 
565    caps = malloc(max_size);
566    if (!caps)
567       return -ENOMEM;
568 
569    virgl_renderer_fill_caps(id, version, caps);
570 
571    resp_buf[VTEST_CMD_LEN] = 1 + max_size / 4;
572    resp_buf[VTEST_CMD_ID] = VCMD_GET_CAPSET;
573    resp_buf[VTEST_CMD_DATA_START] = true;
574    ret = vtest_block_write(ctx->out_fd, resp_buf, sizeof(resp_buf));
575    if (ret >= 0)
576       ret = vtest_block_write(ctx->out_fd, caps, max_size);
577 
578    free(caps);
579    return ret >= 0 ? 0 : ret;
580 }
581 
vtest_context_init(UNUSED uint32_t length_dw)582 int vtest_context_init(UNUSED uint32_t length_dw)
583 {
584    struct vtest_context *ctx = vtest_get_current_context();
585    uint32_t context_init_buf[VCMD_CONTEXT_INIT_SIZE];
586    uint32_t capset_id;
587    int ret;
588 
589    ret = ctx->input->read(ctx->input, context_init_buf, sizeof(context_init_buf));
590    if (ret != sizeof(context_init_buf))
591       return -1;
592 
593    capset_id = context_init_buf[VCMD_CONTEXT_INIT_CAPSET_ID];
594    if (!capset_id)
595       return -EINVAL;
596 
597    if (ctx->context_initialized) {
598       return ctx->capset_id == capset_id ? 0 : -EINVAL;
599    }
600 
601    ctx->capset_id = capset_id;
602 
603    return vtest_lazy_init_context(ctx);
604 }
605 
vtest_send_caps2(UNUSED uint32_t length_dw)606 int vtest_send_caps2(UNUSED uint32_t length_dw)
607 {
608    struct vtest_context *ctx = vtest_get_current_context();
609    uint32_t hdr_buf[2];
610    void *caps_buf;
611    int ret;
612    uint32_t max_ver, max_size;
613 
614    virgl_renderer_get_cap_set(2, &max_ver, &max_size);
615 
616    if (max_size == 0) {
617       return -1;
618    }
619 
620    caps_buf = malloc(max_size);
621    if (!caps_buf) {
622       return -1;
623    }
624 
625    virgl_renderer_fill_caps(2, 1, caps_buf);
626 
627    hdr_buf[0] = max_size + 1;
628    hdr_buf[1] = 2;
629    ret = vtest_block_write(ctx->out_fd, hdr_buf, 8);
630    if (ret < 0) {
631       goto end;
632    }
633 
634    vtest_block_write(ctx->out_fd, caps_buf, max_size);
635    if (ret < 0) {
636       goto end;
637    }
638 
639 end:
640    free(caps_buf);
641    return 0;
642 }
643 
vtest_send_caps(UNUSED uint32_t length_dw)644 int vtest_send_caps(UNUSED uint32_t length_dw)
645 {
646    struct vtest_context *ctx = vtest_get_current_context();
647    uint32_t  max_ver, max_size;
648    void *caps_buf;
649    uint32_t hdr_buf[2];
650    int ret;
651 
652    virgl_renderer_get_cap_set(1, &max_ver, &max_size);
653 
654    caps_buf = malloc(max_size);
655    if (!caps_buf) {
656       return -1;
657    }
658 
659    virgl_renderer_fill_caps(1, 1, caps_buf);
660 
661    hdr_buf[0] = max_size + 1;
662    hdr_buf[1] = 1;
663    ret = vtest_block_write(ctx->out_fd, hdr_buf, 8);
664    if (ret < 0) {
665       goto end;
666    }
667 
668    vtest_block_write(ctx->out_fd, caps_buf, max_size);
669    if (ret < 0) {
670       goto end;
671    }
672 
673 end:
674    free(caps_buf);
675    return 0;
676 }
677 
vtest_create_resource_decode_args(struct vtest_context * ctx,struct virgl_renderer_resource_create_args * args)678 static int vtest_create_resource_decode_args(struct vtest_context *ctx,
679                                              struct virgl_renderer_resource_create_args *args)
680 {
681    uint32_t res_create_buf[VCMD_RES_CREATE_SIZE];
682    int ret;
683 
684    ret = ctx->input->read(ctx->input, &res_create_buf,
685                           sizeof(res_create_buf));
686    if (ret != sizeof(res_create_buf)) {
687       return -1;
688    }
689 
690    args->handle = res_create_buf[VCMD_RES_CREATE_RES_HANDLE];
691    args->target = res_create_buf[VCMD_RES_CREATE_TARGET];
692    args->format = res_create_buf[VCMD_RES_CREATE_FORMAT];
693    args->bind = res_create_buf[VCMD_RES_CREATE_BIND];
694 
695    args->width = res_create_buf[VCMD_RES_CREATE_WIDTH];
696    args->height = res_create_buf[VCMD_RES_CREATE_HEIGHT];
697    args->depth = res_create_buf[VCMD_RES_CREATE_DEPTH];
698    args->array_size = res_create_buf[VCMD_RES_CREATE_ARRAY_SIZE];
699    args->last_level = res_create_buf[VCMD_RES_CREATE_LAST_LEVEL];
700    args->nr_samples = res_create_buf[VCMD_RES_CREATE_NR_SAMPLES];
701    args->flags = 0;
702 
703    return 0;
704 }
705 
vtest_create_resource_decode_args2(struct vtest_context * ctx,struct virgl_renderer_resource_create_args * args,size_t * shm_size)706 static int vtest_create_resource_decode_args2(struct vtest_context *ctx,
707                                               struct virgl_renderer_resource_create_args *args,
708                                               size_t *shm_size)
709 {
710    uint32_t res_create_buf[VCMD_RES_CREATE2_SIZE];
711    int ret;
712 
713    ret = ctx->input->read(ctx->input, &res_create_buf,
714                           sizeof(res_create_buf));
715    if (ret != sizeof(res_create_buf)) {
716       return -1;
717    }
718 
719    args->handle = res_create_buf[VCMD_RES_CREATE2_RES_HANDLE];
720    args->target = res_create_buf[VCMD_RES_CREATE2_TARGET];
721    args->format = res_create_buf[VCMD_RES_CREATE2_FORMAT];
722    args->bind = res_create_buf[VCMD_RES_CREATE2_BIND];
723 
724    args->width = res_create_buf[VCMD_RES_CREATE2_WIDTH];
725    args->height = res_create_buf[VCMD_RES_CREATE2_HEIGHT];
726    args->depth = res_create_buf[VCMD_RES_CREATE2_DEPTH];
727    args->array_size = res_create_buf[VCMD_RES_CREATE2_ARRAY_SIZE];
728    args->last_level = res_create_buf[VCMD_RES_CREATE2_LAST_LEVEL];
729    args->nr_samples = res_create_buf[VCMD_RES_CREATE2_NR_SAMPLES];
730    args->flags = 0;
731 
732    *shm_size = res_create_buf[VCMD_RES_CREATE2_DATA_SIZE];
733 
734    return 0;
735 }
736 
vtest_create_resource_setup_shm(struct vtest_resource * res,size_t size)737 static int vtest_create_resource_setup_shm(struct vtest_resource *res,
738                                            size_t size)
739 {
740    int fd;
741    void *ptr;
742 
743    fd = vtest_new_shm(res->res_id, size);
744    if (fd < 0)
745       return report_failed_call("vtest_new_shm", fd);
746 
747    ptr = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
748    if (ptr == MAP_FAILED) {
749       close(fd);
750       return -1;
751    }
752 
753    res->iov.iov_base = ptr;
754    res->iov.iov_len = size;
755 
756    return fd;
757 }
758 
vtest_create_resource_internal(struct vtest_context * ctx,struct virgl_renderer_resource_create_args * args,size_t shm_size)759 static int vtest_create_resource_internal(struct vtest_context *ctx,
760                                           struct virgl_renderer_resource_create_args *args,
761                                           size_t shm_size)
762 {
763    struct vtest_resource *res;
764    int ret;
765 
766    // Check that the handle doesn't already exist.
767    if (util_hash_table_get(ctx->resource_table, intptr_to_pointer(args->handle)))
768       return -EEXIST;
769 
770    ret = virgl_renderer_resource_create(args, NULL, 0);
771    if (ret)
772       return report_failed_call("virgl_renderer_resource_create", ret);
773 
774    virgl_renderer_ctx_attach_resource(ctx->ctx_id, args->handle);
775 
776    res = CALLOC_STRUCT(vtest_resource);
777    if (!res)
778       return -ENOMEM;
779    res->res_id = args->handle;
780 
781    /* no shm for v1 resources or v2 multi-sample resources */
782    if (shm_size) {
783       int fd;
784 
785       fd = vtest_create_resource_setup_shm(res, shm_size);
786       if (fd < 0) {
787          FREE(res);
788          return -ENOMEM;
789       }
790 
791       ret = vtest_send_fd(ctx->out_fd, fd);
792       if (ret < 0) {
793          munmap(res->iov.iov_base, res->iov.iov_len);
794          close(fd);
795          FREE(res);
796          return report_failed_call("vtest_send_fd", ret);
797       }
798 
799       /* Closing the file descriptor does not unmap the region. */
800       close(fd);
801 
802       virgl_renderer_resource_attach_iov(args->handle, &res->iov, 1);
803    }
804 
805    util_hash_table_set(ctx->resource_table, intptr_to_pointer(res->res_id), res);
806 
807    return 0;
808 }
809 
vtest_create_resource(UNUSED uint32_t length_dw)810 int vtest_create_resource(UNUSED uint32_t length_dw)
811 {
812    struct vtest_context *ctx = vtest_get_current_context();
813    struct virgl_renderer_resource_create_args args;
814    int ret;
815 
816    ret = vtest_create_resource_decode_args(ctx, &args);
817    if (ret < 0) {
818       return ret;
819    }
820 
821    return vtest_create_resource_internal(ctx, &args, 0);
822 }
823 
vtest_create_resource2(UNUSED uint32_t length_dw)824 int vtest_create_resource2(UNUSED uint32_t length_dw)
825 {
826    struct vtest_context *ctx = vtest_get_current_context();
827    struct virgl_renderer_resource_create_args args;
828    size_t shm_size;
829    int ret;
830 
831    ret = vtest_create_resource_decode_args2(ctx, &args, &shm_size);
832    if (ret < 0) {
833       return ret;
834    }
835 
836    return vtest_create_resource_internal(ctx, &args, shm_size);
837 }
838 
vtest_resource_unref(UNUSED uint32_t length_dw)839 int vtest_resource_unref(UNUSED uint32_t length_dw)
840 {
841    struct vtest_context *ctx = vtest_get_current_context();
842    uint32_t res_unref_buf[VCMD_RES_UNREF_SIZE];
843    int ret;
844    uint32_t handle;
845 
846    ret = ctx->input->read(ctx->input, &res_unref_buf,
847                           sizeof(res_unref_buf));
848    if (ret != sizeof(res_unref_buf)) {
849       return -1;
850    }
851 
852    handle = res_unref_buf[VCMD_RES_UNREF_RES_HANDLE];
853    util_hash_table_remove(ctx->resource_table, intptr_to_pointer(handle));
854 
855    return 0;
856 }
857 
vtest_submit_cmd(uint32_t length_dw)858 int vtest_submit_cmd(uint32_t length_dw)
859 {
860    struct vtest_context *ctx = vtest_get_current_context();
861    uint32_t *cbuf;
862    int ret;
863 
864    if (length_dw > renderer.max_length / 4) {
865       return -1;
866    }
867 
868    cbuf = malloc(length_dw * 4);
869    if (!cbuf) {
870       return -1;
871    }
872 
873    ret = ctx->input->read(ctx->input, cbuf, length_dw * 4);
874    if (ret != (int)length_dw * 4) {
875       free(cbuf);
876       return -1;
877    }
878 
879    ret = virgl_renderer_submit_cmd(cbuf, ctx->ctx_id, length_dw);
880 
881    free(cbuf);
882    return ret ? -1 : 0;
883 }
884 
885 struct vtest_transfer_args {
886    uint32_t handle;
887    uint32_t level;
888    uint32_t stride;
889    uint32_t layer_stride;
890    struct virgl_box box;
891    uint32_t offset;
892 };
893 
vtest_transfer_decode_args(struct vtest_context * ctx,struct vtest_transfer_args * args,uint32_t * data_size)894 static int vtest_transfer_decode_args(struct vtest_context *ctx,
895                                       struct vtest_transfer_args *args,
896                                       uint32_t *data_size)
897 {
898    uint32_t thdr_buf[VCMD_TRANSFER_HDR_SIZE];
899    int ret;
900 
901    ret = ctx->input->read(ctx->input, thdr_buf, sizeof(thdr_buf));
902    if (ret != sizeof(thdr_buf)) {
903       return -1;
904    }
905 
906    args->handle = thdr_buf[VCMD_TRANSFER_RES_HANDLE];
907    args->level = thdr_buf[VCMD_TRANSFER_LEVEL];
908    args->stride = thdr_buf[VCMD_TRANSFER_STRIDE];
909    args->layer_stride = thdr_buf[VCMD_TRANSFER_LAYER_STRIDE];
910    args->box.x = thdr_buf[VCMD_TRANSFER_X];
911    args->box.y = thdr_buf[VCMD_TRANSFER_Y];
912    args->box.z = thdr_buf[VCMD_TRANSFER_Z];
913    args->box.w = thdr_buf[VCMD_TRANSFER_WIDTH];
914    args->box.h = thdr_buf[VCMD_TRANSFER_HEIGHT];
915    args->box.d = thdr_buf[VCMD_TRANSFER_DEPTH];
916    args->offset = 0;
917 
918    *data_size = thdr_buf[VCMD_TRANSFER_DATA_SIZE];
919 
920    if (*data_size > renderer.max_length) {
921       return -ENOMEM;
922    }
923 
924    return 0;
925 }
926 
vtest_transfer_decode_args2(struct vtest_context * ctx,struct vtest_transfer_args * args)927 static int vtest_transfer_decode_args2(struct vtest_context *ctx,
928                                        struct vtest_transfer_args *args)
929 {
930    uint32_t thdr_buf[VCMD_TRANSFER2_HDR_SIZE];
931    int ret;
932 
933    ret = ctx->input->read(ctx->input, thdr_buf, sizeof(thdr_buf));
934    if (ret != sizeof(thdr_buf)) {
935       return -1;
936    }
937 
938    args->handle = thdr_buf[VCMD_TRANSFER2_RES_HANDLE];
939    args->level = thdr_buf[VCMD_TRANSFER2_LEVEL];
940    args->stride = 0;
941    args->layer_stride = 0;
942    args->box.x = thdr_buf[VCMD_TRANSFER2_X];
943    args->box.y = thdr_buf[VCMD_TRANSFER2_Y];
944    args->box.z = thdr_buf[VCMD_TRANSFER2_Z];
945    args->box.w = thdr_buf[VCMD_TRANSFER2_WIDTH];
946    args->box.h = thdr_buf[VCMD_TRANSFER2_HEIGHT];
947    args->box.d = thdr_buf[VCMD_TRANSFER2_DEPTH];
948    args->offset = thdr_buf[VCMD_TRANSFER2_OFFSET];
949 
950    return 0;
951 }
952 
vtest_transfer_get_internal(struct vtest_context * ctx,struct vtest_transfer_args * args,uint32_t data_size,bool do_transfer)953 static int vtest_transfer_get_internal(struct vtest_context *ctx,
954                                        struct vtest_transfer_args *args,
955                                        uint32_t data_size,
956                                        bool do_transfer)
957 {
958    struct vtest_resource *res;
959    struct iovec data_iov;
960    int ret = 0;
961 
962    res = util_hash_table_get(ctx->resource_table,
963                              intptr_to_pointer(args->handle));
964    if (!res) {
965       return report_failed_call("util_hash_table_get", -ESRCH);
966    }
967 
968    if (data_size) {
969       data_iov.iov_len = data_size;
970       data_iov.iov_base = malloc(data_size);
971       if (!data_iov.iov_base) {
972          return -ENOMEM;
973       }
974    } else {
975       if (args->offset >= res->iov.iov_len) {
976          return report_failure("offset larger then length of backing store", -EFAULT);
977       }
978    }
979 
980    if (do_transfer) {
981       ret = virgl_renderer_transfer_read_iov(res->res_id,
982                                              ctx->ctx_id,
983                                              args->level,
984                                              args->stride,
985                                              args->layer_stride,
986                                              &args->box,
987                                              args->offset,
988                                              data_size ? &data_iov : NULL,
989                                              data_size ? 1 : 0);
990       if (ret) {
991          report_failed_call("virgl_renderer_transfer_read_iov", ret);
992       }
993    } else if (data_size) {
994       memset(data_iov.iov_base, 0, data_iov.iov_len);
995    }
996 
997    if (data_size) {
998       ret = vtest_block_write(ctx->out_fd, data_iov.iov_base, data_iov.iov_len);
999       if (ret > 0)
1000          ret = 0;
1001 
1002       free(data_iov.iov_base);
1003    }
1004 
1005    return ret;
1006 }
1007 
vtest_transfer_put_internal(struct vtest_context * ctx,struct vtest_transfer_args * args,uint32_t data_size,bool do_transfer)1008 static int vtest_transfer_put_internal(struct vtest_context *ctx,
1009                                        struct vtest_transfer_args *args,
1010                                        uint32_t data_size,
1011                                        bool do_transfer)
1012 {
1013    struct vtest_resource *res;
1014    struct iovec data_iov;
1015    int ret = 0;
1016 
1017    res = util_hash_table_get(ctx->resource_table,
1018                              intptr_to_pointer(args->handle));
1019    if (!res) {
1020       return report_failed_call("util_hash_table_get", -ESRCH);
1021    }
1022 
1023    if (data_size) {
1024       data_iov.iov_len = data_size;
1025       data_iov.iov_base = malloc(data_size);
1026       if (!data_iov.iov_base) {
1027          return -ENOMEM;
1028       }
1029 
1030       ret = ctx->input->read(ctx->input, data_iov.iov_base, data_iov.iov_len);
1031       if (ret < 0) {
1032          return ret;
1033       }
1034    }
1035 
1036    if (do_transfer) {
1037       ret = virgl_renderer_transfer_write_iov(res->res_id,
1038                                               ctx->ctx_id,
1039                                               args->level,
1040                                               args->stride,
1041                                               args->layer_stride,
1042                                               &args->box,
1043                                               args->offset,
1044                                               data_size ? &data_iov : NULL,
1045                                               data_size ? 1 : 0);
1046       if (ret) {
1047          report_failed_call("virgl_renderer_transfer_write_iov", ret);
1048       }
1049    }
1050 
1051    if (data_size) {
1052       free(data_iov.iov_base);
1053    }
1054 
1055    return ret;
1056 }
1057 
vtest_transfer_get(UNUSED uint32_t length_dw)1058 int vtest_transfer_get(UNUSED uint32_t length_dw)
1059 {
1060    struct vtest_context *ctx = vtest_get_current_context();
1061    int ret;
1062    struct vtest_transfer_args args;
1063    uint32_t data_size;
1064 
1065    ret = vtest_transfer_decode_args(ctx, &args, &data_size);
1066    if (ret < 0) {
1067       return ret;
1068    }
1069 
1070    return vtest_transfer_get_internal(ctx, &args, data_size, true);
1071 }
1072 
vtest_transfer_get_nop(UNUSED uint32_t length_dw)1073 int vtest_transfer_get_nop(UNUSED uint32_t length_dw)
1074 {
1075    struct vtest_context *ctx = vtest_get_current_context();
1076    int ret;
1077    struct vtest_transfer_args args;
1078    uint32_t data_size;
1079 
1080    ret = vtest_transfer_decode_args(ctx, &args, &data_size);
1081    if (ret < 0) {
1082       return ret;
1083    }
1084 
1085    return vtest_transfer_get_internal(ctx, &args, data_size, false);
1086 }
1087 
vtest_transfer_put(UNUSED uint32_t length_dw)1088 int vtest_transfer_put(UNUSED uint32_t length_dw)
1089 {
1090    struct vtest_context *ctx = vtest_get_current_context();
1091    int ret;
1092    struct vtest_transfer_args args;
1093    uint32_t data_size;
1094 
1095    ret = vtest_transfer_decode_args(ctx, &args, &data_size);
1096    if (ret < 0) {
1097       return ret;
1098    }
1099 
1100    return vtest_transfer_put_internal(ctx, &args, data_size, true);
1101 }
1102 
vtest_transfer_put_nop(UNUSED uint32_t length_dw)1103 int vtest_transfer_put_nop(UNUSED uint32_t length_dw)
1104 {
1105    struct vtest_context *ctx = vtest_get_current_context();
1106    int ret;
1107    struct vtest_transfer_args args;
1108    uint32_t data_size;
1109 
1110    ret = vtest_transfer_decode_args(ctx, &args, &data_size);
1111    if (ret < 0) {
1112       return ret;
1113    }
1114 
1115    return vtest_transfer_put_internal(ctx, &args, data_size, false);
1116 }
1117 
vtest_transfer_get2(UNUSED uint32_t length_dw)1118 int vtest_transfer_get2(UNUSED uint32_t length_dw)
1119 {
1120    struct vtest_context *ctx = vtest_get_current_context();
1121    int ret;
1122    struct vtest_transfer_args args;
1123 
1124    ret = vtest_transfer_decode_args2(ctx, &args);
1125    if (ret < 0) {
1126       return ret;
1127    }
1128 
1129    return vtest_transfer_get_internal(ctx, &args, 0, true);
1130 }
1131 
vtest_transfer_get2_nop(UNUSED uint32_t length_dw)1132 int vtest_transfer_get2_nop(UNUSED uint32_t length_dw)
1133 {
1134    struct vtest_context *ctx = vtest_get_current_context();
1135    int ret;
1136    struct vtest_transfer_args args;
1137 
1138    ret = vtest_transfer_decode_args2(ctx, &args);
1139    if (ret < 0) {
1140       return ret;
1141    }
1142 
1143    return vtest_transfer_get_internal(ctx, &args, 0, false);
1144 }
1145 
vtest_transfer_put2(UNUSED uint32_t length_dw)1146 int vtest_transfer_put2(UNUSED uint32_t length_dw)
1147 {
1148    struct vtest_context *ctx = vtest_get_current_context();
1149    int ret;
1150    struct vtest_transfer_args args;
1151 
1152    ret = vtest_transfer_decode_args2(ctx, &args);
1153    if (ret < 0) {
1154       return ret;
1155    }
1156 
1157    return vtest_transfer_put_internal(ctx, &args, 0, true);
1158 }
1159 
vtest_transfer_put2_nop(UNUSED uint32_t length_dw)1160 int vtest_transfer_put2_nop(UNUSED uint32_t length_dw)
1161 {
1162    struct vtest_context *ctx = vtest_get_current_context();
1163    int ret;
1164    struct vtest_transfer_args args;
1165 
1166    ret = vtest_transfer_decode_args2(ctx, &args);
1167    if (ret < 0) {
1168       return ret;
1169    }
1170 
1171    return vtest_transfer_put_internal(ctx, &args, 0, false);
1172 }
1173 
vtest_resource_busy_wait(UNUSED uint32_t length_dw)1174 int vtest_resource_busy_wait(UNUSED uint32_t length_dw)
1175 {
1176    struct vtest_context *ctx = vtest_get_current_context();
1177    uint32_t bw_buf[VCMD_BUSY_WAIT_SIZE];
1178    int ret, fd;
1179    int flags;
1180    uint32_t hdr_buf[VTEST_HDR_SIZE];
1181    uint32_t reply_buf[1];
1182    bool busy = false;
1183 
1184    ret = ctx->input->read(ctx->input, &bw_buf, sizeof(bw_buf));
1185    if (ret != sizeof(bw_buf)) {
1186       return -1;
1187    }
1188 
1189    /* clients often send VCMD_PING_PROTOCOL_VERSION followed by
1190     * VCMD_RESOURCE_BUSY_WAIT with handle 0 to figure out if
1191     * VCMD_PING_PROTOCOL_VERSION is supported.  We need to make a special case
1192     * for that.
1193     */
1194    if (!ctx->context_initialized && bw_buf[VCMD_BUSY_WAIT_HANDLE])
1195       return -1;
1196 
1197    /*  handle = bw_buf[VCMD_BUSY_WAIT_HANDLE]; unused as of now */
1198    flags = bw_buf[VCMD_BUSY_WAIT_FLAGS];
1199 
1200    if (flags == VCMD_BUSY_WAIT_FLAG_WAIT) {
1201       do {
1202          if (renderer.last_fence == (renderer.fence_id - 1)) {
1203             break;
1204          }
1205 
1206          fd = virgl_renderer_get_poll_fd();
1207          if (fd != -1) {
1208             vtest_wait_for_fd_read(fd);
1209          }
1210 
1211          virgl_renderer_poll();
1212       } while (1);
1213 
1214       busy = false;
1215    } else {
1216       busy = renderer.last_fence != (renderer.fence_id - 1);
1217    }
1218 
1219    hdr_buf[VTEST_CMD_LEN] = 1;
1220    hdr_buf[VTEST_CMD_ID] = VCMD_RESOURCE_BUSY_WAIT;
1221    reply_buf[0] = busy ? 1 : 0;
1222 
1223    ret = vtest_block_write(ctx->out_fd, hdr_buf, sizeof(hdr_buf));
1224    if (ret < 0) {
1225       return ret;
1226    }
1227 
1228    ret = vtest_block_write(ctx->out_fd, reply_buf, sizeof(reply_buf));
1229    if (ret < 0) {
1230       return ret;
1231    }
1232 
1233    return 0;
1234 }
1235 
vtest_renderer_create_fence(void)1236 int vtest_renderer_create_fence(void)
1237 {
1238    struct vtest_context *ctx = vtest_get_current_context();
1239    virgl_renderer_create_fence(renderer.fence_id++, ctx->ctx_id);
1240    return 0;
1241 }
1242 
vtest_poll(void)1243 int vtest_poll(void)
1244 {
1245    virgl_renderer_poll();
1246    return 0;
1247 }
1248 
vtest_set_max_length(uint32_t length)1249 void vtest_set_max_length(uint32_t length)
1250 {
1251    renderer.max_length = length;
1252 }
1253