Skip to content

Commit

Permalink
Drivers: hv: Add modules to expose /dev/mshv to VMMs running on Hyper-V
Browse files Browse the repository at this point in the history
Add mshv, mshv_root, and mshv_vtl modules.
- mshv provides /dev/mshv and common code, and is the parent module
- mshv_root provides APIs for creating and managing child partitions
- mshv_vtl provides VTL (Virtual Trust Level) support for VMMs

Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
  • Loading branch information
NunoDasNeves committed Jul 27, 2023
1 parent bb82b2c commit 54329d1
Show file tree
Hide file tree
Showing 17 changed files with 7,439 additions and 0 deletions.
54 changes: 54 additions & 0 deletions drivers/hv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,58 @@ config HYPERV_BALLOON
help
Select this option to enable Hyper-V Balloon driver.

config MSHV
tristate "Microsoft Hypervisor root partition interfaces: /dev/mshv"
depends on X86_64 && HYPERV
select EVENTFD
select MSHV_VFIO
select MSHV_XFER_TO_GUEST_WORK
help
Select this option to enable core functionality for managing guest
virtual machines running under the Microsoft Hypervisor.

The interfaces are provided via a device named /dev/mshv.

To compile this as a module, choose M here.

If unsure, say N.

config MSHV_ROOT
tristate "Microsoft Hyper-V root partition APIs driver"
depends on MSHV
help
Select this option to provide /dev/mshv interfaces specific to
running as the root partition on Microsoft Hypervisor.

To compile this as a module, choose M here.

If unsure, say N.

config MSHV_VTL
tristate "Microsoft Hyper-V VTL driver"
depends on MSHV
select HYPERV_VTL_MODE
select TRANSPARENT_HUGEPAGE
help
Select this option to enable Hyper-V VTL driver.
Virtual Secure Mode (VSM) is a set of hypervisor capabilities and
enlightenments offered to host and guest partitions which enables
the creation and management of new security boundaries within
operating system software.

VSM achieves and maintains isolation through Virtual Trust Levels
(VTLs). Virtual Trust Levels are hierarchical, with higher levels
being more privileged than lower levels. VTL0 is the least privileged
level, and currently only other level supported is VTL2.

To compile this as a module, choose M here.

If unsure, say N.

config MSHV_VFIO
bool

config MSHV_XFER_TO_GUEST_WORK
bool

endmenu
21 changes: 21 additions & 0 deletions drivers/hv/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,31 @@
obj-$(CONFIG_HYPERV) += hv_vmbus.o
obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o
obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o
obj-$(CONFIG_DXGKRNL) += dxgkrnl/
obj-$(CONFIG_MSHV) += mshv.o
obj-$(CONFIG_MSHV_VTL) += mshv_vtl.o
obj-$(CONFIG_MSHV_ROOT) += mshv_root.o

CFLAGS_hv_trace.o = -I$(src)
CFLAGS_hv_balloon.o = -I$(src)

CFLAGS_mshv_main.o = -DHV_HYPERV_DEFS
CFLAGS_hv_call.o = -DHV_HYPERV_DEFS
CFLAGS_mshv_root_main.o = -DHV_HYPERV_DEFS
CFLAGS_mshv_root_hv_call.o = -DHV_HYPERV_DEFS
CFLAGS_mshv_synic.o = -DHV_HYPERV_DEFS
CFLAGS_mshv_portid_table.o = -DHV_HYPERV_DEFS
CFLAGS_mshv_eventfd.o = -DHV_HYPERV_DEFS
CFLAGS_mshv_msi.o = -DHV_HYPERV_DEFS
CFLAGS_mshv_vtl_main.o = -DHV_HYPERV_DEFS

mshv-y += mshv_main.o
mshv_root-y := mshv_root_main.o mshv_synic.o mshv_portid_table.o \
mshv_eventfd.o mshv_msi.o mshv_root_hv_call.o hv_call.o
mshv_vtl-y := mshv_vtl_main.o hv_call.o

obj-$(CONFIG_MSHV_XFER_TO_GUEST_WORK) += xfer_to_guest.o

hv_vmbus-y := vmbus_drv.o \
hv.o connection.o channel.o \
channel_mgmt.o ring_buffer.o hv_trace.o
Expand Down
119 changes: 119 additions & 0 deletions drivers/hv/hv_call.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023, Microsoft Corporation.
*
* Hypercall helper functions shared between mshv modules.
*
* Authors:
* Nuno Das Neves <nunodasneves@linux.microsoft.com>
*/

