aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/hyperv/vmbus/hyperv.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/hyperv/vmbus/hyperv.c')
-rw-r--r--sys/dev/hyperv/vmbus/hyperv.c63
1 files changed, 63 insertions, 0 deletions
diff --git a/sys/dev/hyperv/vmbus/hyperv.c b/sys/dev/hyperv/vmbus/hyperv.c
index e0e85a022090..1f85203146d0 100644
--- a/sys/dev/hyperv/vmbus/hyperv.c
+++ b/sys/dev/hyperv/vmbus/hyperv.c
@@ -35,6 +35,7 @@
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/timetc.h>
+#include <sys/cpuset.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
@@ -50,6 +51,7 @@
#include <dev/hyperv/vmbus/x86/hyperv_machdep.h>
#include <dev/hyperv/vmbus/x86/hyperv_reg.h>
#endif
+#include <dev/hyperv/vmbus/vmbus_var.h>
#include <dev/hyperv/vmbus/hyperv_common_reg.h>
#include <dev/hyperv/vmbus/hyperv_var.h>
@@ -72,10 +74,12 @@
MSR_HV_GUESTID_OSID_FREEBSD | \
MSR_HV_GUESTID_OSTYPE_FREEBSD)
+
static bool hyperv_identify(void);
static void hypercall_memfree(void);
static struct hypercall_ctx hypercall_context;
+
uint64_t
hypercall_post_message(bus_addr_t msg_paddr)
{
@@ -90,6 +94,65 @@ hypercall_signal_event(bus_addr_t monprm_paddr)
HYPERCALL_SIGNAL_EVENT, monprm_paddr, 0);
}
+static inline int hv_result(uint64_t status)
+{
+ return status & HV_HYPERCALL_RESULT_MASK;
+}
+
+static inline bool hv_result_success(uint64_t status)
+{
+ return hv_result(status) == HV_STATUS_SUCCESS;
+}
+
+static inline unsigned int hv_repcomp(uint64_t status)
+{
+ /* Bits [43:32] of status have 'Reps completed' data. */
+ return ((status & HV_HYPERCALL_REP_COMP_MASK) >>
+ HV_HYPERCALL_REP_COMP_OFFSET);
+}
+
+/*
+ * Rep hypercalls. Callers of this functions are supposed to ensure that
+ * rep_count and varhead_size comply with Hyper-V hypercall definition.
+ */
+uint64_t
+hv_do_rep_hypercall(uint16_t code, uint16_t rep_count, uint16_t varhead_size,
+ uint64_t input, uint64_t output)
+{
+ uint64_t control = code;
+ uint64_t status;
+ uint16_t rep_comp;
+
+ control |= (uint64_t)varhead_size << HV_HYPERCALL_VARHEAD_OFFSET;
+ control |= (uint64_t)rep_count << HV_HYPERCALL_REP_COMP_OFFSET;
+
+ do {
+ status = hypercall_do_md(control, input, output);
+ if (!hv_result_success(status))
+ return status;
+
+ rep_comp = hv_repcomp(status);
+
+ control &= ~HV_HYPERCALL_REP_START_MASK;
+ control |= (uint64_t)rep_comp << HV_HYPERCALL_REP_START_OFFSET;
+
+ } while (rep_comp < rep_count);
+ if (hv_result_success(status))
+ return HV_STATUS_SUCCESS;
+
+ return status;
+}
+
+uint64_t
+hypercall_do_md(uint64_t input_val, uint64_t input_addr, uint64_t out_addr)
+{
+ uint64_t phys_inaddr, phys_outaddr;
+ phys_inaddr = input_addr ? vtophys(input_addr) : 0;
+ phys_outaddr = out_addr ? vtophys(out_addr) : 0;
+ return hypercall_md(hypercall_context.hc_addr,
+ input_val, phys_inaddr, phys_outaddr);
+}
+
int
hyperv_guid2str(const struct hyperv_guid *guid, char *buf, size_t sz)
{