mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
486 lines
9.6 KiB
486 lines
9.6 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
|
|
#include <dirent.h> |
|
#include <errno.h> |
|
#include <fcntl.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <stdint.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
#include <sys/ioctl.h> |
|
#include <sys/mman.h> |
|
#include <sys/types.h> |
|
|
|
#include <linux/dma-buf.h> |
|
#include <drm/drm.h> |
|
|
|
#include "../../../../include/uapi/linux/dma-heap.h" |
|
|
|
#define DEVPATH "/dev/dma_heap" |
|
|
|
static int check_vgem(int fd) |
|
{ |
|
drm_version_t version = { 0 }; |
|
char name[5]; |
|
int ret; |
|
|
|
version.name_len = 4; |
|
version.name = name; |
|
|
|
ret = ioctl(fd, DRM_IOCTL_VERSION, &version); |
|
if (ret) |
|
return 0; |
|
|
|
return !strcmp(name, "vgem"); |
|
} |
|
|
|
static int open_vgem(void) |
|
{ |
|
int i, fd; |
|
const char *drmstr = "/dev/dri/card"; |
|
|
|
fd = -1; |
|
for (i = 0; i < 16; i++) { |
|
char name[80]; |
|
|
|
snprintf(name, 80, "%s%u", drmstr, i); |
|
|
|
fd = open(name, O_RDWR); |
|
if (fd < 0) |
|
continue; |
|
|
|
if (!check_vgem(fd)) { |
|
close(fd); |
|
fd = -1; |
|
continue; |
|
} else { |
|
break; |
|
} |
|
} |
|
return fd; |
|
} |
|
|
|
static int import_vgem_fd(int vgem_fd, int dma_buf_fd, uint32_t *handle) |
|
{ |
|
struct drm_prime_handle import_handle = { |
|
.fd = dma_buf_fd, |
|
.flags = 0, |
|
.handle = 0, |
|
}; |
|
int ret; |
|
|
|
ret = ioctl(vgem_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &import_handle); |
|
if (ret == 0) |
|
*handle = import_handle.handle; |
|
return ret; |
|
} |
|
|
|
static void close_handle(int vgem_fd, uint32_t handle) |
|
{ |
|
struct drm_gem_close close = { |
|
.handle = handle, |
|
}; |
|
|
|
ioctl(vgem_fd, DRM_IOCTL_GEM_CLOSE, &close); |
|
} |
|
|
|
static int dmabuf_heap_open(char *name) |
|
{ |
|
int ret, fd; |
|
char buf[256]; |
|
|
|
ret = snprintf(buf, 256, "%s/%s", DEVPATH, name); |
|
if (ret < 0) { |
|
printf("snprintf failed!\n"); |
|
return ret; |
|
} |
|
|
|
fd = open(buf, O_RDWR); |
|
if (fd < 0) |
|
printf("open %s failed!\n", buf); |
|
return fd; |
|
} |
|
|
|
static int dmabuf_heap_alloc_fdflags(int fd, size_t len, unsigned int fd_flags, |
|
unsigned int heap_flags, int *dmabuf_fd) |
|
{ |
|
struct dma_heap_allocation_data data = { |
|
.len = len, |
|
.fd = 0, |
|
.fd_flags = fd_flags, |
|
.heap_flags = heap_flags, |
|
}; |
|
int ret; |
|
|
|
if (!dmabuf_fd) |
|
return -EINVAL; |
|
|
|
ret = ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &data); |
|
if (ret < 0) |
|
return ret; |
|
*dmabuf_fd = (int)data.fd; |
|
return ret; |
|
} |
|
|
|
static int dmabuf_heap_alloc(int fd, size_t len, unsigned int flags, |
|
int *dmabuf_fd) |
|
{ |
|
return dmabuf_heap_alloc_fdflags(fd, len, O_RDWR | O_CLOEXEC, flags, |
|
dmabuf_fd); |
|
} |
|
|
|
static int dmabuf_sync(int fd, int start_stop) |
|
{ |
|
struct dma_buf_sync sync = { |
|
.flags = start_stop | DMA_BUF_SYNC_RW, |
|
}; |
|
|
|
return ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync); |
|
} |
|
|
|
#define ONE_MEG (1024 * 1024) |
|
|
|
static int test_alloc_and_import(char *heap_name) |
|
{ |
|
int heap_fd = -1, dmabuf_fd = -1, importer_fd = -1; |
|
uint32_t handle = 0; |
|
void *p = NULL; |
|
int ret; |
|
|
|
heap_fd = dmabuf_heap_open(heap_name); |
|
if (heap_fd < 0) |
|
return -1; |
|
|
|
printf(" Testing allocation and importing: "); |
|
ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0, &dmabuf_fd); |
|
if (ret) { |
|
printf("FAIL (Allocation Failed!)\n"); |
|
ret = -1; |
|
goto out; |
|
} |
|
/* mmap and write a simple pattern */ |
|
p = mmap(NULL, |
|
ONE_MEG, |
|
PROT_READ | PROT_WRITE, |
|
MAP_SHARED, |
|
dmabuf_fd, |
|
0); |
|
if (p == MAP_FAILED) { |
|
printf("FAIL (mmap() failed)\n"); |
|
ret = -1; |
|
goto out; |
|
} |
|
|
|
dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START); |
|
memset(p, 1, ONE_MEG / 2); |
|
memset((char *)p + ONE_MEG / 2, 0, ONE_MEG / 2); |
|
dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END); |
|
|
|
importer_fd = open_vgem(); |
|
if (importer_fd < 0) { |
|
ret = importer_fd; |
|
printf("(Could not open vgem - skipping): "); |
|
} else { |
|
ret = import_vgem_fd(importer_fd, dmabuf_fd, &handle); |
|
if (ret < 0) { |
|
printf("FAIL (Failed to import buffer)\n"); |
|
goto out; |
|
} |
|
} |
|
|
|
ret = dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START); |
|
if (ret < 0) { |
|
printf("FAIL (DMA_BUF_SYNC_START failed!)\n"); |
|
goto out; |
|
} |
|
|
|
memset(p, 0xff, ONE_MEG); |
|
ret = dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END); |
|
if (ret < 0) { |
|
printf("FAIL (DMA_BUF_SYNC_END failed!)\n"); |
|
goto out; |
|
} |
|
|
|
close_handle(importer_fd, handle); |
|
ret = 0; |
|
printf(" OK\n"); |
|
out: |
|
if (p) |
|
munmap(p, ONE_MEG); |
|
if (importer_fd >= 0) |
|
close(importer_fd); |
|
if (dmabuf_fd >= 0) |
|
close(dmabuf_fd); |
|
if (heap_fd >= 0) |
|
close(heap_fd); |
|
|
|
return ret; |
|
} |
|
|
|
static int test_alloc_zeroed(char *heap_name, size_t size) |
|
{ |
|
int heap_fd = -1, dmabuf_fd[32]; |
|
int i, j, ret; |
|
void *p = NULL; |
|
char *c; |
|
|
|
printf(" Testing alloced %ldk buffers are zeroed: ", size / 1024); |
|
heap_fd = dmabuf_heap_open(heap_name); |
|
if (heap_fd < 0) |
|
return -1; |
|
|
|
/* Allocate and fill a bunch of buffers */ |
|
for (i = 0; i < 32; i++) { |
|
ret = dmabuf_heap_alloc(heap_fd, size, 0, &dmabuf_fd[i]); |
|
if (ret < 0) { |
|
printf("FAIL (Allocation (%i) failed)\n", i); |
|
goto out; |
|
} |
|
/* mmap and fill with simple pattern */ |
|
p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf_fd[i], 0); |
|
if (p == MAP_FAILED) { |
|
printf("FAIL (mmap() failed!)\n"); |
|
ret = -1; |
|
goto out; |
|
} |
|
dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_START); |
|
memset(p, 0xff, size); |
|
dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_END); |
|
munmap(p, size); |
|
} |
|
/* close them all */ |
|
for (i = 0; i < 32; i++) |
|
close(dmabuf_fd[i]); |
|
|
|
/* Allocate and validate all buffers are zeroed */ |
|
for (i = 0; i < 32; i++) { |
|
ret = dmabuf_heap_alloc(heap_fd, size, 0, &dmabuf_fd[i]); |
|
if (ret < 0) { |
|
printf("FAIL (Allocation (%i) failed)\n", i); |
|
goto out; |
|
} |
|
|
|
/* mmap and validate everything is zero */ |
|
p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf_fd[i], 0); |
|
if (p == MAP_FAILED) { |
|
printf("FAIL (mmap() failed!)\n"); |
|
ret = -1; |
|
goto out; |
|
} |
|
dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_START); |
|
c = (char *)p; |
|
for (j = 0; j < size; j++) { |
|
if (c[j] != 0) { |
|
printf("FAIL (Allocated buffer not zeroed @ %i)\n", j); |
|
break; |
|
} |
|
} |
|
dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_END); |
|
munmap(p, size); |
|
} |
|
/* close them all */ |
|
for (i = 0; i < 32; i++) |
|
close(dmabuf_fd[i]); |
|
|
|
close(heap_fd); |
|
printf("OK\n"); |
|
return 0; |
|
|
|
out: |
|
while (i > 0) { |
|
close(dmabuf_fd[i]); |
|
i--; |
|
} |
|
close(heap_fd); |
|
return ret; |
|
} |
|
|
|
/* Test the ioctl version compatibility w/ a smaller structure then expected */ |
|
static int dmabuf_heap_alloc_older(int fd, size_t len, unsigned int flags, |
|
int *dmabuf_fd) |
|
{ |
|
int ret; |
|
unsigned int older_alloc_ioctl; |
|
struct dma_heap_allocation_data_smaller { |
|
__u64 len; |
|
__u32 fd; |
|
__u32 fd_flags; |
|
} data = { |
|
.len = len, |
|
.fd = 0, |
|
.fd_flags = O_RDWR | O_CLOEXEC, |
|
}; |
|
|
|
older_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0, |
|
struct dma_heap_allocation_data_smaller); |
|
if (!dmabuf_fd) |
|
return -EINVAL; |
|
|
|
ret = ioctl(fd, older_alloc_ioctl, &data); |
|
if (ret < 0) |
|
return ret; |
|
*dmabuf_fd = (int)data.fd; |
|
return ret; |
|
} |
|
|
|
/* Test the ioctl version compatibility w/ a larger structure then expected */ |
|
static int dmabuf_heap_alloc_newer(int fd, size_t len, unsigned int flags, |
|
int *dmabuf_fd) |
|
{ |
|
int ret; |
|
unsigned int newer_alloc_ioctl; |
|
struct dma_heap_allocation_data_bigger { |
|
__u64 len; |
|
__u32 fd; |
|
__u32 fd_flags; |
|
__u64 heap_flags; |
|
__u64 garbage1; |
|
__u64 garbage2; |
|
__u64 garbage3; |
|
} data = { |
|
.len = len, |
|
.fd = 0, |
|
.fd_flags = O_RDWR | O_CLOEXEC, |
|
.heap_flags = flags, |
|
.garbage1 = 0xffffffff, |
|
.garbage2 = 0x88888888, |
|
.garbage3 = 0x11111111, |
|
}; |
|
|
|
newer_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0, |
|
struct dma_heap_allocation_data_bigger); |
|
if (!dmabuf_fd) |
|
return -EINVAL; |
|
|
|
ret = ioctl(fd, newer_alloc_ioctl, &data); |
|
if (ret < 0) |
|
return ret; |
|
|
|
*dmabuf_fd = (int)data.fd; |
|
return ret; |
|
} |
|
|
|
static int test_alloc_compat(char *heap_name) |
|
{ |
|
int heap_fd = -1, dmabuf_fd = -1; |
|
int ret; |
|
|
|
heap_fd = dmabuf_heap_open(heap_name); |
|
if (heap_fd < 0) |
|
return -1; |
|
|
|
printf(" Testing (theoretical)older alloc compat: "); |
|
ret = dmabuf_heap_alloc_older(heap_fd, ONE_MEG, 0, &dmabuf_fd); |
|
if (ret) { |
|
printf("FAIL (Older compat allocation failed!)\n"); |
|
ret = -1; |
|
goto out; |
|
} |
|
close(dmabuf_fd); |
|
printf("OK\n"); |
|
|
|
printf(" Testing (theoretical)newer alloc compat: "); |
|
ret = dmabuf_heap_alloc_newer(heap_fd, ONE_MEG, 0, &dmabuf_fd); |
|
if (ret) { |
|
printf("FAIL (Newer compat allocation failed!)\n"); |
|
ret = -1; |
|
goto out; |
|
} |
|
printf("OK\n"); |
|
out: |
|
if (dmabuf_fd >= 0) |
|
close(dmabuf_fd); |
|
if (heap_fd >= 0) |
|
close(heap_fd); |
|
|
|
return ret; |
|
} |
|
|
|
static int test_alloc_errors(char *heap_name) |
|
{ |
|
int heap_fd = -1, dmabuf_fd = -1; |
|
int ret; |
|
|
|
heap_fd = dmabuf_heap_open(heap_name); |
|
if (heap_fd < 0) |
|
return -1; |
|
|
|
printf(" Testing expected error cases: "); |
|
ret = dmabuf_heap_alloc(0, ONE_MEG, 0x111111, &dmabuf_fd); |
|
if (!ret) { |
|
printf("FAIL (Did not see expected error (invalid fd)!)\n"); |
|
ret = -1; |
|
goto out; |
|
} |
|
|
|
ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0x111111, &dmabuf_fd); |
|
if (!ret) { |
|
printf("FAIL (Did not see expected error (invalid heap flags)!)\n"); |
|
ret = -1; |
|
goto out; |
|
} |
|
|
|
ret = dmabuf_heap_alloc_fdflags(heap_fd, ONE_MEG, |
|
~(O_RDWR | O_CLOEXEC), 0, &dmabuf_fd); |
|
if (!ret) { |
|
printf("FAIL (Did not see expected error (invalid fd flags)!)\n"); |
|
ret = -1; |
|
goto out; |
|
} |
|
|
|
printf("OK\n"); |
|
ret = 0; |
|
out: |
|
if (dmabuf_fd >= 0) |
|
close(dmabuf_fd); |
|
if (heap_fd >= 0) |
|
close(heap_fd); |
|
|
|
return ret; |
|
} |
|
|
|
int main(void) |
|
{ |
|
DIR *d; |
|
struct dirent *dir; |
|
int ret = -1; |
|
|
|
d = opendir(DEVPATH); |
|
if (!d) { |
|
printf("No %s directory?\n", DEVPATH); |
|
return -1; |
|
} |
|
|
|
while ((dir = readdir(d)) != NULL) { |
|
if (!strncmp(dir->d_name, ".", 2)) |
|
continue; |
|
if (!strncmp(dir->d_name, "..", 3)) |
|
continue; |
|
|
|
printf("Testing heap: %s\n", dir->d_name); |
|
printf("=======================================\n"); |
|
ret = test_alloc_and_import(dir->d_name); |
|
if (ret) |
|
break; |
|
|
|
ret = test_alloc_zeroed(dir->d_name, 4 * 1024); |
|
if (ret) |
|
break; |
|
|
|
ret = test_alloc_zeroed(dir->d_name, ONE_MEG); |
|
if (ret) |
|
break; |
|
|
|
ret = test_alloc_compat(dir->d_name); |
|
if (ret) |
|
break; |
|
|
|
ret = test_alloc_errors(dir->d_name); |
|
if (ret) |
|
break; |
|
} |
|
closedir(d); |
|
|
|
return ret; |
|
}
|
|
|