diff options
-rw-r--r-- | usr/src/pkg/manifests/system-bhyve-tests.p5m | 1 | ||||
-rw-r--r-- | usr/src/test/bhyve-tests/runfiles/default.run | 8 | ||||
-rw-r--r-- | usr/src/test/bhyve-tests/tests/vmm/Makefile | 1 | ||||
-rw-r--r-- | usr/src/test/bhyve-tests/tests/vmm/mem_devmem.c | 160 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c | 58 |
5 files changed, 200 insertions, 28 deletions
diff --git a/usr/src/pkg/manifests/system-bhyve-tests.p5m b/usr/src/pkg/manifests/system-bhyve-tests.p5m index 823ed69a60..d0d31a0190 100644 --- a/usr/src/pkg/manifests/system-bhyve-tests.p5m +++ b/usr/src/pkg/manifests/system-bhyve-tests.p5m @@ -39,6 +39,7 @@ file path=opt/bhyve-tests/tests/mevent/vnode_file mode=0555 file path=opt/bhyve-tests/tests/mevent/vnode_zvol mode=0555 dir path=opt/bhyve-tests/tests/vmm file path=opt/bhyve-tests/tests/vmm/fpu_getset mode=0555 +file path=opt/bhyve-tests/tests/vmm/mem_devmem mode=0555 file path=opt/bhyve-tests/tests/vmm/mem_partial mode=0555 file path=opt/bhyve-tests/tests/vmm/mem_seg_map mode=0555 license lic_CDDL license=lic_CDDL diff --git a/usr/src/test/bhyve-tests/runfiles/default.run b/usr/src/test/bhyve-tests/runfiles/default.run index 3055f3e2d8..0aae1bcb46 100644 --- a/usr/src/test/bhyve-tests/runfiles/default.run +++ b/usr/src/test/bhyve-tests/runfiles/default.run @@ -20,7 +20,13 @@ post = outputdir = /var/tmp/test_results [/opt/bhyve-tests/tests/vmm] -tests = ['mem_partial', 'mem_seg_map', 'fpu_getset'] +user = root +tests = [ + 'fpu_getset', + 'mem_devmem', + 'mem_partial', + 'mem_seg_map' + ] # Tests of userspace mevent system, built from cmd/bhyve [/opt/bhyve-tests/tests/mevent] diff --git a/usr/src/test/bhyve-tests/tests/vmm/Makefile b/usr/src/test/bhyve-tests/tests/vmm/Makefile index 30d06a0f6b..97cf709ca3 100644 --- a/usr/src/test/bhyve-tests/tests/vmm/Makefile +++ b/usr/src/test/bhyve-tests/tests/vmm/Makefile @@ -17,6 +17,7 @@ include $(SRC)/test/Makefile.com PROG = mem_partial \ mem_seg_map \ + mem_devmem \ fpu_getset COMMON_OBJS = common.o diff --git a/usr/src/test/bhyve-tests/tests/vmm/mem_devmem.c b/usr/src/test/bhyve-tests/tests/vmm/mem_devmem.c new file mode 100644 index 0000000000..5742739f39 --- /dev/null +++ b/usr/src/test/bhyve-tests/tests/vmm/mem_devmem.c @@ -0,0 +1,160 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2022 Oxide Computer Company + */ + + +#include <stdio.h> +#include <unistd.h> +#include <stropts.h> +#include <strings.h> +#include <signal.h> +#include <setjmp.h> +#include <libgen.h> + +#include <sys/vmm.h> +#include <sys/vmm_dev.h> +#include <sys/mman.h> +#include <vmmapi.h> + +#include "common.h" + +enum test_segs { + SEG_LOWMEM = 0, + SEG_BOOTROM = 1, +}; +#define PAGE_CNT 2 +#define PAGE_SZ 4096 +#define SEG_SZ (PAGE_CNT * PAGE_SZ) +#define WHOLE_SZ (SEG_SZ * 2) + +#define TESTVAL_LOWMEM 0x1000ffff +#define TESTVAL_BOOTROM 0x2000eeee + +int +main(int argc, char *argv[]) +{ + struct vmctx *ctx; + int res, fd; + const char *suite_name = basename(argv[0]); + + ctx = create_test_vm(suite_name); + if (ctx == NULL) { + perror("could open test VM"); + return (1); + } + fd = vm_get_device_fd(ctx); + + res = alloc_memseg(ctx, SEG_LOWMEM, SEG_SZ, ""); + if (res != 0) { + perror("could not alloc lowmem seg"); + goto bail; + } + res = alloc_memseg(ctx, SEG_BOOTROM, SEG_SZ, "bootrom"); + if (res != 0) { + perror("could not alloc bootrom seg"); + goto bail; + } + + res = vm_mmap_memseg(ctx, 0, SEG_LOWMEM, 0, SEG_SZ, PROT_ALL); + if (res != 0) { + perror("could not map lowmem into vmspace"); + goto bail; + } + res = vm_mmap_memseg(ctx, SEG_SZ, SEG_BOOTROM, 0, SEG_SZ, PROT_READ); + if (res != 0) { + perror("could not map bootrom into vmspace"); + goto bail; + } + + void *guest_mem; + guest_mem = mmap(NULL, WHOLE_SZ, PROT_READ, MAP_SHARED, fd, 0); + if (guest_mem == MAP_FAILED) { + perror("could not mmap guest-physical memory"); + goto bail; + } + + void *direct_lowmem, *direct_bootrom; + off_t seg_obj_off; + + res = vm_get_devmem_offset(ctx, SEG_LOWMEM, &seg_obj_off); + if (res != 0) { + perror("could not find mapping offset for lowmem seg"); + goto bail; + } + direct_lowmem = mmap(NULL, SEG_SZ, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, seg_obj_off); + if (direct_lowmem == MAP_FAILED) { + perror("could not mmap lowmem directly"); + goto bail; + } + + res = vm_get_devmem_offset(ctx, SEG_BOOTROM, &seg_obj_off); + if (res != 0) { + perror("could not find mapping offset for lowmem seg"); + goto bail; + } + direct_bootrom = mmap(NULL, SEG_SZ, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, seg_obj_off); + if (direct_bootrom == MAP_FAILED) { + perror("could not mmap bootrom directly"); + goto bail; + } + + uint32_t *datap; + + datap = direct_lowmem; + *datap = TESTVAL_LOWMEM; + datap = direct_bootrom; + *datap = TESTVAL_BOOTROM; + + /* check that data written though direct access is as expected */ + datap = guest_mem; + if (*datap != TESTVAL_LOWMEM) { + (void) fprintf(stderr, "unexpected data in lowmem %x != %x\n", + *datap, TESTVAL_LOWMEM); + goto bail; + } + datap = (guest_mem + SEG_SZ); + if (*datap != TESTVAL_BOOTROM) { + (void) fprintf(stderr, "unexpected data in bootrom %x != %x\n", + *datap, TESTVAL_BOOTROM); + goto bail; + } + + /* unmap access mappings */ + res = munmap(guest_mem, WHOLE_SZ); + if (res != 0) { + perror("could not munmap vmspace"); + goto bail; + } + res = munmap(direct_lowmem, SEG_SZ); + if (res != 0) { + perror("could not munmap lowmem object"); + goto bail; + } + res = munmap(direct_bootrom, SEG_SZ); + if (res != 0) { + perror("could not munmap bootrom object"); + goto bail; + } + + /* mission accomplished */ + vm_destroy(ctx); + (void) printf("%s\tPASS\n", suite_name); + return (0); + +bail: + vm_destroy(ctx); + return (1); +} diff --git a/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c b/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c index 4ef2e5f583..24dd851831 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c +++ b/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c @@ -115,6 +115,33 @@ static int vmm_kstat_alloc(vmm_softc_t *, minor_t, const cred_t *); static void vmm_kstat_init(vmm_softc_t *); static void vmm_kstat_fini(vmm_softc_t *); +/* + * The 'devmem' hack: + * + * On native FreeBSD, bhyve consumers are allowed to create 'devmem' segments + * in the vm which appear with their own name related to the vm under /dev. + * Since this would be a hassle from an sdev perspective and would require a + * new cdev interface (or complicate the existing one), we choose to implement + * this in a different manner. Direct access to the underlying vm memory + * segments is exposed by placing them in a range of offsets beyond the normal + * guest memory space. Userspace can query the appropriate offset to mmap() + * for a given segment-id with the VM_DEVMEM_GETOFFSET ioctl. + */ + +static vmm_devmem_entry_t * +vmmdev_devmem_find(vmm_softc_t *sc, int segid) +{ + vmm_devmem_entry_t *ent = NULL; + list_t *dl = &sc->vmm_devmem_list; + + for (ent = list_head(dl); ent != NULL; ent = list_next(dl, ent)) { + if (ent->vde_segid == segid) { + return (ent); + } + } + return (NULL); +} + static int vmmdev_get_memseg(vmm_softc_t *sc, struct vm_memseg *mseg) { @@ -128,13 +155,8 @@ vmmdev_get_memseg(vmm_softc_t *sc, struct vm_memseg *mseg) if (!sysmem) { vmm_devmem_entry_t *de; - list_t *dl = &sc->vmm_devmem_list; - for (de = list_head(dl); de != NULL; de = list_next(dl, de)) { - if (de->vde_segid == mseg->segid) { - break; - } - } + de = vmmdev_devmem_find(sc, mseg->segid); if (de != NULL) { (void) strlcpy(mseg->name, de->vde_name, sizeof (mseg->name)); @@ -146,19 +168,6 @@ vmmdev_get_memseg(vmm_softc_t *sc, struct vm_memseg *mseg) return (error); } -/* - * The 'devmem' hack: - * - * On native FreeBSD, bhyve consumers are allowed to create 'devmem' segments - * in the vm which appear with their own name related to the vm under /dev. - * Since this would be a hassle from an sdev perspective and would require a - * new cdev interface (or complicate the existing one), we choose to implement - * this in a different manner. When 'devmem' mappings are created, an - * identifying off_t is communicated back out to userspace. That off_t, - * residing above the normal guest memory space, can be used to mmap the - * 'devmem' mapping from the already-open vm device. - */ - static int vmmdev_devmem_create(vmm_softc_t *sc, struct vm_memseg *mseg, const char *name) { @@ -239,7 +248,7 @@ vmmdev_alloc_memseg(vmm_softc_t *sc, struct vm_memseg *mseg) } error = vm_alloc_memseg(sc->vmm_vm, mseg->segid, mseg->len, sysmem); - if (error == 0 && VM_MEMSEG_NAME(mseg)) { + if (error == 0) { /* * Rather than create a whole fresh device from which userspace * can mmap this segment, instead make it available at an @@ -1442,19 +1451,14 @@ vmmdev_do_ioctl(vmm_softc_t *sc, int cmd, intptr_t arg, int md, } case VM_DEVMEM_GETOFFSET: { struct vm_devmem_offset vdo; - list_t *dl = &sc->vmm_devmem_list; - vmm_devmem_entry_t *de = NULL; + vmm_devmem_entry_t *de; if (ddi_copyin(datap, &vdo, sizeof (vdo), md) != 0) { error = EFAULT; break; } - for (de = list_head(dl); de != NULL; de = list_next(dl, de)) { - if (de->vde_segid == vdo.segid) { - break; - } - } + de = vmmdev_devmem_find(sc, vdo.segid); if (de != NULL) { vdo.offset = de->vde_off; if (ddi_copyout(&vdo, datap, sizeof (vdo), md) != 0) { |