xref: /aosp_15_r20/external/mesa3d/src/asahi/lib/agx_device.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright 2021 Alyssa Rosenzweig
3  * Copyright 2019 Collabora, Ltd.
4  * Copyright 2020 Igalia S.L.
5  * SPDX-License-Identifier: MIT
6  */
7 
8 #include "agx_device.h"
9 #include <inttypes.h>
10 #include "util/ralloc.h"
11 #include "util/timespec.h"
12 #include "agx_bo.h"
13 #include "agx_compile.h"
14 #include "agx_device_virtio.h"
15 #include "agx_scratch.h"
16 #include "decode.h"
17 #include "glsl_types.h"
18 #include "libagx_shaders.h"
19 
20 #include <fcntl.h>
21 #include <xf86drm.h>
22 #include "drm-uapi/dma-buf.h"
23 #include "util/blob.h"
24 #include "util/log.h"
25 #include "util/mesa-sha1.h"
26 #include "util/os_file.h"
27 #include "util/os_mman.h"
28 #include "util/os_time.h"
29 #include "util/simple_mtx.h"
30 #include "git_sha1.h"
31 #include "nir_serialize.h"
32 #include "unstable_asahi_drm.h"
33 #include "vdrm.h"
34 
35 static inline int
asahi_simple_ioctl(struct agx_device * dev,unsigned cmd,void * req)36 asahi_simple_ioctl(struct agx_device *dev, unsigned cmd, void *req)
37 {
38    if (dev->is_virtio) {
39       return agx_virtio_simple_ioctl(dev, cmd, req);
40    } else {
41       return drmIoctl(dev->fd, cmd, req);
42    }
43 }
44 
45 /* clang-format off */
46 static const struct debug_named_value agx_debug_options[] = {
47    {"trace",     AGX_DBG_TRACE,    "Trace the command stream"},
48    {"no16",      AGX_DBG_NO16,     "Disable 16-bit support"},
49    {"perf",      AGX_DBG_PERF,     "Print performance warnings"},
50 #ifndef NDEBUG
51    {"dirty",     AGX_DBG_DIRTY,    "Disable dirty tracking"},
52 #endif
53    {"precompile",AGX_DBG_PRECOMPILE,"Precompile shaders for shader-db"},
54    {"nocompress",AGX_DBG_NOCOMPRESS,"Disable lossless compression"},
55    {"nocluster", AGX_DBG_NOCLUSTER,"Disable vertex clustering"},
56    {"sync",      AGX_DBG_SYNC,     "Synchronously wait for all submissions"},
57    {"stats",     AGX_DBG_STATS,    "Show command execution statistics"},
58    {"resource",  AGX_DBG_RESOURCE, "Log resource operations"},
59    {"batch",     AGX_DBG_BATCH,    "Log batches"},
60    {"nowc",      AGX_DBG_NOWC,     "Disable write-combining"},
61    {"synctvb",   AGX_DBG_SYNCTVB,  "Synchronous TVB growth"},
62    {"smalltile", AGX_DBG_SMALLTILE,"Force 16x16 tiles"},
63    {"feedback",  AGX_DBG_FEEDBACK, "Debug feedback loops"},
64    {"nomsaa",    AGX_DBG_NOMSAA,   "Force disable MSAA"},
65    {"noshadow",  AGX_DBG_NOSHADOW, "Force disable resource shadowing"},
66    {"scratch",   AGX_DBG_SCRATCH,  "Debug scratch memory usage"},
67    {"1queue",    AGX_DBG_1QUEUE,   "Force usage of a single queue for multiple contexts"},
68    DEBUG_NAMED_VALUE_END
69 };
70 /* clang-format on */
71 
72 void
agx_bo_free(struct agx_device * dev,struct agx_bo * bo)73 agx_bo_free(struct agx_device *dev, struct agx_bo *bo)
74 {
75    const uint64_t handle = bo->handle;
76 
77    if (bo->map)
78       munmap(bo->map, bo->size);
79 
80    /* Free the VA. No need to unmap the BO, as the kernel will take care of that
81     * when we close it.
82     */
83    agx_va_free(dev, bo->va);
84 
85    if (bo->prime_fd != -1)
86       close(bo->prime_fd);
87 
88    /* Reset the handle. This has to happen before the GEM close to avoid a race.
89     */
90    memset(bo, 0, sizeof(*bo));
91    __sync_synchronize();
92 
93    struct drm_gem_close args = {.handle = handle};
94    drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &args);
95 }
96 
97 static int
agx_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)98 agx_bo_bind(struct agx_device *dev, struct agx_bo *bo, uint64_t addr,
99             size_t size_B, uint64_t offset_B, uint32_t flags, bool unbind)
100 {
101    struct drm_asahi_gem_bind gem_bind = {
102       .op = unbind ? ASAHI_BIND_OP_UNBIND : ASAHI_BIND_OP_BIND,
103       .flags = flags,
104       .handle = bo->handle,
105       .vm_id = dev->vm_id,
106       .offset = offset_B,
107       .range = size_B,
108       .addr = addr,
109    };
110 
111    int ret = drmIoctl(dev->fd, DRM_IOCTL_ASAHI_GEM_BIND, &gem_bind);
112    if (ret) {
113       fprintf(stderr, "DRM_IOCTL_ASAHI_GEM_BIND failed: %m (handle=%d)\n",
114               bo->handle);
115    }
116 
117    return ret;
118 }
119 
120 static struct agx_bo *
agx_bo_alloc(struct agx_device * dev,size_t size,size_t align,enum agx_bo_flags flags)121 agx_bo_alloc(struct agx_device *dev, size_t size, size_t align,
122              enum agx_bo_flags flags)
123 {
124    struct agx_bo *bo;
125    unsigned handle = 0;
126 
127    assert(size > 0);
128    size = ALIGN_POT(size, dev->params.vm_page_size);
129 
130    /* executable implies low va */
131    assert(!(flags & AGX_BO_EXEC) || (flags & AGX_BO_LOW_VA));
132 
133    struct drm_asahi_gem_create gem_create = {.size = size};
134 
135    if (flags & AGX_BO_WRITEBACK)
136       gem_create.flags |= ASAHI_GEM_WRITEBACK;
137 
138    if (!(flags & (AGX_BO_SHARED | AGX_BO_SHAREABLE))) {
139       gem_create.flags |= ASAHI_GEM_VM_PRIVATE;
140       gem_create.vm_id = dev->vm_id;
141    }
142 
143    int ret = drmIoctl(dev->fd, DRM_IOCTL_ASAHI_GEM_CREATE, &gem_create);
144    if (ret) {
145       fprintf(stderr, "DRM_IOCTL_ASAHI_GEM_CREATE failed: %m\n");
146       return NULL;
147    }
148 
149    handle = gem_create.handle;
150 
151    pthread_mutex_lock(&dev->bo_map_lock);
152    bo = agx_lookup_bo(dev, handle);
153    dev->max_handle = MAX2(dev->max_handle, handle);
154    pthread_mutex_unlock(&dev->bo_map_lock);
155 
156    /* Fresh handle */
157    assert(!memcmp(bo, &((struct agx_bo){}), sizeof(*bo)));
158 
159    bo->size = gem_create.size;
160    bo->align = MAX2(dev->params.vm_page_size, align);
161    bo->flags = flags;
162    bo->handle = handle;
163    bo->prime_fd = -1;
164 
165    enum agx_va_flags va_flags = flags & AGX_BO_LOW_VA ? AGX_VA_USC : 0;
166    bo->va = agx_va_alloc(dev, size, bo->align, va_flags, 0);
167    if (!bo->va) {
168       fprintf(stderr, "Failed to allocate BO VMA\n");
169       agx_bo_free(dev, bo);
170       return NULL;
171    }
172 
173    uint32_t bind = ASAHI_BIND_READ;
174    if (!(flags & AGX_BO_READONLY)) {
175       bind |= ASAHI_BIND_WRITE;
176    }
177 
178    ret = dev->ops.bo_bind(dev, bo, bo->va->addr, bo->size, 0, bind, false);
179    if (ret) {
180       agx_bo_free(dev, bo);
181       return NULL;
182    }
183 
184    dev->ops.bo_mmap(dev, bo);
185    return bo;
186 }
187 
188 static void
agx_bo_mmap(struct agx_device * dev,struct agx_bo * bo)189 agx_bo_mmap(struct agx_device *dev, struct agx_bo *bo)
190 {
191    struct drm_asahi_gem_mmap_offset gem_mmap_offset = {.handle = bo->handle};
192    int ret;
193 
194    if (bo->map)
195       return;
196 
197    ret = drmIoctl(dev->fd, DRM_IOCTL_ASAHI_GEM_MMAP_OFFSET, &gem_mmap_offset);
198    if (ret) {
199       fprintf(stderr, "DRM_IOCTL_ASAHI_MMAP_BO failed: %m\n");
200       assert(0);
201    }
202 
203    bo->map = os_mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
204                      dev->fd, gem_mmap_offset.offset);
205    if (bo->map == MAP_FAILED) {
206       bo->map = NULL;
207       fprintf(stderr,
208               "mmap failed: result=%p size=0x%llx fd=%i offset=0x%llx %m\n",
209               bo->map, (long long)bo->size, dev->fd,
210               (long long)gem_mmap_offset.offset);
211    }
212 }
213 
214 struct agx_bo *
agx_bo_import(struct agx_device * dev,int fd)215 agx_bo_import(struct agx_device *dev, int fd)
216 {
217    struct agx_bo *bo;
218    ASSERTED int ret;
219    unsigned gem_handle;
220 
221    pthread_mutex_lock(&dev->bo_map_lock);
222 
223    ret = drmPrimeFDToHandle(dev->fd, fd, &gem_handle);
224    if (ret) {
225       fprintf(stderr, "import failed: Could not map fd %d to handle\n", fd);
226       pthread_mutex_unlock(&dev->bo_map_lock);
227       return NULL;
228    }
229 
230    bo = agx_lookup_bo(dev, gem_handle);
231    dev->max_handle = MAX2(dev->max_handle, gem_handle);
232 
233    if (!bo->size) {
234       bo->size = lseek(fd, 0, SEEK_END);
235       bo->align = dev->params.vm_page_size;
236 
237       /* Sometimes this can fail and return -1. size of -1 is not
238        * a nice thing for mmap to try mmap. Be more robust also
239        * for zero sized maps and fail nicely too
240        */
241       if ((bo->size == 0) || (bo->size == (size_t)-1)) {
242          pthread_mutex_unlock(&dev->bo_map_lock);
243          return NULL;
244       }
245       if (bo->size & (dev->params.vm_page_size - 1)) {
246          fprintf(
247             stderr,
248             "import failed: BO is not a multiple of the page size (0x%llx bytes)\n",
249             (long long)bo->size);
250          goto error;
251       }
252 
253       bo->flags = AGX_BO_SHARED | AGX_BO_SHAREABLE;
254       bo->handle = gem_handle;
255       bo->prime_fd = os_dupfd_cloexec(fd);
256       bo->label = "Imported BO";
257       assert(bo->prime_fd >= 0);
258 
259       p_atomic_set(&bo->refcnt, 1);
260       bo->va = agx_va_alloc(dev, bo->size, bo->align, 0, 0);
261 
262       if (!bo->va) {
263          fprintf(
264             stderr,
265             "import failed: Could not allocate from VMA heap (0x%llx bytes)\n",
266             (long long)bo->size);
267          abort();
268       }
269 
270       if (dev->is_virtio) {
271          bo->vbo_res_id = vdrm_handle_to_res_id(dev->vdrm, bo->handle);
272       }
273 
274       ret = dev->ops.bo_bind(dev, bo, bo->va->addr, bo->size, 0,
275                              ASAHI_BIND_READ | ASAHI_BIND_WRITE, false);
276       if (ret) {
277          fprintf(stderr, "import failed: Could not bind BO at 0x%llx\n",
278                  (long long)bo->va->addr);
279          abort();
280       }
281    } else {
282       /* bo->refcnt == 0 can happen if the BO
283        * was being released but agx_bo_import() acquired the
284        * lock before agx_bo_unreference(). In that case, refcnt
285        * is 0 and we can't use agx_bo_reference() directly, we
286        * have to re-initialize the refcnt().
287        * Note that agx_bo_unreference() checks
288        * refcnt value just after acquiring the lock to
289        * make sure the object is not freed if agx_bo_import()
290        * acquired it in the meantime.
291        */
292       if (p_atomic_read(&bo->refcnt) == 0)
293          p_atomic_set(&bo->refcnt, 1);
294       else
295          agx_bo_reference(bo);
296    }
297    pthread_mutex_unlock(&dev->bo_map_lock);
298 
299    if (dev->debug & AGX_DBG_TRACE)
300       agxdecode_track_alloc(dev->agxdecode, bo);
301 
302    return bo;
303 
304 error:
305    memset(bo, 0, sizeof(*bo));
306    pthread_mutex_unlock(&dev->bo_map_lock);
307    return NULL;
308 }
309 
310 int
agx_bo_export(struct agx_device * dev,struct agx_bo * bo)311 agx_bo_export(struct agx_device *dev, struct agx_bo *bo)
312 {
313    int fd;
314 
315    assert(bo->flags & AGX_BO_SHAREABLE);
316 
317    if (drmPrimeHandleToFD(dev->fd, bo->handle, DRM_CLOEXEC, &fd))
318       return -1;
319 
320    if (!(bo->flags & AGX_BO_SHARED)) {
321       bo->flags |= AGX_BO_SHARED;
322       assert(bo->prime_fd == -1);
323       bo->prime_fd = os_dupfd_cloexec(fd);
324 
325       /* If there is a pending writer to this BO, import it into the buffer
326        * for implicit sync.
327        */
328       uint64_t writer = p_atomic_read_relaxed(&bo->writer);
329       if (writer) {
330          int out_sync_fd = -1;
331          int ret = drmSyncobjExportSyncFile(
332             dev->fd, agx_bo_writer_syncobj(writer), &out_sync_fd);
333          assert(ret >= 0);
334          assert(out_sync_fd >= 0);
335 
336          ret = agx_import_sync_file(dev, bo, out_sync_fd);
337          assert(ret >= 0);
338          close(out_sync_fd);
339       }
340    }
341 
342    assert(bo->prime_fd >= 0);
343    return fd;
344 }
345 
346 static void
agx_get_global_ids(struct agx_device * dev)347 agx_get_global_ids(struct agx_device *dev)
348 {
349    dev->next_global_id = 0;
350    dev->last_global_id = 0x1000000;
351 }
352 
353 uint64_t
agx_get_global_id(struct agx_device * dev)354 agx_get_global_id(struct agx_device *dev)
355 {
356    if (unlikely(dev->next_global_id >= dev->last_global_id)) {
357       agx_get_global_ids(dev);
358    }
359 
360    return dev->next_global_id++;
361 }
362 
363 static ssize_t
agx_get_params(struct agx_device * dev,void * buf,size_t size)364 agx_get_params(struct agx_device *dev, void *buf, size_t size)
365 {
366    struct drm_asahi_get_params get_param = {
367       .param_group = 0,
368       .pointer = (uint64_t)(uintptr_t)buf,
369       .size = size,
370    };
371 
372    memset(buf, 0, size);
373 
374    int ret = drmIoctl(dev->fd, DRM_IOCTL_ASAHI_GET_PARAMS, &get_param);
375    if (ret) {
376       fprintf(stderr, "DRM_IOCTL_ASAHI_GET_PARAMS failed: %m\n");
377       return -EINVAL;
378    }
379 
380    return get_param.size;
381 }
382 
383 static int
agx_submit(struct agx_device * dev,struct drm_asahi_submit * submit,uint32_t vbo_res_id)384 agx_submit(struct agx_device *dev, struct drm_asahi_submit *submit,
385            uint32_t vbo_res_id)
386 {
387    return drmIoctl(dev->fd, DRM_IOCTL_ASAHI_SUBMIT, submit);
388 }
389 
390 const agx_device_ops_t agx_device_drm_ops = {
391    .bo_alloc = agx_bo_alloc,
392    .bo_bind = agx_bo_bind,
393    .bo_mmap = agx_bo_mmap,
394    .get_params = agx_get_params,
395    .submit = agx_submit,
396 };
397 
398 bool
agx_open_device(void * memctx,struct agx_device * dev)399 agx_open_device(void *memctx, struct agx_device *dev)
400 {
401    dev->debug =
402       debug_get_flags_option("ASAHI_MESA_DEBUG", agx_debug_options, 0);
403 
404    dev->ops = agx_device_drm_ops;
405 
406    ssize_t params_size = -1;
407    drmVersionPtr version;
408 
409    version = drmGetVersion(dev->fd);
410    if (!version) {
411       fprintf(stderr, "cannot get version: %s", strerror(errno));
412       return NULL;
413    }
414 
415    if (!strcmp(version->name, "asahi")) {
416       dev->is_virtio = false;
417       dev->ops = agx_device_drm_ops;
418    } else if (!strcmp(version->name, "virtio_gpu")) {
419       dev->is_virtio = true;
420       if (!agx_virtio_open_device(dev)) {
421          fprintf(stderr,
422                  "Error opening virtio-gpu device for Asahi native context\n");
423          return false;
424       }
425    } else {
426       return false;
427    }
428 
429    params_size = dev->ops.get_params(dev, &dev->params, sizeof(dev->params));
430    if (params_size <= 0) {
431       assert(0);
432       return false;
433    }
434    assert(params_size >= sizeof(dev->params));
435 
436    /* Refuse to probe. */
437    if (dev->params.unstable_uabi_version != DRM_ASAHI_UNSTABLE_UABI_VERSION) {
438       fprintf(
439          stderr,
440          "You are attempting to use upstream Mesa with a downstream kernel!\n"
441          "This WILL NOT work.\n"
442          "The Asahi UABI is unstable and NOT SUPPORTED in upstream Mesa.\n"
443          "UABI related code in upstream Mesa is not for use!\n"
444          "\n"
445          "Do NOT attempt to patch out checks, you WILL break your system.\n"
446          "Do NOT report bugs.\n"
447          "Do NOT ask Mesa developers for support.\n"
448          "Do NOT write guides about how to patch out these checks.\n"
449          "Do NOT package patches to Mesa to bypass this.\n"
450          "\n"
451          "~~~\n"
452          "This is not a place of honor.\n"
453          "No highly esteemed deed is commemorated here.\n"
454          "Nothing valued is here.\n"
455          "\n"
456          "What is here was dangerous and repulsive to us.\n"
457          "This message is a warning about danger.\n"
458          "\n"
459          "The danger is still present, in your time, as it was in ours.\n"
460          "The danger is unleashed only if you substantially disturb this place physically.\n"
461          "This place is best shunned and left uninhabited.\n"
462          "~~~\n"
463          "\n"
464          "THIS IS NOT A BUG. THIS IS YOU DOING SOMETHING BROKEN!\n");
465       abort();
466    }
467 
468    uint64_t incompat =
469       dev->params.feat_incompat & (~AGX_SUPPORTED_INCOMPAT_FEATURES);
470    if (incompat) {
471       fprintf(stderr, "Missing GPU incompat features: 0x%" PRIx64 "\n",
472               incompat);
473       assert(0);
474       return false;
475    }
476 
477    assert(dev->params.gpu_generation >= 13);
478    const char *variant = " Unknown";
479    switch (dev->params.gpu_variant) {
480    case 'G':
481       variant = "";
482       break;
483    case 'S':
484       variant = " Pro";
485       break;
486    case 'C':
487       variant = " Max";
488       break;
489    case 'D':
490       variant = " Ultra";
491       break;
492    }
493    snprintf(dev->name, sizeof(dev->name), "Apple M%d%s (G%d%c %02X)",
494             dev->params.gpu_generation - 12, variant,
495             dev->params.gpu_generation, dev->params.gpu_variant,
496             dev->params.gpu_revision + 0xA0);
497 
498    dev->guard_size = dev->params.vm_page_size;
499    if (dev->params.vm_usc_start) {
500       dev->shader_base = dev->params.vm_usc_start;
501    } else {
502       // Put the USC heap at the bottom of the user address space, 4GiB aligned
503       dev->shader_base = ALIGN_POT(dev->params.vm_user_start, 0x100000000ull);
504    }
505 
506    uint64_t shader_size = 0x100000000ull;
507    // Put the user heap after the USC heap
508    uint64_t user_start = dev->shader_base + shader_size;
509 
510    assert(dev->shader_base >= dev->params.vm_user_start);
511    assert(user_start < dev->params.vm_user_end);
512 
513    dev->agxdecode = agxdecode_new_context(dev->shader_base);
514 
515    util_sparse_array_init(&dev->bo_map, sizeof(struct agx_bo), 512);
516    pthread_mutex_init(&dev->bo_map_lock, NULL);
517 
518    simple_mtx_init(&dev->bo_cache.lock, mtx_plain);
519    list_inithead(&dev->bo_cache.lru);
520 
521    for (unsigned i = 0; i < ARRAY_SIZE(dev->bo_cache.buckets); ++i)
522       list_inithead(&dev->bo_cache.buckets[i]);
523 
524    // Put the kernel heap at the top of the address space.
525    // Give it 32GB of address space, should be more than enough for any
526    // reasonable use case.
527    uint64_t kernel_size = MAX2(dev->params.vm_kernel_min_size, 32ull << 30);
528    struct drm_asahi_vm_create vm_create = {
529       .kernel_start = dev->params.vm_user_end - kernel_size,
530       .kernel_end = dev->params.vm_user_end,
531    };
532 
533    uint64_t user_size = vm_create.kernel_start - user_start;
534 
535    int ret = asahi_simple_ioctl(dev, DRM_IOCTL_ASAHI_VM_CREATE, &vm_create);
536    if (ret) {
537       fprintf(stderr, "DRM_IOCTL_ASAHI_VM_CREATE failed: %m\n");
538       assert(0);
539       return false;
540    }
541 
542    simple_mtx_init(&dev->vma_lock, mtx_plain);
543    util_vma_heap_init(&dev->main_heap, user_start, user_size);
544    util_vma_heap_init(&dev->usc_heap, dev->shader_base, shader_size);
545 
546    dev->vm_id = vm_create.vm_id;
547 
548    agx_get_global_ids(dev);
549 
550    glsl_type_singleton_init_or_ref();
551    struct blob_reader blob;
552    blob_reader_init(&blob, (void *)libagx_shaders_nir,
553                     sizeof(libagx_shaders_nir));
554    dev->libagx = nir_deserialize(memctx, &agx_nir_options, &blob);
555 
556    dev->helper = agx_build_helper(dev);
557 
558    return true;
559 }
560 
561 void
agx_close_device(struct agx_device * dev)562 agx_close_device(struct agx_device *dev)
563 {
564    ralloc_free((void *)dev->libagx);
565    agx_bo_unreference(dev, dev->helper);
566    agx_bo_cache_evict_all(dev);
567    util_sparse_array_finish(&dev->bo_map);
568    agxdecode_destroy_context(dev->agxdecode);
569 
570    util_vma_heap_finish(&dev->main_heap);
571    util_vma_heap_finish(&dev->usc_heap);
572    glsl_type_singleton_decref();
573 
574    close(dev->fd);
575 }
576 
577 uint32_t
agx_create_command_queue(struct agx_device * dev,uint32_t caps,uint32_t priority)578 agx_create_command_queue(struct agx_device *dev, uint32_t caps,
579                          uint32_t priority)
580 {
581 
582    if (dev->debug & AGX_DBG_1QUEUE) {
583       // Abuse this lock for this, it's debug only anyway
584       simple_mtx_lock(&dev->vma_lock);
585       if (dev->queue_id) {
586          simple_mtx_unlock(&dev->vma_lock);
587          return dev->queue_id;
588       }
589    }
590 
591    struct drm_asahi_queue_create queue_create = {
592       .vm_id = dev->vm_id,
593       .queue_caps = caps,
594       .priority = priority,
595       .flags = 0,
596    };
597 
598    int ret =
599       asahi_simple_ioctl(dev, DRM_IOCTL_ASAHI_QUEUE_CREATE, &queue_create);
600    if (ret) {
601       fprintf(stderr, "DRM_IOCTL_ASAHI_QUEUE_CREATE failed: %m\n");
602       assert(0);
603    }
604 
605    if (dev->debug & AGX_DBG_1QUEUE) {
606       dev->queue_id = queue_create.queue_id;
607       simple_mtx_unlock(&dev->vma_lock);
608    }
609 
610    return queue_create.queue_id;
611 }
612 
613 int
agx_destroy_command_queue(struct agx_device * dev,uint32_t queue_id)614 agx_destroy_command_queue(struct agx_device *dev, uint32_t queue_id)
615 {
616    if (dev->debug & AGX_DBG_1QUEUE)
617       return 0;
618 
619    struct drm_asahi_queue_destroy queue_destroy = {
620       .queue_id = queue_id,
621    };
622 
623    return drmIoctl(dev->fd, DRM_IOCTL_ASAHI_QUEUE_DESTROY, &queue_destroy);
624 }
625 
626 int
agx_import_sync_file(struct agx_device * dev,struct agx_bo * bo,int fd)627 agx_import_sync_file(struct agx_device *dev, struct agx_bo *bo, int fd)
628 {
629    struct dma_buf_import_sync_file import_sync_file_ioctl = {
630       .flags = DMA_BUF_SYNC_WRITE,
631       .fd = fd,
632    };
633 
634    assert(fd >= 0);
635    assert(bo->prime_fd != -1);
636 
637    int ret = drmIoctl(bo->prime_fd, DMA_BUF_IOCTL_IMPORT_SYNC_FILE,
638                       &import_sync_file_ioctl);
639    assert(ret >= 0);
640 
641    return ret;
642 }
643 
644 int
agx_export_sync_file(struct agx_device * dev,struct agx_bo * bo)645 agx_export_sync_file(struct agx_device *dev, struct agx_bo *bo)
646 {
647    struct dma_buf_export_sync_file export_sync_file_ioctl = {
648       .flags = DMA_BUF_SYNC_RW,
649       .fd = -1,
650    };
651 
652    assert(bo->prime_fd != -1);
653 
654    int ret = drmIoctl(bo->prime_fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE,
655                       &export_sync_file_ioctl);
656    assert(ret >= 0);
657    assert(export_sync_file_ioctl.fd >= 0);
658 
659    return ret >= 0 ? export_sync_file_ioctl.fd : ret;
660 }
661 
662 void
agx_debug_fault(struct agx_device * dev,uint64_t addr)663 agx_debug_fault(struct agx_device *dev, uint64_t addr)
664 {
665    pthread_mutex_lock(&dev->bo_map_lock);
666 
667    struct agx_bo *best = NULL;
668 
669    for (uint32_t handle = 0; handle < dev->max_handle; handle++) {
670       struct agx_bo *bo = agx_lookup_bo(dev, handle);
671       if (!bo->va)
672          continue;
673 
674       uint64_t bo_addr = bo->va->addr;
675       if (bo->flags & AGX_BO_LOW_VA)
676          bo_addr += dev->shader_base;
677 
678       if (!bo->size || bo_addr > addr)
679          continue;
680 
681       if (!best || bo_addr > best->va->addr)
682          best = bo;
683    }
684 
685    if (!best) {
686       mesa_logw("Address 0x%" PRIx64 " is unknown\n", addr);
687    } else {
688       uint64_t start = best->va->addr;
689       uint64_t end = best->va->addr + best->size;
690       if (addr > (end + 1024 * 1024 * 1024)) {
691          /* 1GiB max as a sanity check */
692          mesa_logw("Address 0x%" PRIx64 " is unknown\n", addr);
693       } else if (addr > end) {
694          mesa_logw("Address 0x%" PRIx64 " is 0x%" PRIx64
695                    " bytes beyond an object at 0x%" PRIx64 "..0x%" PRIx64
696                    " (%s)\n",
697                    addr, addr - end, start, end - 1, best->label);
698       } else {
699          mesa_logw("Address 0x%" PRIx64 " is 0x%" PRIx64
700                    " bytes into an object at 0x%" PRIx64 "..0x%" PRIx64
701                    " (%s)\n",
702                    addr, addr - start, start, end - 1, best->label);
703       }
704    }
705 
706    pthread_mutex_unlock(&dev->bo_map_lock);
707 }
708 
709 uint64_t
agx_get_gpu_timestamp(struct agx_device * dev)710 agx_get_gpu_timestamp(struct agx_device *dev)
711 {
712 #if DETECT_ARCH_AARCH64
713    uint64_t ret;
714    __asm__ volatile("mrs \t%0, cntvct_el0" : "=r"(ret));
715    return ret;
716 #elif DETECT_ARCH_X86 || DETECT_ARCH_X86_64
717    /* Maps to the above when run under FEX without thunking */
718    uint32_t high, low;
719    __asm__ volatile("rdtsc" : "=a"(low), "=d"(high));
720    return (uint64_t)low | ((uint64_t)high << 32);
721 #else
722 #error "invalid architecture for asahi"
723 #endif
724 }
725 
726 /* (Re)define UUID_SIZE to avoid including vulkan.h (or p_defines.h) here. */
727 #define UUID_SIZE 16
728 
729 void
agx_get_device_uuid(const struct agx_device * dev,void * uuid)730 agx_get_device_uuid(const struct agx_device *dev, void *uuid)
731 {
732    struct mesa_sha1 sha1_ctx;
733    _mesa_sha1_init(&sha1_ctx);
734 
735    /* The device UUID uniquely identifies the given device within the machine.
736     * Since we never have more than one device, this doesn't need to be a real
737     * UUID, so we use SHA1("agx" + gpu_generation + gpu_variant + gpu_revision).
738     */
739    static const char *device_name = "agx";
740    _mesa_sha1_update(&sha1_ctx, device_name, strlen(device_name));
741 
742    _mesa_sha1_update(&sha1_ctx, &dev->params.gpu_generation,
743                      sizeof(dev->params.gpu_generation));
744    _mesa_sha1_update(&sha1_ctx, &dev->params.gpu_variant,
745                      sizeof(dev->params.gpu_variant));
746    _mesa_sha1_update(&sha1_ctx, &dev->params.gpu_revision,
747                      sizeof(dev->params.gpu_revision));
748 
749    uint8_t sha1[SHA1_DIGEST_LENGTH];
750    _mesa_sha1_final(&sha1_ctx, sha1);
751 
752    assert(SHA1_DIGEST_LENGTH >= UUID_SIZE);
753    memcpy(uuid, sha1, UUID_SIZE);
754 }
755 
756 void
agx_get_driver_uuid(void * uuid)757 agx_get_driver_uuid(void *uuid)
758 {
759    const char *driver_id = PACKAGE_VERSION MESA_GIT_SHA1;
760 
761    /* The driver UUID is used for determining sharability of images and memory
762     * between two Vulkan instances in separate processes, but also to
763     * determining memory objects and sharability between Vulkan and OpenGL
764     * driver. People who want to share memory need to also check the device
765     * UUID.
766     */
767    struct mesa_sha1 sha1_ctx;
768    _mesa_sha1_init(&sha1_ctx);
769 
770    _mesa_sha1_update(&sha1_ctx, driver_id, strlen(driver_id));
771 
772    uint8_t sha1[SHA1_DIGEST_LENGTH];
773    _mesa_sha1_final(&sha1_ctx, sha1);
774 
775    assert(SHA1_DIGEST_LENGTH >= UUID_SIZE);
776    memcpy(uuid, sha1, UUID_SIZE);
777 }
778