162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * HMM stands for Heterogeneous Memory Management, it is a helper layer inside
462306a36Sopenharmony_ci * the linux kernel to help device drivers mirror a process address space in
562306a36Sopenharmony_ci * the device. This allows the device to use the same address space which
662306a36Sopenharmony_ci * makes communication and data exchange a lot easier.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This framework's sole purpose is to exercise various code paths inside
962306a36Sopenharmony_ci * the kernel to make sure that HMM performs as expected and to flush out any
1062306a36Sopenharmony_ci * bugs.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "../kselftest_harness.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <errno.h>
1662306a36Sopenharmony_ci#include <fcntl.h>
1762306a36Sopenharmony_ci#include <stdio.h>
1862306a36Sopenharmony_ci#include <stdlib.h>
1962306a36Sopenharmony_ci#include <stdint.h>
2062306a36Sopenharmony_ci#include <unistd.h>
2162306a36Sopenharmony_ci#include <strings.h>
2262306a36Sopenharmony_ci#include <time.h>
2362306a36Sopenharmony_ci#include <pthread.h>
2462306a36Sopenharmony_ci#include <sys/types.h>
2562306a36Sopenharmony_ci#include <sys/stat.h>
2662306a36Sopenharmony_ci#include <sys/mman.h>
2762306a36Sopenharmony_ci#include <sys/ioctl.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * This is a private UAPI to the kernel test module so it isn't exported
3262306a36Sopenharmony_ci * in the usual include/uapi/... directory.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci#include <lib/test_hmm_uapi.h>
3562306a36Sopenharmony_ci#include <mm/gup_test.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct hmm_buffer {
3862306a36Sopenharmony_ci	void		*ptr;
3962306a36Sopenharmony_ci	void		*mirror;
4062306a36Sopenharmony_ci	unsigned long	size;
4162306a36Sopenharmony_ci	int		fd;
4262306a36Sopenharmony_ci	uint64_t	cpages;
4362306a36Sopenharmony_ci	uint64_t	faults;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cienum {
4762306a36Sopenharmony_ci	HMM_PRIVATE_DEVICE_ONE,
4862306a36Sopenharmony_ci	HMM_PRIVATE_DEVICE_TWO,
4962306a36Sopenharmony_ci	HMM_COHERENCE_DEVICE_ONE,
5062306a36Sopenharmony_ci	HMM_COHERENCE_DEVICE_TWO,
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define TWOMEG		(1 << 21)
5462306a36Sopenharmony_ci#define HMM_BUFFER_SIZE (1024 << 12)
5562306a36Sopenharmony_ci#define HMM_PATH_MAX    64
5662306a36Sopenharmony_ci#define NTIMES		10
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
5962306a36Sopenharmony_ci/* Just the flags we need, copied from mm.h: */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#ifndef FOLL_WRITE
6262306a36Sopenharmony_ci#define FOLL_WRITE	0x01	/* check pte is writable */
6362306a36Sopenharmony_ci#endif
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#ifndef FOLL_LONGTERM
6662306a36Sopenharmony_ci#define FOLL_LONGTERM   0x100 /* mapping lifetime is indefinite */
6762306a36Sopenharmony_ci#endif
6862306a36Sopenharmony_ciFIXTURE(hmm)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	int		fd;
7162306a36Sopenharmony_ci	unsigned int	page_size;
7262306a36Sopenharmony_ci	unsigned int	page_shift;
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ciFIXTURE_VARIANT(hmm)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	int     device_number;
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(hmm, hmm_device_private)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	.device_number = HMM_PRIVATE_DEVICE_ONE,
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(hmm, hmm_device_coherent)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	.device_number = HMM_COHERENCE_DEVICE_ONE,
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ciFIXTURE(hmm2)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	int		fd0;
9362306a36Sopenharmony_ci	int		fd1;
9462306a36Sopenharmony_ci	unsigned int	page_size;
9562306a36Sopenharmony_ci	unsigned int	page_shift;
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ciFIXTURE_VARIANT(hmm2)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	int     device_number0;
10162306a36Sopenharmony_ci	int     device_number1;
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(hmm2, hmm2_device_private)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	.device_number0 = HMM_PRIVATE_DEVICE_ONE,
10762306a36Sopenharmony_ci	.device_number1 = HMM_PRIVATE_DEVICE_TWO,
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(hmm2, hmm2_device_coherent)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	.device_number0 = HMM_COHERENCE_DEVICE_ONE,
11362306a36Sopenharmony_ci	.device_number1 = HMM_COHERENCE_DEVICE_TWO,
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int hmm_open(int unit)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	char pathname[HMM_PATH_MAX];
11962306a36Sopenharmony_ci	int fd;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	snprintf(pathname, sizeof(pathname), "/dev/hmm_dmirror%d", unit);
12262306a36Sopenharmony_ci	fd = open(pathname, O_RDWR, 0);
12362306a36Sopenharmony_ci	if (fd < 0)
12462306a36Sopenharmony_ci		fprintf(stderr, "could not open hmm dmirror driver (%s)\n",
12562306a36Sopenharmony_ci			pathname);
12662306a36Sopenharmony_ci	return fd;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic bool hmm_is_coherent_type(int dev_num)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	return (dev_num >= HMM_COHERENCE_DEVICE_ONE);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ciFIXTURE_SETUP(hmm)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	self->page_size = sysconf(_SC_PAGE_SIZE);
13762306a36Sopenharmony_ci	self->page_shift = ffs(self->page_size) - 1;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	self->fd = hmm_open(variant->device_number);
14062306a36Sopenharmony_ci	if (self->fd < 0 && hmm_is_coherent_type(variant->device_number))
14162306a36Sopenharmony_ci		SKIP(exit(0), "DEVICE_COHERENT not available");
14262306a36Sopenharmony_ci	ASSERT_GE(self->fd, 0);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciFIXTURE_SETUP(hmm2)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	self->page_size = sysconf(_SC_PAGE_SIZE);
14862306a36Sopenharmony_ci	self->page_shift = ffs(self->page_size) - 1;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	self->fd0 = hmm_open(variant->device_number0);
15162306a36Sopenharmony_ci	if (self->fd0 < 0 && hmm_is_coherent_type(variant->device_number0))
15262306a36Sopenharmony_ci		SKIP(exit(0), "DEVICE_COHERENT not available");
15362306a36Sopenharmony_ci	ASSERT_GE(self->fd0, 0);
15462306a36Sopenharmony_ci	self->fd1 = hmm_open(variant->device_number1);
15562306a36Sopenharmony_ci	ASSERT_GE(self->fd1, 0);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ciFIXTURE_TEARDOWN(hmm)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	int ret = close(self->fd);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
16362306a36Sopenharmony_ci	self->fd = -1;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ciFIXTURE_TEARDOWN(hmm2)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	int ret = close(self->fd0);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
17162306a36Sopenharmony_ci	self->fd0 = -1;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	ret = close(self->fd1);
17462306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
17562306a36Sopenharmony_ci	self->fd1 = -1;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic int hmm_dmirror_cmd(int fd,
17962306a36Sopenharmony_ci			   unsigned long request,
18062306a36Sopenharmony_ci			   struct hmm_buffer *buffer,
18162306a36Sopenharmony_ci			   unsigned long npages)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct hmm_dmirror_cmd cmd;
18462306a36Sopenharmony_ci	int ret;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* Simulate a device reading system memory. */
18762306a36Sopenharmony_ci	cmd.addr = (__u64)buffer->ptr;
18862306a36Sopenharmony_ci	cmd.ptr = (__u64)buffer->mirror;
18962306a36Sopenharmony_ci	cmd.npages = npages;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	for (;;) {
19262306a36Sopenharmony_ci		ret = ioctl(fd, request, &cmd);
19362306a36Sopenharmony_ci		if (ret == 0)
19462306a36Sopenharmony_ci			break;
19562306a36Sopenharmony_ci		if (errno == EINTR)
19662306a36Sopenharmony_ci			continue;
19762306a36Sopenharmony_ci		return -errno;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	buffer->cpages = cmd.cpages;
20062306a36Sopenharmony_ci	buffer->faults = cmd.faults;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return 0;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic void hmm_buffer_free(struct hmm_buffer *buffer)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	if (buffer == NULL)
20862306a36Sopenharmony_ci		return;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (buffer->ptr)
21162306a36Sopenharmony_ci		munmap(buffer->ptr, buffer->size);
21262306a36Sopenharmony_ci	free(buffer->mirror);
21362306a36Sopenharmony_ci	free(buffer);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/*
21762306a36Sopenharmony_ci * Create a temporary file that will be deleted on close.
21862306a36Sopenharmony_ci */
21962306a36Sopenharmony_cistatic int hmm_create_file(unsigned long size)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	char path[HMM_PATH_MAX];
22262306a36Sopenharmony_ci	int fd;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	strcpy(path, "/tmp");
22562306a36Sopenharmony_ci	fd = open(path, O_TMPFILE | O_EXCL | O_RDWR, 0600);
22662306a36Sopenharmony_ci	if (fd >= 0) {
22762306a36Sopenharmony_ci		int r;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		do {
23062306a36Sopenharmony_ci			r = ftruncate(fd, size);
23162306a36Sopenharmony_ci		} while (r == -1 && errno == EINTR);
23262306a36Sopenharmony_ci		if (!r)
23362306a36Sopenharmony_ci			return fd;
23462306a36Sopenharmony_ci		close(fd);
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci	return -1;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/*
24062306a36Sopenharmony_ci * Return a random unsigned number.
24162306a36Sopenharmony_ci */
24262306a36Sopenharmony_cistatic unsigned int hmm_random(void)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	static int fd = -1;
24562306a36Sopenharmony_ci	unsigned int r;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (fd < 0) {
24862306a36Sopenharmony_ci		fd = open("/dev/urandom", O_RDONLY);
24962306a36Sopenharmony_ci		if (fd < 0) {
25062306a36Sopenharmony_ci			fprintf(stderr, "%s:%d failed to open /dev/urandom\n",
25162306a36Sopenharmony_ci					__FILE__, __LINE__);
25262306a36Sopenharmony_ci			return ~0U;
25362306a36Sopenharmony_ci		}
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci	read(fd, &r, sizeof(r));
25662306a36Sopenharmony_ci	return r;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void hmm_nanosleep(unsigned int n)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct timespec t;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	t.tv_sec = 0;
26462306a36Sopenharmony_ci	t.tv_nsec = n;
26562306a36Sopenharmony_ci	nanosleep(&t, NULL);
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic int hmm_migrate_sys_to_dev(int fd,
26962306a36Sopenharmony_ci				   struct hmm_buffer *buffer,
27062306a36Sopenharmony_ci				   unsigned long npages)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	return hmm_dmirror_cmd(fd, HMM_DMIRROR_MIGRATE_TO_DEV, buffer, npages);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic int hmm_migrate_dev_to_sys(int fd,
27662306a36Sopenharmony_ci				   struct hmm_buffer *buffer,
27762306a36Sopenharmony_ci				   unsigned long npages)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	return hmm_dmirror_cmd(fd, HMM_DMIRROR_MIGRATE_TO_SYS, buffer, npages);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci/*
28362306a36Sopenharmony_ci * Simple NULL test of device open/close.
28462306a36Sopenharmony_ci */
28562306a36Sopenharmony_ciTEST_F(hmm, open_close)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci/*
29062306a36Sopenharmony_ci * Read private anonymous memory.
29162306a36Sopenharmony_ci */
29262306a36Sopenharmony_ciTEST_F(hmm, anon_read)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct hmm_buffer *buffer;
29562306a36Sopenharmony_ci	unsigned long npages;
29662306a36Sopenharmony_ci	unsigned long size;
29762306a36Sopenharmony_ci	unsigned long i;
29862306a36Sopenharmony_ci	int *ptr;
29962306a36Sopenharmony_ci	int ret;
30062306a36Sopenharmony_ci	int val;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
30362306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
30462306a36Sopenharmony_ci	size = npages << self->page_shift;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
30762306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	buffer->fd = -1;
31062306a36Sopenharmony_ci	buffer->size = size;
31162306a36Sopenharmony_ci	buffer->mirror = malloc(size);
31262306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
31562306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
31662306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
31762306a36Sopenharmony_ci			   buffer->fd, 0);
31862306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/*
32162306a36Sopenharmony_ci	 * Initialize buffer in system memory but leave the first two pages
32262306a36Sopenharmony_ci	 * zero (pte_none and pfn_zero).
32362306a36Sopenharmony_ci	 */
32462306a36Sopenharmony_ci	i = 2 * self->page_size / sizeof(*ptr);
32562306a36Sopenharmony_ci	for (ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
32662306a36Sopenharmony_ci		ptr[i] = i;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* Set buffer permission to read-only. */
32962306a36Sopenharmony_ci	ret = mprotect(buffer->ptr, size, PROT_READ);
33062306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* Populate the CPU page table with a special zero page. */
33362306a36Sopenharmony_ci	val = *(int *)(buffer->ptr + self->page_size);
33462306a36Sopenharmony_ci	ASSERT_EQ(val, 0);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* Simulate a device reading system memory. */
33762306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, npages);
33862306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
33962306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
34062306a36Sopenharmony_ci	ASSERT_EQ(buffer->faults, 1);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* Check what the device read. */
34362306a36Sopenharmony_ci	ptr = buffer->mirror;
34462306a36Sopenharmony_ci	for (i = 0; i < 2 * self->page_size / sizeof(*ptr); ++i)
34562306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], 0);
34662306a36Sopenharmony_ci	for (; i < size / sizeof(*ptr); ++i)
34762306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	hmm_buffer_free(buffer);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci/*
35362306a36Sopenharmony_ci * Read private anonymous memory which has been protected with
35462306a36Sopenharmony_ci * mprotect() PROT_NONE.
35562306a36Sopenharmony_ci */
35662306a36Sopenharmony_ciTEST_F(hmm, anon_read_prot)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct hmm_buffer *buffer;
35962306a36Sopenharmony_ci	unsigned long npages;
36062306a36Sopenharmony_ci	unsigned long size;
36162306a36Sopenharmony_ci	unsigned long i;
36262306a36Sopenharmony_ci	int *ptr;
36362306a36Sopenharmony_ci	int ret;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
36662306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
36762306a36Sopenharmony_ci	size = npages << self->page_shift;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
37062306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	buffer->fd = -1;
37362306a36Sopenharmony_ci	buffer->size = size;
37462306a36Sopenharmony_ci	buffer->mirror = malloc(size);
37562306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
37862306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
37962306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
38062306a36Sopenharmony_ci			   buffer->fd, 0);
38162306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* Initialize buffer in system memory. */
38462306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
38562306a36Sopenharmony_ci		ptr[i] = i;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/* Initialize mirror buffer so we can verify it isn't written. */
38862306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
38962306a36Sopenharmony_ci		ptr[i] = -i;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* Protect buffer from reading. */
39262306a36Sopenharmony_ci	ret = mprotect(buffer->ptr, size, PROT_NONE);
39362306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/* Simulate a device reading system memory. */
39662306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, npages);
39762306a36Sopenharmony_ci	ASSERT_EQ(ret, -EFAULT);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/* Allow CPU to read the buffer so we can check it. */
40062306a36Sopenharmony_ci	ret = mprotect(buffer->ptr, size, PROT_READ);
40162306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
40262306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
40362306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* Check what the device read. */
40662306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
40762306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], -i);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	hmm_buffer_free(buffer);
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci/*
41362306a36Sopenharmony_ci * Write private anonymous memory.
41462306a36Sopenharmony_ci */
41562306a36Sopenharmony_ciTEST_F(hmm, anon_write)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct hmm_buffer *buffer;
41862306a36Sopenharmony_ci	unsigned long npages;
41962306a36Sopenharmony_ci	unsigned long size;
42062306a36Sopenharmony_ci	unsigned long i;
42162306a36Sopenharmony_ci	int *ptr;
42262306a36Sopenharmony_ci	int ret;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
42562306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
42662306a36Sopenharmony_ci	size = npages << self->page_shift;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
42962306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	buffer->fd = -1;
43262306a36Sopenharmony_ci	buffer->size = size;
43362306a36Sopenharmony_ci	buffer->mirror = malloc(size);
43462306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
43762306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
43862306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
43962306a36Sopenharmony_ci			   buffer->fd, 0);
44062306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	/* Initialize data that the device will write to buffer->ptr. */
44362306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
44462306a36Sopenharmony_ci		ptr[i] = i;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/* Simulate a device writing system memory. */
44762306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
44862306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
44962306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
45062306a36Sopenharmony_ci	ASSERT_EQ(buffer->faults, 1);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/* Check what the device wrote. */
45362306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
45462306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	hmm_buffer_free(buffer);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci/*
46062306a36Sopenharmony_ci * Write private anonymous memory which has been protected with
46162306a36Sopenharmony_ci * mprotect() PROT_READ.
46262306a36Sopenharmony_ci */
46362306a36Sopenharmony_ciTEST_F(hmm, anon_write_prot)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	struct hmm_buffer *buffer;
46662306a36Sopenharmony_ci	unsigned long npages;
46762306a36Sopenharmony_ci	unsigned long size;
46862306a36Sopenharmony_ci	unsigned long i;
46962306a36Sopenharmony_ci	int *ptr;
47062306a36Sopenharmony_ci	int ret;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
47362306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
47462306a36Sopenharmony_ci	size = npages << self->page_shift;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
47762306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	buffer->fd = -1;
48062306a36Sopenharmony_ci	buffer->size = size;
48162306a36Sopenharmony_ci	buffer->mirror = malloc(size);
48262306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
48562306a36Sopenharmony_ci			   PROT_READ,
48662306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
48762306a36Sopenharmony_ci			   buffer->fd, 0);
48862306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	/* Simulate a device reading a zero page of memory. */
49162306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, 1);
49262306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
49362306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, 1);
49462306a36Sopenharmony_ci	ASSERT_EQ(buffer->faults, 1);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* Initialize data that the device will write to buffer->ptr. */
49762306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
49862306a36Sopenharmony_ci		ptr[i] = i;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	/* Simulate a device writing system memory. */
50162306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
50262306a36Sopenharmony_ci	ASSERT_EQ(ret, -EPERM);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/* Check what the device wrote. */
50562306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
50662306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], 0);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* Now allow writing and see that the zero page is replaced. */
50962306a36Sopenharmony_ci	ret = mprotect(buffer->ptr, size, PROT_WRITE | PROT_READ);
51062306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/* Simulate a device writing system memory. */
51362306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
51462306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
51562306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
51662306a36Sopenharmony_ci	ASSERT_EQ(buffer->faults, 1);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/* Check what the device wrote. */
51962306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
52062306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	hmm_buffer_free(buffer);
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci/*
52662306a36Sopenharmony_ci * Check that a device writing an anonymous private mapping
52762306a36Sopenharmony_ci * will copy-on-write if a child process inherits the mapping.
52862306a36Sopenharmony_ci */
52962306a36Sopenharmony_ciTEST_F(hmm, anon_write_child)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct hmm_buffer *buffer;
53262306a36Sopenharmony_ci	unsigned long npages;
53362306a36Sopenharmony_ci	unsigned long size;
53462306a36Sopenharmony_ci	unsigned long i;
53562306a36Sopenharmony_ci	int *ptr;
53662306a36Sopenharmony_ci	pid_t pid;
53762306a36Sopenharmony_ci	int child_fd;
53862306a36Sopenharmony_ci	int ret;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
54162306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
54262306a36Sopenharmony_ci	size = npages << self->page_shift;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
54562306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	buffer->fd = -1;
54862306a36Sopenharmony_ci	buffer->size = size;
54962306a36Sopenharmony_ci	buffer->mirror = malloc(size);
55062306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
55362306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
55462306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
55562306a36Sopenharmony_ci			   buffer->fd, 0);
55662306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/* Initialize buffer->ptr so we can tell if it is written. */
55962306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
56062306a36Sopenharmony_ci		ptr[i] = i;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	/* Initialize data that the device will write to buffer->ptr. */
56362306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
56462306a36Sopenharmony_ci		ptr[i] = -i;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	pid = fork();
56762306a36Sopenharmony_ci	if (pid == -1)
56862306a36Sopenharmony_ci		ASSERT_EQ(pid, 0);
56962306a36Sopenharmony_ci	if (pid != 0) {
57062306a36Sopenharmony_ci		waitpid(pid, &ret, 0);
57162306a36Sopenharmony_ci		ASSERT_EQ(WIFEXITED(ret), 1);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		/* Check that the parent's buffer did not change. */
57462306a36Sopenharmony_ci		for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
57562306a36Sopenharmony_ci			ASSERT_EQ(ptr[i], i);
57662306a36Sopenharmony_ci		return;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	/* Check that we see the parent's values. */
58062306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
58162306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
58262306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
58362306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], -i);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/* The child process needs its own mirror to its own mm. */
58662306a36Sopenharmony_ci	child_fd = hmm_open(0);
58762306a36Sopenharmony_ci	ASSERT_GE(child_fd, 0);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/* Simulate a device writing system memory. */
59062306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(child_fd, HMM_DMIRROR_WRITE, buffer, npages);
59162306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
59262306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
59362306a36Sopenharmony_ci	ASSERT_EQ(buffer->faults, 1);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	/* Check what the device wrote. */
59662306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
59762306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], -i);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	close(child_fd);
60062306a36Sopenharmony_ci	exit(0);
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci/*
60462306a36Sopenharmony_ci * Check that a device writing an anonymous shared mapping
60562306a36Sopenharmony_ci * will not copy-on-write if a child process inherits the mapping.
60662306a36Sopenharmony_ci */
60762306a36Sopenharmony_ciTEST_F(hmm, anon_write_child_shared)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct hmm_buffer *buffer;
61062306a36Sopenharmony_ci	unsigned long npages;
61162306a36Sopenharmony_ci	unsigned long size;
61262306a36Sopenharmony_ci	unsigned long i;
61362306a36Sopenharmony_ci	int *ptr;
61462306a36Sopenharmony_ci	pid_t pid;
61562306a36Sopenharmony_ci	int child_fd;
61662306a36Sopenharmony_ci	int ret;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
61962306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
62062306a36Sopenharmony_ci	size = npages << self->page_shift;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
62362306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	buffer->fd = -1;
62662306a36Sopenharmony_ci	buffer->size = size;
62762306a36Sopenharmony_ci	buffer->mirror = malloc(size);
62862306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
63162306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
63262306a36Sopenharmony_ci			   MAP_SHARED | MAP_ANONYMOUS,
63362306a36Sopenharmony_ci			   buffer->fd, 0);
63462306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	/* Initialize buffer->ptr so we can tell if it is written. */
63762306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
63862306a36Sopenharmony_ci		ptr[i] = i;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	/* Initialize data that the device will write to buffer->ptr. */
64162306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
64262306a36Sopenharmony_ci		ptr[i] = -i;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	pid = fork();
64562306a36Sopenharmony_ci	if (pid == -1)
64662306a36Sopenharmony_ci		ASSERT_EQ(pid, 0);
64762306a36Sopenharmony_ci	if (pid != 0) {
64862306a36Sopenharmony_ci		waitpid(pid, &ret, 0);
64962306a36Sopenharmony_ci		ASSERT_EQ(WIFEXITED(ret), 1);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci		/* Check that the parent's buffer did change. */
65262306a36Sopenharmony_ci		for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
65362306a36Sopenharmony_ci			ASSERT_EQ(ptr[i], -i);
65462306a36Sopenharmony_ci		return;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/* Check that we see the parent's values. */
65862306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
65962306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
66062306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
66162306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], -i);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	/* The child process needs its own mirror to its own mm. */
66462306a36Sopenharmony_ci	child_fd = hmm_open(0);
66562306a36Sopenharmony_ci	ASSERT_GE(child_fd, 0);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	/* Simulate a device writing system memory. */
66862306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(child_fd, HMM_DMIRROR_WRITE, buffer, npages);
66962306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
67062306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
67162306a36Sopenharmony_ci	ASSERT_EQ(buffer->faults, 1);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	/* Check what the device wrote. */
67462306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
67562306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], -i);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	close(child_fd);
67862306a36Sopenharmony_ci	exit(0);
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci/*
68262306a36Sopenharmony_ci * Write private anonymous huge page.
68362306a36Sopenharmony_ci */
68462306a36Sopenharmony_ciTEST_F(hmm, anon_write_huge)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct hmm_buffer *buffer;
68762306a36Sopenharmony_ci	unsigned long npages;
68862306a36Sopenharmony_ci	unsigned long size;
68962306a36Sopenharmony_ci	unsigned long i;
69062306a36Sopenharmony_ci	void *old_ptr;
69162306a36Sopenharmony_ci	void *map;
69262306a36Sopenharmony_ci	int *ptr;
69362306a36Sopenharmony_ci	int ret;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	size = 2 * TWOMEG;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
69862306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	buffer->fd = -1;
70162306a36Sopenharmony_ci	buffer->size = size;
70262306a36Sopenharmony_ci	buffer->mirror = malloc(size);
70362306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
70662306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
70762306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
70862306a36Sopenharmony_ci			   buffer->fd, 0);
70962306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	size = TWOMEG;
71262306a36Sopenharmony_ci	npages = size >> self->page_shift;
71362306a36Sopenharmony_ci	map = (void *)ALIGN((uintptr_t)buffer->ptr, size);
71462306a36Sopenharmony_ci	ret = madvise(map, size, MADV_HUGEPAGE);
71562306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
71662306a36Sopenharmony_ci	old_ptr = buffer->ptr;
71762306a36Sopenharmony_ci	buffer->ptr = map;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/* Initialize data that the device will write to buffer->ptr. */
72062306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
72162306a36Sopenharmony_ci		ptr[i] = i;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/* Simulate a device writing system memory. */
72462306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
72562306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
72662306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
72762306a36Sopenharmony_ci	ASSERT_EQ(buffer->faults, 1);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	/* Check what the device wrote. */
73062306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
73162306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	buffer->ptr = old_ptr;
73462306a36Sopenharmony_ci	hmm_buffer_free(buffer);
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci/*
73862306a36Sopenharmony_ci * Read numeric data from raw and tagged kernel status files.  Used to read
73962306a36Sopenharmony_ci * /proc and /sys data (without a tag) and from /proc/meminfo (with a tag).
74062306a36Sopenharmony_ci */
74162306a36Sopenharmony_cistatic long file_read_ulong(char *file, const char *tag)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	int fd;
74462306a36Sopenharmony_ci	char buf[2048];
74562306a36Sopenharmony_ci	int len;
74662306a36Sopenharmony_ci	char *p, *q;
74762306a36Sopenharmony_ci	long val;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	fd = open(file, O_RDONLY);
75062306a36Sopenharmony_ci	if (fd < 0) {
75162306a36Sopenharmony_ci		/* Error opening the file */
75262306a36Sopenharmony_ci		return -1;
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	len = read(fd, buf, sizeof(buf));
75662306a36Sopenharmony_ci	close(fd);
75762306a36Sopenharmony_ci	if (len < 0) {
75862306a36Sopenharmony_ci		/* Error in reading the file */
75962306a36Sopenharmony_ci		return -1;
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci	if (len == sizeof(buf)) {
76262306a36Sopenharmony_ci		/* Error file is too large */
76362306a36Sopenharmony_ci		return -1;
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci	buf[len] = '\0';
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	/* Search for a tag if provided */
76862306a36Sopenharmony_ci	if (tag) {
76962306a36Sopenharmony_ci		p = strstr(buf, tag);
77062306a36Sopenharmony_ci		if (!p)
77162306a36Sopenharmony_ci			return -1; /* looks like the line we want isn't there */
77262306a36Sopenharmony_ci		p += strlen(tag);
77362306a36Sopenharmony_ci	} else
77462306a36Sopenharmony_ci		p = buf;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	val = strtol(p, &q, 0);
77762306a36Sopenharmony_ci	if (*q != ' ') {
77862306a36Sopenharmony_ci		/* Error parsing the file */
77962306a36Sopenharmony_ci		return -1;
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	return val;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci/*
78662306a36Sopenharmony_ci * Write huge TLBFS page.
78762306a36Sopenharmony_ci */
78862306a36Sopenharmony_ciTEST_F(hmm, anon_write_hugetlbfs)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	struct hmm_buffer *buffer;
79162306a36Sopenharmony_ci	unsigned long npages;
79262306a36Sopenharmony_ci	unsigned long size;
79362306a36Sopenharmony_ci	unsigned long default_hsize;
79462306a36Sopenharmony_ci	unsigned long i;
79562306a36Sopenharmony_ci	int *ptr;
79662306a36Sopenharmony_ci	int ret;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	default_hsize = file_read_ulong("/proc/meminfo", "Hugepagesize:");
79962306a36Sopenharmony_ci	if (default_hsize < 0 || default_hsize*1024 < default_hsize)
80062306a36Sopenharmony_ci		SKIP(return, "Huge page size could not be determined");
80162306a36Sopenharmony_ci	default_hsize = default_hsize*1024; /* KB to B */
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	size = ALIGN(TWOMEG, default_hsize);
80462306a36Sopenharmony_ci	npages = size >> self->page_shift;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
80762306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
81062306a36Sopenharmony_ci				   PROT_READ | PROT_WRITE,
81162306a36Sopenharmony_ci				   MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
81262306a36Sopenharmony_ci				   -1, 0);
81362306a36Sopenharmony_ci	if (buffer->ptr == MAP_FAILED) {
81462306a36Sopenharmony_ci		free(buffer);
81562306a36Sopenharmony_ci		SKIP(return, "Huge page could not be allocated");
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	buffer->fd = -1;
81962306a36Sopenharmony_ci	buffer->size = size;
82062306a36Sopenharmony_ci	buffer->mirror = malloc(size);
82162306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	/* Initialize data that the device will write to buffer->ptr. */
82462306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
82562306a36Sopenharmony_ci		ptr[i] = i;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	/* Simulate a device writing system memory. */
82862306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
82962306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
83062306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
83162306a36Sopenharmony_ci	ASSERT_EQ(buffer->faults, 1);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	/* Check what the device wrote. */
83462306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
83562306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	munmap(buffer->ptr, buffer->size);
83862306a36Sopenharmony_ci	buffer->ptr = NULL;
83962306a36Sopenharmony_ci	hmm_buffer_free(buffer);
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci/*
84362306a36Sopenharmony_ci * Read mmap'ed file memory.
84462306a36Sopenharmony_ci */
84562306a36Sopenharmony_ciTEST_F(hmm, file_read)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	struct hmm_buffer *buffer;
84862306a36Sopenharmony_ci	unsigned long npages;
84962306a36Sopenharmony_ci	unsigned long size;
85062306a36Sopenharmony_ci	unsigned long i;
85162306a36Sopenharmony_ci	int *ptr;
85262306a36Sopenharmony_ci	int ret;
85362306a36Sopenharmony_ci	int fd;
85462306a36Sopenharmony_ci	ssize_t len;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
85762306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
85862306a36Sopenharmony_ci	size = npages << self->page_shift;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	fd = hmm_create_file(size);
86162306a36Sopenharmony_ci	ASSERT_GE(fd, 0);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
86462306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	buffer->fd = fd;
86762306a36Sopenharmony_ci	buffer->size = size;
86862306a36Sopenharmony_ci	buffer->mirror = malloc(size);
86962306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	/* Write initial contents of the file. */
87262306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
87362306a36Sopenharmony_ci		ptr[i] = i;
87462306a36Sopenharmony_ci	len = pwrite(fd, buffer->mirror, size, 0);
87562306a36Sopenharmony_ci	ASSERT_EQ(len, size);
87662306a36Sopenharmony_ci	memset(buffer->mirror, 0, size);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
87962306a36Sopenharmony_ci			   PROT_READ,
88062306a36Sopenharmony_ci			   MAP_SHARED,
88162306a36Sopenharmony_ci			   buffer->fd, 0);
88262306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	/* Simulate a device reading system memory. */
88562306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, npages);
88662306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
88762306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
88862306a36Sopenharmony_ci	ASSERT_EQ(buffer->faults, 1);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	/* Check what the device read. */
89162306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
89262306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	hmm_buffer_free(buffer);
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci/*
89862306a36Sopenharmony_ci * Write mmap'ed file memory.
89962306a36Sopenharmony_ci */
90062306a36Sopenharmony_ciTEST_F(hmm, file_write)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	struct hmm_buffer *buffer;
90362306a36Sopenharmony_ci	unsigned long npages;
90462306a36Sopenharmony_ci	unsigned long size;
90562306a36Sopenharmony_ci	unsigned long i;
90662306a36Sopenharmony_ci	int *ptr;
90762306a36Sopenharmony_ci	int ret;
90862306a36Sopenharmony_ci	int fd;
90962306a36Sopenharmony_ci	ssize_t len;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
91262306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
91362306a36Sopenharmony_ci	size = npages << self->page_shift;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	fd = hmm_create_file(size);
91662306a36Sopenharmony_ci	ASSERT_GE(fd, 0);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
91962306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	buffer->fd = fd;
92262306a36Sopenharmony_ci	buffer->size = size;
92362306a36Sopenharmony_ci	buffer->mirror = malloc(size);
92462306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
92762306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
92862306a36Sopenharmony_ci			   MAP_SHARED,
92962306a36Sopenharmony_ci			   buffer->fd, 0);
93062306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	/* Initialize data that the device will write to buffer->ptr. */
93362306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
93462306a36Sopenharmony_ci		ptr[i] = i;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	/* Simulate a device writing system memory. */
93762306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
93862306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
93962306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
94062306a36Sopenharmony_ci	ASSERT_EQ(buffer->faults, 1);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	/* Check what the device wrote. */
94362306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
94462306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	/* Check that the device also wrote the file. */
94762306a36Sopenharmony_ci	len = pread(fd, buffer->mirror, size, 0);
94862306a36Sopenharmony_ci	ASSERT_EQ(len, size);
94962306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
95062306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	hmm_buffer_free(buffer);
95362306a36Sopenharmony_ci}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci/*
95662306a36Sopenharmony_ci * Migrate anonymous memory to device private memory.
95762306a36Sopenharmony_ci */
95862306a36Sopenharmony_ciTEST_F(hmm, migrate)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	struct hmm_buffer *buffer;
96162306a36Sopenharmony_ci	unsigned long npages;
96262306a36Sopenharmony_ci	unsigned long size;
96362306a36Sopenharmony_ci	unsigned long i;
96462306a36Sopenharmony_ci	int *ptr;
96562306a36Sopenharmony_ci	int ret;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
96862306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
96962306a36Sopenharmony_ci	size = npages << self->page_shift;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
97262306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	buffer->fd = -1;
97562306a36Sopenharmony_ci	buffer->size = size;
97662306a36Sopenharmony_ci	buffer->mirror = malloc(size);
97762306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
98062306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
98162306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
98262306a36Sopenharmony_ci			   buffer->fd, 0);
98362306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	/* Initialize buffer in system memory. */
98662306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
98762306a36Sopenharmony_ci		ptr[i] = i;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	/* Migrate memory to device. */
99062306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
99162306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
99262306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	/* Check what the device read. */
99562306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
99662306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	hmm_buffer_free(buffer);
99962306a36Sopenharmony_ci}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci/*
100262306a36Sopenharmony_ci * Migrate anonymous memory to device private memory and fault some of it back
100362306a36Sopenharmony_ci * to system memory, then try migrating the resulting mix of system and device
100462306a36Sopenharmony_ci * private memory to the device.
100562306a36Sopenharmony_ci */
100662306a36Sopenharmony_ciTEST_F(hmm, migrate_fault)
100762306a36Sopenharmony_ci{
100862306a36Sopenharmony_ci	struct hmm_buffer *buffer;
100962306a36Sopenharmony_ci	unsigned long npages;
101062306a36Sopenharmony_ci	unsigned long size;
101162306a36Sopenharmony_ci	unsigned long i;
101262306a36Sopenharmony_ci	int *ptr;
101362306a36Sopenharmony_ci	int ret;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
101662306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
101762306a36Sopenharmony_ci	size = npages << self->page_shift;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
102062306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	buffer->fd = -1;
102362306a36Sopenharmony_ci	buffer->size = size;
102462306a36Sopenharmony_ci	buffer->mirror = malloc(size);
102562306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
102862306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
102962306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
103062306a36Sopenharmony_ci			   buffer->fd, 0);
103162306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	/* Initialize buffer in system memory. */
103462306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
103562306a36Sopenharmony_ci		ptr[i] = i;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	/* Migrate memory to device. */
103862306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
103962306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
104062306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	/* Check what the device read. */
104362306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
104462306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	/* Fault half the pages back to system memory and check them. */
104762306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / (2 * sizeof(*ptr)); ++i)
104862306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	/* Migrate memory to the device again. */
105162306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
105262306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
105362306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	/* Check what the device read. */
105662306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
105762306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	hmm_buffer_free(buffer);
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ciTEST_F(hmm, migrate_release)
106362306a36Sopenharmony_ci{
106462306a36Sopenharmony_ci	struct hmm_buffer *buffer;
106562306a36Sopenharmony_ci	unsigned long npages;
106662306a36Sopenharmony_ci	unsigned long size;
106762306a36Sopenharmony_ci	unsigned long i;
106862306a36Sopenharmony_ci	int *ptr;
106962306a36Sopenharmony_ci	int ret;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
107262306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
107362306a36Sopenharmony_ci	size = npages << self->page_shift;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
107662306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	buffer->fd = -1;
107962306a36Sopenharmony_ci	buffer->size = size;
108062306a36Sopenharmony_ci	buffer->mirror = malloc(size);
108162306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
108462306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS, buffer->fd, 0);
108562306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	/* Initialize buffer in system memory. */
108862306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
108962306a36Sopenharmony_ci		ptr[i] = i;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	/* Migrate memory to device. */
109262306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
109362306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
109462306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	/* Check what the device read. */
109762306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
109862306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	/* Release device memory. */
110162306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_RELEASE, buffer, npages);
110262306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	/* Fault pages back to system memory and check them. */
110562306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / (2 * sizeof(*ptr)); ++i)
110662306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	hmm_buffer_free(buffer);
110962306a36Sopenharmony_ci}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci/*
111262306a36Sopenharmony_ci * Migrate anonymous shared memory to device private memory.
111362306a36Sopenharmony_ci */
111462306a36Sopenharmony_ciTEST_F(hmm, migrate_shared)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	struct hmm_buffer *buffer;
111762306a36Sopenharmony_ci	unsigned long npages;
111862306a36Sopenharmony_ci	unsigned long size;
111962306a36Sopenharmony_ci	int ret;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
112262306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
112362306a36Sopenharmony_ci	size = npages << self->page_shift;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
112662306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	buffer->fd = -1;
112962306a36Sopenharmony_ci	buffer->size = size;
113062306a36Sopenharmony_ci	buffer->mirror = malloc(size);
113162306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
113462306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
113562306a36Sopenharmony_ci			   MAP_SHARED | MAP_ANONYMOUS,
113662306a36Sopenharmony_ci			   buffer->fd, 0);
113762306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	/* Migrate memory to device. */
114062306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
114162306a36Sopenharmony_ci	ASSERT_EQ(ret, -ENOENT);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	hmm_buffer_free(buffer);
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci/*
114762306a36Sopenharmony_ci * Try to migrate various memory types to device private memory.
114862306a36Sopenharmony_ci */
114962306a36Sopenharmony_ciTEST_F(hmm2, migrate_mixed)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	struct hmm_buffer *buffer;
115262306a36Sopenharmony_ci	unsigned long npages;
115362306a36Sopenharmony_ci	unsigned long size;
115462306a36Sopenharmony_ci	int *ptr;
115562306a36Sopenharmony_ci	unsigned char *p;
115662306a36Sopenharmony_ci	int ret;
115762306a36Sopenharmony_ci	int val;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	npages = 6;
116062306a36Sopenharmony_ci	size = npages << self->page_shift;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
116362306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	buffer->fd = -1;
116662306a36Sopenharmony_ci	buffer->size = size;
116762306a36Sopenharmony_ci	buffer->mirror = malloc(size);
116862306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	/* Reserve a range of addresses. */
117162306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
117262306a36Sopenharmony_ci			   PROT_NONE,
117362306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
117462306a36Sopenharmony_ci			   buffer->fd, 0);
117562306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
117662306a36Sopenharmony_ci	p = buffer->ptr;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	/* Migrating a protected area should be an error. */
117962306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd1, buffer, npages);
118062306a36Sopenharmony_ci	ASSERT_EQ(ret, -EINVAL);
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	/* Punch a hole after the first page address. */
118362306a36Sopenharmony_ci	ret = munmap(buffer->ptr + self->page_size, self->page_size);
118462306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	/* We expect an error if the vma doesn't cover the range. */
118762306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd1, buffer, 3);
118862306a36Sopenharmony_ci	ASSERT_EQ(ret, -EINVAL);
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	/* Page 2 will be a read-only zero page. */
119162306a36Sopenharmony_ci	ret = mprotect(buffer->ptr + 2 * self->page_size, self->page_size,
119262306a36Sopenharmony_ci				PROT_READ);
119362306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
119462306a36Sopenharmony_ci	ptr = (int *)(buffer->ptr + 2 * self->page_size);
119562306a36Sopenharmony_ci	val = *ptr + 3;
119662306a36Sopenharmony_ci	ASSERT_EQ(val, 3);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	/* Page 3 will be read-only. */
119962306a36Sopenharmony_ci	ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size,
120062306a36Sopenharmony_ci				PROT_READ | PROT_WRITE);
120162306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
120262306a36Sopenharmony_ci	ptr = (int *)(buffer->ptr + 3 * self->page_size);
120362306a36Sopenharmony_ci	*ptr = val;
120462306a36Sopenharmony_ci	ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size,
120562306a36Sopenharmony_ci				PROT_READ);
120662306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	/* Page 4-5 will be read-write. */
120962306a36Sopenharmony_ci	ret = mprotect(buffer->ptr + 4 * self->page_size, 2 * self->page_size,
121062306a36Sopenharmony_ci				PROT_READ | PROT_WRITE);
121162306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
121262306a36Sopenharmony_ci	ptr = (int *)(buffer->ptr + 4 * self->page_size);
121362306a36Sopenharmony_ci	*ptr = val;
121462306a36Sopenharmony_ci	ptr = (int *)(buffer->ptr + 5 * self->page_size);
121562306a36Sopenharmony_ci	*ptr = val;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	/* Now try to migrate pages 2-5 to device 1. */
121862306a36Sopenharmony_ci	buffer->ptr = p + 2 * self->page_size;
121962306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd1, buffer, 4);
122062306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
122162306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, 4);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	/* Page 5 won't be migrated to device 0 because it's on device 1. */
122462306a36Sopenharmony_ci	buffer->ptr = p + 5 * self->page_size;
122562306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd0, buffer, 1);
122662306a36Sopenharmony_ci	ASSERT_EQ(ret, -ENOENT);
122762306a36Sopenharmony_ci	buffer->ptr = p;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	buffer->ptr = p;
123062306a36Sopenharmony_ci	hmm_buffer_free(buffer);
123162306a36Sopenharmony_ci}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci/*
123462306a36Sopenharmony_ci * Migrate anonymous memory to device memory and back to system memory
123562306a36Sopenharmony_ci * multiple times. In case of private zone configuration, this is done
123662306a36Sopenharmony_ci * through fault pages accessed by CPU. In case of coherent zone configuration,
123762306a36Sopenharmony_ci * the pages from the device should be explicitly migrated back to system memory.
123862306a36Sopenharmony_ci * The reason is Coherent device zone has coherent access by CPU, therefore
123962306a36Sopenharmony_ci * it will not generate any page fault.
124062306a36Sopenharmony_ci */
124162306a36Sopenharmony_ciTEST_F(hmm, migrate_multiple)
124262306a36Sopenharmony_ci{
124362306a36Sopenharmony_ci	struct hmm_buffer *buffer;
124462306a36Sopenharmony_ci	unsigned long npages;
124562306a36Sopenharmony_ci	unsigned long size;
124662306a36Sopenharmony_ci	unsigned long i;
124762306a36Sopenharmony_ci	unsigned long c;
124862306a36Sopenharmony_ci	int *ptr;
124962306a36Sopenharmony_ci	int ret;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
125262306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
125362306a36Sopenharmony_ci	size = npages << self->page_shift;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	for (c = 0; c < NTIMES; c++) {
125662306a36Sopenharmony_ci		buffer = malloc(sizeof(*buffer));
125762306a36Sopenharmony_ci		ASSERT_NE(buffer, NULL);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci		buffer->fd = -1;
126062306a36Sopenharmony_ci		buffer->size = size;
126162306a36Sopenharmony_ci		buffer->mirror = malloc(size);
126262306a36Sopenharmony_ci		ASSERT_NE(buffer->mirror, NULL);
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci		buffer->ptr = mmap(NULL, size,
126562306a36Sopenharmony_ci				   PROT_READ | PROT_WRITE,
126662306a36Sopenharmony_ci				   MAP_PRIVATE | MAP_ANONYMOUS,
126762306a36Sopenharmony_ci				   buffer->fd, 0);
126862306a36Sopenharmony_ci		ASSERT_NE(buffer->ptr, MAP_FAILED);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci		/* Initialize buffer in system memory. */
127162306a36Sopenharmony_ci		for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
127262306a36Sopenharmony_ci			ptr[i] = i;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci		/* Migrate memory to device. */
127562306a36Sopenharmony_ci		ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
127662306a36Sopenharmony_ci		ASSERT_EQ(ret, 0);
127762306a36Sopenharmony_ci		ASSERT_EQ(buffer->cpages, npages);
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci		/* Check what the device read. */
128062306a36Sopenharmony_ci		for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
128162306a36Sopenharmony_ci			ASSERT_EQ(ptr[i], i);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci		/* Migrate back to system memory and check them. */
128462306a36Sopenharmony_ci		if (hmm_is_coherent_type(variant->device_number)) {
128562306a36Sopenharmony_ci			ret = hmm_migrate_dev_to_sys(self->fd, buffer, npages);
128662306a36Sopenharmony_ci			ASSERT_EQ(ret, 0);
128762306a36Sopenharmony_ci			ASSERT_EQ(buffer->cpages, npages);
128862306a36Sopenharmony_ci		}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci		for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
129162306a36Sopenharmony_ci			ASSERT_EQ(ptr[i], i);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci		hmm_buffer_free(buffer);
129462306a36Sopenharmony_ci	}
129562306a36Sopenharmony_ci}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci/*
129862306a36Sopenharmony_ci * Read anonymous memory multiple times.
129962306a36Sopenharmony_ci */
130062306a36Sopenharmony_ciTEST_F(hmm, anon_read_multiple)
130162306a36Sopenharmony_ci{
130262306a36Sopenharmony_ci	struct hmm_buffer *buffer;
130362306a36Sopenharmony_ci	unsigned long npages;
130462306a36Sopenharmony_ci	unsigned long size;
130562306a36Sopenharmony_ci	unsigned long i;
130662306a36Sopenharmony_ci	unsigned long c;
130762306a36Sopenharmony_ci	int *ptr;
130862306a36Sopenharmony_ci	int ret;
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
131162306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
131262306a36Sopenharmony_ci	size = npages << self->page_shift;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	for (c = 0; c < NTIMES; c++) {
131562306a36Sopenharmony_ci		buffer = malloc(sizeof(*buffer));
131662306a36Sopenharmony_ci		ASSERT_NE(buffer, NULL);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci		buffer->fd = -1;
131962306a36Sopenharmony_ci		buffer->size = size;
132062306a36Sopenharmony_ci		buffer->mirror = malloc(size);
132162306a36Sopenharmony_ci		ASSERT_NE(buffer->mirror, NULL);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci		buffer->ptr = mmap(NULL, size,
132462306a36Sopenharmony_ci				   PROT_READ | PROT_WRITE,
132562306a36Sopenharmony_ci				   MAP_PRIVATE | MAP_ANONYMOUS,
132662306a36Sopenharmony_ci				   buffer->fd, 0);
132762306a36Sopenharmony_ci		ASSERT_NE(buffer->ptr, MAP_FAILED);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci		/* Initialize buffer in system memory. */
133062306a36Sopenharmony_ci		for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
133162306a36Sopenharmony_ci			ptr[i] = i + c;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci		/* Simulate a device reading system memory. */
133462306a36Sopenharmony_ci		ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer,
133562306a36Sopenharmony_ci				      npages);
133662306a36Sopenharmony_ci		ASSERT_EQ(ret, 0);
133762306a36Sopenharmony_ci		ASSERT_EQ(buffer->cpages, npages);
133862306a36Sopenharmony_ci		ASSERT_EQ(buffer->faults, 1);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci		/* Check what the device read. */
134162306a36Sopenharmony_ci		for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
134262306a36Sopenharmony_ci			ASSERT_EQ(ptr[i], i + c);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci		hmm_buffer_free(buffer);
134562306a36Sopenharmony_ci	}
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_civoid *unmap_buffer(void *p)
134962306a36Sopenharmony_ci{
135062306a36Sopenharmony_ci	struct hmm_buffer *buffer = p;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	/* Delay for a bit and then unmap buffer while it is being read. */
135362306a36Sopenharmony_ci	hmm_nanosleep(hmm_random() % 32000);
135462306a36Sopenharmony_ci	munmap(buffer->ptr + buffer->size / 2, buffer->size / 2);
135562306a36Sopenharmony_ci	buffer->ptr = NULL;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	return NULL;
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci/*
136162306a36Sopenharmony_ci * Try reading anonymous memory while it is being unmapped.
136262306a36Sopenharmony_ci */
136362306a36Sopenharmony_ciTEST_F(hmm, anon_teardown)
136462306a36Sopenharmony_ci{
136562306a36Sopenharmony_ci	unsigned long npages;
136662306a36Sopenharmony_ci	unsigned long size;
136762306a36Sopenharmony_ci	unsigned long c;
136862306a36Sopenharmony_ci	void *ret;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
137162306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
137262306a36Sopenharmony_ci	size = npages << self->page_shift;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	for (c = 0; c < NTIMES; ++c) {
137562306a36Sopenharmony_ci		pthread_t thread;
137662306a36Sopenharmony_ci		struct hmm_buffer *buffer;
137762306a36Sopenharmony_ci		unsigned long i;
137862306a36Sopenharmony_ci		int *ptr;
137962306a36Sopenharmony_ci		int rc;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci		buffer = malloc(sizeof(*buffer));
138262306a36Sopenharmony_ci		ASSERT_NE(buffer, NULL);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci		buffer->fd = -1;
138562306a36Sopenharmony_ci		buffer->size = size;
138662306a36Sopenharmony_ci		buffer->mirror = malloc(size);
138762306a36Sopenharmony_ci		ASSERT_NE(buffer->mirror, NULL);
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci		buffer->ptr = mmap(NULL, size,
139062306a36Sopenharmony_ci				   PROT_READ | PROT_WRITE,
139162306a36Sopenharmony_ci				   MAP_PRIVATE | MAP_ANONYMOUS,
139262306a36Sopenharmony_ci				   buffer->fd, 0);
139362306a36Sopenharmony_ci		ASSERT_NE(buffer->ptr, MAP_FAILED);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci		/* Initialize buffer in system memory. */
139662306a36Sopenharmony_ci		for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
139762306a36Sopenharmony_ci			ptr[i] = i + c;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci		rc = pthread_create(&thread, NULL, unmap_buffer, buffer);
140062306a36Sopenharmony_ci		ASSERT_EQ(rc, 0);
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci		/* Simulate a device reading system memory. */
140362306a36Sopenharmony_ci		rc = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer,
140462306a36Sopenharmony_ci				     npages);
140562306a36Sopenharmony_ci		if (rc == 0) {
140662306a36Sopenharmony_ci			ASSERT_EQ(buffer->cpages, npages);
140762306a36Sopenharmony_ci			ASSERT_EQ(buffer->faults, 1);
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci			/* Check what the device read. */
141062306a36Sopenharmony_ci			for (i = 0, ptr = buffer->mirror;
141162306a36Sopenharmony_ci			     i < size / sizeof(*ptr);
141262306a36Sopenharmony_ci			     ++i)
141362306a36Sopenharmony_ci				ASSERT_EQ(ptr[i], i + c);
141462306a36Sopenharmony_ci		}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci		pthread_join(thread, &ret);
141762306a36Sopenharmony_ci		hmm_buffer_free(buffer);
141862306a36Sopenharmony_ci	}
141962306a36Sopenharmony_ci}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci/*
142262306a36Sopenharmony_ci * Test memory snapshot without faulting in pages accessed by the device.
142362306a36Sopenharmony_ci */
142462306a36Sopenharmony_ciTEST_F(hmm, mixedmap)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	struct hmm_buffer *buffer;
142762306a36Sopenharmony_ci	unsigned long npages;
142862306a36Sopenharmony_ci	unsigned long size;
142962306a36Sopenharmony_ci	unsigned char *m;
143062306a36Sopenharmony_ci	int ret;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	npages = 1;
143362306a36Sopenharmony_ci	size = npages << self->page_shift;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
143662306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	buffer->fd = -1;
143962306a36Sopenharmony_ci	buffer->size = size;
144062306a36Sopenharmony_ci	buffer->mirror = malloc(npages);
144162306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	/* Reserve a range of addresses. */
144562306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
144662306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
144762306a36Sopenharmony_ci			   MAP_PRIVATE,
144862306a36Sopenharmony_ci			   self->fd, 0);
144962306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	/* Simulate a device snapshotting CPU pagetables. */
145262306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages);
145362306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
145462306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	/* Check what the device saw. */
145762306a36Sopenharmony_ci	m = buffer->mirror;
145862306a36Sopenharmony_ci	ASSERT_EQ(m[0], HMM_DMIRROR_PROT_READ);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	hmm_buffer_free(buffer);
146162306a36Sopenharmony_ci}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci/*
146462306a36Sopenharmony_ci * Test memory snapshot without faulting in pages accessed by the device.
146562306a36Sopenharmony_ci */
146662306a36Sopenharmony_ciTEST_F(hmm2, snapshot)
146762306a36Sopenharmony_ci{
146862306a36Sopenharmony_ci	struct hmm_buffer *buffer;
146962306a36Sopenharmony_ci	unsigned long npages;
147062306a36Sopenharmony_ci	unsigned long size;
147162306a36Sopenharmony_ci	int *ptr;
147262306a36Sopenharmony_ci	unsigned char *p;
147362306a36Sopenharmony_ci	unsigned char *m;
147462306a36Sopenharmony_ci	int ret;
147562306a36Sopenharmony_ci	int val;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	npages = 7;
147862306a36Sopenharmony_ci	size = npages << self->page_shift;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
148162306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	buffer->fd = -1;
148462306a36Sopenharmony_ci	buffer->size = size;
148562306a36Sopenharmony_ci	buffer->mirror = malloc(npages);
148662306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	/* Reserve a range of addresses. */
148962306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
149062306a36Sopenharmony_ci			   PROT_NONE,
149162306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
149262306a36Sopenharmony_ci			   buffer->fd, 0);
149362306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
149462306a36Sopenharmony_ci	p = buffer->ptr;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	/* Punch a hole after the first page address. */
149762306a36Sopenharmony_ci	ret = munmap(buffer->ptr + self->page_size, self->page_size);
149862306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	/* Page 2 will be read-only zero page. */
150162306a36Sopenharmony_ci	ret = mprotect(buffer->ptr + 2 * self->page_size, self->page_size,
150262306a36Sopenharmony_ci				PROT_READ);
150362306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
150462306a36Sopenharmony_ci	ptr = (int *)(buffer->ptr + 2 * self->page_size);
150562306a36Sopenharmony_ci	val = *ptr + 3;
150662306a36Sopenharmony_ci	ASSERT_EQ(val, 3);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	/* Page 3 will be read-only. */
150962306a36Sopenharmony_ci	ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size,
151062306a36Sopenharmony_ci				PROT_READ | PROT_WRITE);
151162306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
151262306a36Sopenharmony_ci	ptr = (int *)(buffer->ptr + 3 * self->page_size);
151362306a36Sopenharmony_ci	*ptr = val;
151462306a36Sopenharmony_ci	ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size,
151562306a36Sopenharmony_ci				PROT_READ);
151662306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	/* Page 4-6 will be read-write. */
151962306a36Sopenharmony_ci	ret = mprotect(buffer->ptr + 4 * self->page_size, 3 * self->page_size,
152062306a36Sopenharmony_ci				PROT_READ | PROT_WRITE);
152162306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
152262306a36Sopenharmony_ci	ptr = (int *)(buffer->ptr + 4 * self->page_size);
152362306a36Sopenharmony_ci	*ptr = val;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	/* Page 5 will be migrated to device 0. */
152662306a36Sopenharmony_ci	buffer->ptr = p + 5 * self->page_size;
152762306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd0, buffer, 1);
152862306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
152962306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, 1);
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	/* Page 6 will be migrated to device 1. */
153262306a36Sopenharmony_ci	buffer->ptr = p + 6 * self->page_size;
153362306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd1, buffer, 1);
153462306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
153562306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, 1);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	/* Simulate a device snapshotting CPU pagetables. */
153862306a36Sopenharmony_ci	buffer->ptr = p;
153962306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_SNAPSHOT, buffer, npages);
154062306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
154162306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	/* Check what the device saw. */
154462306a36Sopenharmony_ci	m = buffer->mirror;
154562306a36Sopenharmony_ci	ASSERT_EQ(m[0], HMM_DMIRROR_PROT_ERROR);
154662306a36Sopenharmony_ci	ASSERT_EQ(m[1], HMM_DMIRROR_PROT_ERROR);
154762306a36Sopenharmony_ci	ASSERT_EQ(m[2], HMM_DMIRROR_PROT_ZERO | HMM_DMIRROR_PROT_READ);
154862306a36Sopenharmony_ci	ASSERT_EQ(m[3], HMM_DMIRROR_PROT_READ);
154962306a36Sopenharmony_ci	ASSERT_EQ(m[4], HMM_DMIRROR_PROT_WRITE);
155062306a36Sopenharmony_ci	if (!hmm_is_coherent_type(variant->device_number0)) {
155162306a36Sopenharmony_ci		ASSERT_EQ(m[5], HMM_DMIRROR_PROT_DEV_PRIVATE_LOCAL |
155262306a36Sopenharmony_ci				HMM_DMIRROR_PROT_WRITE);
155362306a36Sopenharmony_ci		ASSERT_EQ(m[6], HMM_DMIRROR_PROT_NONE);
155462306a36Sopenharmony_ci	} else {
155562306a36Sopenharmony_ci		ASSERT_EQ(m[5], HMM_DMIRROR_PROT_DEV_COHERENT_LOCAL |
155662306a36Sopenharmony_ci				HMM_DMIRROR_PROT_WRITE);
155762306a36Sopenharmony_ci		ASSERT_EQ(m[6], HMM_DMIRROR_PROT_DEV_COHERENT_REMOTE |
155862306a36Sopenharmony_ci				HMM_DMIRROR_PROT_WRITE);
155962306a36Sopenharmony_ci	}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	hmm_buffer_free(buffer);
156262306a36Sopenharmony_ci}
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci/*
156562306a36Sopenharmony_ci * Test the hmm_range_fault() HMM_PFN_PMD flag for large pages that
156662306a36Sopenharmony_ci * should be mapped by a large page table entry.
156762306a36Sopenharmony_ci */
156862306a36Sopenharmony_ciTEST_F(hmm, compound)
156962306a36Sopenharmony_ci{
157062306a36Sopenharmony_ci	struct hmm_buffer *buffer;
157162306a36Sopenharmony_ci	unsigned long npages;
157262306a36Sopenharmony_ci	unsigned long size;
157362306a36Sopenharmony_ci	unsigned long default_hsize;
157462306a36Sopenharmony_ci	int *ptr;
157562306a36Sopenharmony_ci	unsigned char *m;
157662306a36Sopenharmony_ci	int ret;
157762306a36Sopenharmony_ci	unsigned long i;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	/* Skip test if we can't allocate a hugetlbfs page. */
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	default_hsize = file_read_ulong("/proc/meminfo", "Hugepagesize:");
158262306a36Sopenharmony_ci	if (default_hsize < 0 || default_hsize*1024 < default_hsize)
158362306a36Sopenharmony_ci		SKIP(return, "Huge page size could not be determined");
158462306a36Sopenharmony_ci	default_hsize = default_hsize*1024; /* KB to B */
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	size = ALIGN(TWOMEG, default_hsize);
158762306a36Sopenharmony_ci	npages = size >> self->page_shift;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
159062306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
159362306a36Sopenharmony_ci				   PROT_READ | PROT_WRITE,
159462306a36Sopenharmony_ci				   MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
159562306a36Sopenharmony_ci				   -1, 0);
159662306a36Sopenharmony_ci	if (buffer->ptr == MAP_FAILED) {
159762306a36Sopenharmony_ci		free(buffer);
159862306a36Sopenharmony_ci		return;
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	buffer->size = size;
160262306a36Sopenharmony_ci	buffer->mirror = malloc(npages);
160362306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	/* Initialize the pages the device will snapshot in buffer->ptr. */
160662306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
160762306a36Sopenharmony_ci		ptr[i] = i;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	/* Simulate a device snapshotting CPU pagetables. */
161062306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages);
161162306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
161262306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	/* Check what the device saw. */
161562306a36Sopenharmony_ci	m = buffer->mirror;
161662306a36Sopenharmony_ci	for (i = 0; i < npages; ++i)
161762306a36Sopenharmony_ci		ASSERT_EQ(m[i], HMM_DMIRROR_PROT_WRITE |
161862306a36Sopenharmony_ci				HMM_DMIRROR_PROT_PMD);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	/* Make the region read-only. */
162162306a36Sopenharmony_ci	ret = mprotect(buffer->ptr, size, PROT_READ);
162262306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	/* Simulate a device snapshotting CPU pagetables. */
162562306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages);
162662306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
162762306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	/* Check what the device saw. */
163062306a36Sopenharmony_ci	m = buffer->mirror;
163162306a36Sopenharmony_ci	for (i = 0; i < npages; ++i)
163262306a36Sopenharmony_ci		ASSERT_EQ(m[i], HMM_DMIRROR_PROT_READ |
163362306a36Sopenharmony_ci				HMM_DMIRROR_PROT_PMD);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	munmap(buffer->ptr, buffer->size);
163662306a36Sopenharmony_ci	buffer->ptr = NULL;
163762306a36Sopenharmony_ci	hmm_buffer_free(buffer);
163862306a36Sopenharmony_ci}
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci/*
164162306a36Sopenharmony_ci * Test two devices reading the same memory (double mapped).
164262306a36Sopenharmony_ci */
164362306a36Sopenharmony_ciTEST_F(hmm2, double_map)
164462306a36Sopenharmony_ci{
164562306a36Sopenharmony_ci	struct hmm_buffer *buffer;
164662306a36Sopenharmony_ci	unsigned long npages;
164762306a36Sopenharmony_ci	unsigned long size;
164862306a36Sopenharmony_ci	unsigned long i;
164962306a36Sopenharmony_ci	int *ptr;
165062306a36Sopenharmony_ci	int ret;
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	npages = 6;
165362306a36Sopenharmony_ci	size = npages << self->page_shift;
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
165662306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	buffer->fd = -1;
165962306a36Sopenharmony_ci	buffer->size = size;
166062306a36Sopenharmony_ci	buffer->mirror = malloc(npages);
166162306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	/* Reserve a range of addresses. */
166462306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
166562306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
166662306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
166762306a36Sopenharmony_ci			   buffer->fd, 0);
166862306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	/* Initialize buffer in system memory. */
167162306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
167262306a36Sopenharmony_ci		ptr[i] = i;
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	/* Make region read-only. */
167562306a36Sopenharmony_ci	ret = mprotect(buffer->ptr, size, PROT_READ);
167662306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	/* Simulate device 0 reading system memory. */
167962306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_READ, buffer, npages);
168062306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
168162306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
168262306a36Sopenharmony_ci	ASSERT_EQ(buffer->faults, 1);
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	/* Check what the device read. */
168562306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
168662306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	/* Simulate device 1 reading system memory. */
168962306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_READ, buffer, npages);
169062306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
169162306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
169262306a36Sopenharmony_ci	ASSERT_EQ(buffer->faults, 1);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	/* Check what the device read. */
169562306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
169662306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	/* Migrate pages to device 1 and try to read from device 0. */
169962306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd1, buffer, npages);
170062306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
170162306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_READ, buffer, npages);
170462306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
170562306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
170662306a36Sopenharmony_ci	ASSERT_EQ(buffer->faults, 1);
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	/* Check what device 0 read. */
170962306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
171062306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	hmm_buffer_free(buffer);
171362306a36Sopenharmony_ci}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci/*
171662306a36Sopenharmony_ci * Basic check of exclusive faulting.
171762306a36Sopenharmony_ci */
171862306a36Sopenharmony_ciTEST_F(hmm, exclusive)
171962306a36Sopenharmony_ci{
172062306a36Sopenharmony_ci	struct hmm_buffer *buffer;
172162306a36Sopenharmony_ci	unsigned long npages;
172262306a36Sopenharmony_ci	unsigned long size;
172362306a36Sopenharmony_ci	unsigned long i;
172462306a36Sopenharmony_ci	int *ptr;
172562306a36Sopenharmony_ci	int ret;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
172862306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
172962306a36Sopenharmony_ci	size = npages << self->page_shift;
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
173262306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	buffer->fd = -1;
173562306a36Sopenharmony_ci	buffer->size = size;
173662306a36Sopenharmony_ci	buffer->mirror = malloc(size);
173762306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
174062306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
174162306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
174262306a36Sopenharmony_ci			   buffer->fd, 0);
174362306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	/* Initialize buffer in system memory. */
174662306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
174762306a36Sopenharmony_ci		ptr[i] = i;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	/* Map memory exclusively for device access. */
175062306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_EXCLUSIVE, buffer, npages);
175162306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
175262306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	/* Check what the device read. */
175562306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
175662306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	/* Fault pages back to system memory and check them. */
175962306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
176062306a36Sopenharmony_ci		ASSERT_EQ(ptr[i]++, i);
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
176362306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i+1);
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	/* Check atomic access revoked */
176662306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_CHECK_EXCLUSIVE, buffer, npages);
176762306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	hmm_buffer_free(buffer);
177062306a36Sopenharmony_ci}
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ciTEST_F(hmm, exclusive_mprotect)
177362306a36Sopenharmony_ci{
177462306a36Sopenharmony_ci	struct hmm_buffer *buffer;
177562306a36Sopenharmony_ci	unsigned long npages;
177662306a36Sopenharmony_ci	unsigned long size;
177762306a36Sopenharmony_ci	unsigned long i;
177862306a36Sopenharmony_ci	int *ptr;
177962306a36Sopenharmony_ci	int ret;
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
178262306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
178362306a36Sopenharmony_ci	size = npages << self->page_shift;
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
178662306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	buffer->fd = -1;
178962306a36Sopenharmony_ci	buffer->size = size;
179062306a36Sopenharmony_ci	buffer->mirror = malloc(size);
179162306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
179462306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
179562306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
179662306a36Sopenharmony_ci			   buffer->fd, 0);
179762306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	/* Initialize buffer in system memory. */
180062306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
180162306a36Sopenharmony_ci		ptr[i] = i;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	/* Map memory exclusively for device access. */
180462306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_EXCLUSIVE, buffer, npages);
180562306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
180662306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	/* Check what the device read. */
180962306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
181062306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	ret = mprotect(buffer->ptr, size, PROT_READ);
181362306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	/* Simulate a device writing system memory. */
181662306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
181762306a36Sopenharmony_ci	ASSERT_EQ(ret, -EPERM);
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	hmm_buffer_free(buffer);
182062306a36Sopenharmony_ci}
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci/*
182362306a36Sopenharmony_ci * Check copy-on-write works.
182462306a36Sopenharmony_ci */
182562306a36Sopenharmony_ciTEST_F(hmm, exclusive_cow)
182662306a36Sopenharmony_ci{
182762306a36Sopenharmony_ci	struct hmm_buffer *buffer;
182862306a36Sopenharmony_ci	unsigned long npages;
182962306a36Sopenharmony_ci	unsigned long size;
183062306a36Sopenharmony_ci	unsigned long i;
183162306a36Sopenharmony_ci	int *ptr;
183262306a36Sopenharmony_ci	int ret;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
183562306a36Sopenharmony_ci	ASSERT_NE(npages, 0);
183662306a36Sopenharmony_ci	size = npages << self->page_shift;
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
183962306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	buffer->fd = -1;
184262306a36Sopenharmony_ci	buffer->size = size;
184362306a36Sopenharmony_ci	buffer->mirror = malloc(size);
184462306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
184762306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
184862306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
184962306a36Sopenharmony_ci			   buffer->fd, 0);
185062306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	/* Initialize buffer in system memory. */
185362306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
185462306a36Sopenharmony_ci		ptr[i] = i;
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	/* Map memory exclusively for device access. */
185762306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_EXCLUSIVE, buffer, npages);
185862306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
185962306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	fork();
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	/* Fault pages back to system memory and check them. */
186462306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
186562306a36Sopenharmony_ci		ASSERT_EQ(ptr[i]++, i);
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
186862306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i+1);
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci	hmm_buffer_free(buffer);
187162306a36Sopenharmony_ci}
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_cistatic int gup_test_exec(int gup_fd, unsigned long addr, int cmd,
187462306a36Sopenharmony_ci			 int npages, int size, int flags)
187562306a36Sopenharmony_ci{
187662306a36Sopenharmony_ci	struct gup_test gup = {
187762306a36Sopenharmony_ci		.nr_pages_per_call	= npages,
187862306a36Sopenharmony_ci		.addr			= addr,
187962306a36Sopenharmony_ci		.gup_flags		= FOLL_WRITE | flags,
188062306a36Sopenharmony_ci		.size			= size,
188162306a36Sopenharmony_ci	};
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	if (ioctl(gup_fd, cmd, &gup)) {
188462306a36Sopenharmony_ci		perror("ioctl on error\n");
188562306a36Sopenharmony_ci		return errno;
188662306a36Sopenharmony_ci	}
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	return 0;
188962306a36Sopenharmony_ci}
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci/*
189262306a36Sopenharmony_ci * Test get user device pages through gup_test. Setting PIN_LONGTERM flag.
189362306a36Sopenharmony_ci * This should trigger a migration back to system memory for both, private
189462306a36Sopenharmony_ci * and coherent type pages.
189562306a36Sopenharmony_ci * This test makes use of gup_test module. Make sure GUP_TEST_CONFIG is added
189662306a36Sopenharmony_ci * to your configuration before you run it.
189762306a36Sopenharmony_ci */
189862306a36Sopenharmony_ciTEST_F(hmm, hmm_gup_test)
189962306a36Sopenharmony_ci{
190062306a36Sopenharmony_ci	struct hmm_buffer *buffer;
190162306a36Sopenharmony_ci	int gup_fd;
190262306a36Sopenharmony_ci	unsigned long npages;
190362306a36Sopenharmony_ci	unsigned long size;
190462306a36Sopenharmony_ci	unsigned long i;
190562306a36Sopenharmony_ci	int *ptr;
190662306a36Sopenharmony_ci	int ret;
190762306a36Sopenharmony_ci	unsigned char *m;
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	gup_fd = open("/sys/kernel/debug/gup_test", O_RDWR);
191062306a36Sopenharmony_ci	if (gup_fd == -1)
191162306a36Sopenharmony_ci		SKIP(return, "Skipping test, could not find gup_test driver");
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	npages = 4;
191462306a36Sopenharmony_ci	size = npages << self->page_shift;
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
191762306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	buffer->fd = -1;
192062306a36Sopenharmony_ci	buffer->size = size;
192162306a36Sopenharmony_ci	buffer->mirror = malloc(size);
192262306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
192562306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
192662306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
192762306a36Sopenharmony_ci			   buffer->fd, 0);
192862306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	/* Initialize buffer in system memory. */
193162306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
193262306a36Sopenharmony_ci		ptr[i] = i;
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	/* Migrate memory to device. */
193562306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
193662306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
193762306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
193862306a36Sopenharmony_ci	/* Check what the device read. */
193962306a36Sopenharmony_ci	for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
194062306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci	ASSERT_EQ(gup_test_exec(gup_fd,
194362306a36Sopenharmony_ci				(unsigned long)buffer->ptr,
194462306a36Sopenharmony_ci				GUP_BASIC_TEST, 1, self->page_size, 0), 0);
194562306a36Sopenharmony_ci	ASSERT_EQ(gup_test_exec(gup_fd,
194662306a36Sopenharmony_ci				(unsigned long)buffer->ptr + 1 * self->page_size,
194762306a36Sopenharmony_ci				GUP_FAST_BENCHMARK, 1, self->page_size, 0), 0);
194862306a36Sopenharmony_ci	ASSERT_EQ(gup_test_exec(gup_fd,
194962306a36Sopenharmony_ci				(unsigned long)buffer->ptr + 2 * self->page_size,
195062306a36Sopenharmony_ci				PIN_FAST_BENCHMARK, 1, self->page_size, FOLL_LONGTERM), 0);
195162306a36Sopenharmony_ci	ASSERT_EQ(gup_test_exec(gup_fd,
195262306a36Sopenharmony_ci				(unsigned long)buffer->ptr + 3 * self->page_size,
195362306a36Sopenharmony_ci				PIN_LONGTERM_BENCHMARK, 1, self->page_size, 0), 0);
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	/* Take snapshot to CPU pagetables */
195662306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages);
195762306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
195862306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
195962306a36Sopenharmony_ci	m = buffer->mirror;
196062306a36Sopenharmony_ci	if (hmm_is_coherent_type(variant->device_number)) {
196162306a36Sopenharmony_ci		ASSERT_EQ(HMM_DMIRROR_PROT_DEV_COHERENT_LOCAL | HMM_DMIRROR_PROT_WRITE, m[0]);
196262306a36Sopenharmony_ci		ASSERT_EQ(HMM_DMIRROR_PROT_DEV_COHERENT_LOCAL | HMM_DMIRROR_PROT_WRITE, m[1]);
196362306a36Sopenharmony_ci	} else {
196462306a36Sopenharmony_ci		ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[0]);
196562306a36Sopenharmony_ci		ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[1]);
196662306a36Sopenharmony_ci	}
196762306a36Sopenharmony_ci	ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[2]);
196862306a36Sopenharmony_ci	ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[3]);
196962306a36Sopenharmony_ci	/*
197062306a36Sopenharmony_ci	 * Check again the content on the pages. Make sure there's no
197162306a36Sopenharmony_ci	 * corrupted data.
197262306a36Sopenharmony_ci	 */
197362306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
197462306a36Sopenharmony_ci		ASSERT_EQ(ptr[i], i);
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	close(gup_fd);
197762306a36Sopenharmony_ci	hmm_buffer_free(buffer);
197862306a36Sopenharmony_ci}
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci/*
198162306a36Sopenharmony_ci * Test copy-on-write in device pages.
198262306a36Sopenharmony_ci * In case of writing to COW private page(s), a page fault will migrate pages
198362306a36Sopenharmony_ci * back to system memory first. Then, these pages will be duplicated. In case
198462306a36Sopenharmony_ci * of COW device coherent type, pages are duplicated directly from device
198562306a36Sopenharmony_ci * memory.
198662306a36Sopenharmony_ci */
198762306a36Sopenharmony_ciTEST_F(hmm, hmm_cow_in_device)
198862306a36Sopenharmony_ci{
198962306a36Sopenharmony_ci	struct hmm_buffer *buffer;
199062306a36Sopenharmony_ci	unsigned long npages;
199162306a36Sopenharmony_ci	unsigned long size;
199262306a36Sopenharmony_ci	unsigned long i;
199362306a36Sopenharmony_ci	int *ptr;
199462306a36Sopenharmony_ci	int ret;
199562306a36Sopenharmony_ci	unsigned char *m;
199662306a36Sopenharmony_ci	pid_t pid;
199762306a36Sopenharmony_ci	int status;
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	npages = 4;
200062306a36Sopenharmony_ci	size = npages << self->page_shift;
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	buffer = malloc(sizeof(*buffer));
200362306a36Sopenharmony_ci	ASSERT_NE(buffer, NULL);
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	buffer->fd = -1;
200662306a36Sopenharmony_ci	buffer->size = size;
200762306a36Sopenharmony_ci	buffer->mirror = malloc(size);
200862306a36Sopenharmony_ci	ASSERT_NE(buffer->mirror, NULL);
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	buffer->ptr = mmap(NULL, size,
201162306a36Sopenharmony_ci			   PROT_READ | PROT_WRITE,
201262306a36Sopenharmony_ci			   MAP_PRIVATE | MAP_ANONYMOUS,
201362306a36Sopenharmony_ci			   buffer->fd, 0);
201462306a36Sopenharmony_ci	ASSERT_NE(buffer->ptr, MAP_FAILED);
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	/* Initialize buffer in system memory. */
201762306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
201862306a36Sopenharmony_ci		ptr[i] = i;
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	/* Migrate memory to device. */
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
202362306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
202462306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	pid = fork();
202762306a36Sopenharmony_ci	if (pid == -1)
202862306a36Sopenharmony_ci		ASSERT_EQ(pid, 0);
202962306a36Sopenharmony_ci	if (!pid) {
203062306a36Sopenharmony_ci		/* Child process waitd for SIGTERM from the parent. */
203162306a36Sopenharmony_ci		while (1) {
203262306a36Sopenharmony_ci		}
203362306a36Sopenharmony_ci		perror("Should not reach this\n");
203462306a36Sopenharmony_ci		exit(0);
203562306a36Sopenharmony_ci	}
203662306a36Sopenharmony_ci	/* Parent process writes to COW pages(s) and gets a
203762306a36Sopenharmony_ci	 * new copy in system. In case of device private pages,
203862306a36Sopenharmony_ci	 * this write causes a migration to system mem first.
203962306a36Sopenharmony_ci	 */
204062306a36Sopenharmony_ci	for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
204162306a36Sopenharmony_ci		ptr[i] = i;
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	/* Terminate child and wait */
204462306a36Sopenharmony_ci	EXPECT_EQ(0, kill(pid, SIGTERM));
204562306a36Sopenharmony_ci	EXPECT_EQ(pid, waitpid(pid, &status, 0));
204662306a36Sopenharmony_ci	EXPECT_NE(0, WIFSIGNALED(status));
204762306a36Sopenharmony_ci	EXPECT_EQ(SIGTERM, WTERMSIG(status));
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci	/* Take snapshot to CPU pagetables */
205062306a36Sopenharmony_ci	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages);
205162306a36Sopenharmony_ci	ASSERT_EQ(ret, 0);
205262306a36Sopenharmony_ci	ASSERT_EQ(buffer->cpages, npages);
205362306a36Sopenharmony_ci	m = buffer->mirror;
205462306a36Sopenharmony_ci	for (i = 0; i < npages; i++)
205562306a36Sopenharmony_ci		ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[i]);
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	hmm_buffer_free(buffer);
205862306a36Sopenharmony_ci}
205962306a36Sopenharmony_ciTEST_HARNESS_MAIN
2060