Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs

Provide a set of IOCTLs for creating and managing child partitions when
running as root partition on Hyper-V. The new driver is enabled via
CONFIG_MSHV_ROOT.

A brief overview of the interface:

MSHV_CREATE_PARTITION is the entry point, returning a file descriptor
representing a child partition. IOCTLs on this fd can be used to map
memory, create VPs, etc.

Creating a VP returns another file descriptor representing that VP which
in turn has another set of corresponding IOCTLs for running the VP,
getting/setting state, etc.

MSHV_ROOT_HVCALL is a generic "passthrough" hypercall IOCTL which can be
used for a number of partition or VP hypercalls. This is for hypercalls
that do not affect any state in the kernel driver, such as getting and
setting VP registers and partition properties, translating addresses,
etc. It is "passthrough" because the binary input and output for the
hypercall is only interpreted by the VMM - the kernel driver does
nothing but insert the VP and partition id where necessary (which are
always in the same place), and execute the hypercall.

Co-developed-by: Anirudh Rayabharam <anrayabh@linux.microsoft.com>
Signed-off-by: Anirudh Rayabharam <anrayabh@linux.microsoft.com>
Co-developed-by: Jinank Jain <jinankjain@microsoft.com>
Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
Co-developed-by: Mukesh Rathor <mrathor@linux.microsoft.com>
Signed-off-by: Mukesh Rathor <mrathor@linux.microsoft.com>
Co-developed-by: Muminul Islam <muislam@microsoft.com>
Signed-off-by: Muminul Islam <muislam@microsoft.com>
Co-developed-by: Praveen K Paladugu <prapal@linux.microsoft.com>
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
Co-developed-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
Co-developed-by: Wei Liu <wei.liu@kernel.org>
Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
Reviewed-by: Roman Kisel <romank@linux.microsoft.com>
Link: https://lore.kernel.org/r/1741980536-3865-11-git-send-email-nunodasneves@linux.microsoft.com
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Message-ID: <1741980536-3865-11-git-send-email-nunodasneves@linux.microsoft.com>
This commit is contained in:
Nuno Das Neves
2025-03-21 18:24:22 +00:00
committed by Wei Liu
parent 0bd921a4b4
commit 621191d709
14 changed files with 5732 additions and 1 deletions
@@ -370,6 +370,8 @@ Code Seq# Include File Comments
0xB7 all uapi/linux/remoteproc_cdev.h <mailto:linux-remoteproc@vger.kernel.org>
0xB7 all uapi/linux/nsfs.h <mailto:Andrei Vagin <avagin@openvz.org>>
0xB8 01-02 uapi/misc/mrvl_cn10k_dpi.h Marvell CN10K DPI driver
0xB8 all uapi/linux/mshv.h Microsoft Hyper-V /dev/mshv driver
<mailto:linux-hyperv@vger.kernel.org>
0xC0 00-0F linux/usb/iowarrior.h
0xCA 00-0F uapi/misc/cxl.h
0xCA 10-2F uapi/misc/ocxl.h
+1
View File
@@ -64,6 +64,7 @@ config MSHV_ROOT
# e.g. When withdrawing memory, the hypervisor gives back 4k pages in
# no particular order, making it impossible to reassemble larger pages
depends on PAGE_SIZE_4KB
select EVENTFD
default n
help
Select this option to enable support for booting and running as root
+4 -1
View File
@@ -2,6 +2,7 @@
obj-$(CONFIG_HYPERV) += hv_vmbus.o
obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o
obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o
obj-$(CONFIG_MSHV_ROOT) += mshv_root.o
CFLAGS_hv_trace.o = -I$(src)
CFLAGS_hv_balloon.o = -I$(src)
@@ -11,7 +12,9 @@ hv_vmbus-y := vmbus_drv.o \
channel_mgmt.o ring_buffer.o hv_trace.o
hv_vmbus-$(CONFIG_HYPERV_TESTING) += hv_debugfs.o
hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_utils_transport.o
mshv_root-y := mshv_root_main.o mshv_synic.o mshv_eventfd.o mshv_irq.o \
mshv_root_hv_call.o mshv_portid_table.o
# Code that must be built-in
obj-$(subst m,y,$(CONFIG_HYPERV)) += hv_common.o
obj-$(subst m,y,$(CONFIG_MSHV_ROOT)) += hv_proc.o
obj-$(subst m,y,$(CONFIG_MSHV_ROOT)) += hv_proc.o mshv_common.o
+30
View File
@@ -0,0 +1,30 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023, Microsoft Corporation.
*/
#ifndef _MSHV_H_
#define _MSHV_H_
#include <linux/stddef.h>
#include <linux/string.h>
#include <hyperv/hvhdk.h>
#define mshv_field_nonzero(STRUCT, MEMBER) \
memchr_inv(&((STRUCT).MEMBER), \
0, sizeof_field(typeof(STRUCT), MEMBER))
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_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_get_partition_property(u64 partition_id, u64 property_code,
u64 *property_value);
int mshv_do_pre_guest_mode_work(ulong th_flags);
#endif /* _MSHV_H */
+161
View File
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024, Microsoft Corporation.
*
* This file contains functions that will be called from one or more modules.
* If any of these modules are configured to build, this file is built and just
* statically linked in.
*
* Authors: Microsoft Linux virtualization team
*/
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/mshyperv.h>
#include <linux/resume_user_mode.h>
#include "mshv.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 = HV_STATUS_SUCCESS;
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))
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_result_to_errno(status);
}
EXPORT_SYMBOL_GPL(hv_call_get_vp_registers);
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 = HV_STATUS_SUCCESS;
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))
break;
completed = hv_repcomp(status);
registers += completed;
remaining -= completed;
}
local_irq_restore(flags);
return hv_result_to_errno(status);
}
EXPORT_SYMBOL_GPL(hv_call_set_vp_registers);
int hv_call_get_partition_property(u64 partition_id,
u64 property_code,
u64 *property_value)
{
u64 status;
unsigned long flags;
struct hv_input_get_partition_property *input;
struct hv_output_get_partition_property *output;
local_irq_save(flags);
input = *this_cpu_ptr(hyperv_pcpu_input_arg);
output = *this_cpu_ptr(hyperv_pcpu_output_arg);
memset(input, 0, sizeof(*input));
input->partition_id = partition_id;
input->property_code = property_code;
status = hv_do_hypercall(HVCALL_GET_PARTITION_PROPERTY, input, output);
if (!hv_result_success(status)) {
local_irq_restore(flags);
return hv_result_to_errno(status);
}
*property_value = output->property_value;
local_irq_restore(flags);
return 0;
}
EXPORT_SYMBOL_GPL(hv_call_get_partition_property);
/*
* Handle any pre-processing before going into the guest mode on this cpu, most
* notably call schedule(). Must be invoked with both preemption and
* interrupts enabled.
*
* Returns: 0 on success, -errno on error.
*/
int mshv_do_pre_guest_mode_work(ulong th_flags)
{
if (th_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL))
return -EINTR;
if (th_flags & _TIF_NEED_RESCHED)
schedule();
if (th_flags & _TIF_NOTIFY_RESUME)
resume_user_mode_work(NULL);
return 0;
}
EXPORT_SYMBOL_GPL(mshv_do_pre_guest_mode_work);
File diff suppressed because it is too large Load Diff
+71
View File
@@ -0,0 +1,71 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* irqfd: Allows an fd to be used to inject an interrupt to the guest.
* ioeventfd: Allow an fd to be used to receive a signal from the guest.
* All credit goes to kvm developers.
*/
#ifndef __LINUX_MSHV_EVENTFD_H
#define __LINUX_MSHV_EVENTFD_H
#include <linux/poll.h>
#include "mshv.h"
#include "mshv_root.h"
/* struct to contain list of irqfds sharing an irq. Updates are protected by
* partition.irqfds.resampler_lock
*/
struct mshv_irqfd_resampler {
struct mshv_partition *rsmplr_partn;
struct hlist_head rsmplr_irqfd_list;
struct mshv_irq_ack_notifier rsmplr_notifier;
struct hlist_node rsmplr_hnode;
};
struct mshv_irqfd {
struct mshv_partition *irqfd_partn;
struct eventfd_ctx *irqfd_eventfd_ctx;
struct mshv_guest_irq_ent irqfd_girq_ent;
seqcount_spinlock_t irqfd_irqe_sc;
u32 irqfd_irqnum;
struct mshv_lapic_irq irqfd_lapic_irq;
struct hlist_node irqfd_hnode;
poll_table irqfd_polltbl;
wait_queue_head_t *irqfd_wqh;
wait_queue_entry_t irqfd_wait;
struct work_struct irqfd_shutdown;
struct mshv_irqfd_resampler *irqfd_resampler;
struct eventfd_ctx *irqfd_resamplefd;
struct hlist_node irqfd_resampler_hnode;
};
void mshv_eventfd_init(struct mshv_partition *partition);
void mshv_eventfd_release(struct mshv_partition *partition);
void mshv_register_irq_ack_notifier(struct mshv_partition *partition,
struct mshv_irq_ack_notifier *mian);
void mshv_unregister_irq_ack_notifier(struct mshv_partition *partition,
struct mshv_irq_ack_notifier *mian);
bool mshv_notify_acked_gsi(struct mshv_partition *partition, int gsi);
int mshv_set_unset_irqfd(struct mshv_partition *partition,
struct mshv_user_irqfd *args);
int mshv_irqfd_wq_init(void);
void mshv_irqfd_wq_cleanup(void);
struct mshv_ioeventfd {
struct hlist_node iovntfd_hnode;
u64 iovntfd_addr;
int iovntfd_length;
struct eventfd_ctx *iovntfd_eventfd;
u64 iovntfd_datamatch;
int iovntfd_doorbell_id;
bool iovntfd_wildcard;
};
int mshv_set_unset_ioeventfd(struct mshv_partition *pt,
struct mshv_user_ioeventfd *args);
#endif /* __LINUX_MSHV_EVENTFD_H */
+124
View File
@@ -0,0 +1,124 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023, Microsoft Corporation.
*
* Authors: Microsoft Linux virtualization team
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <asm/mshyperv.h>
#include "mshv_eventfd.h"
#include "mshv.h"
#include "mshv_root.h"
/* called from the ioctl code, user wants to update the guest irq table */
int mshv_update_routing_table(struct mshv_partition *partition,
const struct mshv_user_irq_entry *ue,
unsigned int numents)
{
struct mshv_girq_routing_table *new = NULL, *old;
u32 i, nr_rt_entries = 0;
int r = 0;
if (numents == 0)
goto swap_routes;
for (i = 0; i < numents; i++) {
if (ue[i].gsi >= MSHV_MAX_GUEST_IRQS)
return -EINVAL;
if (ue[i].address_hi)
return -EINVAL;
nr_rt_entries = max(nr_rt_entries, ue[i].gsi);
}
nr_rt_entries += 1;
new = kzalloc(struct_size(new, mshv_girq_info_tbl, nr_rt_entries),
GFP_KERNEL_ACCOUNT);
if (!new)
return -ENOMEM;
new->num_rt_entries = nr_rt_entries;
for (i = 0; i < numents; i++) {
struct mshv_guest_irq_ent *girq;
girq = &new->mshv_girq_info_tbl[ue[i].gsi];
/*
* Allow only one to one mapping between GSI and MSI routing.
*/
if (girq->guest_irq_num != 0) {
r = -EINVAL;
goto out;
}
girq->guest_irq_num = ue[i].gsi;
girq->girq_addr_lo = ue[i].address_lo;
girq->girq_addr_hi = ue[i].address_hi;
girq->girq_irq_data = ue[i].data;
girq->girq_entry_valid = true;
}
swap_routes:
mutex_lock(&partition->pt_irq_lock);
old = rcu_dereference_protected(partition->pt_girq_tbl, 1);
rcu_assign_pointer(partition->pt_girq_tbl, new);
mshv_irqfd_routing_update(partition);
mutex_unlock(&partition->pt_irq_lock);
synchronize_srcu_expedited(&partition->pt_irq_srcu);
new = old;
out:
kfree(new);
return r;
}
/* vm is going away, kfree the irq routing table */
void mshv_free_routing_table(struct mshv_partition *partition)
{
struct mshv_girq_routing_table *rt =
rcu_access_pointer(partition->pt_girq_tbl);
kfree(rt);
}
struct mshv_guest_irq_ent
mshv_ret_girq_entry(struct mshv_partition *partition, u32 irqnum)
{
struct mshv_guest_irq_ent entry = { 0 };
struct mshv_girq_routing_table *girq_tbl;
girq_tbl = srcu_dereference_check(partition->pt_girq_tbl,
&partition->pt_irq_srcu,
lockdep_is_held(&partition->pt_irq_lock));
if (!girq_tbl || irqnum >= girq_tbl->num_rt_entries) {
/*
* Premature register_irqfd, setting valid_entry = 0
* would ignore this entry anyway
*/
entry.guest_irq_num = irqnum;
return entry;
}
return girq_tbl->mshv_girq_info_tbl[irqnum];
}
void mshv_copy_girq_info(struct mshv_guest_irq_ent *ent,
struct mshv_lapic_irq *lirq)
{
memset(lirq, 0, sizeof(*lirq));
if (!ent || !ent->girq_entry_valid)
return;
lirq->lapic_vector = ent->girq_irq_data & 0xFF;
lirq->lapic_apic_id = (ent->girq_addr_lo >> 12) & 0xFF;
lirq->lapic_control.interrupt_type = (ent->girq_irq_data & 0x700) >> 8;
lirq->lapic_control.level_triggered = (ent->girq_irq_data >> 15) & 0x1;
lirq->lapic_control.logical_dest_mode = (ent->girq_addr_lo >> 2) & 0x1;
}
+83
View File
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/idr.h>
#include <asm/mshyperv.h>
#include "mshv.h"
#include "mshv_root.h"
/*
* Ports and connections are hypervisor struct used for inter-partition
* communication. Port represents the source and connection represents
* the destination. Partitions are responsible for managing the port and
* connection ids.
*
*/
#define PORTID_MIN 1
#define PORTID_MAX INT_MAX
static DEFINE_IDR(port_table_idr);
void
mshv_port_table_fini(void)
{
struct port_table_info *port_info;
unsigned long i, tmp;
idr_lock(&port_table_idr);
if (!idr_is_empty(&port_table_idr)) {
idr_for_each_entry_ul(&port_table_idr, port_info, tmp, i) {
port_info = idr_remove(&port_table_idr, i);
kfree_rcu(port_info, portbl_rcu);
}
}
idr_unlock(&port_table_idr);
}
int
mshv_portid_alloc(struct port_table_info *info)
{
int ret = 0;
idr_lock(&port_table_idr);
ret = idr_alloc(&port_table_idr, info, PORTID_MIN,
PORTID_MAX, GFP_KERNEL);
idr_unlock(&port_table_idr);
return ret;
}
void
mshv_portid_free(int port_id)
{
struct port_table_info *info;
idr_lock(&port_table_idr);
info = idr_remove(&port_table_idr, port_id);
WARN_ON(!info);
idr_unlock(&port_table_idr);
synchronize_rcu();
kfree(info);
}
int
mshv_portid_lookup(int port_id, struct port_table_info *info)
{
struct port_table_info *_info;
int ret = -ENOENT;
rcu_read_lock();
_info = idr_find(&port_table_idr, port_id);
rcu_read_unlock();
if (_info) {
*info = *_info;
ret = 0;
}
return ret;
}
+311
View File
@@ -0,0 +1,311 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023, Microsoft Corporation.
*/
#ifndef _MSHV_ROOT_H_
#define _MSHV_ROOT_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 <linux/hashtable.h>
#include <linux/dev_printk.h>
#include <linux/build_bug.h>
#include <uapi/linux/mshv.h>
/*
* Hypervisor must be between these version numbers (inclusive)
* to guarantee compatibility
*/
#define MSHV_HV_MIN_VERSION (27744)
#define MSHV_HV_MAX_VERSION (27751)
static_assert(HV_HYP_PAGE_SIZE == MSHV_HV_PAGE_SIZE);
#define MSHV_MAX_VPS 256
#define MSHV_PARTITIONS_HASH_BITS 9
#define MSHV_PIN_PAGES_BATCH_SIZE (0x10000000ULL / HV_HYP_PAGE_SIZE)
struct mshv_vp {
u32 vp_index;
struct mshv_partition *vp_partition;
struct mutex vp_mutex;
struct hv_vp_register_page *vp_register_page;
struct hv_message *vp_intercept_msg_page;
void *vp_ghcb_page;
struct hv_stats_page *vp_stats_pages[2];
struct {
atomic64_t vp_signaled_count;
struct {
u64 intercept_suspend: 1;
u64 root_sched_blocked: 1; /* root scheduler only */
u64 root_sched_dispatched: 1; /* root scheduler only */
u64 reserved: 61;
} flags;
unsigned int kicked_by_hv;
wait_queue_head_t vp_suspend_queue;
} run;
};
#define vp_fmt(fmt) "p%lluvp%u: " fmt
#define vp_devprintk(level, v, fmt, ...) \
do { \
const struct mshv_vp *__vp = (v); \
const struct mshv_partition *__pt = __vp->vp_partition; \
dev_##level(__pt->pt_module_dev, vp_fmt(fmt), __pt->pt_id, \
__vp->vp_index, ##__VA_ARGS__); \
} while (0)
#define vp_emerg(v, fmt, ...) vp_devprintk(emerg, v, fmt, ##__VA_ARGS__)
#define vp_crit(v, fmt, ...) vp_devprintk(crit, v, fmt, ##__VA_ARGS__)
#define vp_alert(v, fmt, ...) vp_devprintk(alert, v, fmt, ##__VA_ARGS__)
#define vp_err(v, fmt, ...) vp_devprintk(err, v, fmt, ##__VA_ARGS__)
#define vp_warn(v, fmt, ...) vp_devprintk(warn, v, fmt, ##__VA_ARGS__)
#define vp_notice(v, fmt, ...) vp_devprintk(notice, v, fmt, ##__VA_ARGS__)
#define vp_info(v, fmt, ...) vp_devprintk(info, v, fmt, ##__VA_ARGS__)
#define vp_dbg(v, fmt, ...) vp_devprintk(dbg, v, fmt, ##__VA_ARGS__)
struct mshv_mem_region {
struct hlist_node hnode;
u64 nr_pages;
u64 start_gfn;
u64 start_uaddr;
u32 hv_map_flags;
struct {
u64 large_pages: 1; /* 2MiB */
u64 range_pinned: 1;
u64 reserved: 62;
} flags;
struct mshv_partition *partition;
struct page *pages[];
};
struct mshv_irq_ack_notifier {
struct hlist_node link;
unsigned int irq_ack_gsi;
void (*irq_acked)(struct mshv_irq_ack_notifier *mian);
};
struct mshv_partition {
struct device *pt_module_dev;
struct hlist_node pt_hnode;
u64 pt_id;
refcount_t pt_ref_count;
struct mutex pt_mutex;
struct hlist_head pt_mem_regions; // not ordered
u32 pt_vp_count;
struct mshv_vp *pt_vp_array[MSHV_MAX_VPS];
struct mutex pt_irq_lock;
struct srcu_struct pt_irq_srcu;
struct hlist_head irq_ack_notifier_list;
struct hlist_head pt_devices;
/*
* MSHV does not support more than one async hypercall in flight
* for a single partition. Thus, it is okay to define per partition
* async hypercall status.
*/
struct completion async_hypercall;
u64 async_hypercall_status;
spinlock_t pt_irqfds_lock;
struct hlist_head pt_irqfds_list;
struct mutex irqfds_resampler_lock;
struct hlist_head irqfds_resampler_list;
struct hlist_head ioeventfds_list;
struct mshv_girq_routing_table __rcu *pt_girq_tbl;
u64 isolation_type;
bool import_completed;
bool pt_initialized;
};
#define pt_fmt(fmt) "p%llu: " fmt
#define pt_devprintk(level, p, fmt, ...) \
do { \
const struct mshv_partition *__pt = (p); \
dev_##level(__pt->pt_module_dev, pt_fmt(fmt), __pt->pt_id, \
##__VA_ARGS__); \
} while (0)
#define pt_emerg(p, fmt, ...) pt_devprintk(emerg, p, fmt, ##__VA_ARGS__)
#define pt_crit(p, fmt, ...) pt_devprintk(crit, p, fmt, ##__VA_ARGS__)
#define pt_alert(p, fmt, ...) pt_devprintk(alert, p, fmt, ##__VA_ARGS__)
#define pt_err(p, fmt, ...) pt_devprintk(err, p, fmt, ##__VA_ARGS__)
#define pt_warn(p, fmt, ...) pt_devprintk(warn, p, fmt, ##__VA_ARGS__)
#define pt_notice(p, fmt, ...) pt_devprintk(notice, p, fmt, ##__VA_ARGS__)
#define pt_info(p, fmt, ...) pt_devprintk(info, p, fmt, ##__VA_ARGS__)
#define pt_dbg(p, fmt, ...) pt_devprintk(dbg, p, fmt, ##__VA_ARGS__)
struct mshv_lapic_irq {
u32 lapic_vector;
u64 lapic_apic_id;
union hv_interrupt_control lapic_control;
};
#define MSHV_MAX_GUEST_IRQS 4096
/* representation of one guest irq entry, either msi or legacy */
struct mshv_guest_irq_ent {
u32 girq_entry_valid; /* vfio looks at this */
u32 guest_irq_num; /* a unique number for each irq */
u32 girq_addr_lo; /* guest irq msi address info */
u32 girq_addr_hi;
u32 girq_irq_data; /* idt vector in some cases */
};
struct mshv_girq_routing_table {
u32 num_rt_entries;
struct mshv_guest_irq_ent mshv_girq_info_tbl[];
};
struct hv_synic_pages {
struct hv_message_page *synic_message_page;
struct hv_synic_event_flags_page *synic_event_flags_page;
struct hv_synic_event_ring_page *synic_event_ring_page;
};
struct mshv_root {
struct hv_synic_pages __percpu *synic_pages;
spinlock_t pt_ht_lock;
DECLARE_HASHTABLE(pt_htable, MSHV_PARTITIONS_HASH_BITS);
};
/*
* Callback for doorbell events.
* NOTE: This is called in interrupt context. Callback
* should defer slow and sleeping logic to later.
*/
typedef void (*doorbell_cb_t) (int doorbell_id, void *);
/*
* port table information
*/
struct port_table_info {
struct rcu_head portbl_rcu;
enum hv_port_type hv_port_type;
union {
struct {
u64 reserved[2];
} hv_port_message;
struct {
u64 reserved[2];
} hv_port_event;
struct {
u64 reserved[2];
} hv_port_monitor;
struct {
doorbell_cb_t doorbell_cb;
void *data;
} hv_port_doorbell;
};
};
int mshv_update_routing_table(struct mshv_partition *partition,
const struct mshv_user_irq_entry *entries,
unsigned int numents);
void mshv_free_routing_table(struct mshv_partition *partition);
struct mshv_guest_irq_ent mshv_ret_girq_entry(struct mshv_partition *partition,
u32 irq_num);
void mshv_copy_girq_info(struct mshv_guest_irq_ent *src_irq,
struct mshv_lapic_irq *dest_irq);
void mshv_irqfd_routing_update(struct mshv_partition *partition);
void mshv_port_table_fini(void);
int mshv_portid_alloc(struct port_table_info *info);
int mshv_portid_lookup(int port_id, struct port_table_info *info);
void mshv_portid_free(int port_id);
int mshv_register_doorbell(u64 partition_id, doorbell_cb_t doorbell_cb,
void *data, u64 gpa, u64 val, u64 flags);
void mshv_unregister_doorbell(u64 partition_id, int doorbell_portid);
void mshv_isr(void);
int mshv_synic_init(unsigned int cpu);
int mshv_synic_cleanup(unsigned int cpu);
static inline bool mshv_partition_encrypted(struct mshv_partition *partition)
{
return partition->isolation_type == HV_PARTITION_ISOLATION_TYPE_SNP;
}
struct mshv_partition *mshv_partition_get(struct mshv_partition *partition);
void mshv_partition_put(struct mshv_partition *partition);
struct mshv_partition *mshv_partition_find(u64 partition_id) __must_hold(RCU);
/* 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_mmio_pages(u64 partition_id, u64 gfn, u64 mmio_spa, u64 numpgs);
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_delete_vp(u64 partition_id, u32 vp_index);
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);
int hv_call_get_gpa_access_states(u64 partition_id, u32 count, u64 gpa_base_pfn,
union hv_gpa_page_access_state_flags state_flags,
int *written_total,
union hv_gpa_page_access_state *states);
int hv_call_get_vp_state(u32 vp_index, u64 partition_id,
struct hv_vp_state_data state_data,
/* 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,
/* Choose between pages and bytes */
struct hv_vp_state_data state_data, u64 page_count,
struct page **pages, u32 num_bytes, u8 *bytes);
int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
union hv_input_vtl input_vtl,
struct page **state_page);
int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
union hv_input_vtl input_vtl);
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);
int hv_call_map_stat_page(enum hv_stats_object_type type,
const union hv_stats_object_identity *identity,
void **addr);
int hv_call_unmap_stat_page(enum hv_stats_object_type type,
const union hv_stats_object_identity *identity);
int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
u64 page_struct_count, u32 host_access,
u32 flags, u8 acquire);
extern struct mshv_root mshv_root;
extern enum hv_scheduler_type hv_scheduler_type;
extern u8 * __percpu *hv_synic_eventring_tail;
#endif /* _MSHV_ROOT_H_ */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+291
View File
@@ -0,0 +1,291 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* Userspace interfaces for /dev/mshv* devices and derived fds
*
* This file is divided into sections containing data structures and IOCTLs for
* a particular set of related devices or derived file descriptors.
*
* The IOCTL definitions are at the end of each section. They are grouped by
* device/fd, so that new IOCTLs can easily be added with a monotonically
* increasing number.
*/
#ifndef _UAPI_LINUX_MSHV_H
#define _UAPI_LINUX_MSHV_H
#include <linux/types.h>
#define MSHV_IOCTL 0xB8
/*
*******************************************
* Entry point to main VMM APIs: /dev/mshv *
*******************************************
*/
enum {
MSHV_PT_BIT_LAPIC,
MSHV_PT_BIT_X2APIC,
MSHV_PT_BIT_GPA_SUPER_PAGES,
MSHV_PT_BIT_COUNT,
};
#define MSHV_PT_FLAGS_MASK ((1 << MSHV_PT_BIT_COUNT) - 1)
enum {
MSHV_PT_ISOLATION_NONE,
MSHV_PT_ISOLATION_COUNT,
};
/**
* struct mshv_create_partition - arguments for MSHV_CREATE_PARTITION
* @pt_flags: Bitmask of 1 << MSHV_PT_BIT_*
* @pt_isolation: MSHV_PT_ISOLATION_*
*
* Returns a file descriptor to act as a handle to a guest partition.
* At this point the partition is not yet initialized in the hypervisor.
* Some operations must be done with the partition in this state, e.g. setting
* so-called "early" partition properties. The partition can then be
* initialized with MSHV_INITIALIZE_PARTITION.
*/
struct mshv_create_partition {
__u64 pt_flags;
__u64 pt_isolation;
};
/* /dev/mshv */
#define MSHV_CREATE_PARTITION _IOW(MSHV_IOCTL, 0x00, struct mshv_create_partition)
/*
************************
* Child partition APIs *
************************
*/
struct mshv_create_vp {
__u32 vp_index;
};
enum {
MSHV_SET_MEM_BIT_WRITABLE,
MSHV_SET_MEM_BIT_EXECUTABLE,
MSHV_SET_MEM_BIT_UNMAP,
MSHV_SET_MEM_BIT_COUNT
};
#define MSHV_SET_MEM_FLAGS_MASK ((1 << MSHV_SET_MEM_BIT_COUNT) - 1)
/* The hypervisor's "native" page size */
#define MSHV_HV_PAGE_SIZE 0x1000
/**
* struct mshv_user_mem_region - arguments for MSHV_SET_GUEST_MEMORY
* @size: Size of the memory region (bytes). Must be aligned to
* MSHV_HV_PAGE_SIZE
* @guest_pfn: Base guest page number to map
* @userspace_addr: Base address of userspace memory. Must be aligned to
* MSHV_HV_PAGE_SIZE
* @flags: Bitmask of 1 << MSHV_SET_MEM_BIT_*. If (1 << MSHV_SET_MEM_BIT_UNMAP)
* is set, ignore other bits.
* @rsvd: MBZ
*
* Map or unmap a region of userspace memory to Guest Physical Addresses (GPA).
* Mappings can't overlap in GPA space or userspace.
* To unmap, these fields must match an existing mapping.
*/
struct mshv_user_mem_region {
__u64 size;
__u64 guest_pfn;
__u64 userspace_addr;
__u8 flags;
__u8 rsvd[7];
};
enum {
MSHV_IRQFD_BIT_DEASSIGN,
MSHV_IRQFD_BIT_RESAMPLE,
MSHV_IRQFD_BIT_COUNT,
};
#define MSHV_IRQFD_FLAGS_MASK ((1 << MSHV_IRQFD_BIT_COUNT) - 1)
struct mshv_user_irqfd {
__s32 fd;
__s32 resamplefd;
__u32 gsi;
__u32 flags;
};
enum {
MSHV_IOEVENTFD_BIT_DATAMATCH,
MSHV_IOEVENTFD_BIT_PIO,
MSHV_IOEVENTFD_BIT_DEASSIGN,
MSHV_IOEVENTFD_BIT_COUNT,
};
#define MSHV_IOEVENTFD_FLAGS_MASK ((1 << MSHV_IOEVENTFD_BIT_COUNT) - 1)
struct mshv_user_ioeventfd {
__u64 datamatch;
__u64 addr; /* legal pio/mmio address */
__u32 len; /* 1, 2, 4, or 8 bytes */
__s32 fd;
__u32 flags;
__u8 rsvd[4];
};
struct mshv_user_irq_entry {
__u32 gsi;
__u32 address_lo;
__u32 address_hi;
__u32 data;
};
struct mshv_user_irq_table {
__u32 nr;
__u32 rsvd; /* MBZ */
struct mshv_user_irq_entry entries[];
};
enum {
MSHV_GPAP_ACCESS_TYPE_ACCESSED,
MSHV_GPAP_ACCESS_TYPE_DIRTY,
MSHV_GPAP_ACCESS_TYPE_COUNT /* Count of enum members */
};
enum {
MSHV_GPAP_ACCESS_OP_NOOP,
MSHV_GPAP_ACCESS_OP_CLEAR,
MSHV_GPAP_ACCESS_OP_SET,
MSHV_GPAP_ACCESS_OP_COUNT /* Count of enum members */
};
/**
* struct mshv_gpap_access_bitmap - arguments for MSHV_GET_GPAP_ACCESS_BITMAP
* @access_type: MSHV_GPAP_ACCESS_TYPE_* - The type of access to record in the
* bitmap
* @access_op: MSHV_GPAP_ACCESS_OP_* - Allows an optional clear or set of all
* the access states in the range, after retrieving the current
* states.
* @rsvd: MBZ
* @page_count: Number of pages
* @gpap_base: Base gpa page number
* @bitmap_ptr: Output buffer for bitmap, at least (page_count + 7) / 8 bytes
*
* Retrieve a bitmap of either ACCESSED or DIRTY bits for a given range of guest
* memory, and optionally clear or set the bits.
*/
struct mshv_gpap_access_bitmap {
__u8 access_type;
__u8 access_op;
__u8 rsvd[6];
__u64 page_count;
__u64 gpap_base;
__u64 bitmap_ptr;
};
/**
* struct mshv_root_hvcall - arguments for MSHV_ROOT_HVCALL
* @code: Hypercall code (HVCALL_*)
* @reps: in: Rep count ('repcount')
* out: Reps completed ('repcomp'). MBZ unless rep hvcall
* @in_sz: Size of input incl rep data. <= MSHV_HV_PAGE_SIZE
* @out_sz: Size of output buffer. <= MSHV_HV_PAGE_SIZE. MBZ if out_ptr is 0
* @status: in: MBZ
* out: HV_STATUS_* from hypercall
* @rsvd: MBZ
* @in_ptr: Input data buffer (struct hv_input_*). If used with partition or
* vp fd, partition id field is populated by kernel.
* @out_ptr: Output data buffer (optional)
*/
struct mshv_root_hvcall {
__u16 code;
__u16 reps;
__u16 in_sz;
__u16 out_sz;
__u16 status;
__u8 rsvd[6];
__u64 in_ptr;
__u64 out_ptr;
};
/* Partition fds created with MSHV_CREATE_PARTITION */
#define MSHV_INITIALIZE_PARTITION _IO(MSHV_IOCTL, 0x00)
#define MSHV_CREATE_VP _IOW(MSHV_IOCTL, 0x01, struct mshv_create_vp)
#define MSHV_SET_GUEST_MEMORY _IOW(MSHV_IOCTL, 0x02, struct mshv_user_mem_region)
#define MSHV_IRQFD _IOW(MSHV_IOCTL, 0x03, struct mshv_user_irqfd)
#define MSHV_IOEVENTFD _IOW(MSHV_IOCTL, 0x04, struct mshv_user_ioeventfd)
#define MSHV_SET_MSI_ROUTING _IOW(MSHV_IOCTL, 0x05, struct mshv_user_irq_table)
#define MSHV_GET_GPAP_ACCESS_BITMAP _IOWR(MSHV_IOCTL, 0x06, struct mshv_gpap_access_bitmap)
/* Generic hypercall */
#define MSHV_ROOT_HVCALL _IOWR(MSHV_IOCTL, 0x07, struct mshv_root_hvcall)
/*
********************************
* VP APIs for child partitions *
********************************
*/
#define MSHV_RUN_VP_BUF_SZ 256
/*
* VP state pages may be mapped to userspace via mmap().
* To specify which state page, use MSHV_VP_MMAP_OFFSET_ values multiplied by
* the system page size.
* e.g.
* long page_size = sysconf(_SC_PAGE_SIZE);
* void *reg_page = mmap(NULL, MSHV_HV_PAGE_SIZE, PROT_READ|PROT_WRITE,
* MAP_SHARED, vp_fd,
* MSHV_VP_MMAP_OFFSET_REGISTERS * page_size);
*/
enum {
MSHV_VP_MMAP_OFFSET_REGISTERS,
MSHV_VP_MMAP_OFFSET_INTERCEPT_MESSAGE,
MSHV_VP_MMAP_OFFSET_GHCB,
MSHV_VP_MMAP_OFFSET_COUNT
};
/**
* struct mshv_run_vp - argument for MSHV_RUN_VP
* @msg_buf: On success, the intercept message is copied here. It can be
* interpreted using the relevant hypervisor definitions.
*/
struct mshv_run_vp {
__u8 msg_buf[MSHV_RUN_VP_BUF_SZ];
};
enum {
MSHV_VP_STATE_LAPIC, /* Local interrupt controller state (either arch) */
MSHV_VP_STATE_XSAVE, /* XSAVE data in compacted form (x86_64) */
MSHV_VP_STATE_SIMP,
MSHV_VP_STATE_SIEFP,
MSHV_VP_STATE_SYNTHETIC_TIMERS,
MSHV_VP_STATE_COUNT,
};
/**
* struct mshv_get_set_vp_state - arguments for MSHV_[GET,SET]_VP_STATE
* @type: MSHV_VP_STATE_*
* @rsvd: MBZ
* @buf_sz: in: 4k page-aligned size of buffer
* out: Actual size of data (on EINVAL, check this to see if buffer
* was too small)
* @buf_ptr: 4k page-aligned data buffer
*/
struct mshv_get_set_vp_state {
__u8 type;
__u8 rsvd[3];
__u32 buf_sz;
__u64 buf_ptr;
};
/* VP fds created with MSHV_CREATE_VP */
#define MSHV_RUN_VP _IOR(MSHV_IOCTL, 0x00, struct mshv_run_vp)
#define MSHV_GET_VP_STATE _IOWR(MSHV_IOCTL, 0x01, struct mshv_get_set_vp_state)
#define MSHV_SET_VP_STATE _IOWR(MSHV_IOCTL, 0x02, struct mshv_get_set_vp_state)
/*
* Generic hypercall
* Defined above in partition IOCTLs, avoid redefining it here
* #define MSHV_ROOT_HVCALL _IOWR(MSHV_IOCTL, 0x07, struct mshv_root_hvcall)
*/
#endif