Page MenuHomeFreeBSD

D8100.id20978.diff
No OneTemporary

D8100.id20978.diff

Index: sys/amd64/include/bhyve_hypercall.h
===================================================================
--- /dev/null
+++ sys/amd64/include/bhyve_hypercall.h
@@ -0,0 +1,383 @@
+/*-
+ * Copyright (c) 2016 Domagoj Stolfa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_HYPERCALL_H_
+#define _MACHINE_HYPERCALL_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <x86/x86_var.h>
+#include <x86/cputypes.h>
+
+#define HYPERCALL_RET_NOT_IMPL -2
+#define HYPERCALL_RET_ERROR -1
+#define HYPERCALL_RET_SUCCESS 0
+
+#define VMCALL ".byte 0x0f,0x01,0xc1\n"
+#define VMMCALL ".byte 0x0f,0x01,0xd9\n"
+
+typedef struct hypercall_arg {
+ __uint64_t len;
+ __uint64_t val;
+} hc_arg_t;
+
+/*
+ * Used to create additional known hypercalls. The name
+ * of each of the defines should correspond to the function
+ * being called once the hypercall is initiated.
+ * Keep the defines in order.
+ *
+ * Keep in sync with ring_plevel.
+ */
+#define HYPERCALL_COMPAT_RESERVED1 0
+#define HYPERCALL_COMPAT_RESERVED2 1
+#define HYPERCALL_COMPAT_RESERVED3 2
+#define HYPERCALL_COMPAT_RESERVED4 3
+#define HYPERCALL_COMPAT_RESERVED5 4
+#define HYPERCALL_COMPAT_RESERVED6 5
+#define HYPERCALL_COMPAT_RESERVED7 6
+#define HYPERCALL_COMPAT_RESERVED8 7
+#define HYPERCALL_COMPAT_RESERVED9 8
+#define HYPERCALL_COMPAT_RESERVED10 9
+#define HYPERCALL_COMPAT_RESERVED11 10
+#define HYPERCALL_COMPAT_RESERVED12 11
+#define HYPERCALL_COMPAT_RESERVED13 12
+#define HYPERCALL_COMPAT_RESERVED14 13
+#define HYPERCALL_COMPAT_RESERVED15 14
+#define HYPERCALL_COMPAT_RESERVED16 15
+#define HYPERCALL_COMPAT_RESERVED17 16
+#define HYPERCALL_COMPAT_RESERVED18 17
+#define HYPERCALL_COMPAT_RESERVED19 18
+#define HYPERCALL_COMPAT_RESERVED20 19
+#define HYPERCALL_COMPAT_RESERVED21 20
+#define HYPERCALL_COMPAT_RESERVED22 21
+#define HYPERCALL_COMPAT_RESERVED23 22
+#define HYPERCALL_COMPAT_RESERVED24 23
+#define HYPERCALL_COMPAT_RESERVED25 24
+#define HYPERCALL_COMPAT_RESERVED26 25
+#define HYPERCALL_COMPAT_RESERVED27 26
+#define HYPERCALL_COMPAT_RESERVED28 27
+#define HYPERCALL_COMPAT_RESERVED29 28
+#define HYPERCALL_COMPAT_RESERVED30 29
+#define HYPERCALL_COMPAT_RESERVED31 30
+#define HYPERCALL_COMPAT_RESERVED32 31
+#define HYPERCALL_COMPAT_RESERVED33 32
+#define HYPERCALL_COMPAT_RESERVED34 33
+#define HYPERCALL_COMPAT_RESERVED35 34
+#define HYPERCALL_COMPAT_RESERVED36 35
+#define HYPERCALL_COMPAT_RESERVED37 36
+#define HYPERCALL_COMPAT_RESERVED38 37
+#define HYPERCALL_COMPAT_RESERVED39 38
+#define HYPERCALL_COMPAT_RESERVED40 39
+#define HYPERCALL_COMPAT_RESERVED41 40
+#define HYPERCALL_COMPAT_RESERVED42 41
+#define HYPERCALL_COMPAT_RESERVED43 42
+#define HYPERCALL_COMPAT_RESERVED44 43
+#define HYPERCALL_COMPAT_RESERVED45 44
+#define HYPERCALL_COMPAT_RESERVED46 45
+#define HYPERCALL_COMPAT_RESERVED47 46
+#define HYPERCALL_COMPAT_RESERVED48 47
+#define HYPERCALL_COMPAT_RESERVED49 48
+#define HYPERCALL_COMPAT_RESERVED50 49
+#define HYPERCALL_COMPAT_RESERVED51 50
+#define HYPERCALL_COMPAT_RESERVED52 51
+#define HYPERCALL_COMPAT_RESERVED53 52
+#define HYPERCALL_COMPAT_RESERVED54 53
+#define HYPERCALL_COMPAT_RESERVED55 54
+#define HYPERCALL_COMPAT_RESERVED56 55
+#define HYPERCALL_COMPAT_RESERVED57 56
+#define HYPERCALL_COMPAT_RESERVED58 57
+#define HYPERCALL_COMPAT_RESERVED59 58
+#define HYPERCALL_COMPAT_RESERVED60 59
+#define HYPERCALL_COMPAT_RESERVED61 60
+#define HYPERCALL_COMPAT_RESERVED62 61
+#define HYPERCALL_COMPAT_RESERVED63 62
+#define HYPERCALL_COMPAT_RESERVED64 63
+#define HYPERCALL_COMPAT_RESERVED65 64
+#define HYPERCALL_COMPAT_RESERVED66 65
+#define HYPERCALL_COMPAT_RESERVED67 66
+#define HYPERCALL_COMPAT_RESERVED68 67
+#define HYPERCALL_COMPAT_RESERVED69 68
+#define HYPERCALL_COMPAT_RESERVED70 69
+#define HYPERCALL_COMPAT_RESERVED71 70
+#define HYPERCALL_COMPAT_RESERVED72 71
+#define HYPERCALL_COMPAT_RESERVED73 72
+#define HYPERCALL_COMPAT_RESERVED74 73
+#define HYPERCALL_COMPAT_RESERVED75 74
+#define HYPERCALL_COMPAT_RESERVED76 75
+#define HYPERCALL_COMPAT_RESERVED77 76
+#define HYPERCALL_COMPAT_RESERVED78 77
+#define HYPERCALL_COMPAT_RESERVED79 78
+#define HYPERCALL_COMPAT_RESERVED80 79
+#define HYPERCALL_DTRACE_PROBE_CREATE 81
+#define HYPERCALL_DTRACE_PROBE 82
+#define HYPERCALL_DTRACE_RESERVED1 83
+#define HYPERCALL_DTRACE_RESERVED2 84
+#define HYPERCALL_DTRACE_RESERVED3 85
+#define HYPERCALL_DTRACE_RESERVED4 86
+#define HYPERCALL_DTRACE_RESERVED5 87
+
+#define HYPERCALL_INDEX_MAX 88
+
+/*
+ * The following functions perform hypercalls towards
+ * the host OS. Each of the functions uses registers
+ * RAX and RBX in order to pass in information to the
+ * host OS. The registers used for argument passing
+ * are RCX, RDX, RSI, RDI and the 8 new registers on
+ * amd64 architecture, R8-R15.
+ */
+
+static __inline __int64_t
+hypercall0(__uint64_t c)
+{
+ const __uint64_t nargs = 0;
+ __int64_t ret;
+ if (cpu_vendor_id == CPU_VENDOR_INTEL) {
+ __asm __volatile(
+ VMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs)
+ : "memory", "rsp");
+ } else {
+ __asm __volatile(
+ VMMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs)
+ : "memory", "rsp");
+ }
+ return (ret);
+}
+
+static __inline __int64_t
+hypercall1(__uint64_t c, hc_arg_t *arg0)
+{
+ const __uint64_t nargs = 1;
+ __int64_t ret;
+ if (cpu_vendor_id == CPU_VENDOR_INTEL) {
+ __asm __volatile(
+ VMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs),
+ "c"(arg0->len), "d"(arg0->val)
+ : "rax", "rbx", "rcx", "rdx");
+ } else {
+ __asm __volatile(
+ VMMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs),
+ "c"(arg0->len), "d"(arg0->val)
+ : "rax", "rbx", "rcx", "rdx");
+ }
+ return (ret);
+}
+
+static __inline __int64_t
+hypercall2(__uint64_t c, hc_arg_t *arg0,
+ hc_arg_t *arg1)
+{
+ const __uint64_t nargs = 2;
+ __int64_t ret;
+ if (cpu_vendor_id == CPU_VENDOR_INTEL) {
+ __asm __volatile(
+ VMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs),
+ "c"(arg0->len), "d"(arg0->val),
+ "S"(arg1->len), "D"(arg1->val)
+ : "rax", "rbx", "rcx", "rdx", "rsi", "rdi");
+ } else {
+ __asm __volatile(
+ VMMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs),
+ "c"(arg0->len), "d"(arg0->val),
+ "S"(arg1->len), "D"(arg1->val)
+ : "rax", "rbx", "rcx", "rdx", "rsi", "rdi");
+ }
+ return (ret);
+}
+
+static __inline __int64_t
+hypercall3(__uint64_t c, hc_arg_t *arg0,
+ hc_arg_t *arg1, hc_arg_t *arg2)
+{
+ const __uint64_t nargs = 3;
+ __int64_t ret;
+ register __uint64_t arg2_len __asm("r8") = arg2->len;
+ register __uint64_t arg2_val __asm("r9") = arg2->val;
+ if (cpu_vendor_id == CPU_VENDOR_INTEL) {
+ __asm __volatile(
+ VMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs),
+ "c"(arg0->len), "d"(arg0->val),
+ "S"(arg1->len), "D"(arg1->val),
+ "r"(arg2_len), "r"(arg2_val)
+ : "rax", "rbx", "rcx", "rdx", "rsi", "rdi",
+ "r8", "r9");
+ } else {
+ __asm __volatile(
+ VMMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs),
+ "c"(arg0->len), "d"(arg0->val),
+ "S"(arg1->len), "D"(arg1->val),
+ "r"(arg2_len), "r"(arg2_val)
+ : "rax", "rbx", "rcx", "rdx", "rsi", "rdi",
+ "r8", "r9");
+ }
+ return (ret);
+}
+
+static __inline __int64_t
+hypercall4(__uint64_t c, hc_arg_t *arg0,
+ hc_arg_t *arg1, hc_arg_t *arg2,
+ hc_arg_t *arg3)
+{
+ const __uint64_t nargs = 4;
+ __int64_t ret;
+ register __uint64_t arg2_len __asm("r8") = arg2->len;
+ register __uint64_t arg2_val __asm("r9") = arg2->val;
+ register __uint64_t arg3_len __asm("r10") = arg3->len;
+ register __uint64_t arg3_val __asm("r11") = arg3->val;
+ if (cpu_vendor_id == CPU_VENDOR_INTEL) {
+ __asm __volatile(
+ VMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs),
+ "c"(arg0->len), "d"(arg0->val),
+ "S"(arg1->len), "D"(arg1->val),
+ "r"(arg2_len), "r"(arg2_val),
+ "r"(arg3_len), "r"(arg3_val)
+ : "rax", "rbx", "rcx", "rdx", "rsi", "rdi",
+ "r8", "r9", "r10", "r11");
+ } else {
+ __asm __volatile(
+ VMMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs),
+ "c"(arg0->len), "d"(arg0->val),
+ "S"(arg1->len), "D"(arg1->val),
+ "r"(arg2_len), "r"(arg2_val),
+ "r"(arg3_len), "r"(arg3_val)
+ : "rax", "rbx", "rcx", "rdx", "rsi", "rdi",
+ "r8", "r9", "r10", "r11");
+ }
+ return (ret);
+}
+
+static __inline __int64_t
+hypercall5(__uint64_t c, hc_arg_t *arg0,
+ hc_arg_t *arg1, hc_arg_t *arg2,
+ hc_arg_t *arg3, hc_arg_t *arg4)
+{
+ const __uint64_t nargs = 5;
+ __int64_t ret;
+ register __uint64_t arg2_len __asm("r8") = arg2->len;
+ register __uint64_t arg2_val __asm("r9") = arg2->val;
+ register __uint64_t arg3_len __asm("r10") = arg3->len;
+ register __uint64_t arg3_val __asm("r11") = arg3->val;
+ register __uint64_t arg4_len __asm("r12") = arg4->len;
+ register __uint64_t arg4_val __asm("r13") = arg4->val;
+ if (cpu_vendor_id == CPU_VENDOR_INTEL) {
+ __asm __volatile(
+ VMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs),
+ "c"(arg0->len), "d"(arg0->val),
+ "S"(arg1->len), "D"(arg1->val),
+ "r"(arg2_len), "r"(arg2_val),
+ "r"(arg3_len), "r"(arg3_val),
+ "r"(arg4_len), "r"(arg4_val)
+ : "rax", "rbx", "rcx", "rdx", "rsi", "rdi",
+ "r8", "r9", "r10", "r11", "r12", "r13");
+ } else {
+ __asm __volatile(
+ VMMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs),
+ "c"(arg0->len), "d"(arg0->val),
+ "S"(arg1->len), "D"(arg1->val),
+ "r"(arg2_len), "r"(arg2_val),
+ "r"(arg3_len), "r"(arg3_val),
+ "r"(arg4_len), "r"(arg4_val)
+ : "rax", "rbx", "rcx", "rdx", "rsi", "rdi",
+ "r8", "r9", "r10", "r11", "r12", "r13");
+ }
+ return (ret);
+}
+
+static __inline __int64_t
+hypercall6(__uint64_t c, hc_arg_t *arg0,
+ hc_arg_t *arg1, hc_arg_t *arg2,
+ hc_arg_t *arg3, hc_arg_t *arg4,
+ hc_arg_t *arg5)
+{
+ const __uint64_t nargs = 6;
+ __int64_t ret;
+ register __uint64_t arg2_len __asm("r8") = arg2->len;
+ register __uint64_t arg2_val __asm("r9") = arg2->val;
+ register __uint64_t arg3_len __asm("r10") = arg3->len;
+ register __uint64_t arg3_val __asm("r11") = arg3->val;
+ register __uint64_t arg4_len __asm("r12") = arg4->len;
+ register __uint64_t arg4_val __asm("r13") = arg4->val;
+ register __uint64_t arg5_len __asm("r14") = arg5->len;
+ register __uint64_t arg5_val __asm("r15") = arg5->val;
+ if (cpu_vendor_id == CPU_VENDOR_INTEL) {
+ __asm __volatile(
+ VMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs),
+ "c"(arg0->len), "d"(arg0->val),
+ "S"(arg1->len), "D"(arg1->val),
+ "r"(arg2_len), "r"(arg2_val),
+ "r"(arg3_len), "r"(arg3_val),
+ "r"(arg4_len), "r"(arg4_val),
+ "r"(arg5_len), "r"(arg5_val)
+ : "rax", "rbx", "rcx", "rdx", "rsi", "rdi",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15");
+ } else {
+ __asm __volatile(
+ VMMCALL
+ : "=a"(ret)
+ : "a"(c), "b"(nargs),
+ "c"(arg0->len), "d"(arg0->val),
+ "S"(arg1->len), "D"(arg1->val),
+ "r"(arg2_len), "r"(arg2_val),
+ "r"(arg3_len), "r"(arg3_val),
+ "r"(arg4_len), "r"(arg4_val),
+ "r"(arg5_len), "r"(arg5_val)
+ : "rax", "rbx", "rcx", "rdx", "rsi", "rdi",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15");
+ }
+ return (ret);
+}
+
+#endif /* _MACHINE_HYPERCALL_H_ */
Index: sys/amd64/include/vmm.h
===================================================================
--- sys/amd64/include/vmm.h
+++ sys/amd64/include/vmm.h
@@ -104,6 +104,14 @@
#ifdef _KERNEL
+/*
+ * The maximum amount of arguments currently supproted
+ * through the hypercall functionality in the VMM.
+ * Everything higher than HYPERCALL_MAX_ARGS will be
+ * discarded.
+ */
+#define HYPERCALL_MAX_ARGS 6
+
#define VM_MAX_NAMELEN 32
struct vm;
@@ -167,8 +175,9 @@
vmi_vlapic_cleanup vlapic_cleanup;
};
-extern struct vmm_ops vmm_ops_intel;
-extern struct vmm_ops vmm_ops_amd;
+extern struct vmm_ops vmm_ops_intel;
+extern struct vmm_ops vmm_ops_amd;
+extern int hypercalls_enabled;
int vm_create(const char *name, struct vm **retvm);
void vm_destroy(struct vm *vm);
@@ -533,6 +542,7 @@
VM_EXITCODE_MWAIT,
VM_EXITCODE_SVM,
VM_EXITCODE_REQIDLE,
+ VM_EXITCODE_HYPERCALL,
VM_EXITCODE_MAX
};
@@ -573,6 +583,10 @@
struct vm_guest_paging paging;
};
+struct vm_hypercall {
+ struct vm_guest_paging paging;
+};
+
struct vm_exit {
enum vm_exitcode exitcode;
int inst_length; /* 0 means unknown */
@@ -636,7 +650,8 @@
struct {
enum vm_suspend_how how;
} suspended;
- struct vm_task_switch task_switch;
+ struct vm_task_switch task_switch;
+ struct vm_hypercall hypercall;
} u;
};
Index: sys/amd64/vmm/amd/svm.c
===================================================================
--- sys/amd64/vmm/amd/svm.c
+++ sys/amd64/vmm/amd/svm.c
@@ -158,7 +158,6 @@
static int
svm_cleanup(void)
{
-
smp_rendezvous(NULL, svm_disable, NULL, NULL);
return (0);
}
@@ -469,6 +468,9 @@
svm_enable_intercept(sc, vcpu, VMCB_CTRL2_INTCPT, VMCB_INTCPT_MONITOR);
svm_enable_intercept(sc, vcpu, VMCB_CTRL2_INTCPT, VMCB_INTCPT_MWAIT);
+ /* Enable VMMCALL to be used for DTrace probes */
+ svm_enable_intercept(sc, vcpu, VMCB_CTRL2_INTCPT, VMCB_INTCPT_VMMCALL);
+
/*
* From section "Canonicalization and Consistency Checks" in APMv2
* the VMRUN intercept bit must be set to pass the consistency check.
@@ -849,6 +851,29 @@
vie_init(&vmexit->u.inst_emul.vie, inst_bytes, inst_len);
}
+static void
+svm_handle_hypercall(struct svm_softc *svm_sc, int vcpu, struct vmcb *vmcb, struct vm_exit *vmexit)
+{
+ struct vm_guest_paging *paging;
+ struct vmcb_segment seg;
+ uint64_t rsp;
+ int error;
+
+ paging = &vmexit->u.hypercall.paging;
+ vmexit->exitcode = VM_EXITCODE_HYPERCALL;
+
+ error = vmcb_read(svm_sc, vcpu, VM_REG_GUEST_RSP,
+ &rsp);
+ KASSERT(error == 0, ("%s: error %d getting RSP",
+ __func__, error));
+
+ error = vmcb_seg(vmcb, VM_REG_GUEST_SS, &seg);
+ KASSERT(error == 0, ("%s: error %d getting segment SS",
+ __func__, error));
+
+ svm_paging_info(vmcb, paging);
+}
+
#ifdef KTR
static const char *
intrtype_to_str(int intr_type)
@@ -1243,6 +1268,12 @@
return ("monitor");
case VMCB_EXIT_MWAIT:
return ("mwait");
+ case VMCB_EXIT_VMMCALL:
+ return ("vmmcall");
+ case VMCB_EXIT_VMLOAD:
+ return ("vmload");
+ case VMCB_EXIT_VMSAVE:
+ return ("vmsave");
default:
snprintf(reasonbuf, sizeof(reasonbuf), "%#lx", reason);
return (reasonbuf);
@@ -1344,6 +1375,13 @@
case VMCB_EXIT_NMI: /* external NMI */
handled = 1;
break;
+ case VMCB_EXIT_VMMCALL:
+ if (hypercalls_enabled) {
+ svm_handle_hypercall(svm_sc, vcpu, vmcb, vmexit);
+ } else {
+ handled = 1;
+ }
+ break;
case 0x40 ... 0x5F:
vmm_stat_incr(svm_sc->vm, vcpu, VMEXIT_EXCEPTION, 1);
reflect = 1;
Index: sys/amd64/vmm/amd/vmcb.h
===================================================================
--- sys/amd64/vmm/amd/vmcb.h
+++ sys/amd64/vmm/amd/vmcb.h
@@ -139,6 +139,8 @@
#define VMCB_EXIT_IO 0x7B
#define VMCB_EXIT_MSR 0x7C
#define VMCB_EXIT_SHUTDOWN 0x7F
+#define VMCB_EXIT_VMMCALL 0x81
+#define VMCB_EXIT_VMLOAD 0x82
#define VMCB_EXIT_VMSAVE 0x83
#define VMCB_EXIT_MONITOR 0x8A
#define VMCB_EXIT_MWAIT 0x8B
Index: sys/amd64/vmm/intel/vmx.c
===================================================================
--- sys/amd64/vmm/intel/vmx.c
+++ sys/amd64/vmm/intel/vmx.c
@@ -2474,6 +2474,14 @@
case EXIT_REASON_MWAIT:
vmexit->exitcode = VM_EXITCODE_MWAIT;
break;
+ case EXIT_REASON_VMCALL:
+ if (hypercalls_enabled) {
+ vmexit->exitcode = VM_EXITCODE_HYPERCALL;
+ vmx_paging_info(&vmexit->u.hypercall.paging);
+ } else {
+ handled = HANDLED;
+ }
+ break;
default:
vmm_stat_incr(vmx->vm, vcpu, VMEXIT_UNKNOWN, 1);
break;
Index: sys/amd64/vmm/vmm.c
===================================================================
--- sys/amd64/vmm/vmm.c
+++ sys/amd64/vmm/vmm.c
@@ -55,6 +55,7 @@
#include <machine/cpu.h>
#include <machine/pcb.h>
#include <machine/smp.h>
+#include <machine/bhyve_hypercall.h>
#include <x86/psl.h>
#include <x86/apicreg.h>
@@ -224,6 +225,103 @@
&trace_guest_exceptions, 0,
"Trap into hypervisor on all guest exceptions and reflect them back");
+int hypercalls_enabled = 0;
+SYSCTL_INT(_hw_vmm, OID_AUTO, hypercalls_enabled, CTLFLAG_RWTUN,
+ &hypercalls_enabled, 0,
+ "Enable handling of hypercalls inside the VMM");
+
+
+static int8_t ring_plevel[HYPERCALL_INDEX_MAX] = {
+ [HYPERCALL_COMPAT_RESERVED1] = 0,
+ [HYPERCALL_COMPAT_RESERVED2] = 0,
+ [HYPERCALL_COMPAT_RESERVED3] = 0,
+ [HYPERCALL_COMPAT_RESERVED4] = 0,
+ [HYPERCALL_COMPAT_RESERVED5] = 0,
+ [HYPERCALL_COMPAT_RESERVED6] = 0,
+ [HYPERCALL_COMPAT_RESERVED7] = 0,
+ [HYPERCALL_COMPAT_RESERVED8] = 0,
+ [HYPERCALL_COMPAT_RESERVED9] = 0,
+ [HYPERCALL_COMPAT_RESERVED10] = 0,
+ [HYPERCALL_COMPAT_RESERVED11] = 0,
+ [HYPERCALL_COMPAT_RESERVED12] = 0,
+ [HYPERCALL_COMPAT_RESERVED13] = 0,
+ [HYPERCALL_COMPAT_RESERVED14] = 0,
+ [HYPERCALL_COMPAT_RESERVED15] = 0,
+ [HYPERCALL_COMPAT_RESERVED16] = 0,
+ [HYPERCALL_COMPAT_RESERVED17] = 0,
+ [HYPERCALL_COMPAT_RESERVED18] = 0,
+ [HYPERCALL_COMPAT_RESERVED19] = 0,
+ [HYPERCALL_COMPAT_RESERVED20] = 0,
+ [HYPERCALL_COMPAT_RESERVED21] = 0,
+ [HYPERCALL_COMPAT_RESERVED22] = 0,
+ [HYPERCALL_COMPAT_RESERVED23] = 0,
+ [HYPERCALL_COMPAT_RESERVED24] = 0,
+ [HYPERCALL_COMPAT_RESERVED25] = 0,
+ [HYPERCALL_COMPAT_RESERVED26] = 0,
+ [HYPERCALL_COMPAT_RESERVED27] = 0,
+ [HYPERCALL_COMPAT_RESERVED28] = 0,
+ [HYPERCALL_COMPAT_RESERVED29] = 0,
+ [HYPERCALL_COMPAT_RESERVED30] = 0,
+ [HYPERCALL_COMPAT_RESERVED31] = 0,
+ [HYPERCALL_COMPAT_RESERVED32] = 0,
+ [HYPERCALL_COMPAT_RESERVED33] = 0,
+ [HYPERCALL_COMPAT_RESERVED34] = 0,
+ [HYPERCALL_COMPAT_RESERVED35] = 0,
+ [HYPERCALL_COMPAT_RESERVED36] = 0,
+ [HYPERCALL_COMPAT_RESERVED37] = 0,
+ [HYPERCALL_COMPAT_RESERVED38] = 0,
+ [HYPERCALL_COMPAT_RESERVED39] = 0,
+ [HYPERCALL_COMPAT_RESERVED40] = 0,
+ [HYPERCALL_COMPAT_RESERVED41] = 0,
+ [HYPERCALL_COMPAT_RESERVED42] = 0,
+ [HYPERCALL_COMPAT_RESERVED43] = 0,
+ [HYPERCALL_COMPAT_RESERVED44] = 0,
+ [HYPERCALL_COMPAT_RESERVED45] = 0,
+ [HYPERCALL_COMPAT_RESERVED46] = 0,
+ [HYPERCALL_COMPAT_RESERVED47] = 0,
+ [HYPERCALL_COMPAT_RESERVED48] = 0,
+ [HYPERCALL_COMPAT_RESERVED49] = 0,
+ [HYPERCALL_COMPAT_RESERVED50] = 0,
+ [HYPERCALL_COMPAT_RESERVED51] = 0,
+ [HYPERCALL_COMPAT_RESERVED52] = 0,
+ [HYPERCALL_COMPAT_RESERVED53] = 0,
+ [HYPERCALL_COMPAT_RESERVED54] = 0,
+ [HYPERCALL_COMPAT_RESERVED55] = 0,
+ [HYPERCALL_COMPAT_RESERVED56] = 0,
+ [HYPERCALL_COMPAT_RESERVED57] = 0,
+ [HYPERCALL_COMPAT_RESERVED58] = 0,
+ [HYPERCALL_COMPAT_RESERVED59] = 0,
+ [HYPERCALL_COMPAT_RESERVED60] = 0,
+ [HYPERCALL_COMPAT_RESERVED61] = 0,
+ [HYPERCALL_COMPAT_RESERVED62] = 0,
+ [HYPERCALL_COMPAT_RESERVED63] = 0,
+ [HYPERCALL_COMPAT_RESERVED64] = 0,
+ [HYPERCALL_COMPAT_RESERVED65] = 0,
+ [HYPERCALL_COMPAT_RESERVED66] = 0,
+ [HYPERCALL_COMPAT_RESERVED67] = 0,
+ [HYPERCALL_COMPAT_RESERVED68] = 0,
+ [HYPERCALL_COMPAT_RESERVED69] = 0,
+ [HYPERCALL_COMPAT_RESERVED70] = 0,
+ [HYPERCALL_COMPAT_RESERVED71] = 0,
+ [HYPERCALL_COMPAT_RESERVED72] = 0,
+ [HYPERCALL_COMPAT_RESERVED73] = 0,
+ [HYPERCALL_COMPAT_RESERVED74] = 0,
+ [HYPERCALL_COMPAT_RESERVED75] = 0,
+ [HYPERCALL_COMPAT_RESERVED76] = 0,
+ [HYPERCALL_COMPAT_RESERVED77] = 0,
+ [HYPERCALL_COMPAT_RESERVED78] = 0,
+ [HYPERCALL_COMPAT_RESERVED79] = 0,
+ [HYPERCALL_COMPAT_RESERVED80] = 0,
+ [HYPERCALL_DTRACE_PROBE_CREATE] = 0,
+ [HYPERCALL_DTRACE_PROBE] = 0,
+ [HYPERCALL_DTRACE_RESERVED1] = 3, /* Reserved for DTrace */
+ [HYPERCALL_DTRACE_RESERVED2] = 0, /* Reserved for DTrace */
+ [HYPERCALL_DTRACE_RESERVED3] = 0, /* Reserved for DTrace */
+ [HYPERCALL_DTRACE_RESERVED4] = 0, /* Reserved for DTrace */
+ [HYPERCALL_DTRACE_RESERVED5] = 0, /* Reserved for DTrace */
+};
+
+
static void vm_free_memmap(struct vm *vm, int ident);
static bool sysmem_mapping(struct vm *vm, struct mem_map *mm);
static void vcpu_notify_event_locked(struct vcpu *vcpu, bool lapic_intr);
@@ -538,8 +636,9 @@
if ((obj = vmm_mmio_alloc(vm->vmspace, gpa, len, hpa)) == NULL)
return (ENOMEM);
- else
+ else {
return (0);
+ }
}
int
@@ -1507,6 +1606,166 @@
return (0);
}
+static int
+hypercall_copy_arg(struct vm *vm, int vcpuid, uint64_t ds_base,
+ struct hypercall_arg *arg, struct vm_guest_paging *paging, void *dst)
+{
+ struct vm_copyinfo copyinfo[2];
+ uint64_t gla;
+ int error, fault;
+
+ gla = ds_base + arg->val;
+ error = vm_copy_setup(vm, vcpuid, paging, gla, arg->len,
+ PROT_READ, copyinfo, nitems(copyinfo), &fault);
+ if (error || fault) {
+ return (error);
+ }
+
+ vm_copyin(vm, vcpuid, copyinfo, dst, arg->len);
+ vm_copy_teardown(vm, vcpuid, copyinfo, nitems(copyinfo));
+
+ return (0);
+}
+
+static int
+vm_handle_hypercall(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu)
+{
+ struct vm_guest_paging *paging;
+ struct hypercall_arg args[HYPERCALL_MAX_ARGS] = { {0} };
+ /*
+ * This struct sets the requirement of the following registers being used:
+ *
+ * arg0->len = RCX
+ * arg0->val = RDX
+ *
+ * arg1->len = RSI
+ * arg1->val = RDI
+ *
+ * arg2->len = R8
+ * arg2->val = R9
+ *
+ * arg3->len = R10
+ * arg3->val = R11
+ *
+ * arg4->len = R12
+ * arg4->val = R13
+ *
+ * arg5->len = R14
+ * arg5->val = R15
+ *
+ * Using any different order while passing arguments in from the guest
+ * OS will result in the wrong values at wrong places inside this
+ * function.
+ */
+ struct hypercall_arg regs[] = {
+ [0] = {
+ .len = VM_REG_GUEST_RCX,
+ .val = VM_REG_GUEST_RDX
+ },
+ [1] = {
+ .len = VM_REG_GUEST_RSI,
+ .val = VM_REG_GUEST_RDI
+ },
+ [2] = {
+ .len = VM_REG_GUEST_R8,
+ .val = VM_REG_GUEST_R9
+ },
+ [3] = {
+ .len = VM_REG_GUEST_R10,
+ .val = VM_REG_GUEST_R11
+ },
+ [4] = {
+ .len = VM_REG_GUEST_R12,
+ .val = VM_REG_GUEST_R13
+ },
+ [5] = {
+ .len = VM_REG_GUEST_R14,
+ .val = VM_REG_GUEST_R15
+ },
+ };
+ struct seg_desc cs_desc;
+ uint64_t hcid, nargs, i;
+ int error, handled;
+
+ error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RAX, &hcid);
+ KASSERT(error == 0, ("%s: error %d getting RAX",
+ __func__, error));
+ /*
+ * Ensure that the hypercall called by the guest never exceed
+ * the maximum number of hypercalls defined.
+ */
+ if (hcid >= HYPERCALL_INDEX_MAX) {
+ error = vm_set_register(vm, vcpuid, VM_REG_GUEST_RAX, HYPERCALL_RET_ERROR);
+ KASSERT(error == 0, ("%s: error %d setting RAX",
+ __func__, error));
+ return (0);
+ }
+
+ error = vm_get_seg_desc(vm, vcpuid, VM_REG_GUEST_CS, &cs_desc);
+ KASSERT(error == 0, ("%s: error %d getting CS descriptor",
+ __func__, error));
+
+ /*
+ * The check ensures that each of the hypercalls that is called
+ * from the guest is called from the correct protection ring.
+ */
+ if (SEG_DESC_DPL(cs_desc.access) != ring_plevel[hcid]) {
+ error = vm_set_register(vm, vcpuid, VM_REG_GUEST_RAX, HYPERCALL_RET_ERROR);
+ KASSERT(error == 0, ("%s: error %d setting RAX",
+ __func__, error));
+ return (0);
+ }
+
+ error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RBX, &nargs);
+ KASSERT(error == 0, ("%s: error %d getting RBX",
+ __func__, error));
+
+ if (nargs > HYPERCALL_MAX_ARGS) {
+ error = vm_set_register(vm, vcpuid, VM_REG_GUEST_RAX, HYPERCALL_RET_ERROR);
+ KASSERT(error == 0, ("%s: error %d setting RAX",
+ __func__, error));
+ return (0);
+ }
+
+ handled = 0;
+ paging = &vmexit->u.hypercall.paging;
+
+ for (i = 0; i < nargs; i++) {
+ error = vm_get_register(vm, vcpuid, regs[i].len, &args[i].len);
+ KASSERT(error == 0, ("%s: error %d copying at index %lu",
+ __func__, error, i));
+ error = vm_get_register(vm, vcpuid, regs[i].val, &args[i].val);
+ KASSERT(error == 0, ("%s: error %d copying at index %lu",
+ __func__, error, i));
+ }
+
+ /*
+ * From this point on, all the arguments passed in from the
+ * guest are contained in the args array.
+ */
+
+ switch (hcid) {
+ case HYPERCALL_DTRACE_PROBE_CREATE:
+ case HYPERCALL_DTRACE_PROBE:
+ case HYPERCALL_DTRACE_RESERVED1:
+ case HYPERCALL_DTRACE_RESERVED2:
+ case HYPERCALL_DTRACE_RESERVED3:
+ case HYPERCALL_DTRACE_RESERVED4:
+ case HYPERCALL_DTRACE_RESERVED5:
+ error = vm_set_register(vm, vcpuid, VM_REG_GUEST_RAX, HYPERCALL_RET_NOT_IMPL);
+ KASSERT(error == 0, ("%s: error %d setting RAX",
+ __func__, error));
+ break;
+ default:
+ error = vm_set_register(vm, vcpuid, VM_REG_GUEST_RAX, HYPERCALL_RET_NOT_IMPL);
+ KASSERT(error == 0, ("%s: error %d setting RAX",
+ __func__, error));
+ break;
+ }
+
+ return (0);
+}
+
int
vm_suspend(struct vm *vm, enum vm_suspend_how how)
{
@@ -1675,6 +1934,9 @@
case VM_EXITCODE_MWAIT:
vm_inject_ud(vm, vcpuid);
break;
+ case VM_EXITCODE_HYPERCALL:
+ error = vm_handle_hypercall(vm, vcpuid, vme, &retu);
+ break;
default:
retu = true; /* handled in userland */
break;
@@ -1855,6 +2117,7 @@
/* Handle exceptions serially */
*retinfo = info2;
}
+
return (1);
}
Index: sys/amd64/vmm/x86.h
===================================================================
--- sys/amd64/vmm/x86.h
+++ sys/amd64/vmm/x86.h
@@ -47,6 +47,8 @@
#define CPUID_8000_0006 (0x80000006)
#define CPUID_8000_0007 (0x80000007)
#define CPUID_8000_0008 (0x80000008)
+#define CPUID_4000_0000 (0x40000000)
+#define CPUID_4000_0001 (0x40000001)
/*
* CPUID instruction Fn0000_0001:
Index: sys/amd64/vmm/x86.c
===================================================================
--- sys/amd64/vmm/x86.c
+++ sys/amd64/vmm/x86.c
@@ -50,7 +50,7 @@
SYSCTL_DECL(_hw_vmm);
static SYSCTL_NODE(_hw_vmm, OID_AUTO, topology, CTLFLAG_RD, 0, NULL);
-#define CPUID_VM_HIGH 0x40000000
+#define CPUID_VM_HIGH 0x40000001
static const char bhyve_id[12] = "bhyve bhyve ";
@@ -463,12 +463,18 @@
}
break;
- case 0x40000000:
+ case CPUID_4000_0000:
regs[0] = CPUID_VM_HIGH;
bcopy(bhyve_id, &regs[1], 4);
bcopy(bhyve_id + 4, &regs[2], 4);
bcopy(bhyve_id + 8, &regs[3], 4);
break;
+ case CPUID_4000_0001:
+ regs[0] = hypercalls_enabled;
+ regs[1] = HYPERCALL_MAX_ARGS;
+ regs[2] = 0;
+ regs[3] = 0;
+ break;
default:
/*
Index: sys/kern/subr_param.c
===================================================================
--- sys/kern/subr_param.c
+++ sys/kern/subr_param.c
@@ -149,6 +149,7 @@
"hv",
"vmware",
"kvm",
+ "bhyve",
NULL
};
CTASSERT(nitems(vm_guest_sysctl_names) - 1 == VM_LAST);
Index: sys/sys/systm.h
===================================================================
--- sys/sys/systm.h
+++ sys/sys/systm.h
@@ -74,7 +74,7 @@
* Keep in sync with vm_guest_sysctl_names[].
*/
enum VM_GUEST { VM_GUEST_NO = 0, VM_GUEST_VM, VM_GUEST_XEN, VM_GUEST_HV,
- VM_GUEST_VMWARE, VM_GUEST_KVM, VM_LAST };
+ VM_GUEST_VMWARE, VM_GUEST_KVM, VM_GUEST_BHYVE, VM_LAST };
#if defined(WITNESS) || defined(INVARIANT_SUPPORT)
void kassert_panic(const char *fmt, ...) __printflike(1, 2);
Index: sys/x86/x86/identcpu.c
===================================================================
--- sys/x86/x86/identcpu.c
+++ sys/x86/x86/identcpu.c
@@ -1288,6 +1288,8 @@
vm_guest = VM_GUEST_HV;
else if (strcmp(hv_vendor, "KVMKVMKVM") == 0)
vm_guest = VM_GUEST_KVM;
+ else if (strcmp(hv_vendor, "bhyve bhyve ") == 0)
+ vm_guest = VM_GUEST_BHYVE;
}
return;
}

File Metadata

Mime Type
text/plain
Expires
Wed, Aug 26, 9:41 PM (10 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37330078
Default Alt Text
D8100.id20978.diff (28 KB)

Event Timeline