blob: fc8fc9dcf322a3d30854bfca529528a9f86dec8c [file] [log] [blame]
/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* This is a test meant to exercise the VGEM DRM kernel module's PRIME
* import/export functions. It will create a gem buffer object, mmap, write, and
* then verify it. Then the test will repeat that with the same gem buffer, but
* exported and then imported. Finally, a new gem buffer object is made in a
* different driver which exports into VGEM and the mmap, write, verify sequence
* is repeated on that.
*/
#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <xf86drm.h>
#include <linux/udmabuf.h>
#include "bs_drm.h"
#define HANDLE_EINTR_AND_EAGAIN(x) \
({ \
int result; \
do { \
result = (x); \
} while (result != -1 && (errno == EINTR || errno == EAGAIN)); \
result; \
})
#define fail_if(cond, ...) \
do { \
if (cond) { \
bs_debug_print("FAIL", __func__, __FILE__, __LINE__, __VA_ARGS__); \
exit(EXIT_FAILURE); \
} \
} while (0)
const uint32_t g_bo_pattern = 0xdeadbeef;
void *mmap_dumb_bo(int fd, int handle, size_t size)
{
struct drm_mode_map_dumb mmap_arg;
void *ptr;
int ret;
memset(&mmap_arg, 0, sizeof(mmap_arg));
mmap_arg.handle = handle;
ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mmap_arg);
assert(ret == 0);
assert(mmap_arg.offset != 0);
ptr = mmap(NULL, size, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, mmap_arg.offset);
return ptr;
}
void write_pattern(uint32_t *bo_ptr, size_t bo_size)
{
uint32_t *ptr;
for (ptr = bo_ptr; ptr < bo_ptr + (bo_size / sizeof(*bo_ptr)); ptr++) {
*ptr = g_bo_pattern;
}
}
bool verify_pattern(uint32_t *bo_ptr, size_t bo_size)
{
uint32_t *ptr;
for (ptr = bo_ptr; ptr < bo_ptr + (bo_size / sizeof(*bo_ptr)); ptr++) {
fail_if(*ptr != g_bo_pattern, "buffer object verify");
}
return true;
}
int create_udmabuf(int fd, size_t length)
{
int udmabuf_dev_fd = HANDLE_EINTR_AND_EAGAIN(open("/dev/udmabuf", O_RDWR));
fail_if(udmabuf_dev_fd < 0, "error opening /dev/udmabuf");
struct udmabuf_create create;
create.memfd = fd;
create.flags = UDMABUF_FLAGS_CLOEXEC;
create.offset = 0;
create.size = length;
int dmabuf_fd = HANDLE_EINTR_AND_EAGAIN(ioctl(udmabuf_dev_fd, UDMABUF_CREATE, &create));
fail_if(dmabuf_fd < 0, "error creating udmabuf");
close(udmabuf_dev_fd);
return dmabuf_fd;
}
int create_memfd(size_t length)
{
int fd = memfd_create("test memfd", MFD_ALLOW_SEALING);
fail_if(fd == -1, "memfd_create() error: %s", strerror(errno));
int res = HANDLE_EINTR_AND_EAGAIN(ftruncate(fd, length));
fail_if(res == -1, "ftruncate() error: %s", strerror(errno));
// udmabuf_create requires that file descriptors be sealed with
// F_SEAL_SHRINK.
fail_if(fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK) < 0,
"fcntl() error: %s", strerror(errno));
return fd;
}
void test_import_dma_buf(struct gbm_device *gbm)
{
int width = 64;
int height = 64;
size_t size = width * height * 4;
int memfd_fd = create_memfd(size);
fail_if(memfd_fd == -1, "failed to create memfd");
uint32_t *memfd_map =
mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, memfd_fd, 0);
fail_if(memfd_map == MAP_FAILED, "failed to mmap memfd");
write_pattern(memfd_map, size);
fail_if(!verify_pattern(memfd_map, size), "failed to verify pattern");
fail_if(munmap(memfd_map, size) != 0, "munmap failed");
int udmabuf_fd = create_udmabuf(memfd_fd, size);
fail_if(udmabuf_fd == -1, "failed to create udmabuf");
fail_if(close(memfd_fd) != 0, "close memfd failed");
struct gbm_import_fd_modifier_data gbm_import_data = {
.width = width,
.height = height,
.format = GBM_FORMAT_ARGB8888,
.num_fds = 1,
.fds[0] = udmabuf_fd,
.strides[0] = width * 4,
.offsets[0] = 0,
.modifier = DRM_FORMAT_MOD_LINEAR,
};
struct gbm_bo *bo =
gbm_bo_import(gbm, GBM_BO_IMPORT_FD_MODIFIER,
&gbm_import_data, GBM_BO_USE_RENDERING);
fail_if(bo == NULL, "failed to import bo");
fail_if(close(udmabuf_fd) != 0, "failed to close udmabuf_fd");
uint32_t stride;
void *map_data;
void *bo_map = gbm_bo_map(bo,0, 0, width, height,
GBM_BO_TRANSFER_READ, &stride, &map_data);
fail_if(bo_map == MAP_FAILED, "gbm_bo_map failed");
fail_if(!verify_pattern(bo_map, size), "pattern mismatch after gbm_bo_map");
gbm_bo_unmap(bo, map_data);
gbm_bo_destroy(bo);
}
void test_export_dma_buf(struct gbm_device *gbm)
{
int width = 64;
int height = 64;
struct gbm_bo *bo = gbm_bo_create(gbm, width, height,
GBM_FORMAT_ARGB8888, GBM_BO_USE_LINEAR);
fail_if(bo == NULL, "create bo failed");
uint32_t stride;
void *map_data;
void *bo_map = gbm_bo_map(bo,0, 0, width, height,
GBM_BO_TRANSFER_WRITE, &stride, &map_data);
fail_if(bo_map == MAP_FAILED, "gbm_bo_map failed");
fail_if(stride != gbm_bo_get_stride(bo),
"mapped buffer stride doesn't match bo stride");
uint32_t size = stride * height;
write_pattern(bo_map, size);
gbm_bo_unmap(bo, map_data);
int dmabuf_fd = gbm_bo_get_fd(bo);
fail_if(dmabuf_fd == -1, "dma-buf export failed");
uint32_t *dmabuf_map =
mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, dmabuf_fd, 0);
fail_if(dmabuf_map == MAP_FAILED, "failed to mmap memfd");
fail_if(!verify_pattern(dmabuf_map, size), "pattern mismatch after gbm_bo_map");
fail_if(munmap(dmabuf_map, size) != 0, "munmap failed");
}
int main(int argc, char *argv[])
{
int fd = bs_drm_open_for_display();
fail_if(fd == -1, "error opening dri card");
struct gbm_device* gbm = gbm_create_device(fd);
fail_if(gbm == NULL, "failed to create gbm device");
test_import_dma_buf(gbm);
test_export_dma_buf(gbm);
gbm_device_destroy(gbm);
close(fd);
return EXIT_SUCCESS;
}