Module Name:    src
Committed By:   maxv
Date:           Sat Nov 10 09:28:56 UTC 2018

Modified Files:
        src/distrib/sets/lists/comp: md.amd64
        src/lib: Makefile
Added Files:
        src/lib/libnvmm: Makefile libnvmm.3 libnvmm.c libnvmm_x86.c nvmm.h
            shlib_version

Log Message:
Add libnvmm, NetBSD's new virtualization API. It provides a way for VMM
software to effortlessly create and manage virtual machines via NVMM.

It is mostly complete, only nvmm_assist_mem needs to be filled -- I have
a draft for that, but it needs some more care. This Mem Assist should
not be needed when emulating a system in x2apic mode, so theoretically
the current form of libnvmm is sufficient to emulate a whole class of
systems.

Generally speaking, there are so many modes in x86 that it is difficult
to handle each corner case without introducing a ton of checks that just
slow down the common-case execution. Currently we check a limited number
of things; we may add more checks in the future if they turn out to be
needed, but that's rather low priority.

Libnvmm is compiled and installed only on amd64. A man page (reviewed by
wiz@) is provided.


To generate a diff of this commit:
cvs rdiff -u -r1.260 -r1.261 src/distrib/sets/lists/comp/md.amd64
cvs rdiff -u -r1.261 -r1.262 src/lib/Makefile
cvs rdiff -u -r0 -r1.1 src/lib/libnvmm/Makefile src/lib/libnvmm/libnvmm.3 \
    src/lib/libnvmm/libnvmm.c src/lib/libnvmm/libnvmm_x86.c \
    src/lib/libnvmm/nvmm.h src/lib/libnvmm/shlib_version

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/comp/md.amd64
diff -u src/distrib/sets/lists/comp/md.amd64:1.260 src/distrib/sets/lists/comp/md.amd64:1.261
--- src/distrib/sets/lists/comp/md.amd64:1.260	Wed Nov  7 07:43:07 2018
+++ src/distrib/sets/lists/comp/md.amd64	Sat Nov 10 09:28:56 2018
@@ -1,4 +1,4 @@
-# $NetBSD: md.amd64,v 1.260 2018/11/07 07:43:07 maxv Exp $
+# $NetBSD: md.amd64,v 1.261 2018/11/10 09:28:56 maxv Exp $
 
 ./usr/include/amd64				comp-c-include
 ./usr/include/amd64/ansi.h			comp-c-include
@@ -682,6 +682,7 @@
 ./usr/include/dev/nvmm/nvmm_ioctl.h		comp-c-include
 ./usr/include/dev/nvmm/x86			comp-c-include
 ./usr/include/dev/nvmm/x86/nvmm_x86.h		comp-c-include
+./usr/include/nvmm.h				comp-c-include
 ./usr/include/pmmintrin.h			comp-obsolete		obsolete
 ./usr/include/x64_64				comp-obsolete		obsolete
 ./usr/include/x64_64/ansi.h			comp-obsolete		obsolete
@@ -783,6 +784,12 @@
 ./usr/lib/i386/libi386.so			comp-sys-shlib		compat,pic
 ./usr/lib/i386/libi386_p.a			comp-c-proflib		compat,profile
 ./usr/lib/i386/libi386_pic.a			comp-c-piclib		compat,pic,picinstall
+./usr/lib/libnvmm.a				comp-c-lib		compatfile
+./usr/lib/libnvmm.so				comp-sys-shlib		compat,pic
+./usr/lib/libnvmm.so.0				comp-sys-shlib		compat,pic
+./usr/lib/libnvmm.so.0.1			comp-sys-shlib		compat,pic
+./usr/lib/libnvmm_p.a				comp-c-proflib		compatfile,profile
+./usr/lib/libnvmm_pic.a				comp-c-piclib		compat,pic,picinstall
 ./usr/lib/libx86_64.a				comp-c-lib
 ./usr/lib/libx86_64_p.a				comp-c-proflib		profile
 ./usr/lib/libx86_64_pic.a			comp-c-piclib		pic,picinstall
@@ -888,3 +895,6 @@
 ./usr/share/ldscripts/i386nbsd.xn		comp-obsolete		obsolete
 ./usr/share/ldscripts/i386nbsd.xr		comp-obsolete		obsolete
 ./usr/share/ldscripts/i386nbsd.xu		comp-obsolete		obsolete
+./usr/share/man/cat3/libnvmm.0			comp-c-catman		.cat
+./usr/share/man/html3/libnvmm.html		comp-c-htmlman		html
+./usr/share/man/man3/libnvmm.3			comp-c-man		.man

Index: src/lib/Makefile
diff -u src/lib/Makefile:1.261 src/lib/Makefile:1.262
--- src/lib/Makefile:1.261	Sat Sep  8 14:11:41 2018
+++ src/lib/Makefile	Sat Nov 10 09:28:56 2018
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.261 2018/09/08 14:11:41 christos Exp $
+#	$NetBSD: Makefile,v 1.262 2018/11/10 09:28:56 maxv Exp $
 #	from: @(#)Makefile	5.25.1.1 (Berkeley) 5/7/91
 
 .include <bsd.own.mk>
@@ -50,6 +50,10 @@ SUBDIR+=	librumpclient
 SUBDIR+=	libskey
 .endif
 
+.if ${MACHINE_ARCH} == "x86_64"
+SUBDIR+=	libnvmm
+.endif
+
 .if (${MKMDNS} != "no")
 SUBDIR+=	../external/apache2/mDNSResponder/lib
 .endif

Added files:

Index: src/lib/libnvmm/Makefile
diff -u /dev/null src/lib/libnvmm/Makefile:1.1
--- /dev/null	Sat Nov 10 09:28:56 2018
+++ src/lib/libnvmm/Makefile	Sat Nov 10 09:28:56 2018
@@ -0,0 +1,17 @@
+# $NetBSD: Makefile,v 1.1 2018/11/10 09:28:56 maxv Exp $
+
+USE_SHLIBDIR=   yes
+
+.include <bsd.own.mk>
+
+LIB=		nvmm
+MAN=		libnvmm.3
+
+SRCS=		libnvmm.c libnvmm_x86.c
+
+INCS=		nvmm.h
+INCSDIR=	/usr/include
+
+WARNS=		5
+
+.include <bsd.lib.mk>
Index: src/lib/libnvmm/libnvmm.3
diff -u /dev/null src/lib/libnvmm/libnvmm.3:1.1
--- /dev/null	Sat Nov 10 09:28:56 2018
+++ src/lib/libnvmm/libnvmm.3	Sat Nov 10 09:28:56 2018
@@ -0,0 +1,484 @@
+.Dd September 12, 2018
+.Dt LIBNVMM 3
+.Os
+.Sh NAME
+.Nm libnvmm
+.Nd NetBSD Virtualization API
+.Sh LIBRARY
+.Lb libnvmm
+.Sh SYNOPSIS
+.In nvmm.h
+.Ft int
+.Fn nvmm_capability "struct nvmm_capability *cap"
+.Ft int
+.Fn nvmm_machine_create "struct nvmm_machine *mach"
+.Ft int
+.Fn nvmm_machine_destroy "struct nvmm_machine *mach"
+.Ft int
+.Fn nvmm_machine_configure "struct nvmm_machine *mach" "uint64_t op" \
+    "void *conf"
+.Ft int
+.Fn nvmm_vcpu_create "struct nvmm_machine *mach" "nvmm_cpuid_t cpuid"
+.Ft int
+.Fn nvmm_vcpu_destroy "struct nvmm_machine *mach" "nvmm_cpuid_t cpuid"
+.Ft int
+.Fn nvmm_vcpu_getstate "struct nvmm_machine *mach" "nvmm_cpuid_t cpuid" \
+    "void *state" "uint64_t flags"
+.Ft int
+.Fn nvmm_vcpu_setstate "struct nvmm_machine *mach" "nvmm_cpuid_t cpuid" \
+    "void *state" "uint64_t flags"
+.Ft int
+.Fn nvmm_vcpu_inject "struct nvmm_machine *mach" "nvmm_cpuid_t cpuid" \
+    "struct nvmm_event *event"
+.Ft int
+.Fn nvmm_vcpu_run "struct nvmm_machine *mach" "nvmm_cpuid_t cpuid" \
+    "struct nvmm_exit *exit"
+.Ft int
+.Fn nvmm_gpa_map "struct nvmm_machine *mach" "uintptr_t hva" "gpaddr_t gpa" \
+    "size_t size" "int flags"
+.Ft int
+.Fn nvmm_gpa_unmap "struct nvmm_machine *mach" "uintptr_t hva" "gpaddr_t gpa" \
+    "size_t size"
+.Ft int
+.Fn nvmm_gva_to_gpa "struct nvmm_machine *mach" "nvmm_cpuid_t cpuid" \
+    "gvaddr_t gva" "gpaddr_t *gpa" "nvmm_prot_t *prot"
+.Ft int
+.Fn nvmm_gpa_to_hva "struct nvmm_machine *mach" "gpaddr_t gpa" \
+    "uintptr_t *hva"
+.Ft int
+.Fn nvmm_assist_io "struct nvmm_machine *mach" "nvmm_cpuid_t cpuid" \
+    "struct nvmm_exit *exit" "void (*cb)(struct nvmm_io *)"
+.Ft int
+.Fn nvmm_assist_mem "struct nvmm_machine *mach" "nvmm_cpuid_t cpuid" \
+    "struct nvmm_exit *exit" "void (*cb)(struct nvmm_mem *)"
+.Sh DESCRIPTION
+.Nm
+provides a library for VMM software to handle hardware-accelerated virtual
+machines in
+.Nx .
+A virtual machine is described by an opaque structure,
+.Cd nvmm_machine .
+VMM software should not attempt to modify this structure directly, and should
+use the API provided by
+.Nm
+to handle virtual machines.
+.Pp
+.Fn nvmm_capability
+gets the capabilities of NVMM.
+.Pp
+.Fn nvmm_machine_create
+creates a virtual machine in the kernel.
+The
+.Fa mach
+structure is initialized, and describes the machine.
+.Pp
+.Fn nvmm_machine_destroy
+destroys the virtual machine described in
+.Fa mach .
+.Pp
+.Fn nvmm_machine_configure
+configures, on the machine
+.Fa mach ,
+the parameter indicated in
+.Fa op .
+.Fa conf
+describes the value of the parameter.
+.Pp
+.Fn nvmm_vcpu_create
+creates a virtual CPU in the machine
+.Fa mach ,
+giving it the CPU id
+.Fa cpuid .
+.Pp
+.Fn nvmm_vcpu_destroy
+destroys the virtual CPU identified by
+.Fa cpuid
+in the machine
+.Fa mach .
+.Pp
+.Fn nvmm_vcpu_getstate
+gets the state of the virtual CPU identified by
+.Fa cpuid
+in the machine
+.Fa mach .
+The
+.Fa state
+argument is the address of a state area, and
+.Fa flags
+is the bitmap of the components that are to be retrieved.
+See
+.Sx VCPU State Area
+below for details.
+.Pp
+.Fn nvmm_vcpu_setstate
+sets the state of the virtual CPU identified by
+.Fa cpuid
+in the machine
+.Fa mach .
+The
+.Fa state
+argument is the address of a state area, and
+.Fa flags
+is the bitmap of the components that are to be set.
+See
+.Sx VCPU State Area
+below for details.
+.Pp
+.Fn nvmm_vcpu_run
+runs the CPU identified by
+.Fa cpuid
+in the machine
+.Fa mach ,
+until a VM exit is triggered.
+The
+.Fa exit
+structure is filled to indicate the exit reason, and the associated parameters
+if any.
+.Pp
+.Fn nvmm_gpa_map
+makes the guest physical memory area beginning on address
+.Fa gpa
+and of size
+.Fa size
+available in the machine
+.Fa mach .
+The area is mapped in the calling process' virtual address space, at address
+.Fa hva .
+.Pp
+.Fn nvmm_gpa_unmap
+removes the guest physical memory area beginning on address
+.Fa gpa
+and of size
+.Fa size
+from the machine
+.Fa mach .
+It also unmaps the area beginning on
+.Fa hva
+from the calling process' virtual address space.
+.Pp
+.Fn nvmm_gva_to_gpa
+translates, on the CPU
+.Fa cpuid
+from the machine
+.Fa mach ,
+the guest virtual address given in
+.Fa gva
+into a guest physical address returned in
+.Fa gpa .
+The associated page premissions are returned in
+.Fa prot .
+.Fa gva
+must be page-aligned.
+.Pp
+.Fn nvmm_gpa_to_hva
+translates, on the machine
+.Fa mach ,
+the guest physical address indicated in
+.Fa gpa
+into a host virtual address returned in
+.Fa hva .
+.Fa gpa
+must be page-aligned.
+.Pp
+.Fn nvmm_assist_io
+emulates the I/O operation described in
+.Fa exit
+on CPU
+.Fa cpuid
+from machine
+.Fa mach .
+.Fa cb
+will be called to handle the transaction.
+See
+.Sx I/O Assist
+below for details.
+.Pp
+.Fn nvmm_assist_mem
+emulates the Mem operation described in
+.Fa exit
+on CPU
+.Fa cpuid
+from machine
+.Fa mach .
+.Fa cb
+will be called to handle the transaction.
+See
+.Sx Mem Assist
+below for details.
+.Ss NVMM Capability
+The
+.Cd nvmm_capability
+structure helps VMM software identify the capabilities offered by NVMM on the
+host:
+.Bd -literal
+struct nvmm_capability {
+	uint64_t version;
+	uint64_t state_size;
+	uint64_t max_machines;
+	uint64_t max_vcpus;
+	uint64_t max_ram;
+	union {
+		struct {
+			...
+		} x86;
+		uint64_t rsvd[8];
+	} u;
+};
+.Ed
+.Pp
+For example, the
+.Cd max_machines
+field indicates the maximum number of virtual machines supported, while
+.Cd max_vcpus
+indicates the maximum number of VCPUs supported per virtual machine.
+.Ss VCPU State Area
+A VCPU state area is a structure that entirely defines the content of the
+registers of a VCPU.
+Only one such structure exists, for x86:
+.Bd -literal
+struct nvmm_x64_state {
+	...
+};
+.Ed
+.Pp
+Refer to functional examples to see precisely how to use this structure.
+.Ss Exit Reasons
+The
+.Cd nvmm_exit
+structure is used to handle VM exits:
+.Bd -literal
+enum nvmm_exit_reason {
+	NVMM_EXIT_NONE		= 0x0000000000000000,
+
+	/* General. */
+	NVMM_EXIT_MEMORY	= 0x0000000000000001,
+	NVMM_EXIT_IO		= 0x0000000000000002,
+	NVMM_EXIT_MSR		= 0x0000000000000003,
+	NVMM_EXIT_INT_READY	= 0x0000000000000004,
+	NVMM_EXIT_NMI_READY	= 0x0000000000000005,
+	NVMM_EXIT_SHUTDOWN	= 0x0000000000000006,
+
+	/* Instructions (x86). */
+	...
+
+	NVMM_EXIT_INVALID	= 0xFFFFFFFFFFFFFFFF
+};
+
+struct nvmm_exit {
+	enum nvmm_exit_reason reason;
+	union {
+		...
+	} u;
+	uint64_t exitstate[8];
+};
+.Ed
+.Pp
+The
+.Va reason
+field indicates the reason of the VM exit.
+Additional parameters describing the exit can be present in
+.Va u .
+.Va exitstate
+contains a partial, implementation-specific VCPU state, usable as a fast-path
+to retrieve certain state values.
+.Pp
+It is possible that a VM exit was caused by a reason internal to the host
+kernel, and that VMM software should not be concerned with.
+In this case, the exit reason is set to
+.Cd NVMM_EXIT_NONE .
+This gives a chance for VMM software to halt the VM in its tracks.
+.Pp
+Refer to functional examples to see precisely how to handle VM exits.
+.Ss Event Injection
+It is possible to inject an event into a VCPU.
+An event can be a hardware interrupt, a software interrupt, or a software
+exception, defined by:
+.Bd -literal
+enum nvmm_event_type {
+	NVMM_EVENT_INTERRUPT_HW,
+	NVMM_EVENT_INTERRUPT_SW,
+	NVMM_EVENT_EXCEPTION
+};
+
+struct nvmm_event {
+	enum nvmm_event_type type;
+	uint64_t vector;
+	union {
+		uint64_t error;
+		uint64_t prio;
+	} u;
+};
+.Ed
+.Pp
+This describes an event of type
+.Va type ,
+to be sent to vector number
+.Va vector ,
+with a possible additional
+.Va error
+or
+.Va prio
+code that is implementation-specific.
+.Pp
+It is possible that the VCPU is in a state where it cannot receive this
+event, if:
+.Pp
+.Bl -bullet -offset indent -compact
+.It
+the event is a hardware interrupt, and the VCPU runs with interrupts disabled,
+or
+.It
+the event is a non-maskable interrupt (NMI), and the VCPU is already in a
+in-NMI context.
+.El
+.Pp
+In this case,
+.Fn nvmm_vcpu_inject
+will return
+.Er EAGAIN ,
+and NVMM will cause a VM exit with reason
+.Cd NVMM_EXIT_INT_READY
+or
+.Cd NVMM_EXIT_NMI_READY
+to indicate that VMM software can now reinject the desired event.
+.Ss I/O Assist
+When a VM exit occurs with reason
+.Cd NVMM_EXIT_IO ,
+it is necessary for VMM software to emulate the associated I/O operation.
+.Nm
+provides an easy way for VMM software to perform that.
+.Pp
+.Fn nvmm_assist_io
+will call the
+.Fa cb
+callback function and give it a
+.Cd nvmm_io
+structure as argument.
+This structure describes an I/O transaction:
+.Bd -literal
+struct nvmm_io {
+	uint64_t port;
+	bool in;
+	size_t size;
+	uint8_t data[8];
+};
+.Ed
+.Pp
+The callback can emulate the operation using this descriptor, following two
+unique cases:
+.Pp
+.Bl -bullet -offset indent -compact
+.It
+The operation is an input.
+In this case, the callback should fill
+.Va data
+with the desired value.
+.It
+The operation is an output.
+In this case, the callback should read
+.Va data
+to retrieve the desired value.
+.El
+.Pp
+In either case,
+.Va port
+will indicate the I/O port,
+.Va in
+will indicate if the operation is an input, and
+.Va size
+will indicate the size of the access.
+.Ss Mem Assist
+When a VM exit occurs with reason
+.Cd NVMM_EXIT_MEMORY ,
+it is necessary for VMM software to emulate the associated memory operation.
+.Nm
+provides an easy way for VMM software to perform that, similar to the I/O
+Assist.
+.Pp
+.Fn nvmm_assist_mem
+will call the
+.Fa cb
+callback function and give it a
+.Cd nvmm_mem
+structure as argument.
+This structure describes a Mem transaction:
+.Bd -literal
+struct nvmm_mem {
+	gvaddr_t gva;
+	gpaddr_t gpa;
+	bool write;
+	size_t size;
+	uint8_t data[8];
+};
+.Ed
+.Pp
+The callback can emulate the operation using this descriptor, following two
+unique cases:
+.Pp
+.Bl -bullet -offset indent -compact
+.It
+The operation is a read.
+In this case, the callback should fill
+.Va data
+with the desired value.
+.It
+The operation is a write.
+In this case, the callback should read
+.Va data
+to retrieve the desired value.
+.El
+.Pp
+In either case,
+.Va gva
+will indicate the guest virtual address,
+.Va gpa
+will indicate the guest physical address,
+.Va write
+will indicate if the access is a write, and
+.Va size
+will indicate the size of the access.
+.Sh RETURN VALUES
+Upon successful completion, each of these functions returns zero.
+Otherwise, a value of \-1 is returned and the global
+variable
+.Va errno
+is set to indicate the error.
+.Sh FILES
+Functional examples:
+.Pp
+.Bl -tag -width XXXX -compact
+.It Pa src/share/examples/nvmm/toyvirt/
+Example of virtualizer.
+Launches the binary given as argument in a virtual machine.
+.It Pa src/share/examples/nvmm/smallkern/
+Example of a kernel that can be executed by toyvirt.
+.El
+.Sh ERRORS
+These functions will fail if:
+.Bl -tag -width [ENOBUFS]
+.It Bq Er EEXIST
+An attempt was made to create a machine or a VCPU that already exists.
+.It Bq Er EFAULT
+An attempt was made to emulate a memory-based operation in a guest, and the
+guest page tables did not have the permissions necessary for the operation
+to complete successfully.
+.It Bq Er EINVAL
+An inappropriate parameter was used.
+.It Bq Er ENOBUFS
+The maximum number of machines or VCPUs was reached.
+.It Bq Er ENOENT
+A query was made on a machine or a VCPU that does not exist.
+.It Bq Er EPERM
+An attempt was made to access a machine that does not belong to the process.
+.El
+.Pp
+In addition,
+.Fn nvmm_vcpu_inject
+uses the following error codes:
+.Bl -tag -width [ENOBUFS]
+.It Bq Er EAGAIN
+The VCPU cannot receive the event immediately.
+.El
+.Sh AUTHORS
+NVMM was designed and implemented by
+.An Maxime Villard .
Index: src/lib/libnvmm/libnvmm.c
diff -u /dev/null src/lib/libnvmm/libnvmm.c:1.1
--- /dev/null	Sat Nov 10 09:28:56 2018
+++ src/lib/libnvmm/libnvmm.c	Sat Nov 10 09:28:56 2018
@@ -0,0 +1,433 @@
+/*	$NetBSD: libnvmm.c,v 1.1 2018/11/10 09:28:56 maxv Exp $	*/
+
+/*
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Maxime Villard.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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 <sys/cdefs.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include "nvmm.h"
+
+static int nvmm_fd = -1;
+static size_t nvmm_page_size = 0;
+
+/* -------------------------------------------------------------------------- */
+
+static int
+_nvmm_area_add(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t hva,
+    size_t size)
+{
+	struct nvmm_area *area;
+	void *ptr;
+	size_t i;
+
+	for (i = 0; i < mach->nareas; i++) {
+		if (gpa >= mach->areas[i].gpa &&
+		    gpa < mach->areas[i].gpa + mach->areas[i].size) {
+			goto error;
+		}
+		if (gpa + size >= mach->areas[i].gpa &&
+		    gpa + size < mach->areas[i].gpa + mach->areas[i].size) {
+			goto error;
+		}
+		if (gpa < mach->areas[i].gpa &&
+		    gpa + size >= mach->areas[i].gpa + mach->areas[i].size) {
+			goto error;
+		}
+	}
+
+	mach->nareas++;
+	ptr = realloc(mach->areas, mach->nareas * sizeof(struct nvmm_area));
+	if (ptr == NULL)
+		return -1;
+	mach->areas = ptr;
+
+	area = &mach->areas[mach->nareas-1];
+	area->gpa = gpa;
+	area->hva = hva;
+	area->size = size;
+
+	return 0;
+
+error:
+	errno = EEXIST;
+	return -1;
+}
+
+static int
+_nvmm_area_delete(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t hva,
+    size_t size)
+{
+	size_t i;
+
+	for (i = 0; i < mach->nareas; i++) {
+		if (gpa == mach->areas[i].gpa &&
+		    hva == mach->areas[i].hva &&
+		    size == mach->areas[i].size) {
+			break;
+		}
+	}
+	if (i == mach->nareas) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	memcpy(&mach->areas[i], &mach->areas[i+1],
+	    (mach->nareas - i - 1) * sizeof(struct nvmm_area));
+	mach->nareas--;
+
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int
+nvmm_init(void)
+{
+	if (nvmm_fd != -1)
+		return 0;
+	nvmm_fd = open("/dev/nvmm", O_RDWR);
+	if (nvmm_fd == -1)
+		return -1;
+	nvmm_page_size = sysconf(_SC_PAGESIZE);
+	return 0;
+}
+
+int
+nvmm_capability(struct nvmm_capability *cap)
+{
+	struct nvmm_ioc_capability args;
+	int ret;
+
+	if (nvmm_init() == -1) {
+		return -1;
+	}
+
+	ret = ioctl(nvmm_fd, NVMM_IOC_CAPABILITY, &args);
+	if (ret == -1)
+		return -1;
+
+	memcpy(cap, &args.cap, sizeof(args.cap));
+
+	return 0;
+}
+
+int
+nvmm_machine_create(struct nvmm_machine *mach)
+{
+	struct nvmm_ioc_machine_create args;
+	int ret;
+
+	if (nvmm_init() == -1) {
+		return -1;
+	}
+
+	ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CREATE, &args);
+	if (ret == -1)
+		return -1;
+
+	memset(mach, 0, sizeof(*mach));
+	mach->machid = args.machid;
+
+	return 0;
+}
+
+int
+nvmm_machine_destroy(struct nvmm_machine *mach)
+{
+	struct nvmm_ioc_machine_destroy args;
+	int ret;
+
+	if (nvmm_init() == -1) {
+		return -1;
+	}
+
+	args.machid = mach->machid;
+
+	ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_DESTROY, &args);
+	if (ret == -1)
+		return -1;
+
+	free(mach->areas);
+
+	return 0;
+}
+
+int
+nvmm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *conf)
+{
+	struct nvmm_ioc_machine_configure args;
+	int ret;
+
+	if (nvmm_init() == -1) {
+		return -1;
+	}
+
+	args.machid = mach->machid;
+	args.op = op;
+	args.conf = conf;
+
+	ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CONFIGURE, &args);
+	if (ret == -1)
+		return -1;
+
+	return 0;
+}
+
+int
+nvmm_vcpu_create(struct nvmm_machine *mach, nvmm_cpuid_t cpuid)
+{
+	struct nvmm_ioc_vcpu_create args;
+	int ret;
+
+	if (nvmm_init() == -1) {
+		return -1;
+	}
+
+	args.machid = mach->machid;
+	args.cpuid = cpuid;
+
+	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CREATE, &args);
+	if (ret == -1)
+		return -1;
+
+	return 0;
+}
+
+int
+nvmm_vcpu_destroy(struct nvmm_machine *mach, nvmm_cpuid_t cpuid)
+{
+	struct nvmm_ioc_vcpu_destroy args;
+	int ret;
+
+	if (nvmm_init() == -1) {
+		return -1;
+	}
+
+	args.machid = mach->machid;
+	args.cpuid = cpuid;
+
+	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_DESTROY, &args);
+	if (ret == -1)
+		return -1;
+
+	return 0;
+}
+
+int
+nvmm_vcpu_setstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
+    void *state, uint64_t flags)
+{
+	struct nvmm_ioc_vcpu_setstate args;
+	int ret;
+
+	if (nvmm_init() == -1) {
+		return -1;
+	}
+
+	args.machid = mach->machid;
+	args.cpuid = cpuid;
+	args.state = state;
+	args.flags = flags;
+
+	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_SETSTATE, &args);
+	if (ret == -1)
+		return -1;
+
+	return 0;
+}
+
+int
+nvmm_vcpu_getstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
+    void *state, uint64_t flags)
+{
+	struct nvmm_ioc_vcpu_getstate args;
+	int ret;
+
+	if (nvmm_init() == -1) {
+		return -1;
+	}
+
+	args.machid = mach->machid;
+	args.cpuid = cpuid;
+	args.state = state;
+	args.flags = flags;
+
+	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_GETSTATE, &args);
+	if (ret == -1)
+		return -1;
+
+	return 0;
+}
+
+int
+nvmm_vcpu_inject(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
+    struct nvmm_event *event)
+{
+	struct nvmm_ioc_vcpu_inject args;
+	int ret;
+
+	if (nvmm_init() == -1) {
+		return -1;
+	}
+
+	args.machid = mach->machid;
+	args.cpuid = cpuid;
+	memcpy(&args.event, event, sizeof(args.event));
+
+	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_INJECT, &args);
+	if (ret == -1)
+		return -1;
+
+	return 0;
+}
+
+int
+nvmm_vcpu_run(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
+    struct nvmm_exit *exit)
+{
+	struct nvmm_ioc_vcpu_run args;
+	int ret;
+
+	if (nvmm_init() == -1) {
+		return -1;
+	}
+
+	args.machid = mach->machid;
+	args.cpuid = cpuid;
+	memset(&args.exit, 0, sizeof(args.exit));
+
+	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_RUN, &args);
+	if (ret == -1)
+		return -1;
+
+	memcpy(exit, &args.exit, sizeof(args.exit));
+
+	return 0;
+}
+
+int
+nvmm_gpa_map(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
+    size_t size, int flags)
+{
+	struct nvmm_ioc_gpa_map args;
+	int ret;
+
+	if (nvmm_init() == -1) {
+		return -1;
+	}
+
+	args.machid = mach->machid;
+	args.hva = hva;
+	args.gpa = gpa;
+	args.size = size;
+	args.flags = flags;
+
+	ret = ioctl(nvmm_fd, NVMM_IOC_GPA_MAP, &args);
+	if (ret == -1)
+		return -1;
+
+	ret = _nvmm_area_add(mach, gpa, hva, size);
+	if (ret == -1) {
+		nvmm_gpa_unmap(mach, hva, gpa, size);
+		return -1;
+	}
+
+	return 0;
+}
+
+int
+nvmm_gpa_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
+    size_t size)
+{
+	struct nvmm_ioc_gpa_unmap args;
+	int ret;
+
+	if (nvmm_init() == -1) {
+		return -1;
+	}
+
+	ret = _nvmm_area_delete(mach, gpa, hva, size);
+	if (ret == -1)
+		return -1;
+
+	args.machid = mach->machid;
+	args.gpa = gpa;
+	args.size = size;
+
+	ret = ioctl(nvmm_fd, NVMM_IOC_GPA_UNMAP, &args);
+	if (ret == -1)
+		return -1;
+
+	ret = munmap((void *)hva, size);
+
+	return ret;
+}
+
+/*
+ * nvmm_gva_to_gpa(): architecture-specific.
+ */
+
+int
+nvmm_gpa_to_hva(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t *hva)
+{
+	size_t i;
+
+	if (gpa % nvmm_page_size != 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	for (i = 0; i < mach->nareas; i++) {
+		if (gpa < mach->areas[i].gpa) {
+			continue;
+		}
+		if (gpa >= mach->areas[i].gpa + mach->areas[i].size) {
+			continue;
+		}
+
+		*hva = mach->areas[i].hva + (gpa - mach->areas[i].gpa);
+		return 0;
+	}
+
+	errno = ENOENT;
+	return -1;
+}
+
+/*
+ * nvmm_assist_io(): architecture-specific.
+ */
Index: src/lib/libnvmm/libnvmm_x86.c
diff -u /dev/null src/lib/libnvmm/libnvmm_x86.c:1.1
--- /dev/null	Sat Nov 10 09:28:56 2018
+++ src/lib/libnvmm/libnvmm_x86.c	Sat Nov 10 09:28:56 2018
@@ -0,0 +1,592 @@
+/*	$NetBSD: libnvmm_x86.c,v 1.1 2018/11/10 09:28:56 maxv Exp $	*/
+
+/*
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Maxime Villard.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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 <sys/cdefs.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <machine/vmparam.h>
+#include <machine/pte.h>
+#include <machine/psl.h>
+
+#include "nvmm.h"
+
+#include <x86/specialreg.h>
+
+/* -------------------------------------------------------------------------- */
+
+#define PTE32_L1_SHIFT	12
+#define PTE32_L2_SHIFT	22
+
+#define PTE32_L2_MASK	0xffc00000
+#define PTE32_L1_MASK	0x003ff000
+
+#define PTE32_L2_FRAME	(PTE32_L2_MASK)
+#define PTE32_L1_FRAME	(PTE32_L2_FRAME|PTE32_L1_MASK)
+
+#define pte32_l1idx(va)	(((va) & PTE32_L1_MASK) >> PTE32_L1_SHIFT)
+#define pte32_l2idx(va)	(((va) & PTE32_L2_MASK) >> PTE32_L2_SHIFT)
+
+typedef uint32_t pte_32bit_t;
+
+static int
+x86_gva_to_gpa_32bit(struct nvmm_machine *mach, uint64_t cr3,
+    gvaddr_t gva, gpaddr_t *gpa, bool has_pse, nvmm_prot_t *prot)
+{
+	gpaddr_t L2gpa, L1gpa;
+	uintptr_t L2hva, L1hva;
+	pte_32bit_t *pdir, pte;
+
+	/* We begin with an RWXU access. */
+	*prot = NVMM_PROT_ALL;
+
+	/* Parse L2. */
+	L2gpa = (cr3 & PG_FRAME);
+	if (nvmm_gpa_to_hva(mach, L2gpa, &L2hva) == -1)
+		return -1;
+	pdir = (pte_32bit_t *)L2hva;
+	pte = pdir[pte32_l2idx(gva)];
+	if ((pte & PG_V) == 0)
+		return -1;
+	if ((pte & PG_u) == 0)
+		*prot &= ~NVMM_PROT_USER;
+	if ((pte & PG_KW) == 0)
+		*prot &= ~NVMM_PROT_WRITE;
+	if ((pte & PG_PS) && !has_pse)
+		return -1;
+	if (pte & PG_PS) {
+		*gpa = (pte & PTE32_L2_FRAME);
+		return 0;
+	}
+
+	/* Parse L1. */
+	L1gpa = (pte & PG_FRAME);
+	if (nvmm_gpa_to_hva(mach, L1gpa, &L1hva) == -1)
+		return -1;
+	pdir = (pte_32bit_t *)L1hva;
+	pte = pdir[pte32_l1idx(gva)];
+	if ((pte & PG_V) == 0)
+		return -1;
+	if ((pte & PG_u) == 0)
+		*prot &= ~NVMM_PROT_USER;
+	if ((pte & PG_KW) == 0)
+		*prot &= ~NVMM_PROT_WRITE;
+	if (pte & PG_PS)
+		return -1;
+
+	*gpa = (pte & PG_FRAME);
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+#define	PTE32_PAE_L1_SHIFT	12
+#define	PTE32_PAE_L2_SHIFT	21
+#define	PTE32_PAE_L3_SHIFT	30
+
+#define	PTE32_PAE_L3_MASK	0xc0000000
+#define	PTE32_PAE_L2_MASK	0x3fe00000
+#define	PTE32_PAE_L1_MASK	0x001ff000
+
+#define	PTE32_PAE_L3_FRAME	(PTE32_PAE_L3_MASK)
+#define	PTE32_PAE_L2_FRAME	(PTE32_PAE_L3_FRAME|PTE32_PAE_L2_MASK)
+#define	PTE32_PAE_L1_FRAME	(PTE32_PAE_L2_FRAME|PTE32_PAE_L1_MASK)
+
+#define pte32_pae_l1idx(va)	(((va) & PTE32_PAE_L1_MASK) >> PTE32_PAE_L1_SHIFT)
+#define pte32_pae_l2idx(va)	(((va) & PTE32_PAE_L2_MASK) >> PTE32_PAE_L2_SHIFT)
+#define pte32_pae_l3idx(va)	(((va) & PTE32_PAE_L3_MASK) >> PTE32_PAE_L3_SHIFT)
+
+typedef uint64_t pte_32bit_pae_t;
+
+static int
+x86_gva_to_gpa_32bit_pae(struct nvmm_machine *mach, uint64_t cr3,
+    gvaddr_t gva, gpaddr_t *gpa, bool has_pse, nvmm_prot_t *prot)
+{
+	gpaddr_t L3gpa, L2gpa, L1gpa;
+	uintptr_t L3hva, L2hva, L1hva;
+	pte_32bit_pae_t *pdir, pte;
+
+	/* We begin with an RWXU access. */
+	*prot = NVMM_PROT_ALL;
+
+	/* Parse L3. */
+	L3gpa = (cr3 & PG_FRAME);
+	if (nvmm_gpa_to_hva(mach, L3gpa, &L3hva) == -1)
+		return -1;
+	pdir = (pte_32bit_pae_t *)L3hva;
+	pte = pdir[pte32_pae_l3idx(gva)];
+	if ((pte & PG_V) == 0)
+		return -1;
+	if (pte & PG_NX)
+		*prot &= ~NVMM_PROT_EXEC;
+	if (pte & PG_PS)
+		return -1;
+
+	/* Parse L2. */
+	L2gpa = (pte & PG_FRAME);
+	if (nvmm_gpa_to_hva(mach, L2gpa, &L2hva) == -1)
+		return -1;
+	pdir = (pte_32bit_pae_t *)L2hva;
+	pte = pdir[pte32_pae_l2idx(gva)];
+	if ((pte & PG_V) == 0)
+		return -1;
+	if ((pte & PG_u) == 0)
+		*prot &= ~NVMM_PROT_USER;
+	if ((pte & PG_KW) == 0)
+		*prot &= ~NVMM_PROT_WRITE;
+	if (pte & PG_NX)
+		*prot &= ~NVMM_PROT_EXEC;
+	if ((pte & PG_PS) && !has_pse)
+		return -1;
+	if (pte & PG_PS) {
+		*gpa = (pte & PTE32_PAE_L2_FRAME);
+		return 0;
+	}
+
+	/* Parse L1. */
+	L1gpa = (pte & PG_FRAME);
+	if (nvmm_gpa_to_hva(mach, L1gpa, &L1hva) == -1)
+		return -1;
+	pdir = (pte_32bit_pae_t *)L1hva;
+	pte = pdir[pte32_pae_l1idx(gva)];
+	if ((pte & PG_V) == 0)
+		return -1;
+	if ((pte & PG_u) == 0)
+		*prot &= ~NVMM_PROT_USER;
+	if ((pte & PG_KW) == 0)
+		*prot &= ~NVMM_PROT_WRITE;
+	if (pte & PG_NX)
+		*prot &= ~NVMM_PROT_EXEC;
+	if (pte & PG_PS)
+		return -1;
+
+	*gpa = (pte & PG_FRAME);
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+#define PTE64_L1_SHIFT	12
+#define PTE64_L2_SHIFT	21
+#define PTE64_L3_SHIFT	30
+#define PTE64_L4_SHIFT	39
+
+#define PTE64_L4_MASK	0x0000ff8000000000
+#define PTE64_L3_MASK	0x0000007fc0000000
+#define PTE64_L2_MASK	0x000000003fe00000
+#define PTE64_L1_MASK	0x00000000001ff000
+
+#define PTE64_L4_FRAME	PTE64_L4_MASK
+#define PTE64_L3_FRAME	(PTE64_L4_FRAME|PTE64_L3_MASK)
+#define PTE64_L2_FRAME	(PTE64_L3_FRAME|PTE64_L2_MASK)
+#define PTE64_L1_FRAME	(PTE64_L2_FRAME|PTE64_L1_MASK)
+
+#define pte64_l1idx(va)	(((va) & PTE64_L1_MASK) >> PTE64_L1_SHIFT)
+#define pte64_l2idx(va)	(((va) & PTE64_L2_MASK) >> PTE64_L2_SHIFT)
+#define pte64_l3idx(va)	(((va) & PTE64_L3_MASK) >> PTE64_L3_SHIFT)
+#define pte64_l4idx(va)	(((va) & PTE64_L4_MASK) >> PTE64_L4_SHIFT)
+
+typedef uint64_t pte_64bit_t;
+
+static inline bool
+x86_gva_64bit_canonical(gvaddr_t gva)
+{
+	/* Bits 63:47 must have the same value. */
+#define SIGN_EXTEND	0xffff800000000000ULL
+	return (gva & SIGN_EXTEND) == 0 || (gva & SIGN_EXTEND) == SIGN_EXTEND;
+}
+
+static int
+x86_gva_to_gpa_64bit(struct nvmm_machine *mach, uint64_t cr3,
+    gvaddr_t gva, gpaddr_t *gpa, bool has_pse, nvmm_prot_t *prot)
+{
+	gpaddr_t L4gpa, L3gpa, L2gpa, L1gpa;
+	uintptr_t L4hva, L3hva, L2hva, L1hva;
+	pte_64bit_t *pdir, pte;
+
+	/* We begin with an RWXU access. */
+	*prot = NVMM_PROT_ALL;
+
+	if (!x86_gva_64bit_canonical(gva))
+		return -1;
+
+	/* Parse L4. */
+	L4gpa = (cr3 & PG_FRAME);
+	if (nvmm_gpa_to_hva(mach, L4gpa, &L4hva) == -1)
+		return -1;
+	pdir = (pte_64bit_t *)L4hva;
+	pte = pdir[pte64_l4idx(gva)];
+	if ((pte & PG_V) == 0)
+		return -1;
+	if ((pte & PG_u) == 0)
+		*prot &= ~NVMM_PROT_USER;
+	if ((pte & PG_KW) == 0)
+		*prot &= ~NVMM_PROT_WRITE;
+	if (pte & PG_NX)
+		*prot &= ~NVMM_PROT_EXEC;
+	if (pte & PG_PS)
+		return -1;
+
+	/* Parse L3. */
+	L3gpa = (pte & PG_FRAME);
+	if (nvmm_gpa_to_hva(mach, L3gpa, &L3hva) == -1)
+		return -1;
+	pdir = (pte_64bit_t *)L3hva;
+	pte = pdir[pte64_l3idx(gva)];
+	if ((pte & PG_V) == 0)
+		return -1;
+	if ((pte & PG_u) == 0)
+		*prot &= ~NVMM_PROT_USER;
+	if ((pte & PG_KW) == 0)
+		*prot &= ~NVMM_PROT_WRITE;
+	if (pte & PG_NX)
+		*prot &= ~NVMM_PROT_EXEC;
+	if ((pte & PG_PS) && !has_pse)
+		return -1;
+	if (pte & PG_PS) {
+		*gpa = (pte & PTE64_L3_FRAME);
+		return 0;
+	}
+
+	/* Parse L2. */
+	L2gpa = (pte & PG_FRAME);
+	if (nvmm_gpa_to_hva(mach, L2gpa, &L2hva) == -1)
+		return -1;
+	pdir = (pte_64bit_t *)L2hva;
+	pte = pdir[pte64_l2idx(gva)];
+	if ((pte & PG_V) == 0)
+		return -1;
+	if ((pte & PG_u) == 0)
+		*prot &= ~NVMM_PROT_USER;
+	if ((pte & PG_KW) == 0)
+		*prot &= ~NVMM_PROT_WRITE;
+	if (pte & PG_NX)
+		*prot &= ~NVMM_PROT_EXEC;
+	if ((pte & PG_PS) && !has_pse)
+		return -1;
+	if (pte & PG_PS) {
+		*gpa = (pte & PTE64_L2_FRAME);
+		return 0;
+	}
+
+	/* Parse L1. */
+	L1gpa = (pte & PG_FRAME);
+	if (nvmm_gpa_to_hva(mach, L1gpa, &L1hva) == -1)
+		return -1;
+	pdir = (pte_64bit_t *)L1hva;
+	pte = pdir[pte64_l1idx(gva)];
+	if ((pte & PG_V) == 0)
+		return -1;
+	if ((pte & PG_u) == 0)
+		*prot &= ~NVMM_PROT_USER;
+	if ((pte & PG_KW) == 0)
+		*prot &= ~NVMM_PROT_WRITE;
+	if (pte & PG_NX)
+		*prot &= ~NVMM_PROT_EXEC;
+	if (pte & PG_PS)
+		return -1;
+
+	*gpa = (pte & PG_FRAME);
+	return 0;
+}
+
+static inline int
+x86_gva_to_gpa(struct nvmm_machine *mach, struct nvmm_x64_state *state,
+    gvaddr_t gva, gpaddr_t *gpa, nvmm_prot_t *prot)
+{
+	bool is_pae, is_lng, has_pse;
+	uint64_t cr3;
+	int ret;
+
+	if ((state->crs[NVMM_X64_CR_CR0] & CR0_PG) == 0) {
+		/* No paging. */
+		*gpa = gva;
+		return 0;
+	}
+
+	is_pae = (state->crs[NVMM_X64_CR_CR4] & CR4_PAE) != 0;
+	is_lng = (state->msrs[NVMM_X64_MSR_EFER] & EFER_LME) != 0;
+	has_pse = (state->crs[NVMM_X64_CR_CR4] & CR4_PSE) != 0;
+	cr3 = state->crs[NVMM_X64_CR_CR3];
+
+	if (is_pae && is_lng) {
+		/* 64bit */
+		ret = x86_gva_to_gpa_64bit(mach, cr3, gva, gpa, has_pse, prot);
+	} else if (is_pae && !is_lng) {
+		/* 32bit PAE */
+		ret = x86_gva_to_gpa_32bit_pae(mach, cr3, gva, gpa, has_pse,
+		    prot);
+	} else if (!is_pae && !is_lng) {
+		/* 32bit */
+		ret = x86_gva_to_gpa_32bit(mach, cr3, gva, gpa, has_pse, prot);
+	} else {
+		ret = -1;
+	}
+
+	if (ret == -1) {
+		errno = EFAULT;
+	}
+
+	return ret;
+}
+
+int
+nvmm_gva_to_gpa(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
+    gvaddr_t gva, gpaddr_t *gpa, nvmm_prot_t *prot)
+{
+	struct nvmm_x64_state state;
+	int ret;
+
+	if (gva & PAGE_MASK) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	ret = nvmm_vcpu_getstate(mach, cpuid, &state,
+	    NVMM_X64_STATE_CRS | NVMM_X64_STATE_MSRS);
+	if (ret == -1)
+		return -1;
+
+	return x86_gva_to_gpa(mach, &state, gva, gpa, prot);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static inline bool
+is_long_mode(struct nvmm_x64_state *state)
+{
+	return (state->msrs[NVMM_X64_MSR_EFER] & EFER_LME) != 0;
+}
+
+static inline bool
+is_illegal(struct nvmm_io *io, nvmm_prot_t prot)
+{
+	return (io->in && !(prot & NVMM_PROT_WRITE));
+}
+
+static int
+segment_apply(struct nvmm_x64_state_seg *seg, gvaddr_t *gva, size_t size)
+{
+	uint64_t limit;
+
+	/*
+	 * This is incomplete. We should check topdown, etc, really that's
+	 * tiring.
+	 */
+	if (__predict_false(!seg->attrib.p)) {
+		goto error;
+	}
+
+	limit = (seg->limit + 1);
+	if (__predict_true(seg->attrib.gran)) {
+		limit *= PAGE_SIZE;
+	}
+
+	if (__predict_false(*gva + seg->base + size > limit)) {
+		goto error;
+	}
+
+	*gva += seg->base;
+	return 0;
+
+error:
+	errno = EFAULT;
+	return -1;
+}
+
+int
+nvmm_assist_io(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
+    struct nvmm_exit *exit, void (*cb)(struct nvmm_io *))
+{
+	struct nvmm_x64_state state;
+	struct nvmm_io io;
+	nvmm_prot_t prot;
+	size_t remain, done;
+	uintptr_t hva;
+	gvaddr_t gva, off;
+	gpaddr_t gpa;
+	uint64_t rsi;
+	uint8_t tmp[8];
+	uint8_t *ptr, *ptr2;
+	bool cross;
+	int ret;
+
+	if (__predict_false(exit->reason != NVMM_EXIT_IO)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	io.port = exit->u.io.port;
+	io.in = (exit->u.io.type == NVMM_EXIT_IO_IN);
+	io.size = exit->u.io.operand_size;
+
+	ret = nvmm_vcpu_getstate(mach, cpuid, &state,
+	    NVMM_X64_STATE_GPRS | NVMM_X64_STATE_SEGS |
+	    NVMM_X64_STATE_CRS | NVMM_X64_STATE_MSRS);
+	if (ret == -1)
+		return -1;
+
+	cross = false;
+
+	if (!exit->u.io.str) {
+		ptr = (uint8_t *)&state.gprs[NVMM_X64_GPR_RAX];
+	} else {
+		rsi = state.gprs[NVMM_X64_GPR_RSI];
+
+		switch (exit->u.io.address_size) {
+		case 8:
+			gva = rsi;
+			break;
+		case 4:
+			gva = (rsi & 0x00000000FFFFFFFF);
+			break;
+		case 2:
+		default: /* impossible */
+			gva = (rsi & 0x000000000000FFFF);
+			break;
+		}
+
+		if (!is_long_mode(&state)) {
+			ret = segment_apply(&state.segs[exit->u.io.seg], &gva,
+			    io.size);
+			if (ret == -1)
+				return -1;
+		}
+
+		off = (gva & PAGE_MASK);
+		gva &= ~PAGE_MASK;
+
+		ret = x86_gva_to_gpa(mach, &state, gva, &gpa, &prot);
+		if (ret == -1)
+			return -1;
+		if (__predict_false(is_illegal(&io, prot))) {
+			errno = EFAULT;
+			return -1;
+		}
+		ret = nvmm_gpa_to_hva(mach, gpa, &hva);
+		if (ret == -1)
+			return -1;
+
+		ptr = (uint8_t *)hva + off;
+
+		/*
+		 * Special case. If the buffer is in between two pages, we
+		 * need to retrieve data from the next page.
+		 */
+		if (__predict_false(off + io.size > PAGE_SIZE)) {
+			cross = true;
+			remain = off + io.size - PAGE_SIZE;
+			done = PAGE_SIZE - off;
+
+			memcpy(tmp, ptr, done);
+
+			ret = x86_gva_to_gpa(mach, &state, gva + PAGE_SIZE,
+			    &gpa, &prot);
+			if (ret == -1)
+				return -1;
+			if (__predict_false(is_illegal(&io, prot))) {
+				errno = EFAULT;
+				return -1;
+			}
+			ret = nvmm_gpa_to_hva(mach, gpa, &hva);
+			if (ret == -1)
+				return -1;
+
+			memcpy(&tmp[done], (uint8_t *)hva, remain);
+			ptr2 = &tmp[done];
+		}
+	}
+
+	if (io.in) {
+		/* nothing to do */
+	} else {
+		memcpy(io.data, ptr, io.size);
+	}
+
+	(*cb)(&io);
+
+	if (io.in) {
+		if (!exit->u.io.str)
+			state.gprs[NVMM_X64_GPR_RAX] = 0;
+		if (__predict_false(cross)) {
+			memcpy(ptr, io.data, done);
+			memcpy(ptr2, &io.data[done], remain);
+		} else {
+			memcpy(ptr, io.data, io.size);
+		}
+	} else {
+		/* nothing to do */
+	}
+
+	if (exit->u.io.rep) {
+		state.gprs[NVMM_X64_GPR_RCX] -= 1;
+		if (state.gprs[NVMM_X64_GPR_RCX] == 0) {
+			state.gprs[NVMM_X64_GPR_RIP] = exit->u.io.npc;
+		}
+		if (exit->u.io.str) {
+			if (state.gprs[NVMM_X64_GPR_RFLAGS] & PSL_D) {
+				state.gprs[NVMM_X64_GPR_RSI] -= io.size;
+			} else {
+				state.gprs[NVMM_X64_GPR_RSI] += io.size;
+			}
+		}
+	} else {
+		state.gprs[NVMM_X64_GPR_RIP] = exit->u.io.npc;
+	}
+
+	ret = nvmm_vcpu_setstate(mach, cpuid, &state, NVMM_X64_STATE_GPRS);
+	if (ret == -1)
+		return -1;
+
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+int
+nvmm_assist_mem(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
+    struct nvmm_exit *exit, void (*cb)(struct nvmm_mem *))
+{
+	if (__predict_false(exit->reason != NVMM_EXIT_MEMORY)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	// TODO
+	errno = ENOSYS;
+	return -1;
+}
Index: src/lib/libnvmm/nvmm.h
diff -u /dev/null src/lib/libnvmm/nvmm.h:1.1
--- /dev/null	Sat Nov 10 09:28:56 2018
+++ src/lib/libnvmm/nvmm.h	Sat Nov 10 09:28:56 2018
@@ -0,0 +1,103 @@
+/*	$NetBSD: nvmm.h,v 1.1 2018/11/10 09:28:56 maxv Exp $	*/
+
+/*
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Maxime Villard.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+#ifndef _LIBNVMM_H_
+#define _LIBNVMM_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <dev/nvmm/nvmm.h>
+#include <dev/nvmm/nvmm_ioctl.h>
+#ifdef __x86_64__
+#include <dev/nvmm/x86/nvmm_x86.h>
+#endif
+
+struct nvmm_area {
+	gpaddr_t gpa;
+	uintptr_t hva;
+	size_t size;
+};
+
+struct nvmm_machine {
+	nvmm_machid_t machid;
+	struct nvmm_area *areas;
+	size_t nareas;
+};
+
+struct nvmm_io {
+	uint64_t port;
+	bool in;
+	size_t size;
+	uint8_t data[8];
+};
+
+struct nvmm_mem {
+	gvaddr_t gva;
+	gpaddr_t gpa;
+	bool write;
+	size_t size;
+	uint8_t data[8];
+};
+
+#define NVMM_PROT_READ		0x01
+#define NVMM_PROT_WRITE		0x02
+#define NVMM_PROT_EXEC		0x04
+#define NVMM_PROT_USER		0x08
+#define NVMM_PROT_ALL		0x0F
+typedef uint64_t nvmm_prot_t;
+
+int nvmm_capability(struct nvmm_capability *);
+
+int nvmm_machine_create(struct nvmm_machine *);
+int nvmm_machine_destroy(struct nvmm_machine *);
+int nvmm_machine_configure(struct nvmm_machine *, uint64_t, void *);
+
+int nvmm_vcpu_create(struct nvmm_machine *, nvmm_cpuid_t);
+int nvmm_vcpu_destroy(struct nvmm_machine *, nvmm_cpuid_t);
+int nvmm_vcpu_setstate(struct nvmm_machine *, nvmm_cpuid_t, void *, uint64_t);
+int nvmm_vcpu_getstate(struct nvmm_machine *, nvmm_cpuid_t, void *, uint64_t);
+int nvmm_vcpu_inject(struct nvmm_machine *, nvmm_cpuid_t, struct nvmm_event *);
+int nvmm_vcpu_run(struct nvmm_machine *, nvmm_cpuid_t, struct nvmm_exit *);
+
+int nvmm_gpa_map(struct nvmm_machine *, uintptr_t, gpaddr_t, size_t, int);
+int nvmm_gpa_unmap(struct nvmm_machine *, uintptr_t, gpaddr_t, size_t);
+
+int nvmm_gva_to_gpa(struct nvmm_machine *, nvmm_cpuid_t, gvaddr_t, gpaddr_t *,
+    nvmm_prot_t *);
+int nvmm_gpa_to_hva(struct nvmm_machine *, gpaddr_t, uintptr_t *);
+
+int nvmm_assist_io(struct nvmm_machine *, nvmm_cpuid_t, struct nvmm_exit *,
+    void (*)(struct nvmm_io *));
+int nvmm_assist_mem(struct nvmm_machine *, nvmm_cpuid_t, struct nvmm_exit *,
+    void (*)(struct nvmm_mem *));
+
+#endif /* _LIBNVMM_H_ */
Index: src/lib/libnvmm/shlib_version
diff -u /dev/null src/lib/libnvmm/shlib_version:1.1
--- /dev/null	Sat Nov 10 09:28:56 2018
+++ src/lib/libnvmm/shlib_version	Sat Nov 10 09:28:56 2018
@@ -0,0 +1,5 @@
+# $NetBSD: shlib_version,v 1.1 2018/11/10 09:28:56 maxv Exp $
+# Remember to update distrib/sets/lists/base/shl.* when changing
+#
+major=0
+minor=1

Reply via email to