* [PATCH v3 1/7] mshv: add SEV-SNP UAPI definitions
2026-08-25 4:04 [PATCH v3 0/7] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
@ 2026-08-25 4:04 ` Wei Hu
2026-08-25 4:04 ` [PATCH v3 2/7] mshv: add SEV-SNP PSP request hypercall Wei Hu
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Wei Hu @ 2026-08-25 4:04 UTC (permalink / raw)
To: linux-hyperv
Cc: linux-kernel, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li
From: Wei Liu <wei.liu@kernel.org>
Add the MSHV UAPI and Microsoft Hypervisor ABI definitions required to
create and manage SEV-SNP child partitions.
Use fixed-size ioctl headers with aligned userspace pointers for
variable page arrays and the launch-finish payload. The host-access
interface names its input as guest GPAs, while isolated import takes
guest PFNs and an explicit page-size value. Keep the public launch
structures naturally aligned and represent the SNP policy as a raw
64-bit ABI value rather than compiler-dependent bitfields.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
include/hyperv/hvgdk_mini.h | 19 ++++++++
include/hyperv/hvhdk.h | 45 +++++++++++++++++
include/hyperv/hvhdk_mini.h | 53 ++++++++++++++++++++
include/uapi/linux/mshv.h | 96 +++++++++++++++++++++++++++++++++++++
4 files changed, 213 insertions(+)
diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h
index 6a4e8b9d570f..a53c0f59994a 100644
--- a/include/hyperv/hvgdk_mini.h
+++ b/include/hyperv/hvgdk_mini.h
@@ -497,6 +497,9 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_ASSIST_PAGE */
#define HVCALL_UNMAP_VP_STATE_PAGE 0x00e2
#define HVCALL_GET_VP_STATE 0x00e3
#define HVCALL_SET_VP_STATE 0x00e4
+#define HVCALL_IMPORT_ISOLATED_PAGES 0x00ef
+#define HVCALL_COMPLETE_ISOLATED_IMPORT 0x00f1
+#define HVCALL_ISSUE_SNP_PSP_GUEST_REQUEST 0x00f2
#define HVCALL_GET_VP_CPUID_VALUES 0x00f4
#define HVCALL_GET_PARTITION_PROPERTY_EX 0x0101
#define HVCALL_MMIO_READ 0x0106
@@ -1065,6 +1068,9 @@ enum hv_register_name {
HV_REGISTER_VSM_PARTITION_CONFIG = 0x000D0007,
#if defined(CONFIG_X86)
+ /* AMD SEV-SNP configuration register */
+ HV_X64_REGISTER_SEV_CONTROL = 0x00090040,
+
/* X64 Debug Registers */
HV_X64_REGISTER_DR0 = 0x00050000,
HV_X64_REGISTER_DR1 = 0x00050001,
@@ -1267,6 +1273,18 @@ union hv_x64_pending_interruption_register {
} __packed;
};
+#ifdef CONFIG_X86
+#define HV_SUPPORTS_SEV_SNP_GUESTS
+union hv_x64_register_sev_control {
+ u64 as_uint64;
+ struct {
+ u64 enable_encrypted_state : 1;
+ u64 reserved_z : 11;
+ u64 vmsa_gpa_page_number : 52;
+ } __packed;
+};
+#endif
+
union hv_register_value {
struct hv_u128 reg128;
u64 reg64;
@@ -1286,6 +1304,7 @@ union hv_register_value {
#ifdef CONFIG_X86
union hv_x64_interrupt_state_register interrupt_state;
union hv_x64_pending_interruption_register pending_interruption;
+ union hv_x64_register_sev_control sev_control;
#endif
union hv_arm64_pending_synthetic_exception_event pending_synthetic_exception_event;
};
diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h
index 0c89c62c9706..691ac495a095 100644
--- a/include/hyperv/hvhdk.h
+++ b/include/hyperv/hvhdk.h
@@ -953,4 +953,49 @@ struct hv_input_modify_sparse_spa_page_host_access {
#define HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE 0x4
#define HV_MODIFY_SPA_PAGE_HOST_ACCESS_HUGE_PAGE 0x8
+enum hv_isolated_page_type {
+ HV_ISOLATED_PAGE_TYPE_NORMAL,
+ HV_ISOLATED_PAGE_TYPE_VMSA,
+ HV_ISOLATED_PAGE_TYPE_ZERO,
+ HV_ISOLATED_PAGE_TYPE_UNMEASURED,
+ HV_ISOLATED_PAGE_TYPE_SECRETS,
+ HV_ISOLATED_PAGE_TYPE_CPUID,
+ HV_ISOLATED_PAGE_TYPE_COUNT
+};
+
+enum hv_isolated_page_size {
+ HV_ISOLATED_PAGE_SIZE_4KB,
+ HV_ISOLATED_PAGE_SIZE_2MB
+};
+
+struct hv_input_import_isolated_pages {
+ u64 partition_id;
+ u32 page_type;
+ u32 page_size;
+ u64 page_number[];
+} __packed;
+
+struct hv_input_issue_psp_guest_request {
+ u64 partition_id;
+ u64 request_page;
+ u64 response_page;
+} __packed;
+
+enum hv_partition_isolation_state {
+ HV_PARTITION_ISOLATION_INVALID,
+ HV_PARTITION_ISOLATION_INSECURE_CLEAN,
+ HV_PARTITION_ISOLATION_INSECURE_DIRTY,
+ HV_PARTITION_ISOLATION_SECURE,
+ HV_PARTITION_ISOLATION_SECURE_DIRTY,
+ HV_PARTITION_ISOLATION_SECURE_TERMINATING,
+};
+
+union hv_partition_isolation_control {
+ u64 as_uint64;
+ struct {
+ u64 runnable : 1;
+ u64 reserved_z : 63;
+ } __packed;
+};
+
#endif /* _HV_HVHDK_H */
diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h
index 035ba20870f7..dd84085c0aff 100644
--- a/include/hyperv/hvhdk_mini.h
+++ b/include/hyperv/hvhdk_mini.h
@@ -94,6 +94,8 @@ enum hv_partition_property_code {
/* Resource properties */
HV_PARTITION_PROPERTY_GPA_PAGE_ACCESS_TRACKING = 0x00050005,
+ HV_PARTITION_PROPERTY_ISOLATION_STATE = 0x0005000c,
+ HV_PARTITION_PROPERTY_ISOLATION_CONTROL = 0x0005000d,
HV_PARTITION_PROPERTY_UNIMPLEMENTED_MSR_ACTION = 0x00050017,
/* Compatibility properties */
@@ -145,6 +147,57 @@ enum hv_snp_status {
HV_SNP_STATUS_PSP_INIT_LATE_FAILED = 10,
};
+union hv_snp_guest_policy {
+ struct {
+ u64 minor_version : 8;
+ u64 major_version : 8;
+ u64 smt_allowed : 1;
+ u64 vmpls_required : 1;
+ u64 migration_agent_allowed : 1;
+ u64 debug_allowed : 1;
+ u64 reserved : 44;
+ } __packed;
+ u64 as_uint64;
+};
+
+struct hv_snp_id_block {
+ u8 launch_digest[48];
+ u8 family_id[16];
+ u8 image_id[16];
+ u32 version;
+ u32 guest_svn;
+ union hv_snp_guest_policy policy;
+} __packed;
+
+struct hv_snp_id_auth_info {
+ u32 id_key_algorithm;
+ u32 auth_key_algorithm;
+ u8 reserved0[56];
+ u8 id_block_signature[512];
+ u8 id_key[1028];
+ u8 reserved1[60];
+ u8 id_key_signature[512];
+ u8 author_key[1028];
+} __packed;
+
+struct hv_psp_launch_finish_data {
+ struct hv_snp_id_block id_block;
+ struct hv_snp_id_auth_info id_auth_info;
+ u8 host_data[32];
+ u8 id_block_enabled;
+ u8 author_key_enabled;
+} __packed;
+
+union hv_partition_complete_isolated_import_data {
+ u64 reserved;
+ struct hv_psp_launch_finish_data psp_parameters;
+} __packed;
+
+struct hv_input_complete_isolated_import {
+ u64 partition_id;
+ union hv_partition_complete_isolated_import_data import_data;
+} __packed;
+
enum hv_system_property {
/* Add more values when needed */
HV_SYSTEM_PROPERTY_SLEEP_STATE = 3,
diff --git a/include/uapi/linux/mshv.h b/include/uapi/linux/mshv.h
index 32ff92b6342b..5e338c605072 100644
--- a/include/uapi/linux/mshv.h
+++ b/include/uapi/linux/mshv.h
@@ -36,6 +36,7 @@ enum {
enum {
MSHV_PT_ISOLATION_NONE,
+ MSHV_PT_ISOLATION_SNP,
MSHV_PT_ISOLATION_COUNT,
};
@@ -219,6 +220,96 @@ struct mshv_gpap_access_bitmap {
__u64 bitmap_ptr;
};
+enum {
+ MSHV_GPA_HOST_ACCESS_BIT_ACQUIRE,
+ MSHV_GPA_HOST_ACCESS_BIT_READABLE,
+ MSHV_GPA_HOST_ACCESS_BIT_WRITABLE,
+ MSHV_GPA_HOST_ACCESS_BIT_LARGE_PAGE,
+ MSHV_GPA_HOST_ACCESS_BIT_COUNT
+};
+
+#define MSHV_GPA_HOST_ACCESS_FLAGS_MASK \
+ ((1 << MSHV_GPA_HOST_ACCESS_BIT_COUNT) - 1)
+
+struct mshv_modify_gpa_host_access {
+ __u8 flags;
+ __u8 rsvd[7];
+ __u64 page_count;
+ __aligned_u64 guest_gpas;
+};
+
+enum {
+ MSHV_ISOLATED_PAGE_NORMAL,
+ MSHV_ISOLATED_PAGE_VMSA,
+ MSHV_ISOLATED_PAGE_ZERO,
+ MSHV_ISOLATED_PAGE_UNMEASURED,
+ MSHV_ISOLATED_PAGE_SECRETS,
+ MSHV_ISOLATED_PAGE_CPUID,
+ MSHV_ISOLATED_PAGE_COUNT
+};
+
+enum {
+ MSHV_ISOLATED_PAGE_SIZE_4KB,
+ MSHV_ISOLATED_PAGE_SIZE_2MB,
+ MSHV_ISOLATED_PAGE_SIZE_COUNT
+};
+
+struct mshv_import_isolated_pages {
+ __u8 page_type;
+ __u8 page_size;
+ __u8 rsvd[6];
+ __u64 page_count;
+ __aligned_u64 guest_pfns;
+};
+
+struct mshv_issue_psp_guest_request {
+ __u64 req_gpa;
+ __u64 rsp_gpa;
+};
+
+struct mshv_sev_snp_ap_create {
+ __u64 vp_id;
+ __u64 vmsa_gpa;
+};
+
+struct mshv_snp_id_block {
+ __u8 launch_digest[48];
+ __u8 family_id[16];
+ __u8 image_id[16];
+ __u32 version;
+ __u32 guest_svn;
+ __u64 policy;
+};
+
+struct mshv_snp_id_auth_info {
+ __u32 id_key_algorithm;
+ __u32 auth_key_algorithm;
+ __u8 reserved0[56];
+ __u8 id_block_signature[512];
+ __u8 id_key[1028];
+ __u8 reserved1[60];
+ __u8 id_key_signature[512];
+ __u8 author_key[1028];
+};
+
+/*
+ * This structure describes the PSP launch-finish wire data. The two trailing
+ * reserved bytes make the userspace object naturally aligned; they must be 0
+ * and are not copied into the packed Hyper-V ABI structure.
+ */
+struct mshv_psp_launch_finish_data {
+ struct mshv_snp_id_block id_block;
+ struct mshv_snp_id_auth_info id_auth_info;
+ __u8 host_data[32];
+ __u8 id_block_enabled;
+ __u8 author_key_enabled;
+ __u8 reserved[2];
+};
+
+struct mshv_complete_isolated_import {
+ __aligned_u64 psp_parameters;
+};
+
/**
* struct mshv_root_hvcall - arguments for MSHV_ROOT_HVCALL
* @code: Hypercall code (HVCALL_*)
@@ -254,6 +345,11 @@ struct mshv_root_hvcall {
#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)
+#define MSHV_MODIFY_GPA_HOST_ACCESS _IOW(MSHV_IOCTL, 0x09, struct mshv_modify_gpa_host_access)
+#define MSHV_IMPORT_ISOLATED_PAGES _IOW(MSHV_IOCTL, 0x0A, struct mshv_import_isolated_pages)
+#define MSHV_COMPLETE_ISOLATED_IMPORT _IOW(MSHV_IOCTL, 0xF4, struct mshv_complete_isolated_import)
+#define MSHV_ISSUE_PSP_GUEST_REQUEST _IOW(MSHV_IOCTL, 0xF5, struct mshv_issue_psp_guest_request)
+#define MSHV_SEV_SNP_AP_CREATE _IOW(MSHV_IOCTL, 0xF6, struct mshv_sev_snp_ap_create)
/*
********************************
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v3 3/7] mshv: add SEV-SNP isolated page hypercalls
2026-08-25 4:04 [PATCH v3 0/7] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
2026-08-25 4:04 ` [PATCH v3 1/7] mshv: add SEV-SNP UAPI definitions Wei Hu
2026-08-25 4:04 ` [PATCH v3 2/7] mshv: add SEV-SNP PSP request hypercall Wei Hu
@ 2026-08-25 4:04 ` Wei Hu
2026-08-25 4:04 ` [PATCH v3 4/7] mshv: wire SEV-SNP partition ioctls Wei Hu
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Wei Hu @ 2026-08-25 4:04 UTC (permalink / raw)
To: linux-hyperv
Cc: linux-kernel, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li
From: Wei Liu <wei.liu@kernel.org>
Add helpers for importing isolated pages and completing isolated
imports. These operations can complete asynchronously, so preserve the
rep completion count from the initial pending status unless the final
completion supplies a replacement.
Reject zero or oversized progress, report partial progress on failure,
and yield between successful batches so a large userspace request cannot
monopolize a CPU.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
drivers/hv/mshv_root.h | 12 ++++
drivers/hv/mshv_root_hv_call.c | 104 +++++++++++++++++++++++++++++++++
2 files changed, 116 insertions(+)
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 8abb080938fd..69226179cfbf 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -379,6 +379,18 @@ void mshv_region_movable_fini(struct mshv_mem_region *region);
bool mshv_region_movable_init(struct mshv_mem_region *region);
#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+int hv_call_import_isolated_pages(u64 partition_id, u64 *pages,
+ u64 num_pages,
+ enum hv_isolated_page_type page_type,
+ enum hv_isolated_page_size page_size,
+ void (*completion_handler)(void *data,
+ u64 *status),
+ void *completion_data);
+int hv_call_complete_isolated_import(u64 partition_id,
+ union hv_partition_complete_isolated_import_data *import_data,
+ void (*completion_handler)(void *data,
+ u64 *status),
+ void *completion_data);
int hv_call_issue_psp_guest_request(u64 partition_id, u64 req_pfn,
u64 rsp_pfn,
void (*completion_handler)(void *data,
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index 48abe338c06f..ad5d5089a015 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -14,6 +14,10 @@
#include "mshv_root.h"
+#define HV_ISOLATED_PAGE_BATCH_SIZE \
+ ((HV_HYP_PAGE_SIZE - sizeof(struct hv_input_import_isolated_pages)) / \
+ sizeof(u64))
+
/* Determined empirically */
#define HV_INIT_PARTITION_DEPOSIT_PAGES 208
#define HV_MAP_GPA_DEPOSIT_PAGES 256
@@ -1010,6 +1014,106 @@ int hv_unmap_stats_page(enum hv_stats_object_type type,
}
#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+int hv_call_import_isolated_pages(u64 partition_id, u64 *pages,
+ u64 num_pages,
+ enum hv_isolated_page_type page_type,
+ enum hv_isolated_page_size page_size,
+ void (*completion_handler)(void *data,
+ u64 *status),
+ void *completion_data)
+{
+ struct hv_input_import_isolated_pages *input;
+ unsigned long remaining = num_pages;
+ unsigned long flags;
+ u64 *gpa = pages;
+ u64 completed;
+ u64 status;
+ int rep_count;
+
+ if (!num_pages)
+ return -EINVAL;
+
+ if (!completion_handler) {
+ pr_err("%s: missing completion handler, page_type=%u\n",
+ __func__, page_type);
+ return -EINVAL;
+ }
+
+ while (remaining) {
+ rep_count = min_t(unsigned long, remaining,
+ HV_ISOLATED_PAGE_BATCH_SIZE);
+
+ local_irq_save(flags);
+ input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+ memset(input, 0, sizeof(*input));
+ input->partition_id = partition_id;
+ input->page_type = page_type;
+ input->page_size = page_size;
+ memcpy(input->page_number, gpa, rep_count * sizeof(*gpa));
+ status = hv_do_rep_hypercall(HVCALL_IMPORT_ISOLATED_PAGES,
+ rep_count, 0, input, NULL);
+ local_irq_restore(flags);
+
+ completed = hv_repcomp(status);
+ if (hv_result(status) == HV_STATUS_CALL_PENDING) {
+ completion_handler(completion_data, &status);
+ if (hv_repcomp(status))
+ completed = hv_repcomp(status);
+ }
+
+ if (!hv_result_success(status)) {
+ pr_err("%s: completed %llu of %llu, %s\n", __func__,
+ num_pages - remaining +
+ min_t(u64, completed, rep_count), num_pages,
+ hv_result_to_string(status));
+ return hv_result_to_errno(status);
+ }
+ if (!completed || completed > rep_count)
+ return -EIO;
+
+ gpa += completed;
+ remaining -= completed;
+ cond_resched();
+ }
+
+ return 0;
+}
+
+int hv_call_complete_isolated_import(u64 partition_id,
+ union hv_partition_complete_isolated_import_data *import_data,
+ void (*completion_handler)(void *data,
+ u64 *status),
+ void *completion_data)
+{
+ struct hv_input_complete_isolated_import *input;
+ unsigned long flags;
+ u64 status;
+
+ if (!completion_handler) {
+ pr_err("%s: missing completion handler\n", __func__);
+ return -EINVAL;
+ }
+
+ local_irq_save(flags);
+ input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+ memset(input, 0, sizeof(*input));
+ input->partition_id = partition_id;
+ input->import_data = *import_data;
+ status = hv_do_hypercall(HVCALL_COMPLETE_ISOLATED_IMPORT, input, NULL);
+ local_irq_restore(flags);
+
+ if (hv_result(status) == HV_STATUS_CALL_PENDING)
+ completion_handler(completion_data, &status);
+
+ if (!hv_result_success(status)) {
+ pr_err("%s: status=%s partition_id=%llu\n", __func__,
+ hv_result_to_string(status), partition_id);
+ return hv_result_to_errno(status);
+ }
+
+ return 0;
+}
+
int hv_call_issue_psp_guest_request(u64 partition_id, u64 req_pfn,
u64 rsp_pfn,
void (*completion_handler)(void *data,
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v3 4/7] mshv: wire SEV-SNP partition ioctls
2026-08-25 4:04 [PATCH v3 0/7] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
` (2 preceding siblings ...)
2026-08-25 4:04 ` [PATCH v3 3/7] mshv: add SEV-SNP isolated page hypercalls Wei Hu
@ 2026-08-25 4:04 ` Wei Hu
2026-08-25 4:04 ` [PATCH v3 5/7] mshv: detect and report SEV-SNP support at init Wei Hu
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Wei Hu @ 2026-08-25 4:04 UTC (permalink / raw)
To: linux-hyperv
Cc: linux-kernel, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li
From: Wei Liu <wei.liu@kernel.org>
Wire the SEV-SNP partition ioctls for GPA host-access changes,
isolated-page import, PSP guest requests, AP creation, and ordered SNP
partition teardown.
Process userspace page arrays in bounded batches. The host-access ioctl
validates page-aligned guest GPAs and physical contiguity for large-page
requests; isolated import accepts guest PFNs and an explicit page size.
Translate the userspace launch-finish structure field by field into the
packed hypervisor ABI.
Keep encrypted memory regions pinned until GPA mappings are removed, the
partition reaches INSECURE_DIRTY, and host access has been restored. If
any safety-critical teardown phase fails, retain the partition and a
module reference instead of returning inaccessible pages to the host
allocator.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
drivers/hv/mshv_regions.c | 10 +-
drivers/hv/mshv_root.h | 9 +
drivers/hv/mshv_root_hv_call.c | 47 ++-
drivers/hv/mshv_root_main.c | 627 ++++++++++++++++++++++++++++++++-
include/hyperv/hvgdk_mini.h | 12 +
5 files changed, 691 insertions(+), 14 deletions(-)
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index 6d65e5b42152..ed93bae0fa15 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -337,7 +337,7 @@ static int mshv_region_chunk_unmap(struct mshv_mem_region *region,
page_count, flags);
}
-static int mshv_region_unmap(struct mshv_mem_region *region)
+int mshv_region_unmap(struct mshv_mem_region *region)
{
return mshv_region_process_range(region, 0,
0, region->nr_pages,
@@ -354,17 +354,19 @@ static void mshv_region_destroy(struct kref *ref)
if (region->mreg_type == MSHV_REGION_TYPE_MEM_MOVABLE)
mshv_region_movable_fini(region);
- if (mshv_partition_encrypted(partition)) {
+ if (mshv_partition_encrypted(partition) &&
+ !partition->snp_host_access_restored) {
ret = mshv_region_share(region);
if (ret) {
pt_err(partition,
- "Failed to regain access to memory, unpinning user pages will fail and crash the host error: %d\n",
+ "Failed to regain access to memory, retaining pinned region: %d\n",
ret);
return;
}
}
- mshv_region_unmap(region);
+ if (!partition->snp_regions_unmapped)
+ mshv_region_unmap(region);
mshv_region_invalidate(region);
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 69226179cfbf..fe89c16d622e 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -139,6 +139,9 @@ struct mshv_partition {
struct mshv_girq_routing_table __rcu *pt_girq_tbl;
u64 isolation_type;
bool import_completed;
+ bool snp_regions_unmapped;
+ bool snp_host_access_restored;
+ bool teardown_quarantined;
bool pt_initialized;
#if IS_ENABLED(CONFIG_DEBUG_FS)
struct dentry *pt_stats_dentry;
@@ -370,6 +373,7 @@ struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,
int mshv_region_share(struct mshv_mem_region *region);
int mshv_region_unshare(struct mshv_mem_region *region);
int mshv_region_map(struct mshv_mem_region *region);
+int mshv_region_unmap(struct mshv_mem_region *region);
void mshv_region_invalidate(struct mshv_mem_region *region);
int mshv_region_pin(struct mshv_mem_region *region);
void mshv_region_put(struct mshv_mem_region *region);
@@ -378,6 +382,11 @@ bool mshv_region_handle_gfn_fault(struct mshv_mem_region *region, u64 gfn);
void mshv_region_movable_fini(struct mshv_mem_region *region);
bool mshv_region_movable_init(struct mshv_mem_region *region);
+int hv_call_set_partition_property(u64 partition_id, u64 property_code,
+ u64 property_value,
+ void (*completion_handler)(void *, u64 *),
+ void *completion_data);
+
#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
int hv_call_import_isolated_pages(u64 partition_id, u64 *pages,
u64 num_pages,
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index ad5d5089a015..9791df06e137 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -1014,6 +1014,41 @@ int hv_unmap_stats_page(enum hv_stats_object_type type,
}
#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+int hv_call_set_partition_property(u64 partition_id, u64 property_code,
+ u64 property_value,
+ void (*completion_handler)(void *, u64 *),
+ void *completion_data)
+{
+ u64 status;
+ unsigned long flags;
+ struct hv_input_set_partition_property *input;
+
+ if (!completion_handler) {
+ pr_err("%s: Missing completion handler\n", __func__);
+ return -EINVAL;
+ }
+
+ local_irq_save(flags);
+ input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+
+ memset(input, 0, sizeof(*input));
+ input->partition_id = partition_id;
+ input->property_code = property_code;
+ input->property_value = property_value;
+ status = hv_do_hypercall(HVCALL_SET_PARTITION_PROPERTY, input, NULL);
+ local_irq_restore(flags);
+
+ if (unlikely(hv_result(status) == HV_STATUS_CALL_PENDING))
+ completion_handler(completion_data, &status);
+
+ if (!hv_result_success(status)) {
+ pr_err("%s: %s\n", __func__, hv_result_to_string(status));
+ return hv_result_to_errno(status);
+ }
+
+ return 0;
+}
+
int hv_call_import_isolated_pages(u64 partition_id, u64 *pages,
u64 num_pages,
enum hv_isolated_page_type page_type,
@@ -1194,8 +1229,10 @@ int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
for (i = 0; i < rep_count; i++) {
u64 index = (done + i) << large_shift;
- if (index >= page_struct_count)
+ if (index >= page_struct_count) {
+ local_irq_restore(irq_flags);
return -EINVAL;
+ }
input_page->spa_page_list[i] =
page_to_pfn(pages[index]);
@@ -1207,8 +1244,14 @@ int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
completed = hv_repcomp(status);
- if (!hv_result_success(status))
+ if (!hv_result_success(status)) {
+ pr_err("%s: completed %d + %lu of %llu pages: %s\n",
+ __func__, done, completed, page_count,
+ hv_result_to_string(status));
return hv_result_to_errno(status);
+ }
+ if (!completed || completed > rep_count)
+ return -EIO;
done += completed;
}
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index cc2cfce2aefd..d946b4592d5b 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -65,6 +65,7 @@ static int mshv_vp_mmap(struct file *file, struct vm_area_struct *vma);
static vm_fault_t mshv_vp_fault(struct vm_fault *vmf);
static int mshv_init_async_handler(struct mshv_partition *partition);
static void mshv_async_hvcall_handler(void *data, u64 *status);
+static void mshv_quarantine_partition(struct mshv_partition *partition);
static const union hv_input_vtl input_vtl_zero;
static const union hv_input_vtl input_vtl_normal = {
@@ -641,6 +642,42 @@ mshv_partition_region_by_gfn_get(struct mshv_partition *p, u64 gfn)
return region;
}
+static int mshv_gpfns_to_pages(struct mshv_partition *partition,
+ const u64 *gpfns, u64 page_count,
+ struct page **pages)
+{
+ struct mshv_mem_region *region;
+ u64 i;
+ int ret = 0;
+
+ for (i = 0; i < page_count; i++) {
+ u64 gfn = gpfns[i];
+ u64 offset;
+
+ region = mshv_partition_region_by_gfn_get(partition, gfn);
+ if (!region) {
+ pt_err(partition, "Failed to find region for GFN %#llx\n",
+ gfn);
+ return -ERANGE;
+ }
+
+ offset = gfn - region->start_gfn;
+ mutex_lock(®ion->mreg_mutex);
+ if (offset >= region->nr_pages || !region->mreg_pages[offset]) {
+ ret = -EFAULT;
+ mutex_unlock(®ion->mreg_mutex);
+ mshv_region_put(region);
+ return ret;
+ }
+
+ pages[i] = region->mreg_pages[offset];
+ mutex_unlock(®ion->mreg_mutex);
+ mshv_region_put(region);
+ }
+
+ return 0;
+}
+
/**
* mshv_handle_gpa_intercept - Handle GPA (Guest Physical Address) intercepts.
* @vp: Pointer to the virtual processor structure.
@@ -1213,12 +1250,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
static int mshv_init_async_handler(struct mshv_partition *partition)
{
- if (completion_done(&partition->async_hypercall)) {
- pt_err(partition,
- "Cannot issue async hypercall while another one in progress!\n");
- return -EPERM;
- }
-
+ /* Partition ioctls are serialized by pt_mutex. */
reinit_completion(&partition->async_hypercall);
return 0;
}
@@ -1434,6 +1466,8 @@ mshv_unmap_user_memory(struct mshv_partition *partition,
if (!(mem->flags & BIT(MSHV_SET_MEM_BIT_UNMAP)))
return -EINVAL;
+ if (partition->pt_initialized && mshv_partition_encrypted(partition))
+ return -EBUSY;
spin_lock(&partition->pt_mem_regions_lock);
@@ -1648,6 +1682,409 @@ mshv_partition_ioctl_initialize(struct mshv_partition *partition)
return ret;
}
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+static int set_sev_control_register(struct mshv_vp *vp,
+ u64 enable_encrypted_state,
+ u64 vmsa_gpa_page_number)
+{
+ struct hv_register_assoc sev_control = {
+ .name = HV_X64_REGISTER_SEV_CONTROL,
+ };
+
+ sev_control.value.sev_control.enable_encrypted_state =
+ enable_encrypted_state;
+ sev_control.value.sev_control.vmsa_gpa_page_number =
+ vmsa_gpa_page_number;
+
+ return mshv_set_vp_registers(vp->vp_index, vp->vp_partition->pt_id,
+ 1, &sev_control);
+}
+
+static long
+mshv_partition_ioctl_sev_snp_ap_create(struct mshv_partition *partition,
+ void __user *user_args)
+{
+ struct hv_register_assoc internal_activity = {
+ .name = HV_REGISTER_INTERNAL_ACTIVITY_STATE,
+ .value.internal_activity.as_uint64 = 0,
+ };
+ struct mshv_sev_snp_ap_create req;
+ struct mshv_vp *vp;
+ long ret;
+
+ if (copy_from_user(&req, user_args, sizeof(req)))
+ return -EFAULT;
+
+ if (req.vp_id >= MSHV_MAX_VPS)
+ return -EINVAL;
+
+ vp = partition->pt_vp_array[req.vp_id];
+ if (!vp)
+ return -EINVAL;
+
+ ret = set_sev_control_register(vp, 1, HVPFN_DOWN(req.vmsa_gpa));
+ if (ret) {
+ vp_err(vp, "Failed to set SEV control register\n");
+ return ret;
+ }
+
+ ret = mshv_set_vp_registers(vp->vp_index, vp->vp_partition->pt_id, 1,
+ &internal_activity);
+ if (ret)
+ vp_err(vp, "Failed to set internal activity\n");
+
+ return ret;
+}
+
+static bool mshv_pages_are_contiguous_2m(struct page **pages,
+ u64 page_count)
+{
+ const u64 pages_per_large_page =
+ 1ULL << (HV_HYP_LARGE_PAGE_SHIFT - HV_HYP_PAGE_SHIFT);
+ u64 i, j;
+
+ if (!IS_ALIGNED(page_count, pages_per_large_page))
+ return false;
+
+ for (i = 0; i < page_count; i += pages_per_large_page) {
+ u64 base_pfn = page_to_pfn(pages[i]);
+
+ if (!IS_ALIGNED(base_pfn, pages_per_large_page))
+ return false;
+ for (j = 1; j < pages_per_large_page; j++)
+ if (page_to_pfn(pages[i + j]) != base_pfn + j)
+ return false;
+ }
+
+ return true;
+}
+
+#define MSHV_USER_PAGE_BATCH_SIZE 4096
+
+static long
+mshv_partition_ioctl_modify_gpa_host_access(struct mshv_partition *partition,
+ void __user *user_args)
+{
+ struct mshv_modify_gpa_host_access args;
+ struct page **pages;
+ u64 __user *guest_gpas;
+ u64 *gpfns;
+ u64 offset = 0;
+ u32 host_access = 0;
+ u32 flags = 0;
+ bool acquire;
+ long ret = 0;
+
+ if (copy_from_user(&args, user_args, sizeof(args)))
+ return -EFAULT;
+
+ if ((args.flags & ~MSHV_GPA_HOST_ACCESS_FLAGS_MASK) ||
+ mshv_field_nonzero(args, rsvd) || !args.page_count ||
+ !args.guest_gpas)
+ return -EINVAL;
+ if ((args.flags & BIT(MSHV_GPA_HOST_ACCESS_BIT_LARGE_PAGE)) &&
+ !IS_ALIGNED(args.page_count,
+ 1ULL << (HV_HYP_LARGE_PAGE_SHIFT - HV_HYP_PAGE_SHIFT)))
+ return -EINVAL;
+
+ guest_gpas = u64_to_user_ptr(args.guest_gpas);
+ gpfns = kvmalloc_array(MSHV_USER_PAGE_BATCH_SIZE, sizeof(*gpfns),
+ GFP_KERNEL);
+ pages = kvmalloc_array(MSHV_USER_PAGE_BATCH_SIZE, sizeof(*pages),
+ GFP_KERNEL);
+ if (!gpfns || !pages) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (args.flags & BIT(MSHV_GPA_HOST_ACCESS_BIT_READABLE))
+ host_access |= HV_MAP_GPA_READABLE;
+ if (args.flags & BIT(MSHV_GPA_HOST_ACCESS_BIT_WRITABLE))
+ host_access |= HV_MAP_GPA_WRITABLE;
+ if (args.flags & BIT(MSHV_GPA_HOST_ACCESS_BIT_LARGE_PAGE))
+ flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
+ acquire = args.flags & BIT(MSHV_GPA_HOST_ACCESS_BIT_ACQUIRE);
+
+ while (offset < args.page_count) {
+ u64 count = min_t(u64, args.page_count - offset,
+ MSHV_USER_PAGE_BATCH_SIZE);
+ u64 i;
+
+ if (copy_from_user(gpfns, guest_gpas + offset,
+ array_size(count, sizeof(*gpfns)))) {
+ ret = -EFAULT;
+ break;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (!IS_ALIGNED(gpfns[i], MSHV_HV_PAGE_SIZE)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ gpfns[i] = HVPFN_DOWN(gpfns[i]);
+ }
+
+ ret = mshv_gpfns_to_pages(partition, gpfns, count, pages);
+ if (ret)
+ break;
+
+ if (flags & HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE) {
+ if (!mshv_pages_are_contiguous_2m(pages, count)) {
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ ret = hv_call_modify_spa_host_access(partition->pt_id, pages,
+ count, host_access, flags,
+ acquire);
+ if (ret)
+ break;
+
+ offset += count;
+ cond_resched();
+ }
+
+out:
+ kvfree(pages);
+ kvfree(gpfns);
+ return ret;
+}
+
+static long
+mshv_partition_ioctl_import_isolated_pages(struct mshv_partition *partition,
+ void __user *user_args)
+{
+ struct mshv_import_isolated_pages args;
+ u64 __user *guest_pfns;
+ u64 *pages;
+ u64 offset = 0;
+ long ret = 0;
+
+ if (copy_from_user(&args, user_args, sizeof(args)))
+ return -EFAULT;
+
+ if (args.page_type >= MSHV_ISOLATED_PAGE_COUNT ||
+ args.page_size >= MSHV_ISOLATED_PAGE_SIZE_COUNT ||
+ mshv_field_nonzero(args, rsvd) || !args.page_count ||
+ !args.guest_pfns)
+ return -EINVAL;
+
+ guest_pfns = u64_to_user_ptr(args.guest_pfns);
+ pages = kvmalloc_array(MSHV_USER_PAGE_BATCH_SIZE, sizeof(*pages),
+ GFP_KERNEL);
+ if (!pages)
+ return -ENOMEM;
+
+ while (offset < args.page_count) {
+ u64 count = min_t(u64, args.page_count - offset,
+ MSHV_USER_PAGE_BATCH_SIZE);
+
+ if (copy_from_user(pages, guest_pfns + offset,
+ array_size(count, sizeof(*pages)))) {
+ ret = -EFAULT;
+ break;
+ }
+ if (args.page_size == MSHV_ISOLATED_PAGE_SIZE_2MB) {
+ u64 i;
+
+ for (i = 0; i < count; i++) {
+ if (!IS_ALIGNED(pages[i],
+ 1ULL << (HV_HYP_LARGE_PAGE_SHIFT -
+ HV_HYP_PAGE_SHIFT))) {
+ ret = -EINVAL;
+ goto import_out;
+ }
+ }
+ }
+
+ ret = mshv_init_async_handler(partition);
+ if (ret)
+ break;
+
+ ret = hv_call_import_isolated_pages(partition->pt_id, pages, count,
+ args.page_type, args.page_size,
+ mshv_async_hvcall_handler,
+ partition);
+ if (ret)
+ break;
+
+ offset += count;
+ cond_resched();
+ }
+
+import_out:
+ kvfree(pages);
+ return ret;
+}
+
+static void
+mshv_copy_snp_id_auth_info(struct hv_snp_id_auth_info *dst,
+ const struct mshv_snp_id_auth_info *src)
+{
+ dst->id_key_algorithm = src->id_key_algorithm;
+ dst->auth_key_algorithm = src->auth_key_algorithm;
+ memcpy(dst->reserved0, src->reserved0, sizeof(dst->reserved0));
+ memcpy(dst->id_block_signature, src->id_block_signature,
+ sizeof(dst->id_block_signature));
+ memcpy(dst->id_key, src->id_key, sizeof(dst->id_key));
+ memcpy(dst->reserved1, src->reserved1, sizeof(dst->reserved1));
+ memcpy(dst->id_key_signature, src->id_key_signature,
+ sizeof(dst->id_key_signature));
+ memcpy(dst->author_key, src->author_key, sizeof(dst->author_key));
+}
+
+static long
+mshv_partition_ioctl_complete_isolated_import(struct mshv_partition *partition,
+ void __user *user_args)
+{
+ union hv_partition_complete_isolated_import_data *import_data;
+ struct mshv_psp_launch_finish_data *psp;
+ struct mshv_complete_isolated_import args;
+ long ret;
+
+ if (copy_from_user(&args, user_args, sizeof(args)))
+ return -EFAULT;
+ if (!args.psp_parameters)
+ return -EINVAL;
+
+ psp = memdup_user(u64_to_user_ptr(args.psp_parameters), sizeof(*psp));
+ if (IS_ERR(psp))
+ return PTR_ERR(psp);
+ if (memchr_inv(psp->reserved, 0, sizeof(psp->reserved))) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ import_data = kvzalloc(sizeof(*import_data), GFP_KERNEL);
+ if (!import_data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(import_data->psp_parameters.id_block.launch_digest,
+ psp->id_block.launch_digest,
+ sizeof(psp->id_block.launch_digest));
+ memcpy(import_data->psp_parameters.id_block.family_id,
+ psp->id_block.family_id, sizeof(psp->id_block.family_id));
+ memcpy(import_data->psp_parameters.id_block.image_id,
+ psp->id_block.image_id, sizeof(psp->id_block.image_id));
+ import_data->psp_parameters.id_block.version = psp->id_block.version;
+ import_data->psp_parameters.id_block.guest_svn = psp->id_block.guest_svn;
+ import_data->psp_parameters.id_block.policy.as_uint64 =
+ psp->id_block.policy;
+ mshv_copy_snp_id_auth_info(&import_data->psp_parameters.id_auth_info,
+ &psp->id_auth_info);
+ memcpy(import_data->psp_parameters.host_data, psp->host_data,
+ sizeof(psp->host_data));
+ import_data->psp_parameters.id_block_enabled = psp->id_block_enabled;
+ import_data->psp_parameters.author_key_enabled = psp->author_key_enabled;
+
+ ret = mshv_init_async_handler(partition);
+ if (!ret)
+ ret = hv_call_complete_isolated_import(partition->pt_id,
+ import_data,
+ mshv_async_hvcall_handler,
+ partition);
+ if (!ret)
+ partition->import_completed = true;
+
+ kvfree(import_data);
+out:
+ kfree(psp);
+ return ret;
+}
+
+static long
+mshv_partition_ioctl_issue_psp_guest_request(struct mshv_partition *partition,
+ void __user *user_args)
+{
+ struct mshv_issue_psp_guest_request req;
+ u32 host_access = HV_MAP_GPA_READABLE | HV_MAP_GPA_WRITABLE;
+ struct page *pages[2];
+ u64 gpfns[2];
+ long ret;
+
+ if (copy_from_user(&req, user_args, sizeof(req)))
+ return -EFAULT;
+
+ gpfns[0] = HVPFN_DOWN(req.req_gpa);
+ gpfns[1] = HVPFN_DOWN(req.rsp_gpa);
+
+ ret = mshv_gpfns_to_pages(partition, gpfns, ARRAY_SIZE(gpfns), pages);
+ if (ret)
+ return ret;
+
+ ret = hv_call_modify_spa_host_access(partition->pt_id, pages,
+ ARRAY_SIZE(pages), 0, 0, false);
+ if (ret)
+ goto restore_host_access;
+
+ ret = mshv_init_async_handler(partition);
+ if (ret)
+ goto restore_host_access;
+
+ ret = hv_call_issue_psp_guest_request(partition->pt_id,
+ HVPFN_DOWN(req.req_gpa),
+ HVPFN_DOWN(req.rsp_gpa),
+ mshv_async_hvcall_handler,
+ partition);
+ if (!ret)
+ return 0;
+
+restore_host_access:
+ {
+ int restore_ret;
+
+ restore_ret = hv_call_modify_spa_host_access(partition->pt_id,
+ pages, ARRAY_SIZE(pages),
+ host_access, 0, true);
+ if (restore_ret) {
+ pt_err(partition,
+ "Failed to restore PSP request page access: %d\n",
+ restore_ret);
+ mshv_quarantine_partition(partition);
+ if (!ret)
+ ret = restore_ret;
+ }
+ }
+ return ret;
+}
+
+static long mshv_partition_snp_ioctl(unsigned int ioctl,
+ struct mshv_partition *partition,
+ unsigned long arg)
+{
+ void __user *uarg = (void __user *)arg;
+
+ if (!mshv_partition_encrypted(partition)) {
+ pt_err(partition,
+ "Ioctl(%u) not supported for non SEV-SNP partition\n",
+ ioctl);
+ return -EOPNOTSUPP;
+ }
+
+ switch (ioctl) {
+ case MSHV_MODIFY_GPA_HOST_ACCESS:
+ return mshv_partition_ioctl_modify_gpa_host_access(partition,
+ uarg);
+ case MSHV_IMPORT_ISOLATED_PAGES:
+ return mshv_partition_ioctl_import_isolated_pages(partition,
+ uarg);
+ case MSHV_COMPLETE_ISOLATED_IMPORT:
+ return mshv_partition_ioctl_complete_isolated_import(partition,
+ uarg);
+ case MSHV_ISSUE_PSP_GUEST_REQUEST:
+ return mshv_partition_ioctl_issue_psp_guest_request(partition,
+ uarg);
+ case MSHV_SEV_SNP_AP_CREATE:
+ return mshv_partition_ioctl_sev_snp_ap_create(partition, uarg);
+ default:
+ return -ENOTTY;
+ }
+}
+#endif
+
static long
mshv_partition_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
{
@@ -1684,6 +2121,15 @@ mshv_partition_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
case MSHV_ROOT_HVCALL:
ret = mshv_ioctl_passthru_hvcall(partition, true, uarg);
break;
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+ case MSHV_MODIFY_GPA_HOST_ACCESS:
+ case MSHV_IMPORT_ISOLATED_PAGES:
+ case MSHV_COMPLETE_ISOLATED_IMPORT:
+ case MSHV_ISSUE_PSP_GUEST_REQUEST:
+ case MSHV_SEV_SNP_AP_CREATE:
+ ret = mshv_partition_snp_ioctl(ioctl, partition, arg);
+ break;
+#endif
default:
ret = -ENOTTY;
}
@@ -1795,6 +2241,79 @@ remove_partition(struct mshv_partition *partition)
synchronize_rcu();
}
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+static int destroy_snp_partition_state(struct mshv_partition *partition)
+{
+ struct hv_register_assoc explicit_suspend = {
+ .name = HV_REGISTER_EXPLICIT_SUSPEND,
+ .value.explicit_suspend.suspended = 1,
+ };
+ struct mshv_vp *vp;
+ int i, ret;
+
+ for (i = 0; i < MSHV_MAX_VPS; i++) {
+ vp = partition->pt_vp_array[i];
+ if (!vp)
+ continue;
+
+ ret = mshv_set_vp_registers(vp->vp_index,
+ vp->vp_partition->pt_id, 1,
+ &explicit_suspend);
+ if (ret) {
+ vp_err(vp, "Failed to set explicit suspend\n");
+ return ret;
+ }
+
+ ret = set_sev_control_register(vp, 0, 0);
+ if (ret) {
+ vp_err(vp, "Failed to clear SEV control register\n");
+ return ret;
+ }
+ }
+
+ if (partition->import_completed) {
+ union hv_partition_isolation_control isolation_control = {};
+
+ ret = mshv_init_async_handler(partition);
+ if (ret)
+ return ret;
+
+ ret = hv_call_set_partition_property(partition->pt_id,
+ HV_PARTITION_PROPERTY_ISOLATION_CONTROL,
+ isolation_control.as_uint64,
+ mshv_async_hvcall_handler, partition);
+ if (ret) {
+ pt_err(partition, "Failed to clear runnable bit\n");
+ return ret;
+ }
+ }
+
+ ret = mshv_init_async_handler(partition);
+ if (ret)
+ return ret;
+
+ ret = hv_call_set_partition_property(partition->pt_id,
+ HV_PARTITION_PROPERTY_ISOLATION_STATE,
+ HV_PARTITION_ISOLATION_INSECURE_DIRTY,
+ mshv_async_hvcall_handler, partition);
+ if (ret)
+ pt_err(partition,
+ "Failed to set isolation state to INSECURE_DIRTY\n");
+
+ return ret;
+}
+#endif
+
+static void mshv_quarantine_partition(struct mshv_partition *partition)
+{
+ if (partition->teardown_quarantined)
+ return;
+
+ /* Keep module text and the pinned inaccessible pages alive. */
+ __module_get(THIS_MODULE);
+ partition->teardown_quarantined = true;
+}
+
/*
* Tear down a partition and remove it from the list.
* Partition's refcount must be 0
@@ -1805,6 +2324,9 @@ static void destroy_partition(struct mshv_partition *partition)
struct mshv_mem_region *region;
struct hlist_node *n;
int i;
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+ int ret;
+#endif
if (refcount_read(&partition->pt_ref_count)) {
pt_err(partition,
@@ -1814,7 +2336,87 @@ static void destroy_partition(struct mshv_partition *partition)
trace_mshv_destroy_partition(partition->pt_id);
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+ if (!partition->pt_initialized && mshv_partition_encrypted(partition)) {
+ int region_ret;
+
+ ret = 0;
+ hlist_for_each_entry_safe(region, n,
+ &partition->pt_mem_regions, hnode) {
+ region_ret = mshv_region_share(region);
+ if (region_ret) {
+ pt_err(partition,
+ "Failed to restore host access at GFN %#llx: %d\n",
+ region->start_gfn, region_ret);
+ ret = region_ret;
+ }
+ }
+ if (ret) {
+ pt_err(partition,
+ "Retaining uninitialized partition with inaccessible SNP pages\n");
+ mshv_quarantine_partition(partition);
+ return;
+ }
+ partition->snp_host_access_restored = true;
+ }
+#endif
+
if (partition->pt_initialized) {
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+ if (mshv_partition_encrypted(partition)) {
+ int region_ret;
+
+ ret = 0;
+ hlist_for_each_entry_safe(region, n,
+ &partition->pt_mem_regions,
+ hnode) {
+ region_ret = mshv_region_unmap(region);
+ if (region_ret) {
+ pt_err(partition,
+ "Failed to unmap SNP region at GFN %#llx: %d\n",
+ region->start_gfn, region_ret);
+ ret = region_ret;
+ }
+ }
+ if (ret) {
+ pt_err(partition,
+ "Retaining partition after SNP region unmap failure\n");
+ mshv_quarantine_partition(partition);
+ return;
+ }
+
+ ret = destroy_snp_partition_state(partition);
+ if (ret) {
+ pt_err(partition,
+ "Failed to destroy SNP state: %d; retaining partition\n",
+ ret);
+ mshv_quarantine_partition(partition);
+ return;
+ }
+
+ ret = 0;
+ hlist_for_each_entry_safe(region, n,
+ &partition->pt_mem_regions,
+ hnode) {
+ region_ret = mshv_region_share(region);
+ if (region_ret) {
+ pt_err(partition,
+ "Failed to restore host access at GFN %#llx: %d\n",
+ region->start_gfn, region_ret);
+ ret = region_ret;
+ }
+ }
+ if (ret) {
+ pt_err(partition,
+ "Retaining partition with inaccessible SNP pages\n");
+ mshv_quarantine_partition(partition);
+ return;
+ }
+
+ partition->snp_regions_unmapped = true;
+ partition->snp_host_access_restored = true;
+ }
+#endif
/*
* We only need to drain signals for root scheduler. This should be
* done before removing the partition from the partition list.
@@ -1822,6 +2424,8 @@ static void destroy_partition(struct mshv_partition *partition)
if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
drain_all_vps(partition);
+ remove_partition(partition);
+
/* Remove vps */
for (i = 0; i < MSHV_MAX_VPS; ++i) {
vp = partition->pt_vp_array[i];
@@ -1868,10 +2472,10 @@ static void destroy_partition(struct mshv_partition *partition)
hv_call_finalize_partition(partition->pt_id);
partition->pt_initialized = false;
+ } else {
+ remove_partition(partition);
}
- remove_partition(partition);
-
hlist_for_each_entry_safe(region, n, &partition->pt_mem_regions,
hnode) {
hlist_del(®ion->hnode);
@@ -1883,6 +2487,8 @@ static void destroy_partition(struct mshv_partition *partition)
hv_call_delete_partition(partition->pt_id);
mshv_free_routing_table(partition);
+ if (partition->teardown_quarantined)
+ module_put(THIS_MODULE);
kfree(partition);
}
@@ -2039,6 +2645,9 @@ static long mshv_ioctl_process_pt_flags(void __user *user_arg, u64 *pt_flags,
case MSHV_PT_ISOLATION_NONE:
isol_props->isolation_type = HV_PARTITION_ISOLATION_TYPE_NONE;
break;
+ case MSHV_PT_ISOLATION_SNP:
+ isol_props->isolation_type = HV_PARTITION_ISOLATION_TYPE_SNP;
+ break;
}
return 0;
@@ -2074,6 +2683,8 @@ mshv_ioctl_create_partition(void __user *user_arg, struct device *module_dev)
mutex_init(&partition->pt_irq_lock);
init_completion(&partition->async_hypercall);
+ INIT_DELAYED_WORK(&partition->destroy_work,
+ mshv_partition_destroy_work);
INIT_HLIST_HEAD(&partition->irq_ack_notifier_list);
diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h
index a53c0f59994a..5553d5c918a7 100644
--- a/include/hyperv/hvgdk_mini.h
+++ b/include/hyperv/hvgdk_mini.h
@@ -975,6 +975,7 @@ enum hv_register_name {
HV_REGISTER_EXPLICIT_SUSPEND = 0x00000000,
HV_REGISTER_INTERCEPT_SUSPEND = 0x00000001,
HV_REGISTER_DISPATCH_SUSPEND = 0x00000003,
+ HV_REGISTER_INTERNAL_ACTIVITY_STATE = 0x00000004,
/* Version - 128-bit result same as CPUID 0x40000002 */
HV_REGISTER_HYPERVISOR_VERSION = 0x00000100,
@@ -1250,6 +1251,16 @@ union hv_arm64_pending_synthetic_exception_event {
} __packed;
};
+union hv_internal_activity_register {
+ u64 as_uint64;
+ struct {
+ u64 startup_suspend : 1;
+ u64 halt_suspend : 1;
+ u64 idle_suspend : 1;
+ u64 rsvd_z : 61;
+ } __packed;
+};
+
union hv_x64_interrupt_state_register {
u64 as_uint64;
struct {
@@ -1297,6 +1308,7 @@ union hv_register_value {
union hv_explicit_suspend_register explicit_suspend;
union hv_intercept_suspend_register intercept_suspend;
union hv_dispatch_suspend_register dispatch_suspend;
+ union hv_internal_activity_register internal_activity;
#ifdef CONFIG_ARM64
union hv_arm64_interrupt_state_register interrupt_state;
union hv_arm64_pending_interruption_register pending_interruption;
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread