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