You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
214 lines
7.6 KiB
214 lines
7.6 KiB
/* |
|
* Copyright (c) 2015-2016 Dmitry V. Levin <ldv@altlinux.org> |
|
* Copyright (c) 2016 Red Hat, Inc. |
|
* Copyright (c) 2016-2017 The strace developers. |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* 3. The name of the author may not be used to endorse or promote products |
|
* derived from this software without specific prior written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
|
|
#include "tests.h" |
|
#include <asm/unistd.h> |
|
|
|
#if defined __NR_userfaultfd && defined HAVE_LINUX_USERFAULTFD_H |
|
|
|
# include <fcntl.h> |
|
# include <inttypes.h> |
|
# include <stdint.h> |
|
# include <stdio.h> |
|
# include <string.h> |
|
# include <unistd.h> |
|
|
|
# include <sys/ioctl.h> |
|
# include <sys/mman.h> |
|
# include <linux/ioctl.h> |
|
# include <linux/userfaultfd.h> |
|
|
|
int |
|
main(void) |
|
{ |
|
int rc; |
|
int fd = syscall(__NR_userfaultfd, O_NONBLOCK); |
|
size_t pagesize = getpagesize(); |
|
|
|
if (fd < 0) |
|
perror_msg_and_skip("userfaultfd"); |
|
|
|
/* ---- API ---- */ |
|
TAIL_ALLOC_OBJECT_CONST_PTR(struct uffdio_api, api_struct); |
|
|
|
/* With a bad fd */ |
|
memset(api_struct, 0, sizeof(*api_struct)); |
|
rc = ioctl(-1, UFFDIO_API, api_struct); |
|
printf("ioctl(-1, UFFDIO_API, {api=0, features=0}) = %d %s (%m)\n", |
|
rc, errno2name()); |
|
/* With a bad pointer */ |
|
rc = ioctl(fd, UFFDIO_API, NULL); |
|
printf("ioctl(%d, UFFDIO_API, NULL) = %d %s (%m)\n", |
|
fd, rc, errno2name()); |
|
/* Normal call */ |
|
api_struct->api = UFFD_API; |
|
api_struct->features = 0; |
|
rc = ioctl(fd, UFFDIO_API, api_struct); |
|
printf("ioctl(%d, UFFDIO_API, {api=0xaa, features=0, " |
|
"features.out=%#" PRIx64 ", ioctls=1<<_UFFDIO_REGISTER|" |
|
"1<<_UFFDIO_UNREGISTER|1<<_UFFDIO_API", |
|
fd, (uint64_t)api_struct->features); |
|
api_struct->ioctls &= ~(1ull<<_UFFDIO_REGISTER| |
|
1ull<<_UFFDIO_UNREGISTER| |
|
1ull<<_UFFDIO_API); |
|
if (api_struct->ioctls) |
|
printf("|%#" PRIx64, (uint64_t)api_struct->ioctls); |
|
printf("}) = %d\n", rc); |
|
|
|
/* For the rest of the tests we need some anonymous memory */ |
|
void *area1 = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, |
|
MAP_PRIVATE|MAP_ANONYMOUS, |
|
-1, 0); |
|
if (area1 == MAP_FAILED) |
|
perror_msg_and_fail("mmap area1"); |
|
void *area2 = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, |
|
MAP_PRIVATE|MAP_ANONYMOUS, |
|
-1, 0); |
|
if (area2 == MAP_FAILED) |
|
perror_msg_and_fail("mmap area2"); |
|
madvise(area2, pagesize, MADV_DONTNEED); |
|
*(char *)area1 = 42; |
|
|
|
/* ---- REGISTER ---- */ |
|
struct uffdio_register *register_struct = |
|
tail_alloc(sizeof(*register_struct)); |
|
memset(register_struct, 0, sizeof(*register_struct)); |
|
|
|
rc = ioctl(-1, UFFDIO_REGISTER, register_struct); |
|
printf("ioctl(-1, UFFDIO_REGISTER, {range={start=0, len=0}, " |
|
"mode=0}) = %d %s (%m)\n", rc, errno2name()); |
|
|
|
rc = ioctl(fd, UFFDIO_REGISTER, NULL); |
|
printf("ioctl(%d, UFFDIO_REGISTER, NULL) = %d %s (%m)\n", |
|
fd, rc, errno2name()); |
|
|
|
register_struct->range.start = (uint64_t)(uintptr_t)area2; |
|
register_struct->range.len = pagesize; |
|
register_struct->mode = UFFDIO_REGISTER_MODE_MISSING; |
|
rc = ioctl(fd, UFFDIO_REGISTER, register_struct); |
|
printf("ioctl(%d, UFFDIO_REGISTER, {range={start=%p, len=%#zx}, " |
|
"mode=UFFDIO_REGISTER_MODE_MISSING, ioctls=" |
|
"1<<_UFFDIO_WAKE|1<<_UFFDIO_COPY|1<<_UFFDIO_ZEROPAGE", |
|
fd, area2, pagesize); |
|
register_struct->ioctls &= ~(1ull<<_UFFDIO_WAKE| |
|
1ull<<_UFFDIO_COPY| |
|
1ull<<_UFFDIO_ZEROPAGE); |
|
if (register_struct->ioctls) |
|
printf("|%#" PRIx64, (uint64_t)register_struct->ioctls); |
|
printf("}) = %d\n", rc); |
|
|
|
/* With area2 registered we can now do the atomic copies onto it |
|
* but be careful not to access it in any other way otherwise |
|
* userfaultfd will cause us to stall. |
|
*/ |
|
/* ---- COPY ---- */ |
|
TAIL_ALLOC_OBJECT_CONST_PTR(struct uffdio_copy, copy_struct); |
|
|
|
memset(copy_struct, 0, sizeof(*copy_struct)); |
|
rc = ioctl(-1, UFFDIO_COPY, copy_struct); |
|
printf("ioctl(-1, UFFDIO_COPY, {dst=0, src=0, len=0, mode=0" |
|
"}) = %d %s (%m)\n", rc, errno2name()); |
|
|
|
rc = ioctl(fd, UFFDIO_COPY, NULL); |
|
printf("ioctl(%d, UFFDIO_COPY, NULL) = %d %s (%m)\n", |
|
fd, rc, errno2name()); |
|
|
|
copy_struct->dst = (uint64_t)(uintptr_t)area2; |
|
copy_struct->src = (uint64_t)(uintptr_t)area1; |
|
copy_struct->len = pagesize; |
|
copy_struct->mode = UFFDIO_COPY_MODE_DONTWAKE; |
|
rc = ioctl(fd, UFFDIO_COPY, copy_struct); |
|
printf("ioctl(%d, UFFDIO_COPY, {dst=%p, src=%p, len=%#zx," |
|
" mode=UFFDIO_COPY_MODE_DONTWAKE, copy=%#zx}) = %d\n", |
|
fd, area2, area1, pagesize, pagesize, rc); |
|
|
|
/* ---- ZEROPAGE ---- */ |
|
TAIL_ALLOC_OBJECT_CONST_PTR(struct uffdio_zeropage, zero_struct); |
|
madvise(area2, pagesize, MADV_DONTNEED); |
|
|
|
memset(zero_struct, 0, sizeof(*zero_struct)); |
|
rc = ioctl(-1, UFFDIO_ZEROPAGE, zero_struct); |
|
printf("ioctl(-1, UFFDIO_ZEROPAGE, {range={start=0, len=0}, mode=0" |
|
"}) = %d %s (%m)\n", rc, errno2name()); |
|
|
|
rc = ioctl(fd, UFFDIO_ZEROPAGE, NULL); |
|
printf("ioctl(%d, UFFDIO_ZEROPAGE, NULL) = %d %s (%m)\n", |
|
fd, rc, errno2name()); |
|
|
|
zero_struct->range.start = (uint64_t)(uintptr_t)area2; |
|
zero_struct->range.len = pagesize; |
|
zero_struct->mode = UFFDIO_ZEROPAGE_MODE_DONTWAKE; |
|
rc = ioctl(fd, UFFDIO_ZEROPAGE, zero_struct); |
|
printf("ioctl(%d, UFFDIO_ZEROPAGE, {range={start=%p, len=%#zx}," |
|
" mode=UFFDIO_ZEROPAGE_MODE_DONTWAKE, zeropage=%#zx}) = %d\n", |
|
fd, area2, pagesize, pagesize, rc); |
|
|
|
/* ---- WAKE ---- */ |
|
TAIL_ALLOC_OBJECT_CONST_PTR(struct uffdio_range, range_struct); |
|
memset(range_struct, 0, sizeof(*range_struct)); |
|
|
|
rc = ioctl(-1, UFFDIO_WAKE, range_struct); |
|
printf("ioctl(-1, UFFDIO_WAKE, {start=0, len=0}) = %d %s (%m)\n", |
|
rc, errno2name()); |
|
|
|
rc = ioctl(fd, UFFDIO_WAKE, NULL); |
|
printf("ioctl(%d, UFFDIO_WAKE, NULL) = %d %s (%m)\n", |
|
fd, rc, errno2name()); |
|
|
|
range_struct->start = (uint64_t)(uintptr_t)area2; |
|
range_struct->len = pagesize; |
|
rc = ioctl(fd, UFFDIO_WAKE, range_struct); |
|
printf("ioctl(%d, UFFDIO_WAKE, {start=%p, len=%#zx}) = %d\n", |
|
fd, area2, pagesize, rc); |
|
|
|
/* ---- UNREGISTER ---- */ |
|
memset(range_struct, 0, sizeof(*range_struct)); |
|
|
|
rc = ioctl(-1, UFFDIO_UNREGISTER, range_struct); |
|
printf("ioctl(-1, UFFDIO_UNREGISTER, {start=0, len=0}) = %d %s (%m)\n", |
|
rc, errno2name()); |
|
|
|
rc = ioctl(fd, UFFDIO_UNREGISTER, NULL); |
|
printf("ioctl(%d, UFFDIO_UNREGISTER, NULL) = %d %s (%m)\n", |
|
fd, rc, errno2name()); |
|
|
|
range_struct->start = (uint64_t)(uintptr_t)area2; |
|
range_struct->len = pagesize; |
|
rc = ioctl(fd, UFFDIO_UNREGISTER, range_struct); |
|
printf("ioctl(%d, UFFDIO_UNREGISTER, {start=%p, len=%#zx}) = %d\n", |
|
fd, area2, pagesize, rc); |
|
puts("+++ exited with 0 +++"); |
|
return 0; |
|
} |
|
|
|
#else |
|
|
|
SKIP_MAIN_UNDEFINED("__NR_userfaultfd && HAVE_LINUX_USERFAULTFD_H") |
|
|
|
#endif
|
|
|