1 /*
2 * Copyright © 2016 Red Hat.
3 * Copyright © 2016 Bas Nieuwenhuizen
4 * SPDX-License-Identifier: MIT
5 *
6 * based in part on anv driver which is:
7 * Copyright © 2015 Intel Corporation
8 */
9
10 #include <fcntl.h>
11
12 #ifdef MAJOR_IN_MKDEV
13 #include <sys/mkdev.h>
14 #endif
15 #ifdef MAJOR_IN_SYSMACROS
16 #include <sys/sysmacros.h>
17 #endif
18
19 #include <sys/mman.h>
20
21 #include "vk_debug_utils.h"
22
23 #include "util/libdrm.h"
24
25 #include "tu_device.h"
26 #include "tu_knl.h"
27 #include "tu_rmv.h"
28
29
30 VkResult
tu_bo_init_new_explicit_iova(struct tu_device * dev,struct vk_object_base * base,struct tu_bo ** out_bo,uint64_t size,uint64_t client_iova,VkMemoryPropertyFlags mem_property,enum tu_bo_alloc_flags flags,const char * name)31 tu_bo_init_new_explicit_iova(struct tu_device *dev,
32 struct vk_object_base *base,
33 struct tu_bo **out_bo,
34 uint64_t size,
35 uint64_t client_iova,
36 VkMemoryPropertyFlags mem_property,
37 enum tu_bo_alloc_flags flags, const char *name)
38 {
39 struct tu_instance *instance = dev->physical_device->instance;
40
41 VkResult result =
42 dev->instance->knl->bo_init(dev, base, out_bo, size, client_iova,
43 mem_property, flags, name);
44 if (result != VK_SUCCESS)
45 return result;
46
47 vk_address_binding_report(&instance->vk, base ? base : &dev->vk.base,
48 (*out_bo)->iova, (*out_bo)->size,
49 VK_DEVICE_ADDRESS_BINDING_TYPE_BIND_EXT);
50
51 return VK_SUCCESS;
52 }
53
54 VkResult
tu_bo_init_dmabuf(struct tu_device * dev,struct tu_bo ** bo,uint64_t size,int fd)55 tu_bo_init_dmabuf(struct tu_device *dev,
56 struct tu_bo **bo,
57 uint64_t size,
58 int fd)
59 {
60 return dev->instance->knl->bo_init_dmabuf(dev, bo, size, fd);
61 }
62
63 int
tu_bo_export_dmabuf(struct tu_device * dev,struct tu_bo * bo)64 tu_bo_export_dmabuf(struct tu_device *dev, struct tu_bo *bo)
65 {
66 return dev->instance->knl->bo_export_dmabuf(dev, bo);
67 }
68
69 void
tu_bo_finish(struct tu_device * dev,struct tu_bo * bo)70 tu_bo_finish(struct tu_device *dev, struct tu_bo *bo)
71 {
72 struct tu_instance *instance = dev->physical_device->instance;
73
74 vk_address_binding_report(&instance->vk, bo->base ? bo->base : &dev->vk.base,
75 bo->iova, bo->size,
76 VK_DEVICE_ADDRESS_BINDING_TYPE_UNBIND_EXT);
77
78 dev->instance->knl->bo_finish(dev, bo);
79 }
80
81 VkResult
tu_bo_map(struct tu_device * dev,struct tu_bo * bo,void * placed_addr)82 tu_bo_map(struct tu_device *dev, struct tu_bo *bo, void *placed_addr)
83 {
84 if (bo->map && (placed_addr == NULL || placed_addr == bo->map))
85 return VK_SUCCESS;
86 else if (bo->map)
87 /* The BO is already mapped, but with a different address. */
88 return vk_errorf(dev, VK_ERROR_MEMORY_MAP_FAILED, "Cannot remap BO to a different address");
89
90 return dev->instance->knl->bo_map(dev, bo, placed_addr);
91 }
92
93 VkResult
tu_bo_unmap(struct tu_device * dev,struct tu_bo * bo,bool reserve)94 tu_bo_unmap(struct tu_device *dev, struct tu_bo *bo, bool reserve)
95 {
96 if (!bo->map || bo->never_unmap)
97 return VK_SUCCESS;
98
99 TU_RMV(bo_unmap, dev, bo);
100
101 if (reserve) {
102 void *map = mmap(bo->map, bo->size, PROT_NONE,
103 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
104 if (map == MAP_FAILED)
105 return vk_errorf(dev, VK_ERROR_MEMORY_MAP_FAILED,
106 "Failed to replace mapping with reserved memory");
107 } else {
108 munmap(bo->map, bo->size);
109 }
110
111 bo->map = NULL;
112
113 return VK_SUCCESS;
114 }
115
116 static inline void
tu_sync_cacheline_to_gpu(void const * p)117 tu_sync_cacheline_to_gpu(void const *p __attribute__((unused)))
118 {
119 #if DETECT_ARCH_AARCH64
120 /* Clean data cache. */
121 __asm volatile("dc cvac, %0" : : "r" (p) : "memory");
122 #elif (DETECT_ARCH_X86 || DETECT_ARCH_X86_64)
123 __builtin_ia32_clflush(p);
124 #elif DETECT_ARCH_ARM
125 /* DCCMVAC - same as DC CVAC on aarch64.
126 * Seems to be illegal to call from userspace.
127 */
128 //__asm volatile("mcr p15, 0, %0, c7, c10, 1" : : "r" (p) : "memory");
129 unreachable("Cache line clean is unsupported on ARMv7");
130 #endif
131 }
132
133 static inline void
tu_sync_cacheline_from_gpu(void const * p)134 tu_sync_cacheline_from_gpu(void const *p __attribute__((unused)))
135 {
136 #if DETECT_ARCH_AARCH64
137 /* Clean and Invalidate data cache, there is no separate Invalidate. */
138 __asm volatile("dc civac, %0" : : "r" (p) : "memory");
139 #elif (DETECT_ARCH_X86 || DETECT_ARCH_X86_64)
140 __builtin_ia32_clflush(p);
141 #elif DETECT_ARCH_ARM
142 /* DCCIMVAC - same as DC CIVAC on aarch64.
143 * Seems to be illegal to call from userspace.
144 */
145 //__asm volatile("mcr p15, 0, %0, c7, c14, 1" : : "r" (p) : "memory");
146 unreachable("Cache line invalidate is unsupported on ARMv7");
147 #endif
148 }
149
150 void
tu_bo_sync_cache(struct tu_device * dev,struct tu_bo * bo,VkDeviceSize offset,VkDeviceSize size,enum tu_mem_sync_op op)151 tu_bo_sync_cache(struct tu_device *dev,
152 struct tu_bo *bo,
153 VkDeviceSize offset,
154 VkDeviceSize size,
155 enum tu_mem_sync_op op)
156 {
157 uintptr_t level1_dcache_size = dev->physical_device->level1_dcache_size;
158 char *start = (char *) bo->map + offset;
159 char *end = start + (size == VK_WHOLE_SIZE ? (bo->size - offset) : size);
160
161 start = (char *) ((uintptr_t) start & ~(level1_dcache_size - 1));
162
163 for (; start < end; start += level1_dcache_size) {
164 if (op == TU_MEM_SYNC_CACHE_TO_GPU) {
165 tu_sync_cacheline_to_gpu(start);
166 } else {
167 tu_sync_cacheline_from_gpu(start);
168 }
169 }
170 }
171
172 uint32_t
tu_get_l1_dcache_size()173 tu_get_l1_dcache_size()
174 {
175 if (!(DETECT_ARCH_AARCH64 || DETECT_ARCH_X86 || DETECT_ARCH_X86_64))
176 return 0;
177
178 #if DETECT_ARCH_AARCH64 && \
179 (!defined(_SC_LEVEL1_DCACHE_LINESIZE) || DETECT_OS_ANDROID)
180 /* Bionic does not implement _SC_LEVEL1_DCACHE_LINESIZE properly: */
181 uint64_t ctr_el0;
182 asm("mrs\t%x0, ctr_el0" : "=r"(ctr_el0));
183 return 4 << ((ctr_el0 >> 16) & 0xf);
184 #elif defined(_SC_LEVEL1_DCACHE_LINESIZE)
185 return sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
186 #else
187 return 0;
188 #endif
189 }
190
tu_bo_allow_dump(struct tu_device * dev,struct tu_bo * bo)191 void tu_bo_allow_dump(struct tu_device *dev, struct tu_bo *bo)
192 {
193 dev->instance->knl->bo_allow_dump(dev, bo);
194 }
195
196 void
tu_bo_set_metadata(struct tu_device * dev,struct tu_bo * bo,void * metadata,uint32_t metadata_size)197 tu_bo_set_metadata(struct tu_device *dev, struct tu_bo *bo,
198 void *metadata, uint32_t metadata_size)
199 {
200 if (!dev->instance->knl->bo_set_metadata)
201 return;
202 dev->instance->knl->bo_set_metadata(dev, bo, metadata, metadata_size);
203 }
204
205 int
tu_bo_get_metadata(struct tu_device * dev,struct tu_bo * bo,void * metadata,uint32_t metadata_size)206 tu_bo_get_metadata(struct tu_device *dev, struct tu_bo *bo,
207 void *metadata, uint32_t metadata_size)
208 {
209 if (!dev->instance->knl->bo_get_metadata)
210 return -ENOSYS;
211 return dev->instance->knl->bo_get_metadata(dev, bo, metadata, metadata_size);
212 }
213
214 VkResult
tu_drm_device_init(struct tu_device * dev)215 tu_drm_device_init(struct tu_device *dev)
216 {
217 return dev->instance->knl->device_init(dev);
218 }
219
220 void
tu_drm_device_finish(struct tu_device * dev)221 tu_drm_device_finish(struct tu_device *dev)
222 {
223 dev->instance->knl->device_finish(dev);
224 }
225
226 int
tu_device_get_gpu_timestamp(struct tu_device * dev,uint64_t * ts)227 tu_device_get_gpu_timestamp(struct tu_device *dev,
228 uint64_t *ts)
229 {
230 return dev->instance->knl->device_get_gpu_timestamp(dev, ts);
231 }
232
233 int
tu_device_get_suspend_count(struct tu_device * dev,uint64_t * suspend_count)234 tu_device_get_suspend_count(struct tu_device *dev,
235 uint64_t *suspend_count)
236 {
237 return dev->instance->knl->device_get_suspend_count(dev, suspend_count);
238 }
239
240 VkResult
tu_device_wait_u_trace(struct tu_device * dev,struct tu_u_trace_syncobj * syncobj)241 tu_device_wait_u_trace(struct tu_device *dev, struct tu_u_trace_syncobj *syncobj)
242 {
243 return dev->instance->knl->device_wait_u_trace(dev, syncobj);
244 }
245
246 VkResult
tu_device_check_status(struct vk_device * vk_device)247 tu_device_check_status(struct vk_device *vk_device)
248 {
249 struct tu_device *dev = container_of(vk_device, struct tu_device, vk);
250 return dev->instance->knl->device_check_status(dev);
251 }
252
253 int
tu_drm_submitqueue_new(struct tu_device * dev,int priority,uint32_t * queue_id)254 tu_drm_submitqueue_new(struct tu_device *dev,
255 int priority,
256 uint32_t *queue_id)
257 {
258 return dev->instance->knl->submitqueue_new(dev, priority, queue_id);
259 }
260
261 void
tu_drm_submitqueue_close(struct tu_device * dev,uint32_t queue_id)262 tu_drm_submitqueue_close(struct tu_device *dev, uint32_t queue_id)
263 {
264 dev->instance->knl->submitqueue_close(dev, queue_id);
265 }
266
267 VkResult
tu_queue_submit(struct vk_queue * vk_queue,struct vk_queue_submit * submit)268 tu_queue_submit(struct vk_queue *vk_queue, struct vk_queue_submit *submit)
269 {
270 struct tu_queue *queue = container_of(vk_queue, struct tu_queue, vk);
271 return queue->device->instance->knl->queue_submit(queue, submit);
272 }
273
274 /**
275 * Enumeration entrypoint specific to non-drm devices (ie. kgsl)
276 */
277 VkResult
tu_enumerate_devices(struct vk_instance * vk_instance)278 tu_enumerate_devices(struct vk_instance *vk_instance)
279 {
280 #ifdef TU_HAS_KGSL
281 struct tu_instance *instance =
282 container_of(vk_instance, struct tu_instance, vk);
283
284 static const char path[] = "/dev/kgsl-3d0";
285 int fd;
286
287 fd = open(path, O_RDWR | O_CLOEXEC);
288 if (fd < 0) {
289 if (errno == ENOENT)
290 return VK_ERROR_INCOMPATIBLE_DRIVER;
291
292 return vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
293 "failed to open device %s", path);
294 }
295
296 VkResult result = tu_knl_kgsl_load(instance, fd);
297 if (result != VK_SUCCESS) {
298 close(fd);
299 return result;
300 }
301
302 if (TU_DEBUG(STARTUP))
303 mesa_logi("Found compatible device '%s'.", path);
304
305 return result;
306 #else
307 return VK_ERROR_INCOMPATIBLE_DRIVER;
308 #endif
309 }
310
311 /**
312 * Enumeration entrypoint for drm devices
313 */
314 VkResult
tu_physical_device_try_create(struct vk_instance * vk_instance,struct _drmDevice * drm_device,struct vk_physical_device ** out)315 tu_physical_device_try_create(struct vk_instance *vk_instance,
316 struct _drmDevice *drm_device,
317 struct vk_physical_device **out)
318 {
319 struct tu_instance *instance =
320 container_of(vk_instance, struct tu_instance, vk);
321
322 /* Note that "msm" is a platform device, but "virtio_gpu" is a pci
323 * device. In general we shouldn't care about the bus type.
324 */
325 if (!(drm_device->available_nodes & (1 << DRM_NODE_RENDER)))
326 return VK_ERROR_INCOMPATIBLE_DRIVER;
327
328 const char *primary_path = drm_device->nodes[DRM_NODE_PRIMARY];
329 const char *path = drm_device->nodes[DRM_NODE_RENDER];
330 drmVersionPtr version;
331 int fd;
332 int master_fd = -1;
333
334 fd = open(path, O_RDWR | O_CLOEXEC);
335 if (fd < 0) {
336 return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
337 "failed to open device %s", path);
338 }
339
340 version = drmGetVersion(fd);
341 if (!version) {
342 close(fd);
343 return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
344 "failed to query kernel driver version for device %s",
345 path);
346 }
347
348 struct tu_physical_device *device = NULL;
349
350 VkResult result = VK_ERROR_INCOMPATIBLE_DRIVER;
351 if (strcmp(version->name, "msm") == 0) {
352 #ifdef TU_HAS_MSM
353 result = tu_knl_drm_msm_load(instance, fd, version, &device);
354 #endif
355 } else if (strcmp(version->name, "virtio_gpu") == 0) {
356 #ifdef TU_HAS_VIRTIO
357 result = tu_knl_drm_virtio_load(instance, fd, version, &device);
358 #endif
359 } else if (TU_DEBUG(STARTUP)) {
360 result = vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
361 "device %s (%s) is not compatible with turnip",
362 path, version->name);
363 }
364
365 if (result != VK_SUCCESS)
366 goto out;
367
368 assert(device);
369
370 if (instance->vk.enabled_extensions.KHR_display) {
371 master_fd = open(primary_path, O_RDWR | O_CLOEXEC);
372 }
373
374 device->master_fd = master_fd;
375 device->kgsl_dma_fd = -1;
376
377 assert(strlen(path) < ARRAY_SIZE(device->fd_path));
378 snprintf(device->fd_path, ARRAY_SIZE(device->fd_path), "%s", path);
379
380 struct stat st;
381
382 if (stat(primary_path, &st) == 0) {
383 device->has_master = true;
384 device->master_major = major(st.st_rdev);
385 device->master_minor = minor(st.st_rdev);
386 } else {
387 device->has_master = false;
388 device->master_major = 0;
389 device->master_minor = 0;
390 }
391
392 if (stat(path, &st) == 0) {
393 device->has_local = true;
394 device->local_major = major(st.st_rdev);
395 device->local_minor = minor(st.st_rdev);
396 } else {
397 result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
398 "failed to stat DRM render node %s", path);
399 goto out;
400 }
401
402 result = tu_physical_device_init(device, instance);
403 if (result != VK_SUCCESS)
404 goto out;
405
406 if (TU_DEBUG(STARTUP))
407 mesa_logi("Found compatible device '%s' (%s).", path, version->name);
408
409 *out = &device->vk;
410
411 out:
412 if (result != VK_SUCCESS) {
413 if (master_fd != -1)
414 close(master_fd);
415 close(fd);
416 vk_free(&instance->vk.alloc, device);
417 }
418
419 drmFreeVersion(version);
420
421 return result;
422 }
423