• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 The ChromiumOS Authors
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  */
6 
7 #define _GNU_SOURCE
8 #include <assert.h>
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 
16 #include <rutabaga_gfx/rutabaga_gfx_ffi.h>
17 
18 #include "virtgpu_cross_domain_protocol.h"
19 
20 #define CHECK_RESULT(result)                                                                       \
21     do {                                                                                           \
22         if (result) {                                                                              \
23             printf("CHECK_RESULT failed in %s() %s:%d\n", __func__, __FILE__, __LINE__);           \
24             return result;                                                                         \
25         }                                                                                          \
26     } while (0)
27 
28 #define CHECK(cond)                                                                                \
29     do {                                                                                           \
30         if (!(cond)) {                                                                             \
31             printf("CHECK failed in %s() %s:%d\n", __func__, __FILE__, __LINE__);                  \
32             return -EINVAL;                                                                        \
33         }                                                                                          \
34     } while (0)
35 
36 #define DEFAULT_BUFFER_SIZE 4096
37 #define WIDTH 512
38 #define HEIGHT 512
39 #define NUM_ITERATIONS 4
40 
41 #define GBM_BO_USE_LINEAR (1 << 4)
42 #define GBM_BO_USE_SCANOUT (1 << 5)
43 #define fourcc_code(a, b, c, d)                                                                    \
44     ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
45 #define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4');
46 
47 #define PIPE_TEXTURE_2D 2
48 #define PIPE_BIND_RENDER_TARGET 2
49 #define VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM 1
50 
51 static int s_resource_id = 1;
52 static int s_fence_id = 1;
53 
54 #if defined(__linux__)
55 static char *s_wayland_path = "/run/user/1000/wayland-0";
56 #elif defined(__Fuchsia__)
57 #endif
58 
59 struct rutabaga_test {
60     struct rutabaga *rutabaga;
61     uint32_t ctx_id;
62     uint64_t value;
63     uint32_t guest_blob_resource_id;
64     struct iovec *guest_iovecs;
65 };
66 
rutabaga_test_write_fence(uint64_t user_data,struct rutabaga_fence fence_data)67 static void rutabaga_test_write_fence(uint64_t user_data, struct rutabaga_fence fence_data)
68 {
69     struct rutabaga_test *test = (void *)(uintptr_t)user_data;
70     test->value = fence_data.fence_id;
71 }
72 
test_rutabaga_init(struct rutabaga_test * test,uint64_t capset_mask)73 static int test_rutabaga_init(struct rutabaga_test *test, uint64_t capset_mask)
74 {
75     int result;
76     struct rutabaga_builder builder = { 0 };
77     struct rutabaga_channels channels = { 0 };
78 
79     builder.fence_cb = rutabaga_test_write_fence;
80     builder.capset_mask = capset_mask;
81     if (capset_mask & (1 << RUTABAGA_CAPSET_CROSS_DOMAIN)) {
82         builder.user_data = (uint64_t)(uintptr_t *)(void *)test;
83         channels.channels = (struct rutabaga_channel *)calloc(1, sizeof(struct rutabaga_channel));
84         channels.num_channels = 1;
85 
86         channels.channels[0].channel_name = s_wayland_path;
87         channels.channels[0].channel_type = RUTABAGA_CHANNEL_TYPE_WAYLAND;
88 
89         builder.channels = &channels;
90     }
91 
92     result = rutabaga_init(&builder, &test->rutabaga);
93 
94     if (capset_mask & (1 << RUTABAGA_CAPSET_CROSS_DOMAIN))
95         free(channels.channels);
96 
97     CHECK_RESULT(result);
98     return 0;
99 }
100 
test_create_context(struct rutabaga_test * test,const char * context_name)101 static int test_create_context(struct rutabaga_test *test, const char *context_name)
102 {
103     int result;
104     uint32_t num_capsets;
105     uint32_t capset_id, capset_version, capset_size;
106     bool found_cross_domain = false;
107     struct CrossDomainCapabilities *capset;
108 
109     result = rutabaga_get_num_capsets(test->rutabaga, &num_capsets);
110     CHECK_RESULT(result);
111     CHECK(num_capsets == 1);
112 
113     for (uint32_t i = 0; i < num_capsets; i++) {
114         result =
115             rutabaga_get_capset_info(test->rutabaga, i, &capset_id, &capset_version, &capset_size);
116         CHECK_RESULT(result);
117         if (capset_id == RUTABAGA_CAPSET_CROSS_DOMAIN) {
118             found_cross_domain = true;
119             CHECK(capset_size == (uint32_t)sizeof(struct CrossDomainCapabilities));
120         }
121     }
122 
123     CHECK(found_cross_domain);
124     CHECK_RESULT(result);
125 
126     capset = (struct CrossDomainCapabilities *)calloc(1, capset_size);
127     result = rutabaga_get_capset(test->rutabaga, RUTABAGA_CAPSET_CROSS_DOMAIN, 0, (uint8_t *)capset,
128                                  capset_size);
129     CHECK_RESULT(result);
130 
131     CHECK(capset->version == 1);
132     free(capset);
133 
134     size_t context_name_len = 0;
135     if (context_name)
136         context_name_len = strlen(context_name);
137 
138     result = rutabaga_context_create(test->rutabaga, test->ctx_id, RUTABAGA_CAPSET_CROSS_DOMAIN,
139                                      context_name, context_name_len);
140     CHECK_RESULT(result);
141 
142     return 0;
143 }
144 
test_init_context(struct rutabaga_test * test)145 static int test_init_context(struct rutabaga_test *test)
146 {
147     int result;
148     struct rutabaga_create_blob rc_blob = { 0 };
149     struct rutabaga_iovecs vecs = { 0 };
150     struct CrossDomainInit cmd_init = { { 0 } };
151 
152     struct iovec *iovecs = (struct iovec *)calloc(1, sizeof(struct iovec));
153     iovecs[0].iov_base = calloc(1, DEFAULT_BUFFER_SIZE);
154     iovecs[0].iov_len = DEFAULT_BUFFER_SIZE;
155 
156     test->guest_iovecs = iovecs;
157     rc_blob.blob_mem = RUTABAGA_BLOB_MEM_GUEST;
158     rc_blob.blob_flags = RUTABAGA_BLOB_FLAG_USE_MAPPABLE;
159     rc_blob.size = DEFAULT_BUFFER_SIZE;
160 
161     vecs.iovecs = iovecs;
162     vecs.num_iovecs = 1;
163 
164     result = rutabaga_resource_create_blob(test->rutabaga, 0, test->guest_blob_resource_id,
165                                            &rc_blob, &vecs, NULL);
166     CHECK_RESULT(result);
167 
168     result = rutabaga_context_attach_resource(test->rutabaga, test->ctx_id,
169                                               test->guest_blob_resource_id);
170     CHECK_RESULT(result);
171 
172     cmd_init.hdr.cmd = CROSS_DOMAIN_CMD_INIT;
173     cmd_init.hdr.cmd_size = sizeof(struct CrossDomainInit);
174     cmd_init.ring_id = test->guest_blob_resource_id;
175     cmd_init.channel_type = CROSS_DOMAIN_CHANNEL_TYPE_WAYLAND;
176 
177     result = rutabaga_submit_command(test->rutabaga, test->ctx_id, (uint8_t *)&cmd_init,
178                                      cmd_init.hdr.cmd_size);
179     CHECK_RESULT(result);
180     return 0;
181 }
182 
test_command_submission(struct rutabaga_test * test)183 static int test_command_submission(struct rutabaga_test *test)
184 {
185     int result;
186     struct CrossDomainGetImageRequirements cmd_get_reqs = { 0 };
187     struct CrossDomainImageRequirements *image_reqs = (void *)test->guest_iovecs[0].iov_base;
188     struct rutabaga_create_blob rc_blob = { 0 };
189     struct rutabaga_fence fence;
190     struct rutabaga_handle handle = { 0 };
191     uint32_t map_info;
192 
193     fence.flags = RUTABAGA_FLAG_FENCE | RUTABAGA_FLAG_INFO_RING_IDX;
194     fence.ctx_id = test->ctx_id;
195     fence.ring_idx = 0;
196 
197     cmd_get_reqs.hdr.cmd = CROSS_DOMAIN_CMD_GET_IMAGE_REQUIREMENTS;
198     cmd_get_reqs.hdr.cmd_size = sizeof(struct CrossDomainGetImageRequirements);
199 
200     for (uint32_t i = 0; i < NUM_ITERATIONS; i++) {
201         for (uint32_t j = 0; j < NUM_ITERATIONS; j++) {
202             fence.fence_id = s_fence_id;
203             map_info = 0;
204 
205             cmd_get_reqs.width = WIDTH * i;
206             cmd_get_reqs.height = HEIGHT * j;
207             cmd_get_reqs.drm_format = DRM_FORMAT_XRGB8888;
208 
209             cmd_get_reqs.flags = GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT;
210 
211             result = rutabaga_submit_command(test->rutabaga, test->ctx_id, (uint8_t *)&cmd_get_reqs,
212                                              cmd_get_reqs.hdr.cmd_size);
213 
214             CHECK(test->value < fence.fence_id);
215             result = rutabaga_create_fence(test->rutabaga, &fence);
216 
217             CHECK_RESULT(result);
218             for (;;) {
219                 if (fence.fence_id == test->value)
220                     break;
221             }
222 
223             CHECK(image_reqs->strides[0] >= cmd_get_reqs.width * 4);
224             CHECK(image_reqs->size >= (cmd_get_reqs.width * 4) * cmd_get_reqs.height);
225 
226             rc_blob.blob_mem = RUTABAGA_BLOB_MEM_HOST3D;
227             rc_blob.blob_flags = RUTABAGA_BLOB_FLAG_USE_MAPPABLE | RUTABAGA_BLOB_FLAG_USE_SHAREABLE;
228             rc_blob.blob_id = image_reqs->blob_id;
229             rc_blob.size = image_reqs->size;
230 
231             result = rutabaga_resource_create_blob(test->rutabaga, test->ctx_id, s_resource_id,
232                                                    &rc_blob, NULL, NULL);
233             CHECK_RESULT(result);
234 
235             result = rutabaga_context_attach_resource(test->rutabaga, test->ctx_id, s_resource_id);
236             CHECK_RESULT(result);
237 
238             result = rutabaga_resource_map_info(test->rutabaga, s_resource_id, &map_info);
239             CHECK_RESULT(result);
240             CHECK(map_info > 0);
241 
242             result = rutabaga_resource_export_blob(test->rutabaga, s_resource_id, &handle);
243             CHECK_RESULT(result);
244             CHECK(handle.os_handle >= 0);
245 
246             result = close(handle.os_handle);
247             CHECK_RESULT(result);
248 
249             result = rutabaga_context_detach_resource(test->rutabaga, test->ctx_id, s_resource_id);
250             CHECK_RESULT(result);
251 
252             result = rutabaga_resource_unref(test->rutabaga, s_resource_id);
253             CHECK_RESULT(result);
254 
255             s_resource_id++;
256             s_fence_id++;
257         }
258     }
259 
260     return 0;
261 }
262 
test_context_finish(struct rutabaga_test * test)263 static int test_context_finish(struct rutabaga_test *test)
264 {
265     int result;
266 
267     result = rutabaga_context_detach_resource(test->rutabaga, test->ctx_id,
268                                               test->guest_blob_resource_id);
269     CHECK_RESULT(result);
270 
271     result = rutabaga_resource_unref(test->rutabaga, test->guest_blob_resource_id);
272     CHECK_RESULT(result);
273 
274     free(test->guest_iovecs[0].iov_base);
275 
276     result = rutabaga_context_destroy(test->rutabaga, test->ctx_id);
277     CHECK_RESULT(result);
278 
279     return 0;
280 }
281 
test_rutabaga_2d(struct rutabaga_test * test)282 static int test_rutabaga_2d(struct rutabaga_test *test)
283 {
284     struct rutabaga_create_3d rc_3d = { 0 };
285     struct rutabaga_transfer transfer = { 0 };
286     int result;
287     uint32_t resource_id = s_resource_id++;
288 
289     struct rutabaga_iovecs vecs = { 0 };
290     struct iovec *iovecs = (struct iovec *)calloc(1, sizeof(struct iovec));
291     uint8_t *test_data;
292     struct iovec result_iovec;
293 
294     iovecs[0].iov_base = calloc(1, DEFAULT_BUFFER_SIZE);
295     iovecs[0].iov_len = DEFAULT_BUFFER_SIZE;
296     result_iovec.iov_base = calloc(1, DEFAULT_BUFFER_SIZE);
297     result_iovec.iov_len = DEFAULT_BUFFER_SIZE;
298     test_data = (uint8_t *)result_iovec.iov_base;
299 
300     vecs.iovecs = iovecs;
301     vecs.num_iovecs = 1;
302 
303     rc_3d.target = PIPE_TEXTURE_2D;
304     rc_3d.bind = PIPE_BIND_RENDER_TARGET;
305     rc_3d.format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM;
306     rc_3d.width = DEFAULT_BUFFER_SIZE / 16;
307     rc_3d.height = 4;
308 
309     transfer.w = DEFAULT_BUFFER_SIZE / 16;
310     transfer.h = 4;
311     transfer.d = 1;
312 
313     result = rutabaga_resource_create_3d(test->rutabaga, resource_id, &rc_3d);
314     CHECK_RESULT(result);
315 
316     result = rutabaga_resource_attach_backing(test->rutabaga, resource_id, &vecs);
317     CHECK_RESULT(result);
318 
319     memset(iovecs[0].iov_base, 8, DEFAULT_BUFFER_SIZE);
320 
321     result =
322         rutabaga_resource_transfer_read(test->rutabaga, 0, resource_id, &transfer, &result_iovec);
323     CHECK_RESULT(result);
324 
325     CHECK(test_data[0] == 0);
326 
327     result = rutabaga_resource_transfer_write(test->rutabaga, 0, resource_id, &transfer);
328     CHECK_RESULT(result);
329 
330     result =
331         rutabaga_resource_transfer_read(test->rutabaga, 0, resource_id, &transfer, &result_iovec);
332     CHECK_RESULT(result);
333 
334     CHECK(test_data[0] == 8);
335 
336     result = rutabaga_resource_detach_backing(test->rutabaga, resource_id);
337     CHECK_RESULT(result);
338 
339     result = rutabaga_resource_unref(test->rutabaga, resource_id);
340     CHECK_RESULT(result);
341 
342     free(iovecs[0].iov_base);
343     free(iovecs);
344     free(test_data);
345     return 0;
346 }
347 
test_rutabaga_finish(struct rutabaga_test * test)348 static int test_rutabaga_finish(struct rutabaga_test *test)
349 {
350     int result;
351 
352     result = rutabaga_finish(&test->rutabaga);
353     CHECK_RESULT(result);
354     CHECK(test->rutabaga == NULL);
355     return 0;
356 }
357 
main(int argc,char * argv[])358 int main(int argc, char *argv[])
359 {
360     struct rutabaga_test test = { 0 };
361     test.ctx_id = 1;
362     test.guest_blob_resource_id = s_resource_id++;
363 
364     int result;
365 
366     const char *context_names[] = {
367         NULL,
368         "test_context",
369     };
370     const uint32_t num_context_names = 2;
371 
372     for (uint32_t i = 0; i < num_context_names; i++) {
373         const char *context_name = context_names[i];
374         for (uint32_t j = 0; j < NUM_ITERATIONS; j++) {
375             result = test_rutabaga_init(&test, 1 << RUTABAGA_CAPSET_CROSS_DOMAIN);
376             CHECK_RESULT(result);
377 
378             result |= test_create_context(&test, context_name);
379             CHECK_RESULT(result);
380 
381             result |= test_init_context(&test);
382             CHECK_RESULT(result);
383 
384             result |= test_command_submission(&test);
385             CHECK_RESULT(result);
386 
387             result |= test_context_finish(&test);
388             CHECK_RESULT(result);
389 
390             result |= test_rutabaga_finish(&test);
391             CHECK_RESULT(result);
392         }
393     }
394 
395     for (uint32_t i = 0; i < NUM_ITERATIONS; i++) {
396         result = test_rutabaga_init(&test, 0);
397         CHECK_RESULT(result);
398 
399         result |= test_rutabaga_2d(&test);
400         CHECK_RESULT(result);
401 
402         result |= test_rutabaga_finish(&test);
403         CHECK_RESULT(result);
404     }
405 
406     printf("[  PASSED  ] rutabaga_test success\n");
407     return 0;
408 }
409