From cf720a028a5e097ca79b1d580ea94063418b41b8 Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Sun, 18 Mar 2012 02:07:18 +0100 Subject: [PATCH] Test set for sys_vumap() Located in test/kernel. Invoke "run" to run tests. --- test/kernel/run | 5 + test/kernel/sys_vumap/Makefile | 14 + test/kernel/sys_vumap/Makefile.inc | 5 + test/kernel/sys_vumap/com.h | 13 + test/kernel/sys_vumap/run | 16 + test/kernel/sys_vumap/system.conf | 12 + test/kernel/sys_vumap/vumaprelay.c | 77 ++ test/kernel/sys_vumap/vumaptest.c | 1409 ++++++++++++++++++++++++++++ 8 files changed, 1551 insertions(+) create mode 100755 test/kernel/run create mode 100644 test/kernel/sys_vumap/Makefile create mode 100644 test/kernel/sys_vumap/Makefile.inc create mode 100644 test/kernel/sys_vumap/com.h create mode 100755 test/kernel/sys_vumap/run create mode 100644 test/kernel/sys_vumap/system.conf create mode 100644 test/kernel/sys_vumap/vumaprelay.c create mode 100644 test/kernel/sys_vumap/vumaptest.c diff --git a/test/kernel/run b/test/kernel/run new file mode 100755 index 000000000..c1e8b1d04 --- /dev/null +++ b/test/kernel/run @@ -0,0 +1,5 @@ +#!/bin/sh + +tests="sys_vumap" + +for i in $tests; do (cd $i && ./run); done diff --git a/test/kernel/sys_vumap/Makefile b/test/kernel/sys_vumap/Makefile new file mode 100644 index 000000000..ef021144f --- /dev/null +++ b/test/kernel/sys_vumap/Makefile @@ -0,0 +1,14 @@ +# Makefile for the sys_vumap test. +PROG= vumaptest vumaprelay +SRCS.vumaptest= vumaptest.c +SRCS.vumaprelay= vumaprelay.c + +DPADD+= ${LIBSYS} +LDADD+= -lsys + +MAN= + +BINDIR?= /usr/sbin + +.include "Makefile.inc" +.include diff --git a/test/kernel/sys_vumap/Makefile.inc b/test/kernel/sys_vumap/Makefile.inc new file mode 100644 index 000000000..f166ffed1 --- /dev/null +++ b/test/kernel/sys_vumap/Makefile.inc @@ -0,0 +1,5 @@ +# Copied from drivers/Makefile.inc +CPPFLAGS+= -D_MINIX -D_NETBSD_SOURCE +LDADD+= -lminlib -lcompat_minix +DPADD+= ${LIBMINLIB} ${LIBCOMPAT_MINIX} +BINDIR?=/usr/sbin diff --git a/test/kernel/sys_vumap/com.h b/test/kernel/sys_vumap/com.h new file mode 100644 index 000000000..bec559ec6 --- /dev/null +++ b/test/kernel/sys_vumap/com.h @@ -0,0 +1,13 @@ +#ifndef _VUMAP_COM_H +#define _VUMAP_COM_H + +#define VTR_RELAY 0x3000 /* SYS_VUMAP relay request */ + +#define VTR_VGRANT m10_l1 /* grant for virtual vector */ +#define VTR_VCOUNT m10_i1 /* nr of elements in virtual vector */ +#define VTR_OFFSET m10_l2 /* offset into first element */ +#define VTR_ACCESS m10_i2 /* access flags (VUA_) */ +#define VTR_PGRANT m10_l3 /* grant for physical vector */ +#define VTR_PCOUNT m10_i3 /* nr of physical elements (in/out) */ + +#endif /* _VUMAP_COM_H */ diff --git a/test/kernel/sys_vumap/run b/test/kernel/sys_vumap/run new file mode 100755 index 000000000..18de72e8d --- /dev/null +++ b/test/kernel/sys_vumap/run @@ -0,0 +1,16 @@ +#!/bin/sh + +make >/dev/null + +echo -n "Kernel test (sys_vumap): " +service up `pwd`/vumaprelay -config system.conf -label vumaprelay -script /etc/rs.single +service up `pwd`/vumaptest -config system.conf -script /etc/rs.single 2>/dev/null +r=$? +service down vumaprelay + +if [ $r -ne 0 ]; then + echo "failure" + exit 1 +fi + +echo "ok" diff --git a/test/kernel/sys_vumap/system.conf b/test/kernel/sys_vumap/system.conf new file mode 100644 index 000000000..53a272079 --- /dev/null +++ b/test/kernel/sys_vumap/system.conf @@ -0,0 +1,12 @@ +service vumaptest { + system + UMAP # 14 + VUMAP # 18 + ; +}; + +service vumaprelay { + system + VUMAP # 18 + ; +}; diff --git a/test/kernel/sys_vumap/vumaprelay.c b/test/kernel/sys_vumap/vumaprelay.c new file mode 100644 index 000000000..759affb60 --- /dev/null +++ b/test/kernel/sys_vumap/vumaprelay.c @@ -0,0 +1,77 @@ +/* Test for sys_vumap() - by D.C. van Moolenbroek */ +#include +#include + +#include "com.h" + +PRIVATE int do_request(message *m) +{ + struct vumap_vir vvec[MAPVEC_NR + 3]; + struct vumap_phys pvec[MAPVEC_NR + 3]; + int r, r2, access, vcount, pcount; + size_t offset; + + assert(m->m_type == VTR_RELAY); + + vcount = m->VTR_VCOUNT; + pcount = m->VTR_PCOUNT; + offset = m->VTR_OFFSET; + access = m->VTR_ACCESS; + + r2 = sys_safecopyfrom(m->m_source, m->VTR_VGRANT, 0, (vir_bytes) vvec, + sizeof(vvec[0]) * vcount, D); + assert(r2 == OK); + + r = sys_vumap(m->m_source, vvec, vcount, offset, access, pvec, + &pcount); + + if (pcount >= 1 && pcount <= MAPVEC_NR + 3) { + r2 = sys_safecopyto(m->m_source, m->VTR_PGRANT, 0, + (vir_bytes) pvec, sizeof(pvec[0]) * pcount, D); + assert(r2 == OK); + } + + m->VTR_PCOUNT = pcount; + + return r; +} + +PRIVATE int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info)) +{ + return OK; +} + +PRIVATE void sef_cb_signal_handler(int sig) +{ + if (sig == SIGTERM) + exit(0); +} + +PRIVATE void sef_local_startup(void) +{ + sef_setcb_init_fresh(sef_cb_init_fresh); + sef_setcb_signal_handler(sef_cb_signal_handler); + + sef_startup(); +} + +PUBLIC int main(int argc, char **argv) +{ + message m; + int r; + + env_setargs(argc, argv); + + sef_local_startup(); + + for (;;) { + if ((r = sef_receive(ANY, &m)) != OK) + panic("sef_receive failed (%d)\n", r); + + m.m_type = do_request(&m); + + send(m.m_source, &m); + } + + return 0; +} diff --git a/test/kernel/sys_vumap/vumaptest.c b/test/kernel/sys_vumap/vumaptest.c new file mode 100644 index 000000000..d4cc1c1f6 --- /dev/null +++ b/test/kernel/sys_vumap/vumaptest.c @@ -0,0 +1,1409 @@ +/* Test for sys_vumap() - by D.C. van Moolenbroek */ +#include +#include +#include +#include + +#include "com.h" + +struct buf { + int pages; + int flags; + vir_bytes addr; + phys_bytes phys; +}; +#define BUF_PREALLOC 0x1 /* if set, immediately allocate the page */ +#define BUF_ADJACENT 0x2 /* virtually contiguous with the last buffer */ + +PRIVATE unsigned int count = 0, failures = 0; + +PRIVATE int success; +PRIVATE char *fail_file; +PRIVATE int fail_line; + +PRIVATE int relay; +PRIVATE endpoint_t endpt; + +PRIVATE int verbose; + +PRIVATE enum { + GE_NONE, /* no exception */ + GE_REVOKED, /* revoked grant */ + GE_INVALID /* invalid grant */ +} grant_exception = GE_NONE; + +PRIVATE int grant_access = 0; + +#define expect(r) expect_f((r), __FILE__, __LINE__) + +PRIVATE void alloc_buf(struct buf *buf, phys_bytes next) +{ + void *tmp = NULL; + vir_bytes addr; + size_t len; + int r, prealloc, flags; + + /* is_allocated() cannot handle buffers that are not physically + * contiguous, and we cannot guarantee physical contiguity if not + * not preallocating. + */ + assert((buf->flags & BUF_PREALLOC) || buf->pages == 1); + + len = buf->pages * PAGE_SIZE; + prealloc = (buf->flags & BUF_PREALLOC); + flags = MAP_ANON | (prealloc ? (MAP_CONTIG | MAP_PREALLOC) : 0); + + if (prealloc) { + /* Allocate a same-sized piece of memory elsewhere, to make it + * very unlikely that the actual piece of memory will end up + * being physically contiguous with the last piece. + */ + tmp = minix_mmap((void *) (buf->addr + len + PAGE_SIZE), len, + PROT_READ | PROT_WRITE, MAP_ANON | MAP_PREALLOC | + MAP_CONTIG, -1, 0L); + + if (tmp == MAP_FAILED) + panic("unable to allocate temporary buffer"); + } + + addr = (vir_bytes) minix_mmap((void *) buf->addr, len, + PROT_READ | PROT_WRITE, flags, -1, 0L); + + if (addr != buf->addr) + panic("unable to allocate buffer (2)"); + + if (!prealloc) + return; + + if ((r = minix_munmap(tmp, len)) != OK) + panic("unable to unmap buffer (%d)", errno); + + if ((r = sys_umap(SELF, VM_D, addr, len, &buf->phys)) < 0) + panic("unable to get physical address of buffer (%d)", r); + + if (buf->phys != next) + return; + + if (verbose) + printf("WARNING: alloc noncontigous range, second try\n"); + + /* Can't remap this to elsewhere, so we run the risk of allocating the + * exact same physically contiguous page again. However, now that we've + * unmapped the temporary memory also, there's a small chance we'll end + * up with a different physical page this time. Who knows. + */ + minix_munmap((void *) addr, len); + + addr = (vir_bytes) minix_mmap((void *) buf->addr, len, + PROT_READ | PROT_WRITE, flags, -1, 0L); + + if (addr != buf->addr) + panic("unable to allocate buffer, second try"); + + if ((r = sys_umap(SELF, VM_D, addr, len, &buf->phys)) < 0) + panic("unable to get physical address of buffer (%d)", r); + + /* Still the same page? Screw it. */ + if (buf->phys == next) + panic("unable to allocate noncontiguous range"); +} + +PRIVATE void alloc_bufs(struct buf *buf, int count) +{ + static vir_bytes base = 0x80000000L; + phys_bytes next; + int i; + + /* Allocate the given memory in virtually contiguous blocks whenever + * each next buffer is requested to be adjacent. Insert a virtual gap + * after each such block. Make sure that each two adjacent buffers in a + * block are physically non-contiguous. + */ + for (i = 0; i < count; i++) { + if (i > 0 && (buf[i].flags & BUF_ADJACENT)) { + next = buf[i-1].phys + buf[i-1].pages * PAGE_SIZE; + } else { + base += PAGE_SIZE * 16; + next = 0L; + } + + buf[i].addr = base; + + alloc_buf(&buf[i], next); + + base += buf[i].pages * PAGE_SIZE; + } + +#if DEBUG + for (i = 0; i < count; i++) + printf("Buf %d: %d pages, flags %x, vir %08x, phys %08x\n", i, + buf[i].pages, buf[i].flags, buf[i].addr, buf[i].phys); +#endif +} + +PRIVATE void free_bufs(struct buf *buf, int count) +{ + int i, j, r; + + for (i = 0; i < count; i++) { + for (j = 0; j < buf[i].pages; j++) { + r = minix_munmap((void *) (buf[i].addr + j * PAGE_SIZE), + PAGE_SIZE); + + if (r != OK) + panic("unable to unmap range (%d)", errno); + } + } +} + +PRIVATE int is_allocated(vir_bytes addr, size_t bytes, phys_bytes *phys) +{ + int r; + + /* This will have to do for now. Of course, we could use sys_vumap with + * VUA_READ for this, but that would defeat the point of one test. It + * is still a decent alternative in case sys_umap's behavior ever + * changes, though. + */ + r = sys_umap(SELF, VM_D, addr, bytes, phys); + + return r == OK; +} + +PRIVATE int is_buf_allocated(struct buf *buf) +{ + return is_allocated(buf->addr, buf->pages * PAGE_SIZE, &buf->phys); +} + +PRIVATE void test_group(char *name) +{ + if (verbose) + printf("Test group: %s (%s)\n", + name, relay ? "relay" : "local"); +} + +PRIVATE void expect_f(int res, char *file, int line) +{ + if (!res && success) { + success = FALSE; + fail_file = file; + fail_line = line; + } +} + +PRIVATE void got_result(char *desc) +{ + count++; + + if (!success) { + failures++; + + printf("#%02d: %-38s\t[FAIL]\n", count, desc); + printf("- failure at %s:%d\n", fail_file, fail_line); + } else { + if (verbose) + printf("#%02d: %-38s\t[PASS]\n", count, desc); + } +} + +PRIVATE int relay_vumap(struct vumap_vir *vvec, int vcount, size_t offset, + int access, struct vumap_phys *pvec, int *pcount) +{ + struct vumap_vir gvvec[MAPVEC_NR + 3]; + cp_grant_id_t vgrant, pgrant; + message m; + int i, r, gaccess; + + assert(vcount > 0 && vcount <= MAPVEC_NR + 3); + assert(*pcount > 0 && *pcount <= MAPVEC_NR + 3); + + /* Allow grant access flags to be overridden for testing purposes. */ + if (!(gaccess = grant_access)) { + if (access & VUA_READ) gaccess |= CPF_READ; + if (access & VUA_WRITE) gaccess |= CPF_WRITE; + } + + for (i = 0; i < vcount; i++) { + gvvec[i].vv_grant = cpf_grant_direct(endpt, vvec[i].vv_addr, + vvec[i].vv_size, gaccess); + assert(gvvec[i].vv_grant != GRANT_INVALID); + gvvec[i].vv_size = vvec[i].vv_size; + } + + vgrant = cpf_grant_direct(endpt, (vir_bytes) gvvec, + sizeof(gvvec[0]) * vcount, CPF_READ); + assert(vgrant != GRANT_INVALID); + + pgrant = cpf_grant_direct(endpt, (vir_bytes) pvec, + sizeof(pvec[0]) * *pcount, CPF_WRITE); + assert(pgrant != GRANT_INVALID); + + /* This must be done after allocating all other grants. */ + if (grant_exception != GE_NONE) { + cpf_revoke(gvvec[vcount - 1].vv_grant); + if (grant_exception == GE_INVALID) + gvvec[vcount - 1].vv_grant = GRANT_INVALID; + } + + m.m_type = VTR_RELAY; + m.VTR_VGRANT = vgrant; + m.VTR_VCOUNT = vcount; + m.VTR_OFFSET = offset; + m.VTR_ACCESS = access; + m.VTR_PGRANT = pgrant; + m.VTR_PCOUNT = *pcount; + + r = sendrec(endpt, &m); + + cpf_revoke(pgrant); + cpf_revoke(vgrant); + + for (i = 0; i < vcount - !!grant_exception; i++) + cpf_revoke(gvvec[i].vv_grant); + + *pcount = m.VTR_PCOUNT; + + return (r != OK) ? r : m.m_type; +} + +PRIVATE int do_vumap(endpoint_t endpt, struct vumap_vir *vvec, int vcount, + size_t offset, int access, struct vumap_phys *pvec, int *pcount) +{ + struct vumap_phys pv_backup[MAPVEC_NR + 3]; + int r, pc_backup, pv_test = FALSE; + + /* Make a copy of pvec and pcount for later. */ + pc_backup = *pcount; + + /* We cannot compare pvec contents before and after when relaying, + * since the original contents are not transferred. + */ + if (!relay && pvec != NULL && pc_backup >= 1 && + pc_backup <= MAPVEC_NR + 3) { + pv_test = TRUE; + memcpy(pv_backup, pvec, sizeof(*pvec) * pc_backup); + } + + /* Reset the test result. */ + success = TRUE; + + /* Perform the vumap call, either directly or through a relay. */ + if (relay) { + assert(endpt == SELF); + r = relay_vumap(vvec, vcount, offset, access, pvec, pcount); + } else { + r = sys_vumap(endpt, vvec, vcount, offset, access, pvec, + pcount); + } + + /* Upon failure, pvec and pcount must be unchanged. */ + if (r != OK) { + expect(pc_backup == *pcount); + + if (pv_test) + expect(memcmp(pv_backup, pvec, + sizeof(*pvec) * pc_backup) == 0); + } + + return r; +} + +PRIVATE void test_basics(void) +{ + struct vumap_vir vvec[2]; + struct vumap_phys pvec[4]; + struct buf buf[4]; + int r, pcount; + + test_group("basics"); + + buf[0].pages = 1; + buf[0].flags = BUF_PREALLOC; + buf[1].pages = 2; + buf[1].flags = BUF_PREALLOC; + buf[2].pages = 1; + buf[2].flags = BUF_PREALLOC; + buf[3].pages = 1; + buf[3].flags = BUF_PREALLOC | BUF_ADJACENT; + + alloc_bufs(buf, 4); + + /* Test single whole page. */ + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = PAGE_SIZE; + pcount = 1; + + r = do_vumap(SELF, vvec, 1, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 1); + expect(pvec[0].vp_addr == buf[0].phys); + expect(pvec[0].vp_size == vvec[0].vv_size); + + got_result("single whole page"); + + /* Test single partial page. */ + vvec[0].vv_addr = buf[0].addr + 123; + vvec[0].vv_size = PAGE_SIZE - 456; + pcount = 1; + + r = do_vumap(SELF, vvec, 1, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 1); + expect(pvec[0].vp_addr == buf[0].phys + 123); + expect(pvec[0].vp_size == vvec[0].vv_size); + + got_result("single partial page"); + + /* Test multiple contiguous whole pages. */ + vvec[0].vv_addr = buf[1].addr; + vvec[0].vv_size = PAGE_SIZE * 2; + pcount = 1; + + r = do_vumap(SELF, vvec, 1, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 1); + expect(pvec[0].vp_addr == buf[1].phys); + expect(pvec[0].vp_size == vvec[0].vv_size); + + got_result("multiple contiguous whole pages"); + + /* Test range in multiple contiguous pages. */ + vvec[0].vv_addr = buf[1].addr + 234; + vvec[0].vv_size = PAGE_SIZE * 2 - 234; + pcount = 2; + + r = do_vumap(SELF, vvec, 1, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 1); + expect(pvec[0].vp_addr == buf[1].phys + 234); + expect(pvec[0].vp_size == vvec[0].vv_size); + + got_result("range in multiple contiguous pages"); + + /* Test multiple noncontiguous whole pages. */ + vvec[0].vv_addr = buf[2].addr; + vvec[0].vv_size = PAGE_SIZE * 2; + pcount = 3; + + r = do_vumap(SELF, vvec, 1, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 2); + expect(pvec[0].vp_addr == buf[2].phys); + expect(pvec[0].vp_size == PAGE_SIZE); + expect(pvec[1].vp_addr == buf[3].phys); + expect(pvec[1].vp_size == PAGE_SIZE); + + got_result("multiple noncontiguous whole pages"); + + /* Test range in multiple noncontiguous pages. */ + vvec[0].vv_addr = buf[2].addr + 1; + vvec[0].vv_size = PAGE_SIZE * 2 - 2; + pcount = 2; + + r = do_vumap(SELF, vvec, 1, 0, VUA_WRITE, pvec, &pcount); + + expect(r == OK); + expect(pcount == 2); + expect(pvec[0].vp_addr == buf[2].phys + 1); + expect(pvec[0].vp_size == PAGE_SIZE - 1); + expect(pvec[1].vp_addr == buf[3].phys); + expect(pvec[1].vp_size == PAGE_SIZE - 1); + + got_result("range in multiple noncontiguous pages"); + + /* Test single-input result truncation. */ + vvec[0].vv_addr = buf[2].addr + PAGE_SIZE / 2; + vvec[0].vv_size = PAGE_SIZE; + pvec[1].vp_addr = 0L; + pvec[1].vp_size = 0; + pcount = 1; + + r = do_vumap(SELF, vvec, 1, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 1); + expect(pvec[0].vp_addr == buf[2].phys + PAGE_SIZE / 2); + expect(pvec[0].vp_size == PAGE_SIZE / 2); + expect(pvec[1].vp_addr == 0L); + expect(pvec[1].vp_size == 0); + + got_result("single-input result truncation"); + + /* Test multiple inputs, contiguous first. */ + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = PAGE_SIZE; + vvec[1].vv_addr = buf[2].addr + PAGE_SIZE - 1; + vvec[1].vv_size = 2; + pcount = 3; + + r = do_vumap(SELF, vvec, 2, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 3); + expect(pvec[0].vp_addr == buf[0].phys); + expect(pvec[0].vp_size == PAGE_SIZE); + expect(pvec[1].vp_addr == buf[2].phys + PAGE_SIZE - 1); + expect(pvec[1].vp_size == 1); + expect(pvec[2].vp_addr == buf[3].phys); + expect(pvec[2].vp_size == 1); + + got_result("multiple inputs, contiguous first"); + + /* Test multiple inputs, contiguous last. */ + vvec[0].vv_addr = buf[2].addr + 123; + vvec[0].vv_size = PAGE_SIZE * 2 - 456; + vvec[1].vv_addr = buf[1].addr + 234; + vvec[1].vv_size = PAGE_SIZE * 2 - 345; + pcount = 4; + + r = do_vumap(SELF, vvec, 2, 0, VUA_WRITE, pvec, &pcount); + + expect(r == OK); + expect(pcount == 3); + expect(pvec[0].vp_addr == buf[2].phys + 123); + expect(pvec[0].vp_size == PAGE_SIZE - 123); + expect(pvec[1].vp_addr == buf[3].phys); + expect(pvec[1].vp_size == PAGE_SIZE - (456 - 123)); + expect(pvec[2].vp_addr == buf[1].phys + 234); + expect(pvec[2].vp_size == vvec[1].vv_size); + + got_result("multiple inputs, contiguous last"); + + /* Test multiple-inputs result truncation. */ + vvec[0].vv_addr = buf[2].addr + 2; + vvec[0].vv_size = PAGE_SIZE * 2 - 3; + vvec[1].vv_addr = buf[0].addr; + vvec[1].vv_size = 135; + pvec[2].vp_addr = 0xDEADBEEFL; + pvec[2].vp_size = 1234; + pcount = 2; + + r = do_vumap(SELF, vvec, 2, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 2); + expect(pvec[0].vp_addr == buf[2].phys + 2); + expect(pvec[0].vp_size == PAGE_SIZE - 2); + expect(pvec[1].vp_addr == buf[3].phys); + expect(pvec[1].vp_size == PAGE_SIZE - 1); + expect(pvec[2].vp_addr == 0xDEADBEEFL); + expect(pvec[2].vp_size == 1234); + + got_result("multiple-inputs result truncation"); + + free_bufs(buf, 4); +} + +PRIVATE void test_endpt(void) +{ + struct vumap_vir vvec[1]; + struct vumap_phys pvec[1]; + struct buf buf[1]; + int r, pcount; + + test_group("endpoint"); + + buf[0].pages = 1; + buf[0].flags = BUF_PREALLOC; + + alloc_bufs(buf, 1); + + /* Test NONE endpoint. */ + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = PAGE_SIZE; + pcount = 1; + + r = do_vumap(NONE, vvec, 1, 0, VUA_READ, pvec, &pcount); + + expect(r == EINVAL); + + got_result("NONE endpoint"); + + /* Test ANY endpoint. */ + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = PAGE_SIZE; + pcount = 1; + + r = do_vumap(ANY, vvec, 1, 0, VUA_READ, pvec, &pcount); + + expect(r == EINVAL); + + got_result("ANY endpoint"); + + free_bufs(buf, 1); +} + +PRIVATE void test_vector1(void) +{ + struct vumap_vir vvec[2]; + struct vumap_phys pvec[3]; + struct buf buf[2]; + int r, pcount; + + test_group("vector, part 1"); + + buf[0].pages = 2; + buf[0].flags = BUF_PREALLOC; + buf[1].pages = 1; + buf[1].flags = BUF_PREALLOC; + + alloc_bufs(buf, 2); + + /* Test zero virtual memory size. */ + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = PAGE_SIZE * 2; + vvec[1].vv_addr = buf[1].addr; + vvec[1].vv_size = 0; + pcount = 3; + + r = do_vumap(SELF, vvec, 2, 0, VUA_READ, pvec, &pcount); + + expect(r == EINVAL); + + got_result("zero virtual memory size"); + + /* Test excessive virtual memory size. */ + vvec[1].vv_size = (vir_bytes) -1; + + r = do_vumap(SELF, vvec, 2, 0, VUA_READ, pvec, &pcount); + + expect(r == EFAULT || r == EPERM); + + got_result("excessive virtual memory size"); + + /* Test invalid virtual memory. */ + vvec[1].vv_addr = 0L; + vvec[1].vv_size = PAGE_SIZE; + + r = do_vumap(SELF, vvec, 2, 0, VUA_READ, pvec, &pcount); + + expect(r == EFAULT); + + got_result("invalid virtual memory"); + + /* Test virtual memory overrun. */ + vvec[0].vv_size++; + vvec[1].vv_addr = buf[1].addr; + + r = do_vumap(SELF, vvec, 2, 0, VUA_READ, pvec, &pcount); + + expect(r == EFAULT); + + got_result("virtual memory overrun"); + + free_bufs(buf, 2); +} + +PRIVATE void test_vector2(void) +{ + struct vumap_vir vvec[2], *vvecp; + struct vumap_phys pvec[3], *pvecp; + struct buf buf[2]; + phys_bytes dummy; + int r, pcount; + + test_group("vector, part 2"); + + buf[0].pages = 2; + buf[0].flags = BUF_PREALLOC; + buf[1].pages = 1; + buf[1].flags = BUF_PREALLOC; + + alloc_bufs(buf, 2); + + /* Test zero virtual count. */ + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = PAGE_SIZE * 2; + vvec[1].vv_addr = buf[1].addr; + vvec[1].vv_size = PAGE_SIZE; + pcount = 3; + + r = do_vumap(SELF, vvec, 0, 0, VUA_READ, pvec, &pcount); + + expect(r == EINVAL); + + got_result("zero virtual count"); + + /* Test negative virtual count. */ + r = do_vumap(SELF, vvec, -1, 0, VUA_WRITE, pvec, &pcount); + + expect(r == EINVAL); + + got_result("negative virtual count"); + + /* Test zero physical count. */ + pcount = 0; + + r = do_vumap(SELF, vvec, 2, 0, VUA_WRITE, pvec, &pcount); + + expect(r == EINVAL); + + got_result("zero physical count"); + + /* Test negative physical count. */ + pcount = -1; + + r = do_vumap(SELF, vvec, 2, 0, VUA_READ, pvec, &pcount); + + expect(r == EINVAL); + + got_result("negative physical count"); + + /* Test invalid virtual vector pointer. */ + pcount = 2; + + r = do_vumap(SELF, NULL, 2, 0, VUA_READ, pvec, &pcount); + + expect(r == EFAULT); + + got_result("invalid virtual vector pointer"); + + /* Test unallocated virtual vector. */ + vvecp = (struct vumap_vir *) minix_mmap(NULL, PAGE_SIZE, + PROT_READ | PROT_WRITE, MAP_ANON, -1, 0L); + + if (vvecp == MAP_FAILED) + panic("unable to allocate virtual vector"); + + r = do_vumap(SELF, vvecp, 2, 0, VUA_READ, pvec, &pcount); + + expect(r == EFAULT); + expect(!is_allocated((vir_bytes) vvecp, PAGE_SIZE, &dummy)); + + got_result("unallocated virtual vector pointer"); + + minix_munmap((void *) vvecp, PAGE_SIZE); + + /* Test invalid physical vector pointer. */ + r = do_vumap(SELF, vvec, 2, 0, VUA_READ, NULL, &pcount); + + expect(r == EFAULT); + + got_result("invalid physical vector pointer"); + + /* Test unallocated physical vector. */ + pvecp = (struct vumap_phys *) minix_mmap(NULL, PAGE_SIZE, + PROT_READ | PROT_WRITE, MAP_ANON, -1, 0L); + + if (pvecp == MAP_FAILED) + panic("unable to allocate physical vector"); + + r = do_vumap(SELF, vvec, 2, 0, VUA_READ, pvecp, &pcount); + + expect(r == OK); + expect(is_allocated((vir_bytes) pvecp, PAGE_SIZE, &dummy)); + expect(pcount == 2); + expect(pvecp[0].vp_size == PAGE_SIZE * 2); + expect(pvecp[0].vp_addr == buf[0].phys); + expect(pvecp[1].vp_size == PAGE_SIZE); + expect(pvecp[1].vp_addr == buf[1].phys); + + got_result("unallocated physical vector pointer"); + + minix_munmap((void *) pvecp, PAGE_SIZE); + + free_bufs(buf, 2); +} + +PRIVATE void test_grant(void) +{ + struct vumap_vir vvec[2]; + struct vumap_phys pvec[3]; + struct buf buf[2]; + int r, pcount; + + test_group("grant"); + + buf[0].pages = 1; + buf[0].flags = BUF_PREALLOC; + buf[1].pages = 2; + buf[1].flags = BUF_PREALLOC; + + alloc_bufs(buf, 2); + + /* Test write-only access on read-only grant. */ + grant_access = CPF_READ; /* override */ + + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = PAGE_SIZE; + pcount = 1; + + r = do_vumap(SELF, vvec, 1, 0, VUA_WRITE, pvec, &pcount); + + expect(r == EPERM); + + got_result("write-only access on read-only grant"); + + /* Test read-write access on read-only grant. */ + r = do_vumap(SELF, vvec, 1, 0, VUA_READ | VUA_WRITE, pvec, &pcount); + + expect(r == EPERM); + + got_result("read-write access on read-only grant"); + + /* Test read-only access on write-only grant. */ + grant_access = CPF_WRITE; /* override */ + + r = do_vumap(SELF, vvec, 1, 0, VUA_READ, pvec, &pcount); + + expect(r == EPERM); + + got_result("read-only access on write-only grant"); + + /* Test read-write access on write grant. */ + r = do_vumap(SELF, vvec, 1, 0, VUA_READ | VUA_WRITE, pvec, &pcount); + + expect(r == EPERM); + + got_result("read-write access on write-only grant"); + + /* Test read-only access on read-write grant. */ + grant_access = CPF_READ | CPF_WRITE; /* override */ + + r = do_vumap(SELF, vvec, 1, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 1); + expect(pvec[0].vp_size == PAGE_SIZE); + expect(pvec[0].vp_addr == buf[0].phys); + + got_result("read-only access on read-write grant"); + + grant_access = 0; /* reset */ + + /* Test invalid grant. */ + grant_exception = GE_INVALID; + + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = PAGE_SIZE; + vvec[1].vv_addr = buf[1].addr; + vvec[1].vv_size = PAGE_SIZE * 2; + pcount = 3; + + r = do_vumap(SELF, vvec, 2, 0, VUA_READ, pvec, &pcount); + + expect(r == EINVAL); + + got_result("invalid grant"); + + /* Test revoked grant. */ + grant_exception = GE_REVOKED; + + r = do_vumap(SELF, vvec, 2, 0, VUA_READ, pvec, &pcount); + + expect(r == EPERM); + + got_result("revoked grant"); + + grant_exception = GE_NONE; + + free_bufs(buf, 2); +} + +PRIVATE void test_offset(void) +{ + struct vumap_vir vvec[2]; + struct vumap_phys pvec[3]; + struct buf buf[4]; + size_t off, off2; + int r, pcount; + + test_group("offsets"); + + buf[0].pages = 1; + buf[0].flags = BUF_PREALLOC; + buf[1].pages = 2; + buf[1].flags = BUF_PREALLOC; + buf[2].pages = 1; + buf[2].flags = BUF_PREALLOC; + buf[3].pages = 1; + buf[3].flags = BUF_PREALLOC | BUF_ADJACENT; + + alloc_bufs(buf, 4); + + /* Test offset into aligned page. */ + off = 123; + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = PAGE_SIZE; + pcount = 2; + + r = do_vumap(SELF, vvec, 1, off, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 1); + expect(pvec[0].vp_addr == buf[0].phys + off); + expect(pvec[0].vp_size == vvec[0].vv_size - off); + + got_result("offset into aligned page"); + + /* Test offset into unaligned page. */ + off2 = 456; + assert(off + off2 < PAGE_SIZE); + vvec[0].vv_addr = buf[0].addr + off; + vvec[0].vv_size = PAGE_SIZE - off; + pcount = 2; + + r = do_vumap(SELF, vvec, 1, off2, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 1); + expect(pvec[0].vp_addr == buf[0].phys + off + off2); + expect(pvec[0].vp_size == vvec[0].vv_size - off2); + + got_result("offset into unaligned page"); + + /* Test offset into unaligned page set. */ + off = 1234; + off2 = 567; + assert(off + off2 < PAGE_SIZE); + vvec[0].vv_addr = buf[1].addr + off; + vvec[0].vv_size = (PAGE_SIZE - off) * 2; + pcount = 3; + + r = do_vumap(SELF, vvec, 1, off2, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 1); + expect(pvec[0].vp_addr == buf[1].phys + off + off2); + expect(pvec[0].vp_size == vvec[0].vv_size - off2); + + got_result("offset into contiguous page set"); + + /* Test offset into noncontiguous page set. */ + vvec[0].vv_addr = buf[2].addr + off; + vvec[0].vv_size = (PAGE_SIZE - off) * 2; + pcount = 3; + + r = do_vumap(SELF, vvec, 1, off2, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 2); + expect(pvec[0].vp_addr == buf[2].phys + off + off2); + expect(pvec[0].vp_size == PAGE_SIZE - off - off2); + expect(pvec[1].vp_addr == buf[3].phys); + expect(pvec[1].vp_size == PAGE_SIZE - off); + + got_result("offset into noncontiguous page set"); + + /* Test offset to last byte. */ + off = PAGE_SIZE - off2 - 1; + vvec[0].vv_addr = buf[0].addr + off2; + vvec[0].vv_size = PAGE_SIZE - off2; + pcount = 2; + + r = do_vumap(SELF, vvec, 1, off, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 1); + expect(pvec[0].vp_addr == buf[0].phys + off + off2); + expect(pvec[0].vp_size == 1); + + got_result("offset to last byte"); + + /* Test offset at range end. */ + off = 234; + vvec[0].vv_addr = buf[1].addr + off; + vvec[0].vv_size = PAGE_SIZE - off * 2; + vvec[1].vv_addr = vvec[0].vv_addr + vvec[0].vv_size; + vvec[1].vv_size = off; + + r = do_vumap(SELF, vvec, 2, vvec[0].vv_size, VUA_READ, pvec, &pcount); + + expect(r == EINVAL); + + got_result("offset at range end"); + + /* Test offset beyond range end. */ + vvec[0].vv_addr = buf[1].addr; + vvec[0].vv_size = PAGE_SIZE; + vvec[1].vv_addr = buf[1].addr + PAGE_SIZE; + vvec[1].vv_size = PAGE_SIZE; + + r = do_vumap(SELF, vvec, 2, PAGE_SIZE + off, VUA_READ, pvec, &pcount); + + expect(r == EINVAL); + + got_result("offset beyond range end"); + + /* Test negative offset. */ + vvec[0].vv_addr = buf[1].addr + off + off2; + vvec[0].vv_size = PAGE_SIZE; + + r = do_vumap(SELF, vvec, 1, (size_t) -1, VUA_READ, pvec, &pcount); + + expect(r == EINVAL); + + got_result("negative offset"); + + free_bufs(buf, 4); +} + +PRIVATE void test_access(void) +{ + struct vumap_vir vvec[3]; + struct vumap_phys pvec[4], *pvecp; + struct buf buf[7]; + int i, r, pcount, pindex; + + test_group("access"); + + buf[0].pages = 1; + buf[0].flags = 0; + buf[1].pages = 1; + buf[1].flags = BUF_PREALLOC | BUF_ADJACENT; + buf[2].pages = 1; + buf[2].flags = BUF_ADJACENT; + + alloc_bufs(buf, 3); + + /* Test no access flags. */ + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = PAGE_SIZE * 3; + pcount = 4; + + r = do_vumap(SELF, vvec, 1, 0, 0, pvec, &pcount); + + expect(r == EINVAL); + expect(!is_buf_allocated(&buf[0])); + expect(is_buf_allocated(&buf[1])); + expect(!is_buf_allocated(&buf[2])); + + got_result("no access flags"); + + /* Test read-only access. */ + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = PAGE_SIZE * 3; + pcount = 1; + + r = do_vumap(SELF, vvec, 1, 0, VUA_READ, pvec, &pcount); + + expect(r == EFAULT); + expect(!is_buf_allocated(&buf[0])); + expect(is_buf_allocated(&buf[1])); + expect(!is_buf_allocated(&buf[2])); + + got_result("read-only access"); + + /* Test read-write access. */ + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = PAGE_SIZE * 3; + pcount = 4; + + r = do_vumap(SELF, vvec, 1, 0, VUA_READ | VUA_WRITE, pvec, &pcount); + + expect(r == EFAULT); + expect(!is_buf_allocated(&buf[0])); + expect(is_buf_allocated(&buf[1])); + expect(!is_buf_allocated(&buf[2])); + + got_result("read-write access"); + + /* Test write-only access. */ + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = PAGE_SIZE * 3; + pcount = 4; + + r = do_vumap(SELF, vvec, 1, 0, VUA_WRITE, pvec, &pcount); + + expect(r == OK); + /* We don't control the physical addresses of the faulted-in pages, so + * they may or may not end up being contiguous with their neighbours. + */ + expect(pcount >= 1 && pcount <= 3); + expect(is_buf_allocated(&buf[0])); + expect(is_buf_allocated(&buf[1])); + expect(is_buf_allocated(&buf[2])); + expect(pvec[0].vp_addr == buf[0].phys); + switch (pcount) { + case 1: + expect(pvec[0].vp_size == PAGE_SIZE * 3); + break; + case 2: + expect(pvec[0].vp_size + pvec[1].vp_size == PAGE_SIZE * 3); + if (pvec[0].vp_size > PAGE_SIZE) + expect(pvec[1].vp_addr == buf[2].phys); + else + expect(pvec[1].vp_addr == buf[1].phys); + break; + case 3: + expect(pvec[0].vp_size == PAGE_SIZE); + expect(pvec[1].vp_addr == buf[1].phys); + expect(pvec[1].vp_size == PAGE_SIZE); + expect(pvec[2].vp_addr == buf[2].phys); + expect(pvec[2].vp_size == PAGE_SIZE); + break; + } + + got_result("write-only access"); + + free_bufs(buf, 3); + + /* Test page faulting. */ + buf[0].pages = 1; + buf[0].flags = 0; + buf[1].pages = 1; + buf[1].flags = BUF_PREALLOC | BUF_ADJACENT; + buf[2].pages = 1; + buf[2].flags = 0; + buf[3].pages = 2; + buf[3].flags = BUF_PREALLOC; + buf[4].pages = 1; + buf[4].flags = BUF_ADJACENT; + buf[5].pages = 1; + buf[5].flags = BUF_ADJACENT; + buf[6].pages = 1; + buf[6].flags = 0; + + alloc_bufs(buf, 7); + + vvec[0].vv_addr = buf[0].addr + PAGE_SIZE - 1; + vvec[0].vv_size = PAGE_SIZE - 1; + vvec[1].vv_addr = buf[2].addr; + vvec[1].vv_size = PAGE_SIZE; + vvec[2].vv_addr = buf[3].addr + 123; + vvec[2].vv_size = PAGE_SIZE * 4 - 456; + pvecp = (struct vumap_phys *) buf[6].addr; + pcount = 7; + assert(sizeof(struct vumap_phys) * pcount <= PAGE_SIZE); + + r = do_vumap(SELF, vvec, 3, 0, VUA_WRITE, pvecp, &pcount); + + expect(r == OK); + /* Same story but more possibilities. I hope I got this right. */ + expect(pcount >= 3 || pcount <= 6); + for (i = 0; i < 7; i++) + expect(is_buf_allocated(&buf[i])); + expect(pvecp[0].vp_addr = buf[0].phys); + if (pvecp[0].vp_size == 1) { + expect(pvecp[1].vp_addr == buf[1].phys); + expect(pvecp[1].vp_size == PAGE_SIZE - 2); + pindex = 2; + } else { + expect(pvecp[0].vp_size == PAGE_SIZE - 1); + pindex = 1; + } + expect(pvecp[pindex].vp_addr == buf[2].phys); + expect(pvecp[pindex].vp_size == PAGE_SIZE); + pindex++; + expect(pvecp[pindex].vp_addr == buf[3].phys + 123); + switch (pcount - pindex) { + case 1: + expect(pvecp[pindex].vp_size == PAGE_SIZE * 4 - 456); + break; + case 2: + if (pvecp[pindex].vp_size > PAGE_SIZE * 2 - 123) { + expect(pvecp[pindex].vp_size == PAGE_SIZE * 3 - 123); + expect(pvecp[pindex + 1].vp_addr == buf[5].phys); + expect(pvecp[pindex + 1].vp_size == + PAGE_SIZE - (456 - 123)); + } else { + expect(pvecp[pindex].vp_size == PAGE_SIZE * 2 - 123); + expect(pvecp[pindex + 1].vp_addr == buf[4].phys); + expect(pvecp[pindex + 1].vp_size == + PAGE_SIZE * 2 - (456 - 123)); + } + break; + case 3: + expect(pvecp[pindex].vp_size == PAGE_SIZE * 2 - 123); + expect(pvecp[pindex + 1].vp_addr == buf[4].phys); + expect(pvecp[pindex + 1].vp_size == PAGE_SIZE); + expect(pvecp[pindex + 2].vp_addr == buf[5].phys); + expect(pvecp[pindex + 2].vp_size == PAGE_SIZE - (456 - 123)); + break; + default: + expect(0); + } + + got_result("page faulting"); + + free_bufs(buf, 7); + + /* MISSING: tests to see whether a request with VUA_WRITE or + * (VUA_READ|VUA_WRITE) correctly gets an EFAULT for a read-only page. + * As of writing, support for such protection is missing from the + * system at all. + */ +} + +PRIVATE void phys_limit(struct vumap_vir *vvec, int vcount, + struct vumap_phys *pvec, int pcount, struct buf *buf, char *desc) +{ + int i, r; + + r = do_vumap(SELF, vvec, vcount, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == MAPVEC_NR); + for (i = 0; i < MAPVEC_NR; i++) { + expect(pvec[i].vp_addr == buf[i].phys); + expect(pvec[i].vp_size == PAGE_SIZE); + } + + got_result(desc); +} + +PRIVATE void test_limits(void) +{ + struct vumap_vir vvec[MAPVEC_NR + 3]; + struct vumap_phys pvec[MAPVEC_NR + 3]; + struct buf buf[MAPVEC_NR + 9]; + int i, r, vcount, pcount, nr_bufs; + + test_group("limits"); + + /* Test large contiguous range. */ + buf[0].pages = MAPVEC_NR + 2; + buf[0].flags = BUF_PREALLOC; + + alloc_bufs(buf, 1); + + vvec[0].vv_addr = buf[0].addr; + vvec[0].vv_size = (MAPVEC_NR + 2) * PAGE_SIZE; + pcount = 2; + + r = do_vumap(SELF, vvec, 1, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == 1); + expect(pvec[0].vp_addr == buf[0].phys); + expect(pvec[0].vp_size == vvec[0].vv_size); + + got_result("large contiguous range"); + + free_bufs(buf, 1); + + /* I'd like to test MAPVEC_NR contiguous ranges of MAPVEC_NR pages + * each, but chances are we don't have that much contiguous memory + * available at all. In fact, the previous test may already fail + * because of this.. + */ + + for (i = 0; i < MAPVEC_NR + 2; i++) { + buf[i].pages = 1; + buf[i].flags = BUF_PREALLOC; + } + buf[i].pages = 1; + buf[i].flags = BUF_PREALLOC | BUF_ADJACENT; + + alloc_bufs(buf, MAPVEC_NR + 3); + + /* Test virtual limit, one below. */ + for (i = 0; i < MAPVEC_NR + 2; i++) { + vvec[i].vv_addr = buf[i].addr; + vvec[i].vv_size = PAGE_SIZE; + } + vvec[i - 1].vv_size += PAGE_SIZE; + + pcount = MAPVEC_NR + 3; + + r = do_vumap(SELF, vvec, MAPVEC_NR - 1, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == MAPVEC_NR - 1); + for (i = 0; i < MAPVEC_NR - 1; i++) { + expect(pvec[i].vp_addr == buf[i].phys); + expect(pvec[i].vp_size == PAGE_SIZE); + } + + got_result("virtual limit, one below"); + + /* Test virtual limit, exact match. */ + pcount = MAPVEC_NR + 3; + + r = do_vumap(SELF, vvec, MAPVEC_NR, 0, VUA_WRITE, pvec, &pcount); + + expect(r == OK); + expect(pcount == MAPVEC_NR); + for (i = 0; i < MAPVEC_NR; i++) { + expect(pvec[i].vp_addr == buf[i].phys); + expect(pvec[i].vp_size == PAGE_SIZE); + } + + got_result("virtual limit, exact match"); + + /* Test virtual limit, one above. */ + pcount = MAPVEC_NR + 3; + + r = do_vumap(SELF, vvec, MAPVEC_NR + 1, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == MAPVEC_NR); + for (i = 0; i < MAPVEC_NR; i++) { + expect(pvec[i].vp_addr == buf[i].phys); + expect(pvec[i].vp_size == PAGE_SIZE); + } + + got_result("virtual limit, one above"); + + /* Test virtual limit, two above. */ + pcount = MAPVEC_NR + 3; + + r = do_vumap(SELF, vvec, MAPVEC_NR + 2, 0, VUA_WRITE, pvec, &pcount); + + expect(r == OK); + expect(pcount == MAPVEC_NR); + for (i = 0; i < MAPVEC_NR; i++) { + expect(pvec[i].vp_addr == buf[i].phys); + expect(pvec[i].vp_size == PAGE_SIZE); + } + + got_result("virtual limit, two above"); + + /* Test physical limit, one below, aligned. */ + pcount = MAPVEC_NR - 1; + + r = do_vumap(SELF, vvec + 2, MAPVEC_NR, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == MAPVEC_NR - 1); + for (i = 0; i < MAPVEC_NR - 1; i++) { + expect(pvec[i].vp_addr == buf[i + 2].phys); + expect(pvec[i].vp_size == PAGE_SIZE); + } + + got_result("physical limit, one below, aligned"); + + /* Test physical limit, one below, unaligned. */ + pcount = MAPVEC_NR - 1; + + r = do_vumap(SELF, vvec + 3, MAPVEC_NR, 0, VUA_READ, pvec, &pcount); + + expect(r == OK); + expect(pcount == MAPVEC_NR - 1); + for (i = 0; i < MAPVEC_NR - 1; i++) { + expect(pvec[i].vp_addr == buf[i + 3].phys); + expect(pvec[i].vp_size == PAGE_SIZE); + } + + got_result("physical limit, one below, unaligned"); + + free_bufs(buf, MAPVEC_NR + 3); + + nr_bufs = sizeof(buf) / sizeof(buf[0]); + + /* This ends up looking in our virtual address space as follows: + * [P] [P] [P] [PPP] [PPP] ...(MAPVEC_NR x [PPP])... [PPP] + * ..where P is a page, and the blocks are virtually contiguous. + */ + for (i = 0; i < nr_bufs; i += 3) { + buf[i].pages = 1; + buf[i].flags = BUF_PREALLOC; + buf[i + 1].pages = 1; + buf[i + 1].flags = + BUF_PREALLOC | ((i >= 3) ? BUF_ADJACENT : 0); + buf[i + 2].pages = 1; + buf[i + 2].flags = + BUF_PREALLOC | ((i >= 3) ? BUF_ADJACENT : 0); + } + + alloc_bufs(buf, nr_bufs); + + for (i = 0; i < 3; i++) { + vvec[i].vv_addr = buf[i].addr; + vvec[i].vv_size = PAGE_SIZE; + } + for ( ; i < nr_bufs / 3 + 1; i++) { + vvec[i].vv_addr = buf[(i - 2) * 3].addr; + vvec[i].vv_size = PAGE_SIZE * 3; + } + vcount = i; + + /* Out of each of the following tests, one will be aligned (that is, + * the last pvec entry will be for the last page in a vvec entry) and + * two will be unaligned. + */ + + /* Test physical limit, exact match. */ + phys_limit(vvec, vcount, pvec, MAPVEC_NR, buf, + "physical limit, exact match, try 1"); + phys_limit(vvec + 1, vcount - 1, pvec, MAPVEC_NR, buf + 1, + "physical limit, exact match, try 2"); + phys_limit(vvec + 2, vcount - 2, pvec, MAPVEC_NR, buf + 2, + "physical limit, exact match, try 3"); + + /* Test physical limit, one above. */ + phys_limit(vvec, vcount, pvec, MAPVEC_NR + 1, buf, + "physical limit, one above, try 1"); + phys_limit(vvec + 1, vcount - 1, pvec, MAPVEC_NR + 1, buf + 1, + "physical limit, one above, try 2"); + phys_limit(vvec + 2, vcount - 2, pvec, MAPVEC_NR + 1, buf + 2, + "physical limit, one above, try 3"); + + /* Test physical limit, two above. */ + phys_limit(vvec, vcount, pvec, MAPVEC_NR + 2, buf, + "physical limit, two above, try 1"); + phys_limit(vvec + 1, vcount - 1, pvec, MAPVEC_NR + 2, buf + 1, + "physical limit, two above, try 2"); + phys_limit(vvec + 2, vcount - 2, pvec, MAPVEC_NR + 2, buf + 2, + "physical limit, two above, try 3"); + + free_bufs(buf, nr_bufs); +} + +PRIVATE void do_tests(int use_relay) +{ + relay = use_relay; + + test_basics(); + + if (!relay) test_endpt(); /* local only */ + + test_vector1(); + + if (!relay) test_vector2(); /* local only */ + + if (relay) test_grant(); /* remote only */ + + test_offset(); + + test_access(); + + test_limits(); +} + +PRIVATE int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info)) +{ + int r; + + verbose = (env_argc > 1 && !strcmp(env_argv[1], "-v")); + + if (verbose) + printf("Starting sys_vumap test set\n"); + + do_tests(FALSE /*use_relay*/); + + if ((r = ds_retrieve_label_endpt("vumaprelay", &endpt)) != OK) + panic("unable to obtain endpoint for 'vumaprelay' (%d)", r); + + do_tests(TRUE /*use_relay*/); + + if (verbose) + printf("Completed sys_vumap test set, %u/%u tests failed\n", + failures, count); + + /* The returned code will determine the outcome of the RS call, and + * thus the entire test. The actual error code does not matter. + */ + return (failures) ? EINVAL : OK; +} + +PRIVATE void sef_local_startup(void) +{ + sef_setcb_init_fresh(sef_cb_init_fresh); + + sef_startup(); +} + +PUBLIC int main(int argc, char **argv) +{ + env_setargs(argc, argv); + + sef_local_startup(); + + return 0; +} -- 2.44.0