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