#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/mshyperv.h>

#define HV_GET_REGISTER_BATCH_SIZE \
(HV_HYP_PAGE_SIZE / sizeof(union hv_register_value))
#define HV_SET_REGISTER_BATCH_SIZE \
((HV_HYP_PAGE_SIZE - sizeof(struct hv_input_set_vp_registers)) \
/ sizeof(struct hv_register_assoc))

int hv_call_get_vp_registers(
u32 vp_index,
u64 partition_id,
u16 count,
union hv_input_vtl input_vtl,
struct hv_register_assoc *registers)
{
struct hv_input_get_vp_registers *input_page;
union hv_register_value *output_page;
u16 completed = 0;
unsigned long remaining = count;
int rep_count, i;
u64 status;
unsigned long flags;

local_irq_save(flags);

input_page = *this_cpu_ptr(hyperv_pcpu_input_arg);
output_page = *this_cpu_ptr(hyperv_pcpu_output_arg);

input_page->partition_id = partition_id;
input_page->vp_index = vp_index;
input_page->input_vtl.as_uint8 = input_vtl.as_uint8;
input_page->rsvd_z8 = 0;
input_page->rsvd_z16 = 0;

while (remaining) {
rep_count = min(remaining, HV_GET_REGISTER_BATCH_SIZE);
for (i = 0; i < rep_count; ++i)
input_page->names[i] = registers[i].name;

status = hv_do_rep_hypercall(HVCALL_GET_VP_REGISTERS, rep_count,
0, input_page, output_page);
if (!hv_result_success(status)) {
pr_err("%s: completed %li out of %u, %s\n",
__func__,
count - remaining, count,
hv_status_to_string(status));
break;
}
completed = hv_repcomp(status);
for (i = 0; i < completed; ++i)
registers[i].value = output_page[i];

registers += completed;
remaining -= completed;
}
local_irq_restore(flags);

return hv_status_to_errno(status);
}

int hv_call_set_vp_registers(
u32 vp_index,
u64 partition_id,
u16 count,
union hv_input_vtl input_vtl,
struct hv_register_assoc *registers)
{
struct hv_input_set_vp_registers *input_page;
u16 completed = 0;
unsigned long remaining = count;
int rep_count;
u64 status;
unsigned long flags;

local_irq_save(flags);
input_page = *this_cpu_ptr(hyperv_pcpu_input_arg);

input_page->partition_id = partition_id;
input_page->vp_index = vp_index;
input_page->input_vtl.as_uint8 = input_vtl.as_uint8;
input_page->rsvd_z8 = 0;
input_page->rsvd_z16 = 0;

while (remaining) {
rep_count = min(remaining, HV_SET_REGISTER_BATCH_SIZE);
memcpy(input_page->elements, registers,
sizeof(struct hv_register_assoc) * rep_count);

status = hv_do_rep_hypercall(HVCALL_SET_VP_REGISTERS, rep_count,
0, input_page, NULL);
if (!hv_result_success(status)) {
pr_err("%s: completed %li out of %u, %s\n",
__func__,
count - remaining, count,
hv_status_to_string(status));
break;
}
completed = hv_repcomp(status);
registers += completed;
remaining -= completed;
}

local_irq_restore(flags);

return hv_status_to_errno(status);
}

156 changes: 156 additions & 0 deletions drivers/hv/mshv.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023, Microsoft Corporation.
*/

#ifndef _MSHV_H_
#define _MSHV_H_

#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/semaphore.h>
#include <linux/sched.h>
#include <linux/srcu.h>
#include <linux/wait.h>
#include <uapi/linux/mshv.h>

/*
* Hyper-V hypercalls
*/

int hv_call_withdraw_memory(u64 count, int node, u64 partition_id);
int hv_call_create_partition(
u64 flags,
struct hv_partition_creation_properties creation_properties,
union hv_partition_isolation_properties isolation_properties,
u64 *partition_id);
int hv_call_initialize_partition(u64 partition_id);
int hv_call_finalize_partition(u64 partition_id);
int hv_call_delete_partition(u64 partition_id);
int hv_call_map_gpa_pages(
u64 partition_id,
u64 gpa_target,
u64 page_count, u32 flags,
struct page **pages);
int hv_call_unmap_gpa_pages(
u64 partition_id,
u64 gpa_target,
u64 page_count, u32 flags);
int hv_call_get_vp_registers(
u32 vp_index,
u64 partition_id,
u16 count,
union hv_input_vtl input_vtl,
struct hv_register_assoc *registers);
int hv_call_get_gpa_access_states(
u64 partition_id,
u32 count,
u64 gpa_base_pfn,
u64 state_flags,
int *written_total,
union hv_gpa_page_access_state *states);

