OSDN Git Service

amdgpu: add amdgpu_bo_inc_ref() function.
[android-x86/external-libdrm.git] / amdgpu / amdgpu_bo.c
1 /*
2  * Copyright © 2014 Advanced Micro Devices, Inc.
3  * All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21  * OTHER DEALINGS IN THE SOFTWARE.
22  *
23  */
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <stdint.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <sys/ioctl.h>
33 #include <sys/mman.h>
34 #include <sys/time.h>
35
36 #include "libdrm_macros.h"
37 #include "xf86drm.h"
38 #include "amdgpu_drm.h"
39 #include "amdgpu_internal.h"
40 #include "util_math.h"
41
42 static void amdgpu_close_kms_handle(amdgpu_device_handle dev,
43                                      uint32_t handle)
44 {
45         struct drm_gem_close args = {};
46
47         args.handle = handle;
48         drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &args);
49 }
50
51 static int amdgpu_bo_create(amdgpu_device_handle dev,
52                             uint64_t size,
53                             uint32_t handle,
54                             amdgpu_bo_handle *buf_handle)
55 {
56         struct amdgpu_bo *bo;
57
58         bo = calloc(1, sizeof(struct amdgpu_bo));
59         if (!bo)
60                 return -ENOMEM;
61
62         atomic_set(&bo->refcount, 1);
63         bo->dev = dev;
64         bo->alloc_size = size;
65         bo->handle = handle;
66         pthread_mutex_init(&bo->cpu_access_mutex, NULL);
67
68         *buf_handle = bo;
69         return 0;
70 }
71
72 int amdgpu_bo_alloc(amdgpu_device_handle dev,
73                     struct amdgpu_bo_alloc_request *alloc_buffer,
74                     amdgpu_bo_handle *buf_handle)
75 {
76         union drm_amdgpu_gem_create args;
77         unsigned heap = alloc_buffer->preferred_heap;
78         int r = 0;
79
80         /* It's an error if the heap is not specified */
81         if (!(heap & (AMDGPU_GEM_DOMAIN_GTT | AMDGPU_GEM_DOMAIN_VRAM)))
82                 return -EINVAL;
83
84         memset(&args, 0, sizeof(args));
85         args.in.bo_size = alloc_buffer->alloc_size;
86         args.in.alignment = alloc_buffer->phys_alignment;
87
88         /* Set the placement. */
89         args.in.domains = heap;
90         args.in.domain_flags = alloc_buffer->flags;
91
92         /* Allocate the buffer with the preferred heap. */
93         r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_CREATE,
94                                 &args, sizeof(args));
95         if (r)
96                 goto out;
97
98         r = amdgpu_bo_create(dev, alloc_buffer->alloc_size, args.out.handle,
99                              buf_handle);
100         if (r) {
101                 amdgpu_close_kms_handle(dev, args.out.handle);
102                 goto out;
103         }
104
105         pthread_mutex_lock(&dev->bo_table_mutex);
106         r = handle_table_insert(&dev->bo_handles, (*buf_handle)->handle,
107                                 *buf_handle);
108         pthread_mutex_unlock(&dev->bo_table_mutex);
109         if (r)
110                 amdgpu_bo_free(*buf_handle);
111 out:
112         return r;
113 }
114
115 int amdgpu_bo_set_metadata(amdgpu_bo_handle bo,
116                            struct amdgpu_bo_metadata *info)
117 {
118         struct drm_amdgpu_gem_metadata args = {};
119
120         args.handle = bo->handle;
121         args.op = AMDGPU_GEM_METADATA_OP_SET_METADATA;
122         args.data.flags = info->flags;
123         args.data.tiling_info = info->tiling_info;
124
125         if (info->size_metadata > sizeof(args.data.data))
126                 return -EINVAL;
127
128         if (info->size_metadata) {
129                 args.data.data_size_bytes = info->size_metadata;
130                 memcpy(args.data.data, info->umd_metadata, info->size_metadata);
131         }
132
133         return drmCommandWriteRead(bo->dev->fd,
134                                    DRM_AMDGPU_GEM_METADATA,
135                                    &args, sizeof(args));
136 }
137
138 int amdgpu_bo_query_info(amdgpu_bo_handle bo,
139                          struct amdgpu_bo_info *info)
140 {
141         struct drm_amdgpu_gem_metadata metadata = {};
142         struct drm_amdgpu_gem_create_in bo_info = {};
143         struct drm_amdgpu_gem_op gem_op = {};
144         int r;
145
146         /* Validate the BO passed in */
147         if (!bo->handle)
148                 return -EINVAL;
149
150         /* Query metadata. */
151         metadata.handle = bo->handle;
152         metadata.op = AMDGPU_GEM_METADATA_OP_GET_METADATA;
153
154         r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_METADATA,
155                                 &metadata, sizeof(metadata));
156         if (r)
157                 return r;
158
159         if (metadata.data.data_size_bytes >
160             sizeof(info->metadata.umd_metadata))
161                 return -EINVAL;
162
163         /* Query buffer info. */
164         gem_op.handle = bo->handle;
165         gem_op.op = AMDGPU_GEM_OP_GET_GEM_CREATE_INFO;
166         gem_op.value = (uintptr_t)&bo_info;
167
168         r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_OP,
169                                 &gem_op, sizeof(gem_op));
170         if (r)
171                 return r;
172
173         memset(info, 0, sizeof(*info));
174         info->alloc_size = bo_info.bo_size;
175         info->phys_alignment = bo_info.alignment;
176         info->preferred_heap = bo_info.domains;
177         info->alloc_flags = bo_info.domain_flags;
178         info->metadata.flags = metadata.data.flags;
179         info->metadata.tiling_info = metadata.data.tiling_info;
180
181         info->metadata.size_metadata = metadata.data.data_size_bytes;
182         if (metadata.data.data_size_bytes > 0)
183                 memcpy(info->metadata.umd_metadata, metadata.data.data,
184                        metadata.data.data_size_bytes);
185
186         return 0;
187 }
188
189 static int amdgpu_bo_export_flink(amdgpu_bo_handle bo)
190 {
191         struct drm_gem_flink flink;
192         int fd, dma_fd;
193         uint32_t handle;
194         int r;
195
196         fd = bo->dev->fd;
197         handle = bo->handle;
198         if (bo->flink_name)
199                 return 0;
200
201
202         if (bo->dev->flink_fd != bo->dev->fd) {
203                 r = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
204                                        &dma_fd);
205                 if (!r) {
206                         r = drmPrimeFDToHandle(bo->dev->flink_fd, dma_fd, &handle);
207                         close(dma_fd);
208                 }
209                 if (r)
210                         return r;
211                 fd = bo->dev->flink_fd;
212         }
213         memset(&flink, 0, sizeof(flink));
214         flink.handle = handle;
215
216         r = drmIoctl(fd, DRM_IOCTL_GEM_FLINK, &flink);
217         if (r)
218                 return r;
219
220         bo->flink_name = flink.name;
221
222         if (bo->dev->flink_fd != bo->dev->fd) {
223                 struct drm_gem_close args = {};
224                 args.handle = handle;
225                 drmIoctl(bo->dev->flink_fd, DRM_IOCTL_GEM_CLOSE, &args);
226         }
227
228         pthread_mutex_lock(&bo->dev->bo_table_mutex);
229         r = handle_table_insert(&bo->dev->bo_flink_names, bo->flink_name, bo);
230         pthread_mutex_unlock(&bo->dev->bo_table_mutex);
231
232         return r;
233 }
234
235 int amdgpu_bo_export(amdgpu_bo_handle bo,
236                      enum amdgpu_bo_handle_type type,
237                      uint32_t *shared_handle)
238 {
239         int r;
240
241         switch (type) {
242         case amdgpu_bo_handle_type_gem_flink_name:
243                 r = amdgpu_bo_export_flink(bo);
244                 if (r)
245                         return r;
246
247                 *shared_handle = bo->flink_name;
248                 return 0;
249
250         case amdgpu_bo_handle_type_kms:
251         case amdgpu_bo_handle_type_kms_noimport:
252                 *shared_handle = bo->handle;
253                 return 0;
254
255         case amdgpu_bo_handle_type_dma_buf_fd:
256                 return drmPrimeHandleToFD(bo->dev->fd, bo->handle,
257                                           DRM_CLOEXEC | DRM_RDWR,
258                                           (int*)shared_handle);
259         }
260         return -EINVAL;
261 }
262
263 int amdgpu_bo_import(amdgpu_device_handle dev,
264                      enum amdgpu_bo_handle_type type,
265                      uint32_t shared_handle,
266                      struct amdgpu_bo_import_result *output)
267 {
268         struct drm_gem_open open_arg = {};
269         struct drm_gem_close close_arg = {};
270         struct amdgpu_bo *bo = NULL;
271         uint32_t handle = 0, flink_name = 0;
272         uint64_t alloc_size = 0;
273         int r = 0;
274         int dma_fd;
275         uint64_t dma_buf_size = 0;
276
277         /* We must maintain a list of pairs <handle, bo>, so that we always
278          * return the same amdgpu_bo instance for the same handle. */
279         pthread_mutex_lock(&dev->bo_table_mutex);
280
281         /* Convert a DMA buf handle to a KMS handle now. */
282         if (type == amdgpu_bo_handle_type_dma_buf_fd) {
283                 off_t size;
284
285                 /* Get a KMS handle. */
286                 r = drmPrimeFDToHandle(dev->fd, shared_handle, &handle);
287                 if (r)
288                         goto unlock;
289
290                 /* Query the buffer size. */
291                 size = lseek(shared_handle, 0, SEEK_END);
292                 if (size == (off_t)-1) {
293                         r = -errno;
294                         goto free_bo_handle;
295                 }
296                 lseek(shared_handle, 0, SEEK_SET);
297
298                 dma_buf_size = size;
299                 shared_handle = handle;
300         }
301
302         /* If we have already created a buffer with this handle, find it. */
303         switch (type) {
304         case amdgpu_bo_handle_type_gem_flink_name:
305                 bo = handle_table_lookup(&dev->bo_flink_names, shared_handle);
306                 break;
307
308         case amdgpu_bo_handle_type_dma_buf_fd:
309                 bo = handle_table_lookup(&dev->bo_handles, shared_handle);
310                 break;
311
312         case amdgpu_bo_handle_type_kms:
313         case amdgpu_bo_handle_type_kms_noimport:
314                 /* Importing a KMS handle in not allowed. */
315                 r = -EPERM;
316                 goto unlock;
317
318         default:
319                 r = -EINVAL;
320                 goto unlock;
321         }
322
323         if (bo) {
324                 /* The buffer already exists, just bump the refcount. */
325                 atomic_inc(&bo->refcount);
326                 pthread_mutex_unlock(&dev->bo_table_mutex);
327
328                 output->buf_handle = bo;
329                 output->alloc_size = bo->alloc_size;
330                 return 0;
331         }
332
333         /* Open the handle. */
334         switch (type) {
335         case amdgpu_bo_handle_type_gem_flink_name:
336                 open_arg.name = shared_handle;
337                 r = drmIoctl(dev->flink_fd, DRM_IOCTL_GEM_OPEN, &open_arg);
338                 if (r)
339                         goto unlock;
340
341                 flink_name = shared_handle;
342                 handle = open_arg.handle;
343                 alloc_size = open_arg.size;
344                 if (dev->flink_fd != dev->fd) {
345                         r = drmPrimeHandleToFD(dev->flink_fd, handle,
346                                                DRM_CLOEXEC, &dma_fd);
347                         if (r)
348                                 goto free_bo_handle;
349                         r = drmPrimeFDToHandle(dev->fd, dma_fd, &handle);
350                         close(dma_fd);
351                         if (r)
352                                 goto free_bo_handle;
353                         close_arg.handle = open_arg.handle;
354                         r = drmIoctl(dev->flink_fd, DRM_IOCTL_GEM_CLOSE,
355                                      &close_arg);
356                         if (r)
357                                 goto free_bo_handle;
358                 }
359                 break;
360
361         case amdgpu_bo_handle_type_dma_buf_fd:
362                 handle = shared_handle;
363                 alloc_size = dma_buf_size;
364                 break;
365
366         case amdgpu_bo_handle_type_kms:
367         case amdgpu_bo_handle_type_kms_noimport:
368                 assert(0); /* unreachable */
369         }
370
371         /* Initialize it. */
372         r = amdgpu_bo_create(dev, alloc_size, handle, &bo);
373         if (r)
374                 goto free_bo_handle;
375
376         r = handle_table_insert(&dev->bo_handles, bo->handle, bo);
377         if (r)
378                 goto free_bo_handle;
379         if (flink_name) {
380                 bo->flink_name = flink_name;
381                 r = handle_table_insert(&dev->bo_flink_names, flink_name,
382                                         bo);
383                 if (r)
384                         goto remove_handle;
385
386         }
387
388         output->buf_handle = bo;
389         output->alloc_size = bo->alloc_size;
390         pthread_mutex_unlock(&dev->bo_table_mutex);
391         return 0;
392
393 remove_handle:
394         handle_table_remove(&dev->bo_handles, bo->handle);
395 free_bo_handle:
396         if (flink_name && !close_arg.handle && open_arg.handle) {
397                 close_arg.handle = open_arg.handle;
398                 drmIoctl(dev->flink_fd, DRM_IOCTL_GEM_CLOSE, &close_arg);
399         }
400         if (bo)
401                 amdgpu_bo_free(bo);
402         else
403                 amdgpu_close_kms_handle(dev, handle);
404 unlock:
405         pthread_mutex_unlock(&dev->bo_table_mutex);
406         return r;
407 }
408
409 int amdgpu_bo_free(amdgpu_bo_handle buf_handle)
410 {
411         struct amdgpu_device *dev;
412         struct amdgpu_bo *bo = buf_handle;
413
414         assert(bo != NULL);
415         dev = bo->dev;
416         pthread_mutex_lock(&dev->bo_table_mutex);
417
418         if (update_references(&bo->refcount, NULL)) {
419                 /* Remove the buffer from the hash tables. */
420                 handle_table_remove(&dev->bo_handles, bo->handle);
421
422                 if (bo->flink_name)
423                         handle_table_remove(&dev->bo_flink_names,
424                                             bo->flink_name);
425
426                 /* Release CPU access. */
427                 if (bo->cpu_map_count > 0) {
428                         bo->cpu_map_count = 1;
429                         amdgpu_bo_cpu_unmap(bo);
430                 }
431
432                 amdgpu_close_kms_handle(dev, bo->handle);
433                 pthread_mutex_destroy(&bo->cpu_access_mutex);
434                 free(bo);
435         }
436
437         pthread_mutex_unlock(&dev->bo_table_mutex);
438         return 0;
439 }
440
441 int amdgpu_bo_inc_ref(amdgpu_bo_handle bo)
442 {
443         atomic_inc(&bo->refcount);
444         return 0;
445 }
446
447 int amdgpu_bo_cpu_map(amdgpu_bo_handle bo, void **cpu)
448 {
449         union drm_amdgpu_gem_mmap args;
450         void *ptr;
451         int r;
452
453         pthread_mutex_lock(&bo->cpu_access_mutex);
454
455         if (bo->cpu_ptr) {
456                 /* already mapped */
457                 assert(bo->cpu_map_count > 0);
458                 bo->cpu_map_count++;
459                 *cpu = bo->cpu_ptr;
460                 pthread_mutex_unlock(&bo->cpu_access_mutex);
461                 return 0;
462         }
463
464         assert(bo->cpu_map_count == 0);
465
466         memset(&args, 0, sizeof(args));
467
468         /* Query the buffer address (args.addr_ptr).
469          * The kernel driver ignores the offset and size parameters. */
470         args.in.handle = bo->handle;
471
472         r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_MMAP, &args,
473                                 sizeof(args));
474         if (r) {
475                 pthread_mutex_unlock(&bo->cpu_access_mutex);
476                 return r;
477         }
478
479         /* Map the buffer. */
480         ptr = drm_mmap(NULL, bo->alloc_size, PROT_READ | PROT_WRITE, MAP_SHARED,
481                        bo->dev->fd, args.out.addr_ptr);
482         if (ptr == MAP_FAILED) {
483                 pthread_mutex_unlock(&bo->cpu_access_mutex);
484                 return -errno;
485         }
486
487         bo->cpu_ptr = ptr;
488         bo->cpu_map_count = 1;
489         pthread_mutex_unlock(&bo->cpu_access_mutex);
490
491         *cpu = ptr;
492         return 0;
493 }
494
495 int amdgpu_bo_cpu_unmap(amdgpu_bo_handle bo)
496 {
497         int r;
498
499         pthread_mutex_lock(&bo->cpu_access_mutex);
500         assert(bo->cpu_map_count >= 0);
501
502         if (bo->cpu_map_count == 0) {
503                 /* not mapped */
504                 pthread_mutex_unlock(&bo->cpu_access_mutex);
505                 return -EINVAL;
506         }
507
508         bo->cpu_map_count--;
509         if (bo->cpu_map_count > 0) {
510                 /* mapped multiple times */
511                 pthread_mutex_unlock(&bo->cpu_access_mutex);
512                 return 0;
513         }
514
515         r = drm_munmap(bo->cpu_ptr, bo->alloc_size) == 0 ? 0 : -errno;
516         bo->cpu_ptr = NULL;
517         pthread_mutex_unlock(&bo->cpu_access_mutex);
518         return r;
519 }
520
521 int amdgpu_query_buffer_size_alignment(amdgpu_device_handle dev,
522                                 struct amdgpu_buffer_size_alignments *info)
523 {
524         info->size_local = dev->dev_info.pte_fragment_size;
525         info->size_remote = dev->dev_info.gart_page_size;
526         return 0;
527 }
528
529 int amdgpu_bo_wait_for_idle(amdgpu_bo_handle bo,
530                             uint64_t timeout_ns,
531                             bool *busy)
532 {
533         union drm_amdgpu_gem_wait_idle args;
534         int r;
535
536         memset(&args, 0, sizeof(args));
537         args.in.handle = bo->handle;
538         args.in.timeout = amdgpu_cs_calculate_timeout(timeout_ns);
539
540         r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_WAIT_IDLE,
541                                 &args, sizeof(args));
542
543         if (r == 0) {
544                 *busy = args.out.status;
545                 return 0;
546         } else {
547                 fprintf(stderr, "amdgpu: GEM_WAIT_IDLE failed with %i\n", r);
548                 return r;
549         }
550 }
551
552 int amdgpu_find_bo_by_cpu_mapping(amdgpu_device_handle dev,
553                                   void *cpu,
554                                   uint64_t size,
555                                   amdgpu_bo_handle *buf_handle,
556                                   uint64_t *offset_in_bo)
557 {
558         struct amdgpu_bo *bo;
559         uint32_t i;
560         int r = 0;
561
562         if (cpu == NULL || size == 0)
563                 return -EINVAL;
564
565         /*
566          * Workaround for a buggy application which tries to import previously
567          * exposed CPU pointers. If we find a real world use case we should
568          * improve that by asking the kernel for the right handle.
569          */
570         pthread_mutex_lock(&dev->bo_table_mutex);
571         for (i = 0; i < dev->bo_handles.max_key; i++) {
572                 bo = handle_table_lookup(&dev->bo_handles, i);
573                 if (!bo || !bo->cpu_ptr || size > bo->alloc_size)
574                         continue;
575                 if (cpu >= bo->cpu_ptr &&
576                     cpu < (void*)((uintptr_t)bo->cpu_ptr + bo->alloc_size))
577                         break;
578         }
579
580         if (i < dev->bo_handles.max_key) {
581                 atomic_inc(&bo->refcount);
582                 *buf_handle = bo;
583                 *offset_in_bo = (uintptr_t)cpu - (uintptr_t)bo->cpu_ptr;
584         } else {
585                 *buf_handle = NULL;
586                 *offset_in_bo = 0;
587                 r = -ENXIO;
588         }
589         pthread_mutex_unlock(&dev->bo_table_mutex);
590
591         return r;
592 }
593
594 int amdgpu_create_bo_from_user_mem(amdgpu_device_handle dev,
595                                     void *cpu,
596                                     uint64_t size,
597                                     amdgpu_bo_handle *buf_handle)
598 {
599         int r;
600         struct drm_amdgpu_gem_userptr args;
601
602         args.addr = (uintptr_t)cpu;
603         args.flags = AMDGPU_GEM_USERPTR_ANONONLY | AMDGPU_GEM_USERPTR_REGISTER |
604                 AMDGPU_GEM_USERPTR_VALIDATE;
605         args.size = size;
606         r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_USERPTR,
607                                 &args, sizeof(args));
608         if (r)
609                 goto out;
610
611         r = amdgpu_bo_create(dev, size, args.handle, buf_handle);
612         if (r) {
613                 amdgpu_close_kms_handle(dev, args.handle);
614                 goto out;
615         }
616
617         pthread_mutex_lock(&dev->bo_table_mutex);
618         r = handle_table_insert(&dev->bo_handles, (*buf_handle)->handle,
619                                 *buf_handle);
620         pthread_mutex_unlock(&dev->bo_table_mutex);
621         if (r)
622                 amdgpu_bo_free(*buf_handle);
623 out:
624         return r;
625 }
626
627 int amdgpu_bo_list_create(amdgpu_device_handle dev,
628                           uint32_t number_of_resources,
629                           amdgpu_bo_handle *resources,
630                           uint8_t *resource_prios,
631                           amdgpu_bo_list_handle *result)
632 {
633         struct drm_amdgpu_bo_list_entry *list;
634         union drm_amdgpu_bo_list args;
635         unsigned i;
636         int r;
637
638         if (!number_of_resources)
639                 return -EINVAL;
640
641         /* overflow check for multiplication */
642         if (number_of_resources > UINT32_MAX / sizeof(struct drm_amdgpu_bo_list_entry))
643                 return -EINVAL;
644
645         list = malloc(number_of_resources * sizeof(struct drm_amdgpu_bo_list_entry));
646         if (!list)
647                 return -ENOMEM;
648
649         *result = malloc(sizeof(struct amdgpu_bo_list));
650         if (!*result) {
651                 free(list);
652                 return -ENOMEM;
653         }
654
655         memset(&args, 0, sizeof(args));
656         args.in.operation = AMDGPU_BO_LIST_OP_CREATE;
657         args.in.bo_number = number_of_resources;
658         args.in.bo_info_size = sizeof(struct drm_amdgpu_bo_list_entry);
659         args.in.bo_info_ptr = (uint64_t)(uintptr_t)list;
660
661         for (i = 0; i < number_of_resources; i++) {
662                 list[i].bo_handle = resources[i]->handle;
663                 if (resource_prios)
664                         list[i].bo_priority = resource_prios[i];
665                 else
666                         list[i].bo_priority = 0;
667         }
668
669         r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_BO_LIST,
670                                 &args, sizeof(args));
671         free(list);
672         if (r) {
673                 free(*result);
674                 return r;
675         }
676
677         (*result)->dev = dev;
678         (*result)->handle = args.out.list_handle;
679         return 0;
680 }
681
682 int amdgpu_bo_list_destroy(amdgpu_bo_list_handle list)
683 {
684         union drm_amdgpu_bo_list args;
685         int r;
686
687         memset(&args, 0, sizeof(args));
688         args.in.operation = AMDGPU_BO_LIST_OP_DESTROY;
689         args.in.list_handle = list->handle;
690
691         r = drmCommandWriteRead(list->dev->fd, DRM_AMDGPU_BO_LIST,
692                                 &args, sizeof(args));
693
694         if (!r)
695                 free(list);
696
697         return r;
698 }
699
700 int amdgpu_bo_list_update(amdgpu_bo_list_handle handle,
701                           uint32_t number_of_resources,
702                           amdgpu_bo_handle *resources,
703                           uint8_t *resource_prios)
704 {
705         struct drm_amdgpu_bo_list_entry *list;
706         union drm_amdgpu_bo_list args;
707         unsigned i;
708         int r;
709
710         if (!number_of_resources)
711                 return -EINVAL;
712
713         /* overflow check for multiplication */
714         if (number_of_resources > UINT32_MAX / sizeof(struct drm_amdgpu_bo_list_entry))
715                 return -EINVAL;
716
717         list = malloc(number_of_resources * sizeof(struct drm_amdgpu_bo_list_entry));
718         if (!list)
719                 return -ENOMEM;
720
721         args.in.operation = AMDGPU_BO_LIST_OP_UPDATE;
722         args.in.list_handle = handle->handle;
723         args.in.bo_number = number_of_resources;
724         args.in.bo_info_size = sizeof(struct drm_amdgpu_bo_list_entry);
725         args.in.bo_info_ptr = (uintptr_t)list;
726
727         for (i = 0; i < number_of_resources; i++) {
728                 list[i].bo_handle = resources[i]->handle;
729                 if (resource_prios)
730                         list[i].bo_priority = resource_prios[i];
731                 else
732                         list[i].bo_priority = 0;
733         }
734
735         r = drmCommandWriteRead(handle->dev->fd, DRM_AMDGPU_BO_LIST,
736                                 &args, sizeof(args));
737         free(list);
738         return r;
739 }
740
741 int amdgpu_bo_va_op(amdgpu_bo_handle bo,
742                      uint64_t offset,
743                      uint64_t size,
744                      uint64_t addr,
745                      uint64_t flags,
746                      uint32_t ops)
747 {
748         amdgpu_device_handle dev = bo->dev;
749
750         size = ALIGN(size, getpagesize());
751
752         return amdgpu_bo_va_op_raw(dev, bo, offset, size, addr,
753                                    AMDGPU_VM_PAGE_READABLE |
754                                    AMDGPU_VM_PAGE_WRITEABLE |
755                                    AMDGPU_VM_PAGE_EXECUTABLE, ops);
756 }
757
758 int amdgpu_bo_va_op_raw(amdgpu_device_handle dev,
759                         amdgpu_bo_handle bo,
760                         uint64_t offset,
761                         uint64_t size,
762                         uint64_t addr,
763                         uint64_t flags,
764                         uint32_t ops)
765 {
766         struct drm_amdgpu_gem_va va;
767         int r;
768
769         if (ops != AMDGPU_VA_OP_MAP && ops != AMDGPU_VA_OP_UNMAP &&
770             ops != AMDGPU_VA_OP_REPLACE && ops != AMDGPU_VA_OP_CLEAR)
771                 return -EINVAL;
772
773         memset(&va, 0, sizeof(va));
774         va.handle = bo ? bo->handle : 0;
775         va.operation = ops;
776         va.flags = flags;
777         va.va_address = addr;
778         va.offset_in_bo = offset;
779         va.map_size = size;
780
781         r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_VA, &va, sizeof(va));
782
783         return r;
784 }