578 lines
15 KiB
C
578 lines
15 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|||
|
|
/*
|
||
|
|
* Copyright 2025 Google LLC.
|
||
|
|
* Author: Suren Baghdasaryan <surenb@google.com>
|
||
|
|
*
|
||
|
|
* Permission to use, copy, modify, and distribute this software for any
|
||
|
|
* purpose with or without fee is hereby granted, provided that the above
|
||
|
|
* copyright notice and this permission notice appear in all copies.
|
||
|
|
*
|
||
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
|
|
*/
|
||
|
|
/*
|
||
|
|
* WrapFD API tests
|
||
|
|
*/
|
||
|
|
#define _GNU_SOURCE
|
||
|
|
#include <errno.h>
|
||
|
|
#include <fcntl.h>
|
||
|
|
#include <getopt.h>
|
||
|
|
#include <stdbool.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <time.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
#include <sys/ioctl.h>
|
||
|
|
#include <sys/mman.h>
|
||
|
|
#include <sys/stat.h>
|
||
|
|
#include <sys/types.h>
|
||
|
|
#include <sys/wait.h>
|
||
|
|
#include <linux/dma-buf.h>
|
||
|
|
#include <linux/dma-heap.h>
|
||
|
|
#include <linux/wrapfd.h>
|
||
|
|
#include "../../kselftest_harness.h"
|
||
|
|
|
||
|
|
/* ioctl wrappers */
|
||
|
|
static inline int wrapfd_wrap(int dev_fd, int fd, unsigned int prot)
|
||
|
|
{
|
||
|
|
struct wrapfd_wrap wrap = {
|
||
|
|
.fd = fd,
|
||
|
|
.prot = prot,
|
||
|
|
};
|
||
|
|
|
||
|
|
return ioctl(dev_fd, WRAPFD_DEV_IOC_WRAP, &wrap);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int wrapfd_get_state(int dev_fd, int fd, unsigned long *state)
|
||
|
|
{
|
||
|
|
struct wrapfd_get_state wrap_state = {
|
||
|
|
.fd = fd,
|
||
|
|
};
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
ret = ioctl(dev_fd, WRAPFD_DEV_IOC_GET_STATE, &wrap_state);
|
||
|
|
if (!ret && state)
|
||
|
|
*state = wrap_state.state;
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int wrapfd_get(int wrapfd)
|
||
|
|
{
|
||
|
|
return ioctl(wrapfd, WRAPFD_DEV_IOC_GET, NULL);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int wrapfd_put(int wrapfd)
|
||
|
|
{
|
||
|
|
return ioctl(wrapfd, WRAPFD_DEV_IOC_PUT, NULL);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int wrapfd_load(int wrapfd, int fd, unsigned long file_offs,
|
||
|
|
unsigned long buf_offs, unsigned long len)
|
||
|
|
{
|
||
|
|
struct wrapfd_load load = {
|
||
|
|
.fd = fd,
|
||
|
|
.file_offs = file_offs,
|
||
|
|
.buf_offs = buf_offs,
|
||
|
|
.len = len,
|
||
|
|
};
|
||
|
|
|
||
|
|
return ioctl(wrapfd, WRAPFD_DEV_IOC_LOAD, &load);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int wrapfd_rewrap(int wrapfd, unsigned int prot)
|
||
|
|
{
|
||
|
|
struct wrapfd_rewrap rewrap = {
|
||
|
|
.prot = prot,
|
||
|
|
};
|
||
|
|
|
||
|
|
return ioctl(wrapfd, WRAPFD_DEV_IOC_REWRAP, &rewrap);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int wrapfd_empty(int wrapfd)
|
||
|
|
{
|
||
|
|
return ioctl(wrapfd, WRAPFD_DEV_IOC_EMPTY, NULL);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int wrapfd_allow_guests(int wrapfd)
|
||
|
|
{
|
||
|
|
return ioctl(wrapfd, WRAPFD_DEV_IOC_ALLOW_GUESTS, NULL);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int wrapfd_prohibit_guests(int wrapfd)
|
||
|
|
{
|
||
|
|
return ioctl(wrapfd, WRAPFD_DEV_IOC_PROHIBIT_GUESTS, NULL);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* test utility functions */
|
||
|
|
static int dmabuf_heap_alloc(int heap_fd, size_t len)
|
||
|
|
{
|
||
|
|
struct dma_heap_allocation_data data = {
|
||
|
|
.len = len,
|
||
|
|
.fd = 0,
|
||
|
|
.fd_flags = O_RDWR | O_CLOEXEC,
|
||
|
|
.heap_flags = 0,
|
||
|
|
};
|
||
|
|
int ret = ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, &data);
|
||
|
|
|
||
|
|
return ret < 0 ? ret : data.fd;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void generate_file_content(char *content, size_t size)
|
||
|
|
{
|
||
|
|
srand(time(NULL));
|
||
|
|
/* Generate only printable characters in the range of [32, 126] */
|
||
|
|
for (size_t i = 0; i < size; i++)
|
||
|
|
content[i] = 32 + rand() % 95;
|
||
|
|
}
|
||
|
|
|
||
|
|
FIXTURE(wrapfd_tests)
|
||
|
|
{
|
||
|
|
size_t page_size;
|
||
|
|
char *content;
|
||
|
|
size_t size;
|
||
|
|
int dev_fd;
|
||
|
|
int fd;
|
||
|
|
};
|
||
|
|
|
||
|
|
#define FILE_SZ_PAGES 100
|
||
|
|
|
||
|
|
FIXTURE_SETUP(wrapfd_tests)
|
||
|
|
{
|
||
|
|
ssize_t total_wr;
|
||
|
|
FILE *ftmp;
|
||
|
|
|
||
|
|
EXPECT_EQ(getuid(), 0)
|
||
|
|
SKIP(return, "Skipping all tests as non-root");
|
||
|
|
|
||
|
|
self->page_size = (size_t)sysconf(_SC_PAGESIZE);
|
||
|
|
self->size = self->page_size * FILE_SZ_PAGES;
|
||
|
|
|
||
|
|
self->dev_fd = open("/dev/wrapfd", O_RDONLY);
|
||
|
|
ASSERT_TRUE(self->dev_fd >= 0);
|
||
|
|
|
||
|
|
/* Prepare random content buffer */
|
||
|
|
self->content = malloc(self->size);
|
||
|
|
generate_file_content(self->content, self->size);
|
||
|
|
|
||
|
|
/* Prepare temporary file with the same content */
|
||
|
|
ftmp = tmpfile();
|
||
|
|
ASSERT_NE(ftmp, NULL);
|
||
|
|
self->fd = dup(fileno(ftmp));
|
||
|
|
fclose(ftmp);
|
||
|
|
|
||
|
|
total_wr = 0;
|
||
|
|
do {
|
||
|
|
ssize_t wr = write(self->fd, self->content + total_wr,
|
||
|
|
self->size - total_wr);
|
||
|
|
ASSERT_TRUE(wr >= 0);
|
||
|
|
total_wr += wr;
|
||
|
|
} while (total_wr < self->size);
|
||
|
|
}
|
||
|
|
|
||
|
|
FIXTURE_TEARDOWN(wrapfd_tests)
|
||
|
|
{
|
||
|
|
close(self->fd);
|
||
|
|
close(self->dev_fd);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int cmp_content(struct __test_metadata *_metadata,
|
||
|
|
FIXTURE_DATA(wrapfd_tests) *self, int wrapfd)
|
||
|
|
{
|
||
|
|
char *ptr;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
ptr = mmap(NULL, self->size, PROT_READ, MAP_SHARED, wrapfd, 0);
|
||
|
|
ASSERT_NE(ptr, MAP_FAILED);
|
||
|
|
ret = memcmp(self->content, ptr, self->size);
|
||
|
|
ASSERT_EQ(munmap(ptr, self->size), 0);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void clear_content(struct __test_metadata *_metadata,
|
||
|
|
FIXTURE_DATA(wrapfd_tests) *self, int wrapfd)
|
||
|
|
{
|
||
|
|
char *ptr;
|
||
|
|
|
||
|
|
ptr = mmap(NULL, self->size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||
|
|
wrapfd, 0);
|
||
|
|
ASSERT_NE(ptr, MAP_FAILED);
|
||
|
|
memset(ptr, 0, self->size);
|
||
|
|
ASSERT_EQ(munmap(ptr, self->size), 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void test_wrap(struct __test_metadata *_metadata,
|
||
|
|
FIXTURE_DATA(wrapfd_tests) *self, int fd)
|
||
|
|
{
|
||
|
|
int wrapfd;
|
||
|
|
|
||
|
|
/* Get state of a non-wrapped fd */
|
||
|
|
ASSERT_TRUE(wrapfd_get_state(self->dev_fd, fd, NULL) &&
|
||
|
|
errno == EINVAL);
|
||
|
|
ASSERT_TRUE(wrapfd_get_state(self->dev_fd, self->dev_fd, NULL) &&
|
||
|
|
errno == EINVAL);
|
||
|
|
|
||
|
|
/* Wrap and get state of a wrapped fd */
|
||
|
|
wrapfd = wrapfd_wrap(self->dev_fd, fd, PROT_READ);
|
||
|
|
ASSERT_TRUE(wrapfd >= 0);
|
||
|
|
ASSERT_EQ(wrapfd_get_state(self->dev_fd, wrapfd, NULL), 0);
|
||
|
|
close(wrapfd);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void test_load(struct __test_metadata *_metadata,
|
||
|
|
FIXTURE_DATA(wrapfd_tests) *self, int fd)
|
||
|
|
{
|
||
|
|
int wrapfd;
|
||
|
|
|
||
|
|
/* Load the file content first */
|
||
|
|
wrapfd = wrapfd_wrap(self->dev_fd, fd, PROT_READ | PROT_WRITE);
|
||
|
|
ASSERT_TRUE(wrapfd >= 0);
|
||
|
|
ASSERT_EQ(wrapfd_get(wrapfd), 0);
|
||
|
|
|
||
|
|
clear_content(_metadata, self, wrapfd);
|
||
|
|
ASSERT_NE(cmp_content(_metadata, self, wrapfd), 0);
|
||
|
|
ASSERT_EQ(wrapfd_load(wrapfd, self->fd, 0, 0, self->size), 0);
|
||
|
|
ASSERT_EQ(cmp_content(_metadata, self, wrapfd), 0);
|
||
|
|
/* TODO: test more load offsets */
|
||
|
|
|
||
|
|
ASSERT_EQ(wrapfd_put(wrapfd), 0);
|
||
|
|
close(wrapfd);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void test_wrap_rdonly(struct __test_metadata *_metadata,
|
||
|
|
FIXTURE_DATA(wrapfd_tests) *self, int fd)
|
||
|
|
{
|
||
|
|
int wrapfd;
|
||
|
|
|
||
|
|
wrapfd = wrapfd_wrap(self->dev_fd, fd, PROT_READ);
|
||
|
|
ASSERT_TRUE(wrapfd >= 0);
|
||
|
|
|
||
|
|
/* Check content of the buffer */
|
||
|
|
ASSERT_EQ(cmp_content(_metadata, self, wrapfd), 0);
|
||
|
|
|
||
|
|
/* Try mapping as writable */
|
||
|
|
ASSERT_EQ(mmap(NULL, self->size, PROT_READ | PROT_WRITE,
|
||
|
|
MAP_PRIVATE, wrapfd, 0), MAP_FAILED);
|
||
|
|
|
||
|
|
close(wrapfd);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void test_wrap_rdwr(struct __test_metadata *_metadata,
|
||
|
|
FIXTURE_DATA(wrapfd_tests) *self, int fd)
|
||
|
|
{
|
||
|
|
int wrapfd;
|
||
|
|
char *ptr;
|
||
|
|
|
||
|
|
wrapfd = wrapfd_wrap(self->dev_fd, fd, PROT_READ | PROT_WRITE);
|
||
|
|
ASSERT_TRUE(wrapfd >= 0);
|
||
|
|
|
||
|
|
/* Check content of the buffer before modification */
|
||
|
|
ASSERT_EQ(cmp_content(_metadata, self, wrapfd), 0);
|
||
|
|
|
||
|
|
/* Modify buffer content */
|
||
|
|
ptr = mmap(NULL, self->size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||
|
|
wrapfd, 0);
|
||
|
|
ASSERT_NE(ptr, MAP_FAILED);
|
||
|
|
ptr[0]++;
|
||
|
|
|
||
|
|
/* Check content of the buffer after modification */
|
||
|
|
ASSERT_NE(cmp_content(_metadata, self, wrapfd), 0);
|
||
|
|
|
||
|
|
/* Restore buffer content */
|
||
|
|
ptr[0]--;
|
||
|
|
|
||
|
|
/* Confirm the final content */
|
||
|
|
ASSERT_EQ(cmp_content(_metadata, self, wrapfd), 0);
|
||
|
|
|
||
|
|
ASSERT_EQ(munmap(ptr, self->size), 0);
|
||
|
|
|
||
|
|
close(wrapfd);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void test_dup(struct __test_metadata *_metadata,
|
||
|
|
FIXTURE_DATA(wrapfd_tests) *self, int fd)
|
||
|
|
{
|
||
|
|
int wrapfd, wrapfd2;
|
||
|
|
char *ptr;
|
||
|
|
|
||
|
|
wrapfd = wrapfd_wrap(self->dev_fd, fd, PROT_READ);
|
||
|
|
ASSERT_TRUE(wrapfd >= 0);
|
||
|
|
|
||
|
|
wrapfd2 = dup(wrapfd);
|
||
|
|
ASSERT_TRUE(wrapfd2 >= 0);
|
||
|
|
|
||
|
|
/* Check content of the buffer using both fds */
|
||
|
|
ASSERT_EQ(cmp_content(_metadata, self, wrapfd), 0);
|
||
|
|
ASSERT_EQ(cmp_content(_metadata, self, wrapfd2), 0);
|
||
|
|
|
||
|
|
close(wrapfd2);
|
||
|
|
close(wrapfd);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void test_owner(struct __test_metadata *_metadata,
|
||
|
|
FIXTURE_DATA(wrapfd_tests) *self, int fd)
|
||
|
|
{
|
||
|
|
int wrapfd;
|
||
|
|
char *ptr;
|
||
|
|
|
||
|
|
wrapfd = wrapfd_wrap(self->dev_fd, fd, PROT_READ);
|
||
|
|
ASSERT_TRUE(wrapfd >= 0);
|
||
|
|
|
||
|
|
/* Try taking ownership of a mapped wrapfd */
|
||
|
|
ptr = mmap(NULL, self->size, PROT_READ, MAP_SHARED | MAP_POPULATE,
|
||
|
|
wrapfd, 0);
|
||
|
|
ASSERT_NE(ptr, MAP_FAILED);
|
||
|
|
ASSERT_TRUE(wrapfd_get(wrapfd) && errno == EINVAL);
|
||
|
|
ASSERT_EQ(munmap(ptr, self->size), 0);
|
||
|
|
|
||
|
|
/* Take ownership of an unmapped wrapfd */
|
||
|
|
ASSERT_EQ(wrapfd_get(wrapfd), 0);
|
||
|
|
|
||
|
|
/* Map owned wrapfd */
|
||
|
|
ptr = mmap(NULL, self->size, PROT_READ, MAP_SHARED | MAP_POPULATE,
|
||
|
|
wrapfd, 0);
|
||
|
|
ASSERT_NE(ptr, MAP_FAILED);
|
||
|
|
|
||
|
|
/* Try releasing ownership while still mapped */
|
||
|
|
ASSERT_TRUE(wrapfd_put(wrapfd) && errno == EINVAL);
|
||
|
|
ASSERT_EQ(munmap(ptr, self->size), 0);
|
||
|
|
|
||
|
|
/* Release ownership of an unmapped wrapfd */
|
||
|
|
ASSERT_EQ(wrapfd_put(wrapfd), 0);
|
||
|
|
close(wrapfd);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void test_rewrap(struct __test_metadata *_metadata,
|
||
|
|
FIXTURE_DATA(wrapfd_tests) *self, int fd)
|
||
|
|
{
|
||
|
|
int wrapfd, wrapfd2, wrapfd3;
|
||
|
|
unsigned long state;
|
||
|
|
char *ptr;
|
||
|
|
|
||
|
|
wrapfd = wrapfd_wrap(self->dev_fd, fd, PROT_READ);
|
||
|
|
ASSERT_TRUE(wrapfd >= 0);
|
||
|
|
|
||
|
|
ASSERT_TRUE(wrapfd_get_state(self->dev_fd, wrapfd, &state) == 0 &&
|
||
|
|
state == WRAPFD_CONTENT_RDONLY);
|
||
|
|
|
||
|
|
/* Try rewrapping unowned buffer */
|
||
|
|
wrapfd2 = wrapfd_rewrap(wrapfd, PROT_WRITE);
|
||
|
|
ASSERT_TRUE(wrapfd2 < 0 && errno == EBUSY);
|
||
|
|
|
||
|
|
/* Take buffer ownership */
|
||
|
|
ASSERT_EQ(wrapfd_get(wrapfd), 0);
|
||
|
|
|
||
|
|
/* Try rewrapping a mapped buffer */
|
||
|
|
ptr = mmap(NULL, self->size, PROT_READ, MAP_SHARED | MAP_POPULATE,
|
||
|
|
wrapfd, 0);
|
||
|
|
ASSERT_NE(ptr, MAP_FAILED);
|
||
|
|
wrapfd2 = wrapfd_rewrap(wrapfd, PROT_WRITE);
|
||
|
|
ASSERT_TRUE(wrapfd2 < 0 && errno == EINVAL);
|
||
|
|
ASSERT_EQ(munmap(ptr, self->size), 0);
|
||
|
|
|
||
|
|
/* Rewrap the buffer to writable */
|
||
|
|
wrapfd2 = wrapfd_rewrap(wrapfd, PROT_WRITE);
|
||
|
|
ASSERT_TRUE(wrapfd2 >= 0);
|
||
|
|
|
||
|
|
/* Check the states of the new and original buffers */
|
||
|
|
ASSERT_TRUE(wrapfd_get_state(self->dev_fd, wrapfd2, &state) == 0 &&
|
||
|
|
state == WRAPFD_CONTENT_RDWR);
|
||
|
|
ASSERT_TRUE(wrapfd_get_state(self->dev_fd, wrapfd, &state) == 0 &&
|
||
|
|
state == WRAPFD_CONTENT_EMPTY);
|
||
|
|
|
||
|
|
/* Check rewrapped content */
|
||
|
|
ASSERT_EQ(cmp_content(_metadata, self, wrapfd2), 0);
|
||
|
|
|
||
|
|
/* Take ownership of the rewrapped buffer */
|
||
|
|
ASSERT_EQ(wrapfd_get(wrapfd2), 0);
|
||
|
|
|
||
|
|
/* Convert back to read-only */
|
||
|
|
wrapfd3 = wrapfd_rewrap(wrapfd2, PROT_READ);
|
||
|
|
ASSERT_TRUE(wrapfd3 >= 0);
|
||
|
|
ASSERT_TRUE(wrapfd_get_state(self->dev_fd, wrapfd3, &state) == 0 &&
|
||
|
|
state == WRAPFD_CONTENT_RDONLY);
|
||
|
|
ASSERT_TRUE(wrapfd_get_state(self->dev_fd, wrapfd2, &state) == 0 &&
|
||
|
|
state == WRAPFD_CONTENT_EMPTY);
|
||
|
|
|
||
|
|
/* Try mapping as writable */
|
||
|
|
ptr = mmap(NULL, self->size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
|
||
|
|
wrapfd3, 0);
|
||
|
|
ASSERT_TRUE(ptr == MAP_FAILED && errno == EINVAL);
|
||
|
|
|
||
|
|
/* Check rewrapped content */
|
||
|
|
ASSERT_EQ(cmp_content(_metadata, self, wrapfd3), 0);
|
||
|
|
|
||
|
|
/* Try mapping the original empty wrap file */
|
||
|
|
ptr = mmap(NULL, self->size, PROT_READ, MAP_PRIVATE,
|
||
|
|
wrapfd, 0);
|
||
|
|
ASSERT_TRUE(ptr == MAP_FAILED && errno == ENOENT);
|
||
|
|
|
||
|
|
/* Release ownership of the buffers */
|
||
|
|
ASSERT_EQ(wrapfd_put(wrapfd2), 0);
|
||
|
|
ASSERT_EQ(wrapfd_put(wrapfd), 0);
|
||
|
|
|
||
|
|
close(wrapfd3);
|
||
|
|
close(wrapfd2);
|
||
|
|
close(wrapfd);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void test_empty(struct __test_metadata *_metadata,
|
||
|
|
FIXTURE_DATA(wrapfd_tests) *self, int fd)
|
||
|
|
{
|
||
|
|
unsigned long state;
|
||
|
|
int wrapfd;
|
||
|
|
char *ptr;
|
||
|
|
|
||
|
|
wrapfd = wrapfd_wrap(self->dev_fd, fd, PROT_READ | PROT_WRITE);
|
||
|
|
ASSERT_TRUE(wrapfd >= 0);
|
||
|
|
|
||
|
|
/* Try emptying unowned buffer */
|
||
|
|
ASSERT_TRUE(wrapfd_empty(wrapfd) < 0 && errno == EBUSY);
|
||
|
|
|
||
|
|
/* Take buffer ownership */
|
||
|
|
ASSERT_EQ(wrapfd_get(wrapfd), 0);
|
||
|
|
|
||
|
|
/* Try emtying a mapped buffer */
|
||
|
|
ptr = mmap(NULL, self->size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
|
||
|
|
wrapfd, 0);
|
||
|
|
ASSERT_NE(ptr, MAP_FAILED);
|
||
|
|
ASSERT_TRUE(wrapfd_empty(wrapfd) < 0 && errno == EINVAL);
|
||
|
|
ASSERT_EQ(munmap(ptr, self->size), 0);
|
||
|
|
|
||
|
|
/* Empty the buffer */
|
||
|
|
ASSERT_EQ(wrapfd_empty(wrapfd), 0);
|
||
|
|
ASSERT_TRUE(wrapfd_get_state(self->dev_fd, wrapfd, &state) == 0 &&
|
||
|
|
state == WRAPFD_CONTENT_EMPTY);
|
||
|
|
|
||
|
|
/* Try mapping the empty wrap file */
|
||
|
|
ptr = mmap(NULL, self->size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
|
||
|
|
wrapfd, 0);
|
||
|
|
ASSERT_TRUE(ptr == MAP_FAILED && errno == ENOENT);
|
||
|
|
|
||
|
|
/* Release buffer ownership */
|
||
|
|
ASSERT_EQ(wrapfd_put(wrapfd), 0);
|
||
|
|
|
||
|
|
close(wrapfd);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void test_guests(struct __test_metadata *_metadata,
|
||
|
|
FIXTURE_DATA(wrapfd_tests) *self, int fd)
|
||
|
|
{
|
||
|
|
int wrapfd;
|
||
|
|
char *ptr;
|
||
|
|
|
||
|
|
wrapfd = wrapfd_wrap(self->dev_fd, fd, PROT_READ | PROT_WRITE);
|
||
|
|
ASSERT_TRUE(wrapfd >= 0);
|
||
|
|
|
||
|
|
/* Try allowing guests for unowned buffer */
|
||
|
|
ASSERT_TRUE(wrapfd_allow_guests(wrapfd) < 0 && errno == EBUSY);
|
||
|
|
|
||
|
|
/* Take buffer ownership */
|
||
|
|
ASSERT_EQ(wrapfd_get(wrapfd), 0);
|
||
|
|
|
||
|
|
/* Try allowing guests for a mapped buffer */
|
||
|
|
ptr = mmap(NULL, self->size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
|
||
|
|
wrapfd, 0);
|
||
|
|
ASSERT_NE(ptr, MAP_FAILED);
|
||
|
|
ASSERT_TRUE(wrapfd_allow_guests(wrapfd) < 0 && errno == EINVAL);
|
||
|
|
ASSERT_EQ(munmap(ptr, self->size), 0);
|
||
|
|
|
||
|
|
/* Allow guests */
|
||
|
|
ASSERT_EQ(wrapfd_allow_guests(wrapfd), 0);
|
||
|
|
ASSERT_EQ(wrapfd_prohibit_guests(wrapfd), 0);
|
||
|
|
ASSERT_EQ(wrapfd_allow_guests(wrapfd), 0);
|
||
|
|
|
||
|
|
/* Release buffer ownership */
|
||
|
|
ASSERT_EQ(wrapfd_put(wrapfd), 0);
|
||
|
|
|
||
|
|
close(wrapfd);
|
||
|
|
}
|
||
|
|
|
||
|
|
#define FDINFO_BUF_SIZE 4096
|
||
|
|
|
||
|
|
static void test_ioctl(struct __test_metadata *_metadata,
|
||
|
|
FIXTURE_DATA(wrapfd_tests) *self, int fd)
|
||
|
|
{
|
||
|
|
const char *buf_name = "test_dmabuf";
|
||
|
|
char str[FDINFO_BUF_SIZE];
|
||
|
|
char fdinfo_name[64];
|
||
|
|
int fdinfo_fd;
|
||
|
|
int wrapfd;
|
||
|
|
|
||
|
|
wrapfd = wrapfd_wrap(self->dev_fd, fd, PROT_READ | PROT_WRITE);
|
||
|
|
ASSERT_TRUE(wrapfd >= 0);
|
||
|
|
ASSERT_EQ(ioctl(wrapfd, DMA_BUF_SET_NAME, buf_name), 0);
|
||
|
|
|
||
|
|
/* Verify the dmabuf name is changed */
|
||
|
|
sprintf(fdinfo_name, "/proc/%d/fdinfo/%d", getpid(), fd);
|
||
|
|
fdinfo_fd = open(fdinfo_name, O_RDONLY);
|
||
|
|
ASSERT_TRUE(fdinfo_fd >= 0);
|
||
|
|
ASSERT_TRUE(read(fdinfo_fd, str, FDINFO_BUF_SIZE) > 0);
|
||
|
|
ASSERT_NE(strstr(str, buf_name), NULL);
|
||
|
|
close(fdinfo_fd);
|
||
|
|
|
||
|
|
/* Verify the dmabuf name is changed in wrapfd fdinfo too */
|
||
|
|
sprintf(fdinfo_name, "/proc/%d/fdinfo/%d", getpid(), wrapfd);
|
||
|
|
fdinfo_fd = open(fdinfo_name, O_RDONLY);
|
||
|
|
ASSERT_TRUE(fdinfo_fd >= 0);
|
||
|
|
ASSERT_TRUE(read(fdinfo_fd, str, FDINFO_BUF_SIZE) > 0);
|
||
|
|
ASSERT_NE(strstr(str, buf_name), NULL);
|
||
|
|
close(fdinfo_fd);
|
||
|
|
|
||
|
|
close(wrapfd);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void run_tests(struct __test_metadata *_metadata,
|
||
|
|
FIXTURE_DATA(wrapfd_tests) *self, int fd)
|
||
|
|
{
|
||
|
|
test_wrap(_metadata, self, fd);
|
||
|
|
test_load(_metadata, self, fd);
|
||
|
|
test_wrap_rdonly(_metadata, self, fd);
|
||
|
|
test_wrap_rdwr(_metadata, self, fd);
|
||
|
|
test_dup(_metadata, self, fd);
|
||
|
|
test_owner(_metadata, self, fd);
|
||
|
|
test_rewrap(_metadata, self, fd);
|
||
|
|
test_empty(_metadata, self, fd);
|
||
|
|
test_guests(_metadata, self, fd);
|
||
|
|
test_ioctl(_metadata, self, fd);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(wrapfd_tests, wrapfd_test_dmabuf_system_heap)
|
||
|
|
{
|
||
|
|
int dmabuf_fd;
|
||
|
|
int heap_fd;
|
||
|
|
|
||
|
|
heap_fd = open("/dev/dma_heap/system", O_RDONLY);
|
||
|
|
ASSERT_TRUE(heap_fd >= 0);
|
||
|
|
dmabuf_fd = dmabuf_heap_alloc(heap_fd, self->size);
|
||
|
|
ASSERT_TRUE(dmabuf_fd >= 0);
|
||
|
|
close(heap_fd);
|
||
|
|
run_tests(_metadata, self, dmabuf_fd);
|
||
|
|
close(dmabuf_fd);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(wrapfd_tests, wrapfd_test_dmabuf_cma_heap)
|
||
|
|
{
|
||
|
|
int dmabuf_fd;
|
||
|
|
int heap_fd;
|
||
|
|
|
||
|
|
heap_fd = open("/dev/dma_heap/default_cma_region", O_RDONLY);
|
||
|
|
if (heap_fd < 0 && errno == ENOENT)
|
||
|
|
SKIP(return, "Skipping CMA tests as the CMA heap is missing");
|
||
|
|
ASSERT_TRUE(heap_fd >= 0);
|
||
|
|
dmabuf_fd = dmabuf_heap_alloc(heap_fd, self->size);
|
||
|
|
ASSERT_TRUE(dmabuf_fd >= 0);
|
||
|
|
close(heap_fd);
|
||
|
|
run_tests(_metadata, self, dmabuf_fd);
|
||
|
|
close(dmabuf_fd);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_HARNESS_MAIN
|