int hv_call_set_vp_registers(
u32 vp_index,
u64 partition_id,
u16 count,
union hv_input_vtl input_vtl,
struct hv_register_assoc *registers);
int hv_call_install_intercept(u64 partition_id, u32 access_type,
enum hv_intercept_type intercept_type,
union hv_intercept_parameters intercept_parameter);
int hv_call_assert_virtual_interrupt(
u64 partition_id,
u32 vector,
u64 dest_addr,
union hv_interrupt_control control);
int hv_call_clear_virtual_interrupt(u64 partition_id);

#ifdef HV_SUPPORTS_VP_STATE
int hv_call_get_vp_state(
u32 vp_index,
u64 partition_id,
enum hv_get_set_vp_state_type type,
struct hv_vp_state_data_xsave xsave,
/* Choose between pages and ret_output */
u64 page_count,
struct page **pages,
union hv_output_get_vp_state *ret_output);
int hv_call_set_vp_state(
u32 vp_index,
u64 partition_id,
enum hv_get_set_vp_state_type type,
struct hv_vp_state_data_xsave xsave,
/* Choose between pages and bytes */
u64 page_count,
struct page **pages,
u32 num_bytes,
u8 *bytes);
#endif

int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
struct page **state_page);
int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type);
int hv_call_get_partition_property(
u64 partition_id,
u64 property_code,
u64 *property_value);
int hv_call_set_partition_property(
u64 partition_id, u64 property_code, u64 property_value,
void (*completion_handler)(void * /* data */, u64 * /* status */),
void *completion_data);
int hv_call_translate_virtual_address(
u32 vp_index,
u64 partition_id,
u64 flags,
u64 gva,
u64 *gpa,
union hv_translate_gva_result *result);
int hv_call_get_vp_cpuid_values(
u32 vp_index,
u64 partition_id,
union hv_get_vp_cpuid_values_flags values_flags,
struct hv_cpuid_leaf_info *info,
union hv_output_get_vp_cpuid_values *result);

int hv_call_create_port(u64 port_partition_id, union hv_port_id port_id,
u64 connection_partition_id, struct hv_port_info *port_info,
u8 port_vtl, u8 min_connection_vtl, int node);
int hv_call_delete_port(u64 port_partition_id, union hv_port_id port_id);
int hv_call_connect_port(u64 port_partition_id, union hv_port_id port_id,
u64 connection_partition_id,
union hv_connection_id connection_id,
struct hv_connection_info *connection_info,
u8 connection_vtl, int node);
int hv_call_disconnect_port(u64 connection_partition_id,
union hv_connection_id connection_id);
int hv_call_notify_port_ring_empty(u32 sint_index);
#ifdef HV_SUPPORTS_REGISTER_INTERCEPT
int hv_call_register_intercept_result(u32 vp_index,
u64 partition_id,
enum hv_intercept_type intercept_type,
union hv_register_intercept_result_parameters *params);
#endif
int hv_call_signal_event_direct(u32 vp_index,
u64 partition_id,
u8 vtl,
u8 sint,
u16 flag_number,
u8 *newly_signaled);
int hv_call_post_message_direct(u32 vp_index,
u64 partition_id,
u8 vtl,
u32 sint_index,
u8 *message);

struct mshv_partition *mshv_partition_find(u64 partition_id) __must_hold(RCU);

int mshv_xfer_to_guest_mode_handle_work(unsigned long ti_work);

typedef long (*mshv_create_func_t)(void __user *user_arg);
typedef long (*mshv_check_ext_func_t)(u32 arg);
int mshv_setup_vtl_func(const mshv_create_func_t create_vtl,
const mshv_check_ext_func_t check_ext);
int mshv_set_create_partition_func(const mshv_create_func_t func);

#endif /* _MSHV_H */

0 comments on commit 54329d1

Please sign in to comment.