2019-12-03 17:26:41 +00:00
// 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 ;
2019-12-16 08:34:04 -05:00
ret = ioctl ( fd , DMA_HEAP_IOCTL_ALLOC , & data ) ;
2019-12-03 17:26:41 +00:00
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 ) ;
}
2021-01-29 03:05:11 +00:00
static int dmabuf_sync ( int fd , int start_stop )
2019-12-03 17:26:41 +00:00
{
struct dma_buf_sync sync = {
. flags = start_stop | DMA_BUF_SYNC_RW ,
} ;
2021-01-29 03:05:11 +00:00
return ioctl ( fd , DMA_BUF_IOCTL_SYNC , & sync ) ;
2019-12-03 17:26:41 +00:00
}
# 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 ;
2021-01-29 03:05:13 +00:00
printf ( " Testing allocation and importing: " ) ;
2019-12-03 17:26:41 +00:00
ret = dmabuf_heap_alloc ( heap_fd , ONE_MEG , 0 , & dmabuf_fd ) ;
if ( ret ) {
2021-01-29 03:05:13 +00:00
printf ( " FAIL (Allocation Failed!) \n " ) ;
2019-12-03 17:26:41 +00:00
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 ) {
2021-01-29 03:05:13 +00:00
printf ( " FAIL (mmap() failed) \n " ) ;
2019-12-03 17:26:41 +00:00
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 ;
2021-01-29 03:05:13 +00:00
printf ( " (Could not open vgem - skipping): " ) ;
2021-01-29 03:05:12 +00:00
} else {
ret = import_vgem_fd ( importer_fd , dmabuf_fd , & handle ) ;
if ( ret < 0 ) {
2021-01-29 03:05:13 +00:00
printf ( " FAIL (Failed to import buffer) \n " ) ;
2021-01-29 03:05:12 +00:00
goto out ;
}
2019-12-03 17:26:41 +00:00
}
2021-01-29 03:05:11 +00:00
ret = dmabuf_sync ( dmabuf_fd , DMA_BUF_SYNC_START ) ;
if ( ret < 0 ) {
2021-01-29 03:05:13 +00:00
printf ( " FAIL (DMA_BUF_SYNC_START failed!) \n " ) ;
2021-01-29 03:05:11 +00:00
goto out ;
}
2019-12-03 17:26:41 +00:00
memset ( p , 0xff , ONE_MEG ) ;
2021-01-29 03:05:11 +00:00
ret = dmabuf_sync ( dmabuf_fd , DMA_BUF_SYNC_END ) ;
if ( ret < 0 ) {
2021-01-29 03:05:13 +00:00
printf ( " FAIL (DMA_BUF_SYNC_END failed!) \n " ) ;
2021-01-29 03:05:11 +00:00
goto out ;
}
2019-12-03 17:26:41 +00:00
close_handle ( importer_fd , handle ) ;
ret = 0 ;
2021-01-29 03:05:13 +00:00
printf ( " OK \n " ) ;
2019-12-03 17:26:41 +00:00
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 ;
}
2021-01-29 03:05:14 +00:00
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 ;
}
2019-12-03 17:26:41 +00:00
/* 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 ;
2021-01-29 03:05:13 +00:00
printf ( " Testing (theoretical)older alloc compat: " ) ;
2019-12-03 17:26:41 +00:00
ret = dmabuf_heap_alloc_older ( heap_fd , ONE_MEG , 0 , & dmabuf_fd ) ;
if ( ret ) {
2021-01-29 03:05:13 +00:00
printf ( " FAIL (Older compat allocation failed!) \n " ) ;
2019-12-03 17:26:41 +00:00
ret = - 1 ;
goto out ;
}
close ( dmabuf_fd ) ;
2021-01-29 03:05:13 +00:00
printf ( " OK \n " ) ;
2019-12-03 17:26:41 +00:00
2021-01-29 03:05:13 +00:00
printf ( " Testing (theoretical)newer alloc compat: " ) ;
2019-12-03 17:26:41 +00:00
ret = dmabuf_heap_alloc_newer ( heap_fd , ONE_MEG , 0 , & dmabuf_fd ) ;
if ( ret ) {
2021-01-29 03:05:13 +00:00
printf ( " FAIL (Newer compat allocation failed!) \n " ) ;
2019-12-03 17:26:41 +00:00
ret = - 1 ;
goto out ;
}
2021-01-29 03:05:13 +00:00
printf ( " OK \n " ) ;
2019-12-03 17:26:41 +00:00
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 ;
2021-01-29 03:05:13 +00:00
printf ( " Testing expected error cases: " ) ;
2019-12-03 17:26:41 +00:00
ret = dmabuf_heap_alloc ( 0 , ONE_MEG , 0x111111 , & dmabuf_fd ) ;
if ( ! ret ) {
2021-01-29 03:05:13 +00:00
printf ( " FAIL (Did not see expected error (invalid fd)!) \n " ) ;
2019-12-03 17:26:41 +00:00
ret = - 1 ;
goto out ;
}
ret = dmabuf_heap_alloc ( heap_fd , ONE_MEG , 0x111111 , & dmabuf_fd ) ;
if ( ! ret ) {
2021-01-29 03:05:13 +00:00
printf ( " FAIL (Did not see expected error (invalid heap flags)!) \n " ) ;
2019-12-03 17:26:41 +00:00
ret = - 1 ;
goto out ;
}
ret = dmabuf_heap_alloc_fdflags ( heap_fd , ONE_MEG ,
~ ( O_RDWR | O_CLOEXEC ) , 0 , & dmabuf_fd ) ;
if ( ! ret ) {
2021-01-29 03:05:13 +00:00
printf ( " FAIL (Did not see expected error (invalid fd flags)!) \n " ) ;
2019-12-03 17:26:41 +00:00
ret = - 1 ;
goto out ;
}
2021-01-29 03:05:13 +00:00
printf ( " OK \n " ) ;
2020-05-08 04:56:10 +00:00
ret = 0 ;
2019-12-03 17:26:41 +00:00
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 ;
2021-01-29 03:05:13 +00:00
printf ( " Testing heap: %s \n " , dir - > d_name ) ;
printf ( " ======================================= \n " ) ;
2019-12-03 17:26:41 +00:00
ret = test_alloc_and_import ( dir - > d_name ) ;
if ( ret )
break ;
2021-01-29 03:05:14 +00:00
ret = test_alloc_zeroed ( dir - > d_name , 4 * 1024 ) ;
if ( ret )
break ;
ret = test_alloc_zeroed ( dir - > d_name , ONE_MEG ) ;
if ( ret )
break ;
2019-12-03 17:26:41 +00:00
ret = test_alloc_compat ( dir - > d_name ) ;
if ( ret )
break ;
ret = test_alloc_errors ( dir - > d_name ) ;
if ( ret )
break ;
}
closedir ( d ) ;
return ret ;
}