1/*
2 * Copyright © 2022 Google, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * 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 OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include <unistd.h>
25#include <sys/stat.h>
26#include <sys/types.h>
27
28#include "util/libsync.h"
29#include "util/u_process.h"
30
31#include "virtio_priv.h"
32
33static void
34virtio_device_destroy(struct fd_device *dev)
35{
36   struct virtio_device *virtio_dev = to_virtio_device(dev);
37
38   fd_bo_del_locked(virtio_dev->shmem_bo);
39   util_vma_heap_finish(&virtio_dev->address_space);
40}
41
42static const struct fd_device_funcs funcs = {
43   .bo_new = virtio_bo_new,
44   .bo_from_handle = virtio_bo_from_handle,
45   .pipe_new = virtio_pipe_new,
46   .destroy = virtio_device_destroy,
47};
48
49static int
50get_capset(int fd, struct virgl_renderer_capset_drm *caps)
51{
52   struct drm_virtgpu_get_caps args = {
53         .cap_set_id = VIRGL_RENDERER_CAPSET_DRM,
54         .cap_set_ver = 0,
55         .addr = VOID2U64(caps),
56         .size = sizeof(*caps),
57   };
58
59   memset(caps, 0, sizeof(*caps));
60
61   return drmIoctl(fd, DRM_IOCTL_VIRTGPU_GET_CAPS, &args);
62}
63
64static int
65set_context(int fd)
66{
67   struct drm_virtgpu_context_set_param params[] = {
68         { VIRTGPU_CONTEXT_PARAM_CAPSET_ID, VIRGL_RENDERER_CAPSET_DRM },
69         { VIRTGPU_CONTEXT_PARAM_NUM_RINGS, 64 },
70   };
71   struct drm_virtgpu_context_init args = {
72      .num_params = ARRAY_SIZE(params),
73      .ctx_set_params = VOID2U64(params),
74   };
75
76   return drmIoctl(fd, DRM_IOCTL_VIRTGPU_CONTEXT_INIT, &args);
77}
78
79static void
80set_debuginfo(struct fd_device *dev)
81{
82   const char *comm = util_get_process_name();
83   static char cmdline[0x1000+1];
84   int fd = open("/proc/self/cmdline", O_RDONLY);
85   if (fd < 0)
86      return;
87
88   int n = read(fd, cmdline, sizeof(cmdline) - 1);
89   if (n < 0)
90      return;
91
92   /* arguments are separated by NULL, convert to spaces: */
93   for (int i = 0; i < n; i++) {
94      if (cmdline[i] == '\0') {
95         cmdline[i] = ' ';
96      }
97   }
98
99   cmdline[n] = '\0';
100
101   unsigned comm_len = strlen(comm) + 1;
102   unsigned cmdline_len = strlen(cmdline) + 1;
103
104   struct msm_ccmd_set_debuginfo_req *req;
105
106   unsigned req_len = align(sizeof(*req) + comm_len + cmdline_len, 4);
107
108   req = malloc(req_len);
109
110   req->hdr         = MSM_CCMD(SET_DEBUGINFO, req_len);
111   req->comm_len    = comm_len;
112   req->cmdline_len = cmdline_len;
113
114   memcpy(&req->payload[0], comm, comm_len);
115   memcpy(&req->payload[comm_len], cmdline, cmdline_len);
116
117   virtio_execbuf(dev, &req->hdr, false);
118
119   free(req);
120}
121
122struct fd_device *
123virtio_device_new(int fd, drmVersionPtr version)
124{
125   struct virgl_renderer_capset_drm caps;
126   struct virtio_device *virtio_dev;
127   struct fd_device *dev;
128   int ret;
129
130   STATIC_ASSERT(FD_BO_PREP_READ == MSM_PREP_READ);
131   STATIC_ASSERT(FD_BO_PREP_WRITE == MSM_PREP_WRITE);
132   STATIC_ASSERT(FD_BO_PREP_NOSYNC == MSM_PREP_NOSYNC);
133
134   /* Debug option to force fallback to virgl: */
135   if (debug_get_bool_option("FD_NO_VIRTIO", false))
136      return NULL;
137
138   ret = get_capset(fd, &caps);
139   if (ret) {
140      INFO_MSG("could not get caps: %s", strerror(errno));
141      return NULL;
142   }
143
144   if (caps.context_type != VIRTGPU_DRM_CONTEXT_MSM) {
145      INFO_MSG("wrong context_type: %u", caps.context_type);
146      return NULL;
147   }
148
149   INFO_MSG("wire_format_version: %u", caps.wire_format_version);
150   INFO_MSG("version_major:       %u", caps.version_major);
151   INFO_MSG("version_minor:       %u", caps.version_minor);
152   INFO_MSG("version_patchlevel:  %u", caps.version_patchlevel);
153   INFO_MSG("has_cached_coherent: %u", caps.u.msm.has_cached_coherent);
154   INFO_MSG("va_start:            0x%0"PRIx64, caps.u.msm.va_start);
155   INFO_MSG("va_size:             0x%0"PRIx64, caps.u.msm.va_size);
156   INFO_MSG("gpu_id:              %u", caps.u.msm.gpu_id);
157   INFO_MSG("gmem_size:           %u", caps.u.msm.gmem_size);
158   INFO_MSG("gmem_base:           0x%0" PRIx64, caps.u.msm.gmem_base);
159   INFO_MSG("chip_id:             0x%0" PRIx64, caps.u.msm.chip_id);
160   INFO_MSG("max_freq:            %u", caps.u.msm.max_freq);
161
162   if (caps.wire_format_version != 2) {
163      ERROR_MSG("Unsupported protocol version: %u", caps.wire_format_version);
164      return NULL;
165   }
166
167   if ((caps.version_major != 1) || (caps.version_minor < FD_VERSION_SOFTPIN)) {
168      ERROR_MSG("unsupported version: %u.%u.%u", caps.version_major,
169                caps.version_minor, caps.version_patchlevel);
170      return NULL;
171   }
172
173   if (!caps.u.msm.va_size) {
174      ERROR_MSG("No address space");
175      return NULL;
176   }
177
178   ret = set_context(fd);
179   if (ret) {
180      INFO_MSG("Could not set context type: %s", strerror(errno));
181      return NULL;
182   }
183
184   virtio_dev = calloc(1, sizeof(*virtio_dev));
185   if (!virtio_dev)
186      return NULL;
187
188   dev = &virtio_dev->base;
189   dev->funcs = &funcs;
190   dev->fd = fd;
191   dev->version = caps.version_minor;
192   dev->has_cached_coherent = caps.u.msm.has_cached_coherent;
193
194   p_atomic_set(&virtio_dev->next_blob_id, 1);
195
196   virtio_dev->caps = caps;
197
198   util_queue_init(&dev->submit_queue, "sq", 8, 1, 0, NULL);
199
200   dev->bo_size = sizeof(struct virtio_bo);
201
202   simple_mtx_init(&virtio_dev->rsp_lock, mtx_plain);
203   simple_mtx_init(&virtio_dev->eb_lock, mtx_plain);
204
205   set_debuginfo(dev);
206
207   util_vma_heap_init(&virtio_dev->address_space,
208                      caps.u.msm.va_start,
209                      caps.u.msm.va_size);
210   simple_mtx_init(&virtio_dev->address_space_lock, mtx_plain);
211
212   return dev;
213}
214
215void *
216virtio_alloc_rsp(struct fd_device *dev, struct msm_ccmd_req *req, uint32_t sz)
217{
218   struct virtio_device *virtio_dev = to_virtio_device(dev);
219   unsigned off;
220
221   simple_mtx_lock(&virtio_dev->rsp_lock);
222
223   sz = align(sz, 8);
224
225   if ((virtio_dev->next_rsp_off + sz) >= virtio_dev->rsp_mem_len)
226      virtio_dev->next_rsp_off = 0;
227
228   off = virtio_dev->next_rsp_off;
229   virtio_dev->next_rsp_off += sz;
230
231   simple_mtx_unlock(&virtio_dev->rsp_lock);
232
233   req->rsp_off = off;
234
235   struct msm_ccmd_rsp *rsp = (void *)&virtio_dev->rsp_mem[off];
236   rsp->len = sz;
237
238   return rsp;
239}
240
241static int execbuf_flush_locked(struct fd_device *dev, int *out_fence_fd);
242
243static int
244execbuf_locked(struct fd_device *dev, void *cmd, uint32_t cmd_size,
245               uint32_t *handles, uint32_t num_handles,
246               int in_fence_fd, int *out_fence_fd, int ring_idx)
247{
248#define COND(bool, val) ((bool) ? (val) : 0)
249   struct drm_virtgpu_execbuffer eb = {
250         .flags = COND(out_fence_fd, VIRTGPU_EXECBUF_FENCE_FD_OUT) |
251                  COND(in_fence_fd != -1, VIRTGPU_EXECBUF_FENCE_FD_IN) |
252                  VIRTGPU_EXECBUF_RING_IDX,
253         .fence_fd = in_fence_fd,
254         .size  = cmd_size,
255         .command = VOID2U64(cmd),
256         .ring_idx = ring_idx,
257         .bo_handles = VOID2U64(handles),
258         .num_bo_handles = num_handles,
259   };
260
261   int ret = drmIoctl(dev->fd, DRM_IOCTL_VIRTGPU_EXECBUFFER, &eb);
262   if (ret) {
263      ERROR_MSG("EXECBUFFER failed: %s", strerror(errno));
264      return ret;
265   }
266
267   if (out_fence_fd)
268      *out_fence_fd = eb.fence_fd;
269
270   return 0;
271}
272
273/**
274 * Helper for "execbuf" ioctl.. note that in virtgpu execbuf is just
275 * a generic "send commands to host", not necessarily specific to
276 * cmdstream execution.
277 *
278 * Note that ring_idx 0 is the "CPU ring", ie. for synchronizing btwn
279 * guest and host CPU.
280 */
281int
282virtio_execbuf_fenced(struct fd_device *dev, struct msm_ccmd_req *req,
283                      uint32_t *handles, uint32_t num_handles,
284                      int in_fence_fd, int *out_fence_fd, int ring_idx)
285{
286   struct virtio_device *virtio_dev = to_virtio_device(dev);
287   int ret;
288
289   simple_mtx_lock(&virtio_dev->eb_lock);
290   execbuf_flush_locked(dev, NULL);
291   req->seqno = ++virtio_dev->next_seqno;
292
293   ret = execbuf_locked(dev, req, req->len, handles, num_handles,
294                        in_fence_fd, out_fence_fd, ring_idx);
295
296   simple_mtx_unlock(&virtio_dev->eb_lock);
297
298   return ret;
299}
300
301static int
302execbuf_flush_locked(struct fd_device *dev, int *out_fence_fd)
303{
304   struct virtio_device *virtio_dev = to_virtio_device(dev);
305   int ret;
306
307   if (!virtio_dev->reqbuf_len)
308      return 0;
309
310   ret = execbuf_locked(dev, virtio_dev->reqbuf, virtio_dev->reqbuf_len,
311                        NULL, 0, -1, out_fence_fd, 0);
312   if (ret)
313      return ret;
314
315   virtio_dev->reqbuf_len = 0;
316   virtio_dev->reqbuf_cnt = 0;
317
318   return 0;
319}
320
321int
322virtio_execbuf_flush(struct fd_device *dev)
323{
324   struct virtio_device *virtio_dev = to_virtio_device(dev);
325   simple_mtx_lock(&virtio_dev->eb_lock);
326   int ret = execbuf_flush_locked(dev, NULL);
327   simple_mtx_unlock(&virtio_dev->eb_lock);
328   return ret;
329}
330
331int
332virtio_execbuf(struct fd_device *dev, struct msm_ccmd_req *req, bool sync)
333{
334   struct virtio_device *virtio_dev = to_virtio_device(dev);
335   int fence_fd, ret = 0;
336
337   simple_mtx_lock(&virtio_dev->eb_lock);
338   req->seqno = ++virtio_dev->next_seqno;
339
340   if ((virtio_dev->reqbuf_len + req->len) > sizeof(virtio_dev->reqbuf)) {
341      ret = execbuf_flush_locked(dev, NULL);
342      if (ret)
343         goto out_unlock;
344   }
345
346   memcpy(&virtio_dev->reqbuf[virtio_dev->reqbuf_len], req, req->len);
347   virtio_dev->reqbuf_len += req->len;
348   virtio_dev->reqbuf_cnt++;
349
350   if (!sync)
351      goto out_unlock;
352
353   ret = execbuf_flush_locked(dev, &fence_fd);
354
355out_unlock:
356   simple_mtx_unlock(&virtio_dev->eb_lock);
357
358   if (ret)
359      return ret;
360
361   if (sync) {
362      sync_wait(fence_fd, -1);
363      close(fence_fd);
364      virtio_host_sync(dev, req);
365   }
366
367   return 0;
368}
369
370/**
371 * Wait until host as processed the specified request.
372 */
373void
374virtio_host_sync(struct fd_device *dev, const struct msm_ccmd_req *req)
375{
376   struct virtio_device *virtio_dev = to_virtio_device(dev);
377
378   while (fd_fence_before(virtio_dev->shmem->seqno, req->seqno))
379      sched_yield();
380}
381
382/**
383 * Helper for simple pass-thru ioctls
384 */
385int
386virtio_simple_ioctl(struct fd_device *dev, unsigned cmd, void *_req)
387{
388   unsigned req_len = sizeof(struct msm_ccmd_ioctl_simple_req);
389   unsigned rsp_len = sizeof(struct msm_ccmd_ioctl_simple_rsp);
390
391   req_len += _IOC_SIZE(cmd);
392   if (cmd & IOC_OUT)
393      rsp_len += _IOC_SIZE(cmd);
394
395   uint8_t buf[req_len];
396   struct msm_ccmd_ioctl_simple_req *req = (void *)buf;
397   struct msm_ccmd_ioctl_simple_rsp *rsp;
398
399   req->hdr = MSM_CCMD(IOCTL_SIMPLE, req_len);
400   req->cmd = cmd;
401   memcpy(req->payload, _req, _IOC_SIZE(cmd));
402
403   rsp = virtio_alloc_rsp(dev, &req->hdr, rsp_len);
404
405   int ret = virtio_execbuf(dev, &req->hdr, true);
406
407   if (cmd & IOC_OUT)
408      memcpy(_req, rsp->payload, _IOC_SIZE(cmd));
409
410   ret = rsp->ret;
411
412   return ret;
413}
414