diff options
Diffstat (limited to 'sys/dev/hyperv/vmbus/hyperv.c')
-rw-r--r-- | sys/dev/hyperv/vmbus/hyperv.c | 63 |
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) { |