1 /*
2 * Copyright © 2022 Google, Inc.
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include <unistd.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9
10 #include "util/libsync.h"
11 #include "util/u_process.h"
12
13 #include "virtio_priv.h"
14
15
16 static int virtio_execbuf_flush(struct fd_device *dev);
17
18 static void
virtio_device_destroy(struct fd_device * dev)19 virtio_device_destroy(struct fd_device *dev)
20 {
21 struct virtio_device *virtio_dev = to_virtio_device(dev);
22
23 util_vma_heap_finish(&virtio_dev->address_space);
24 }
25
26 static uint32_t
virtio_handle_from_dmabuf(struct fd_device * dev,int fd)27 virtio_handle_from_dmabuf(struct fd_device *dev, int fd)
28 {
29 struct virtio_device *virtio_dev = to_virtio_device(dev);
30
31 return vdrm_dmabuf_to_handle(virtio_dev->vdrm, fd);
32 }
33
34 static void
virtio_close_handle(struct fd_bo * bo)35 virtio_close_handle(struct fd_bo *bo)
36 {
37 struct virtio_device *virtio_dev = to_virtio_device(bo->dev);
38
39 vdrm_bo_close(virtio_dev->vdrm, bo->handle);
40 }
41
42 static const struct fd_device_funcs funcs = {
43 .bo_new = virtio_bo_new,
44 .bo_from_handle = virtio_bo_from_handle,
45 .handle_from_dmabuf = virtio_handle_from_dmabuf,
46 .bo_from_dmabuf = fd_bo_from_dmabuf_drm,
47 .bo_close_handle = virtio_close_handle,
48 .pipe_new = virtio_pipe_new,
49 .flush = virtio_execbuf_flush,
50 .destroy = virtio_device_destroy,
51 };
52
53 static void
set_debuginfo(struct fd_device * dev)54 set_debuginfo(struct fd_device *dev)
55 {
56 const char *comm = util_get_process_name();
57 static char cmdline[0x1000+1];
58 int fd = open("/proc/self/cmdline", O_RDONLY);
59 if (fd < 0)
60 return;
61
62 int n = read(fd, cmdline, sizeof(cmdline) - 1);
63 if (n < 0)
64 return;
65
66 /* arguments are separated by NULL, convert to spaces: */
67 for (int i = 0; i < n; i++) {
68 if (cmdline[i] == '\0') {
69 cmdline[i] = ' ';
70 }
71 }
72
73 cmdline[n] = '\0';
74
75 unsigned comm_len = strlen(comm) + 1;
76 unsigned cmdline_len = strlen(cmdline) + 1;
77
78 struct msm_ccmd_set_debuginfo_req *req;
79
80 unsigned req_len = align(sizeof(*req) + comm_len + cmdline_len, 4);
81
82 req = malloc(req_len);
83
84 req->hdr = MSM_CCMD(SET_DEBUGINFO, req_len);
85 req->comm_len = comm_len;
86 req->cmdline_len = cmdline_len;
87
88 memcpy(&req->payload[0], comm, comm_len);
89 memcpy(&req->payload[comm_len], cmdline, cmdline_len);
90
91 vdrm_send_req(to_virtio_device(dev)->vdrm, &req->hdr, false);
92
93 free(req);
94 }
95
96 struct fd_device *
virtio_device_new(int fd,drmVersionPtr version)97 virtio_device_new(int fd, drmVersionPtr version)
98 {
99 struct virgl_renderer_capset_drm caps;
100 struct virtio_device *virtio_dev;
101 struct vdrm_device *vdrm;
102 struct fd_device *dev;
103
104 STATIC_ASSERT(FD_BO_PREP_READ == MSM_PREP_READ);
105 STATIC_ASSERT(FD_BO_PREP_WRITE == MSM_PREP_WRITE);
106 STATIC_ASSERT(FD_BO_PREP_NOSYNC == MSM_PREP_NOSYNC);
107
108 /* Debug option to force fallback to virgl: */
109 if (debug_get_bool_option("FD_NO_VIRTIO", false))
110 return NULL;
111
112 vdrm = vdrm_device_connect(fd, VIRTGPU_DRM_CONTEXT_MSM);
113 if (!vdrm) {
114 INFO_MSG("could not connect vdrm");
115 return NULL;
116 }
117
118 caps = vdrm->caps;
119
120 INFO_MSG("wire_format_version: %u", caps.wire_format_version);
121 INFO_MSG("version_major: %u", caps.version_major);
122 INFO_MSG("version_minor: %u", caps.version_minor);
123 INFO_MSG("version_patchlevel: %u", caps.version_patchlevel);
124 INFO_MSG("has_cached_coherent: %u", caps.u.msm.has_cached_coherent);
125 INFO_MSG("va_start: 0x%0"PRIx64, caps.u.msm.va_start);
126 INFO_MSG("va_size: 0x%0"PRIx64, caps.u.msm.va_size);
127 INFO_MSG("gpu_id: %u", caps.u.msm.gpu_id);
128 INFO_MSG("gmem_size: %u", caps.u.msm.gmem_size);
129 INFO_MSG("gmem_base: 0x%0" PRIx64, caps.u.msm.gmem_base);
130 INFO_MSG("chip_id: 0x%0" PRIx64, caps.u.msm.chip_id);
131 INFO_MSG("max_freq: %u", caps.u.msm.max_freq);
132
133 if (caps.wire_format_version != 2) {
134 ERROR_MSG("Unsupported protocol version: %u", caps.wire_format_version);
135 goto error;
136 }
137
138 if ((caps.version_major != 1) || (caps.version_minor < FD_VERSION_SOFTPIN)) {
139 ERROR_MSG("unsupported version: %u.%u.%u", caps.version_major,
140 caps.version_minor, caps.version_patchlevel);
141 goto error;
142 }
143
144 if (!caps.u.msm.va_size) {
145 ERROR_MSG("No address space");
146 goto error;
147 }
148
149 virtio_dev = calloc(1, sizeof(*virtio_dev));
150 if (!virtio_dev)
151 goto error;
152
153 dev = &virtio_dev->base;
154 dev->funcs = &funcs;
155 dev->fd = fd;
156 dev->version = caps.version_minor;
157 dev->has_cached_coherent = caps.u.msm.has_cached_coherent;
158
159 p_atomic_set(&virtio_dev->next_blob_id, 1);
160 virtio_dev->shmem = to_msm_shmem(vdrm->shmem);
161 virtio_dev->vdrm = vdrm;
162
163 util_queue_init(&dev->submit_queue, "sq", 8, 1, 0, NULL);
164
165 dev->bo_size = sizeof(struct virtio_bo);
166
167 set_debuginfo(dev);
168
169 util_vma_heap_init(&virtio_dev->address_space,
170 caps.u.msm.va_start,
171 caps.u.msm.va_size);
172 simple_mtx_init(&virtio_dev->address_space_lock, mtx_plain);
173
174 return dev;
175
176 error:
177 vdrm_device_close(vdrm);
178 return NULL;
179 }
180
181 static int
virtio_execbuf_flush(struct fd_device * dev)182 virtio_execbuf_flush(struct fd_device *dev)
183 {
184 return vdrm_flush(to_virtio_device(dev)->vdrm);
185 }
186
187 /**
188 * Helper for simple pass-thru ioctls
189 */
190 int
virtio_simple_ioctl(struct fd_device * dev,unsigned cmd,void * _req)191 virtio_simple_ioctl(struct fd_device *dev, unsigned cmd, void *_req)
192 {
193 MESA_TRACE_FUNC();
194 struct vdrm_device *vdrm = to_virtio_device(dev)->vdrm;
195 unsigned req_len = sizeof(struct msm_ccmd_ioctl_simple_req);
196 unsigned rsp_len = sizeof(struct msm_ccmd_ioctl_simple_rsp);
197
198 req_len += _IOC_SIZE(cmd);
199 if (cmd & IOC_OUT)
200 rsp_len += _IOC_SIZE(cmd);
201
202 uint8_t buf[req_len];
203 struct msm_ccmd_ioctl_simple_req *req = (void *)buf;
204 struct msm_ccmd_ioctl_simple_rsp *rsp;
205
206 req->hdr = MSM_CCMD(IOCTL_SIMPLE, req_len);
207 req->cmd = cmd;
208 memcpy(req->payload, _req, _IOC_SIZE(cmd));
209
210 rsp = vdrm_alloc_rsp(vdrm, &req->hdr, rsp_len);
211
212 int ret = vdrm_send_req(vdrm, &req->hdr, true);
213
214 if (cmd & IOC_OUT)
215 memcpy(_req, rsp->payload, _IOC_SIZE(cmd));
216
217 ret = rsp->ret;
218
219 return ret;
220 }
221