/* 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 #include #include #include #include #include #include #include #include #include #include #include #include "bs_drm.h" #define WITH_COLOR 1 #if WITH_COLOR #define ANSI_COLOR_RED "\x1b[31m" #define ANSI_COLOR_GREEN "\x1b[32m" #define ANSI_COLOR_YELLOW "\x1b[33m" #define ANSI_COLOR_BLUE "\x1b[34m" #define ANSI_COLOR_MAGENTA "\x1b[35m" #define ANSI_COLOR_CYAN "\x1b[36m" #define ANSI_COLOR_RESET "\x1b[0m" #else #define ANSI_COLOR_RED "" #define ANSI_COLOR_GREEN "" #define ANSI_COLOR_YELLOW "" #define ANSI_COLOR_BLUE "" #define ANSI_COLOR_MAGENTA "" #define ANSI_COLOR_CYAN "" #define ANSI_COLOR_RESET "" #endif #define FAIL_COLOR ANSI_COLOR_RED "failed" ANSI_COLOR_RESET #define SUCCESS_COLOR(x) ANSI_COLOR_GREEN x ANSI_COLOR_RESET const uint32_t g_bo_pattern = 0xdeadbeef; const char g_dev_card_path_format[] = "/dev/dri/card%d"; int create_vgem_bo(int fd, size_t size, uint32_t *handle) { struct drm_mode_create_dumb create; int ret; memset(&create, 0, sizeof(create)); create.height = size; create.width = 1; create.bpp = 8; ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create); if (ret) return ret; assert(create.size >= size); *handle = create.handle; return 0; } 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(volatile uint32_t *bo_ptr, size_t bo_size) { volatile uint32_t *ptr; for (ptr = bo_ptr; ptr < bo_ptr + (bo_size / sizeof(*bo_ptr)); ptr++) { *ptr = g_bo_pattern; } } bool verify_pattern(volatile uint32_t *bo_ptr, size_t bo_size) { volatile uint32_t *ptr; for (ptr = bo_ptr; ptr < bo_ptr + (bo_size / sizeof(*bo_ptr)); ptr++) { if (*ptr != g_bo_pattern) { fprintf(stderr, "buffer object verify " FAIL_COLOR " at offset %td = 0x%X\n", (void *)ptr - (void *)bo_ptr, *ptr); return false; } } return true; } static const char help_text[] = "Usage: %s [OPTIONS]\n" " -h Print this help.\n" " -d [DEVICE] Open the given vgem device file (defaults to trying all cards under " "/dev/dri/).\n" " -c [SIZE] Create a buffer objects of the given size in bytes.\n"; void print_help(const char *argv0) { printf(help_text, argv0); } static const char optstr[] = "hd:c:"; int main(int argc, char *argv[]) { int ret = 0; const char *device_file = NULL; long bo_size = 65536; bool test_pattern = true; bool export_to_fd = true; bool import_to_handle = true; bool import_foreign = true; int c; while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { case 'h': print_help(argv[0]); exit(0); break; case 'd': device_file = optarg; break; case 'c': bo_size = atol(optarg); break; default: print_help(argv[0]); return 1; } } int vgem_fd = -1; if (device_file) vgem_fd = open(device_file, O_RDWR); else vgem_fd = bs_drm_open_vgem(); if (vgem_fd < 0) { perror(FAIL_COLOR " to open vgem device"); return 1; } printf(SUCCESS_COLOR("opened") " vgem device\n"); uint32_t bo_handle; if (bo_size > 0) { ret = create_vgem_bo(vgem_fd, bo_size, &bo_handle); if (ret) { fprintf(stderr, FAIL_COLOR " to create bo: %d\n", ret); ret = 1; goto close_vgem_fd; } printf(SUCCESS_COLOR("created") " vgem buffer object, handle = %u\n", bo_handle); } if (test_pattern) { if (bo_size <= 0) { fprintf(stderr, "buffer object must be created before it can be written to\n"); ret = 1; goto close_vgem_fd; } volatile uint32_t *bo_ptr = mmap_dumb_bo(vgem_fd, bo_handle, bo_size); if (bo_ptr == MAP_FAILED) { fprintf(stderr, FAIL_COLOR " to map buffer object\n"); ret = 1; goto close_vgem_fd; } printf(SUCCESS_COLOR("mapped") " vgem buffer object\n"); write_pattern(bo_ptr, bo_size); assert(!munmap((uint32_t *)bo_ptr, bo_size)); printf(SUCCESS_COLOR("wrote") " to vgem buffer object\n"); bo_ptr = mmap_dumb_bo(vgem_fd, bo_handle, bo_size); if (!verify_pattern(bo_ptr, bo_size)) { ret = 1; goto close_vgem_fd; } assert(!munmap((uint32_t *)bo_ptr, bo_size)); printf(SUCCESS_COLOR("verified") " vgem buffer object writes\n"); } int prime_fd; if (export_to_fd) { if (bo_size <= 0) { fprintf(stderr, "created buffer object required to perform export\n"); ret = 1; goto close_vgem_fd; } ret = drmPrimeHandleToFD(vgem_fd, bo_handle, O_CLOEXEC, &prime_fd); if (ret) { fprintf(stderr, FAIL_COLOR " to export buffer object: %d\n", ret); ret = 1; goto close_vgem_fd; } printf(SUCCESS_COLOR("exported") " vgem buffer object, fd = %d\n", prime_fd); } uint32_t imported_handle; if (import_to_handle) { if (bo_size <= 0 || !export_to_fd) { fprintf(stderr, "exported buffer object required to perform import\n"); ret = 1; goto close_vgem_fd; } ret = drmPrimeFDToHandle(vgem_fd, prime_fd, &imported_handle); if (ret) { fprintf(stderr, FAIL_COLOR " to import buffer object: %d\n", ret); ret = 1; goto close_vgem_fd; } printf(SUCCESS_COLOR("imported") " to vgem buffer object, handle = %d\n", imported_handle); volatile uint32_t *bo_ptr = mmap_dumb_bo(vgem_fd, imported_handle, bo_size); if (bo_ptr == MAP_FAILED) { fprintf(stderr, FAIL_COLOR " to map imported buffer object\n"); ret = 1; goto close_vgem_fd; } printf(SUCCESS_COLOR("mapped") " imported vgem buffer object\n"); if (!verify_pattern(bo_ptr, bo_size)) { ret = 1; goto close_vgem_fd; } assert(!munmap((uint32_t *)bo_ptr, bo_size)); printf(SUCCESS_COLOR("verified") " imported vgem buffer object writes\n"); } if (import_foreign) { /* Open a card that's _not_ vgem. */ int dumb_fd = bs_drm_open_for_display(); if (dumb_fd == -1) { perror(FAIL_COLOR " to open non-vgem card\n"); ret = 1; goto close_vgem_fd; } printf(SUCCESS_COLOR("opened") " non-vgem device\n"); struct drm_mode_create_dumb create; memset(&create, 0, sizeof(create)); create.width = 640; create.height = 480; create.bpp = 32; ret = drmIoctl(dumb_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create); if (ret) { fprintf(stderr, FAIL_COLOR " to create non-vgem buffer object: %d\n", ret); ret = 1; goto close_dumb_fd; } printf(SUCCESS_COLOR("created") " non-vgem buffer object, handle = %u\n", create.handle); assert(create.size == create.width * create.height * 4); int foreign_prime_fd; ret = drmPrimeHandleToFD(dumb_fd, create.handle, O_CLOEXEC, &foreign_prime_fd); if (ret) { fprintf(stderr, FAIL_COLOR " to export non-vgem buffer object: %d\n", ret); ret = 1; goto close_dumb_fd; } printf(SUCCESS_COLOR("exported") " non-vgem buffer object, fd = %d\n", foreign_prime_fd); uint32_t foreign_imported_handle; ret = drmPrimeFDToHandle(vgem_fd, foreign_prime_fd, &foreign_imported_handle); if (ret) { fprintf(stderr, FAIL_COLOR " to import non-vgem buffer object: %d\n", ret); ret = 1; goto close_vgem_fd; } printf(SUCCESS_COLOR("imported") " to vgem buffer object, handle = %d\n", foreign_imported_handle); volatile uint32_t *bo_ptr = mmap_dumb_bo(vgem_fd, foreign_imported_handle, create.size); if (bo_ptr == MAP_FAILED) { fprintf(stderr, FAIL_COLOR " to map imported non-vgem buffer object\n"); ret = 1; goto close_vgem_fd; } printf(SUCCESS_COLOR("mapped") " imported non-vgem vgem buffer object\n"); write_pattern(bo_ptr, create.size); assert(!munmap((uint32_t *)bo_ptr, create.size)); printf(SUCCESS_COLOR("wrote") " to non-vgem buffer object\n"); bo_ptr = mmap_dumb_bo(vgem_fd, foreign_imported_handle, create.size); if (bo_ptr == MAP_FAILED) { fprintf(stderr, FAIL_COLOR " to map imported non-vgem buffer object\n"); ret = 1; goto close_vgem_fd; } if (!verify_pattern(bo_ptr, create.size)) { ret = 1; goto close_vgem_fd; } assert(!munmap((uint32_t *)bo_ptr, create.size)); printf(SUCCESS_COLOR("verified") " imported non-vgem buffer object writes\n"); bo_ptr = mmap_dumb_bo(dumb_fd, create.handle, create.size); if (bo_ptr == MAP_FAILED) { fprintf(stderr, FAIL_COLOR " to map non-vgem buffer object\n"); ret = 1; goto close_vgem_fd; } if (!verify_pattern(bo_ptr, create.size)) { ret = 1; goto close_vgem_fd; } printf(SUCCESS_COLOR("verified") " non-vgem buffer object writes\n"); assert(!munmap((uint32_t *)bo_ptr, create.size)); close_dumb_fd: close(dumb_fd); } close_vgem_fd: close(vgem_fd); return ret; }