xref: /aosp_15_r20/external/mesa3d/src/asahi/lib/agx_device_virtio.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright 2024 Sergio Lopez
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include "agx_device_virtio.h"
7 
8 #include <inttypes.h>
9 #include <sys/mman.h>
10 
11 #include "drm-uapi/virtgpu_drm.h"
12 #include "unstable_asahi_drm.h"
13 
14 #define VIRGL_RENDERER_UNSTABLE_APIS 1
15 #include "vdrm.h"
16 #include "virglrenderer_hw.h"
17 
18 #include "asahi_proto.h"
19 
20 /**
21  * Helper for simple pass-thru ioctls
22  */
23 int
agx_virtio_simple_ioctl(struct agx_device * dev,unsigned cmd,void * _req)24 agx_virtio_simple_ioctl(struct agx_device *dev, unsigned cmd, void *_req)
25 {
26    struct vdrm_device *vdrm = dev->vdrm;
27    unsigned req_len = sizeof(struct asahi_ccmd_ioctl_simple_req);
28    unsigned rsp_len = sizeof(struct asahi_ccmd_ioctl_simple_rsp);
29 
30    req_len += _IOC_SIZE(cmd);
31    if (cmd & IOC_OUT)
32       rsp_len += _IOC_SIZE(cmd);
33 
34    uint8_t buf[req_len];
35    struct asahi_ccmd_ioctl_simple_req *req = (void *)buf;
36    struct asahi_ccmd_ioctl_simple_rsp *rsp;
37 
38    req->hdr = ASAHI_CCMD(IOCTL_SIMPLE, req_len);
39    req->cmd = cmd;
40    memcpy(req->payload, _req, _IOC_SIZE(cmd));
41 
42    rsp = vdrm_alloc_rsp(vdrm, &req->hdr, rsp_len);
43 
44    int ret = vdrm_send_req(vdrm, &req->hdr, true);
45    if (ret) {
46       fprintf(stderr, "simple_ioctl: vdrm_send_req failed\n");
47       return ret;
48    }
49 
50    if (cmd & IOC_OUT)
51       memcpy(_req, rsp->payload, _IOC_SIZE(cmd));
52 
53    return rsp->ret;
54 }
55 
56 static struct agx_bo *
agx_virtio_bo_alloc(struct agx_device * dev,size_t size,size_t align,enum agx_bo_flags flags)57 agx_virtio_bo_alloc(struct agx_device *dev, size_t size, size_t align,
58                     enum agx_bo_flags flags)
59 {
60    struct agx_bo *bo;
61    unsigned handle = 0;
62 
63    size = ALIGN_POT(size, dev->params.vm_page_size);
64 
65    /* executable implies low va */
66    assert(!(flags & AGX_BO_EXEC) || (flags & AGX_BO_LOW_VA));
67 
68    struct asahi_ccmd_gem_new_req req = {
69       .hdr = ASAHI_CCMD(GEM_NEW, sizeof(req)),
70       .size = size,
71    };
72 
73    if (flags & AGX_BO_WRITEBACK)
74       req.flags |= ASAHI_GEM_WRITEBACK;
75 
76    uint32_t blob_flags =
77       VIRTGPU_BLOB_FLAG_USE_MAPPABLE | VIRTGPU_BLOB_FLAG_USE_SHAREABLE;
78 
79    req.bind_flags = ASAHI_BIND_READ;
80    if (!(flags & AGX_BO_READONLY)) {
81       req.bind_flags |= ASAHI_BIND_WRITE;
82    }
83 
84    uint32_t blob_id = p_atomic_inc_return(&dev->next_blob_id);
85 
86    enum agx_va_flags va_flags = flags & AGX_BO_LOW_VA ? AGX_VA_USC : 0;
87    struct agx_va *va =
88       agx_va_alloc(dev, size, dev->params.vm_page_size, va_flags, 0);
89    if (!va) {
90       fprintf(stderr, "Failed to allocate BO VMA\n");
91       return NULL;
92    }
93 
94    req.addr = va->addr;
95    req.blob_id = blob_id;
96    req.vm_id = dev->vm_id;
97 
98    handle = vdrm_bo_create(dev->vdrm, size, blob_flags, blob_id, &req.hdr);
99    if (!handle) {
100       fprintf(stderr, "vdrm_bo_created failed\n");
101       return NULL;
102    }
103 
104    pthread_mutex_lock(&dev->bo_map_lock);
105    bo = agx_lookup_bo(dev, handle);
106    dev->max_handle = MAX2(dev->max_handle, handle);
107    pthread_mutex_unlock(&dev->bo_map_lock);
108 
109    /* Fresh handle */
110    assert(!memcmp(bo, &((struct agx_bo){}), sizeof(*bo)));
111 
112    bo->size = size;
113    bo->align = MAX2(dev->params.vm_page_size, align);
114    bo->flags = flags;
115    bo->handle = handle;
116    bo->prime_fd = -1;
117    bo->blob_id = blob_id;
118    bo->va = va;
119    bo->vbo_res_id = vdrm_handle_to_res_id(dev->vdrm, handle);
120 
121    dev->ops.bo_mmap(dev, bo);
122    return bo;
123 }
124 
125 static int
agx_virtio_bo_bind(struct agx_device * dev,struct agx_bo * bo,uint64_t addr,size_t size_B,uint64_t offset_B,uint32_t flags,bool unbind)126 agx_virtio_bo_bind(struct agx_device *dev, struct agx_bo *bo, uint64_t addr,
127                    size_t size_B, uint64_t offset_B, uint32_t flags,
128                    bool unbind)
129 {
130    assert(offset_B == 0 && "TODO: need to extend virtgpu");
131 
132    struct asahi_ccmd_gem_bind_req req = {
133       .op = unbind ? ASAHI_BIND_OP_UNBIND : ASAHI_BIND_OP_BIND,
134       .flags = flags,
135       .vm_id = dev->vm_id,
136       .res_id = bo->vbo_res_id,
137       .size = size_B,
138       .addr = addr,
139       .hdr.cmd = ASAHI_CCMD_GEM_BIND,
140       .hdr.len = sizeof(struct asahi_ccmd_gem_bind_req),
141    };
142 
143    int ret = vdrm_send_req(dev->vdrm, &req.hdr, false);
144    if (ret) {
145       fprintf(stderr, "DRM_IOCTL_ASAHI_GEM_BIND failed: %d (handle=%d)\n", ret,
146               bo->handle);
147    }
148 
149    return ret;
150 }
151 
152 static void
agx_virtio_bo_mmap(struct agx_device * dev,struct agx_bo * bo)153 agx_virtio_bo_mmap(struct agx_device *dev, struct agx_bo *bo)
154 {
155    if (bo->map) {
156       return;
157    }
158 
159    bo->map = vdrm_bo_map(dev->vdrm, bo->handle, bo->size, NULL);
160    if (bo->map == MAP_FAILED) {
161       bo->map = NULL;
162       fprintf(stderr, "mmap failed: result=%p size=0x%llx fd=%i\n", bo->map,
163               (long long)bo->size, dev->fd);
164    }
165 }
166 
167 static ssize_t
agx_virtio_get_params(struct agx_device * dev,void * buf,size_t size)168 agx_virtio_get_params(struct agx_device *dev, void *buf, size_t size)
169 {
170    struct vdrm_device *vdrm = dev->vdrm;
171    struct asahi_ccmd_get_params_req req = {
172       .params.size = size,
173       .hdr.cmd = ASAHI_CCMD_GET_PARAMS,
174       .hdr.len = sizeof(struct asahi_ccmd_get_params_req),
175    };
176    struct asahi_ccmd_get_params_rsp *rsp;
177 
178    rsp =
179       vdrm_alloc_rsp(vdrm, &req.hdr, sizeof(struct asahi_ccmd_get_params_rsp));
180 
181    int ret = vdrm_send_req(vdrm, &req.hdr, true);
182    if (ret)
183       goto out;
184 
185    ret = rsp->ret;
186    if (!ret) {
187       memcpy(buf, &rsp->params, size);
188       return size;
189    }
190 
191 out:
192    return ret;
193 }
194 
195 static int
agx_virtio_submit(struct agx_device * dev,struct drm_asahi_submit * submit,uint32_t vbo_res_id)196 agx_virtio_submit(struct agx_device *dev, struct drm_asahi_submit *submit,
197                   uint32_t vbo_res_id)
198 {
199    struct drm_asahi_command *commands =
200       (struct drm_asahi_command *)submit->commands;
201    struct drm_asahi_sync *in_syncs = (struct drm_asahi_sync *)submit->in_syncs;
202    struct drm_asahi_sync *out_syncs =
203       (struct drm_asahi_sync *)submit->out_syncs;
204    size_t req_len = sizeof(struct asahi_ccmd_submit_req);
205 
206    for (int i = 0; i < submit->command_count; i++) {
207       switch (commands[i].cmd_type) {
208       case DRM_ASAHI_CMD_COMPUTE: {
209          req_len += sizeof(struct drm_asahi_command) +
210                     sizeof(struct drm_asahi_cmd_compute);
211          break;
212       }
213 
214       case DRM_ASAHI_CMD_RENDER: {
215          struct drm_asahi_cmd_render *render =
216             (struct drm_asahi_cmd_render *)commands[i].cmd_buffer;
217          req_len += sizeof(struct drm_asahi_command) +
218                     sizeof(struct drm_asahi_cmd_render);
219          req_len += render->fragment_attachment_count *
220                     sizeof(struct drm_asahi_attachment);
221          break;
222       }
223 
224       default:
225          return EINVAL;
226       }
227    }
228 
229    struct asahi_ccmd_submit_req *req =
230       (struct asahi_ccmd_submit_req *)calloc(1, req_len);
231 
232    req->queue_id = submit->queue_id;
233    req->result_res_id = vbo_res_id;
234    req->command_count = submit->command_count;
235 
236    char *ptr = (char *)&req->payload;
237 
238    for (int i = 0; i < submit->command_count; i++) {
239       memcpy(ptr, &commands[i], sizeof(struct drm_asahi_command));
240       ptr += sizeof(struct drm_asahi_command);
241 
242       memcpy(ptr, (char *)commands[i].cmd_buffer, commands[i].cmd_buffer_size);
243       ptr += commands[i].cmd_buffer_size;
244 
245       if (commands[i].cmd_type == DRM_ASAHI_CMD_RENDER) {
246          struct drm_asahi_cmd_render *render =
247             (struct drm_asahi_cmd_render *)commands[i].cmd_buffer;
248          size_t fragments_size = sizeof(struct drm_asahi_attachment) *
249                                  render->fragment_attachment_count;
250          memcpy(ptr, (char *)render->fragment_attachments, fragments_size);
251          ptr += fragments_size;
252       }
253    }
254 
255    req->hdr.cmd = ASAHI_CCMD_SUBMIT;
256    req->hdr.len = req_len;
257 
258    struct drm_virtgpu_execbuffer_syncobj *vdrm_in_syncs = calloc(
259       submit->in_sync_count, sizeof(struct drm_virtgpu_execbuffer_syncobj));
260    for (int i = 0; i < submit->in_sync_count; i++) {
261       vdrm_in_syncs[i].handle = in_syncs[i].handle;
262       vdrm_in_syncs[i].point = in_syncs[i].timeline_value;
263    }
264 
265    struct drm_virtgpu_execbuffer_syncobj *vdrm_out_syncs = calloc(
266       submit->out_sync_count, sizeof(struct drm_virtgpu_execbuffer_syncobj));
267    for (int i = 0; i < submit->out_sync_count; i++) {
268       vdrm_out_syncs[i].handle = out_syncs[i].handle;
269       vdrm_out_syncs[i].point = out_syncs[i].timeline_value;
270    }
271 
272    struct vdrm_execbuf_params p = {
273       /* Signal the host we want to wait for the command to complete */
274       .ring_idx = 1,
275       .req = &req->hdr,
276       .num_in_syncobjs = submit->in_sync_count,
277       .in_syncobjs = vdrm_in_syncs,
278       .num_out_syncobjs = submit->out_sync_count,
279       .out_syncobjs = vdrm_out_syncs,
280    };
281 
282    int ret = vdrm_execbuf(dev->vdrm, &p);
283 
284    free(vdrm_out_syncs);
285    free(vdrm_in_syncs);
286    free(req);
287    return ret;
288 }
289 
290 const agx_device_ops_t agx_virtio_device_ops = {
291    .bo_alloc = agx_virtio_bo_alloc,
292    .bo_bind = agx_virtio_bo_bind,
293    .bo_mmap = agx_virtio_bo_mmap,
294    .get_params = agx_virtio_get_params,
295    .submit = agx_virtio_submit,
296 };
297 
298 bool
agx_virtio_open_device(struct agx_device * dev)299 agx_virtio_open_device(struct agx_device *dev)
300 {
301    struct vdrm_device *vdrm;
302 
303    vdrm = vdrm_device_connect(dev->fd, 2);
304    if (!vdrm) {
305       fprintf(stderr, "could not connect vdrm\n");
306       return false;
307    }
308 
309    dev->vdrm = vdrm;
310    dev->ops = agx_virtio_device_ops;
311    return true;
312 }
313