* [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions
@ 2026-08-07 13:51 Wei Hu
2026-08-07 13:51 ` [PATCH v1 01/13] mshv: add SEV-SNP UAPI definitions Wei Hu
` (12 more replies)
0 siblings, 13 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 UTC (permalink / raw)
To: linux-hyperv
Cc: linux-kernel, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Wei Hu
From: Wei Hu <weh@microsoft.com>
This series adds support for creating and managing AMD SEV-SNP
confidential virtual machines through the Microsoft Hypervisor root
partition driver.
The series adds the required Hyper-V ABI and MSHV UAPI definitions,
hypercall helpers, partition ioctls, capability discovery, processor
feature handling, memory teardown, and crash-dump support.
It also adds the EFI HvLoader handoff required to boot Linux as an MSHV
root partition and corrects SynIC register ownership when the root
partition is itself running nested.
The patches are based on Linux v7.2-rc5.
Testing:
- Built the complete 13-patch series on x86_64.
- Booted Linux as an MSHV root partition with SEV-SNP available.
- Created and booted single-vCPU and four-vCPU SEV-SNP guests with
Cloud Hypervisor.
- Verified SNP memory teardown and panic-time page unlock for kdump.
Functional testing on the development host used an additional local
nested-VMBus interrupt workaround required by that nested environment.
That workaround is not part of this series.
Wei Hu (4):
hyperv: fix hv_input_get_system_property layout for SNP status
mshv: unlock SNP pages on panic for crashdump collection
hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader)
mshv: set up own SynIC registers on a nested root partition
Wei Liu (9):
mshv: add SEV-SNP UAPI definitions
mshv: add SEV-SNP PSP request hypercall
mshv: add SEV-SNP isolated page hypercalls
mshv: wire SEV-SNP partition ioctls
mshv: detect and report SEV-SNP support at init
mshv: default to safe partition CPU features
mshv: accept partial CPU feature banks
mshv: define full processor and xsave feature masks
mshv: unmap SNP memory before state teardown
MAINTAINERS | 2 +
arch/x86/hyperv/hv_init.c | 3 +
arch/x86/include/asm/mshyperv.h | 1 +
arch/x86/include/uapi/asm/setup_data.h | 3 +-
arch/x86/kernel/cpu/mshyperv.c | 80 ++-
drivers/firmware/efi/libstub/Makefile | 7 +
drivers/firmware/efi/libstub/arm64-efi-mshv.c | 41 ++
.../firmware/efi/libstub/efi-mshv-common.c | 136 ++++
drivers/firmware/efi/libstub/efi-mshv.h | 118 +++
drivers/firmware/efi/libstub/x86-efi-mshv.c | 202 ++++++
drivers/firmware/efi/libstub/x86-stub.c | 18 +-
drivers/hv/hv_common.c | 82 +++
drivers/hv/mshv_regions.c | 2 +-
drivers/hv/mshv_root.h | 26 +
drivers/hv/mshv_root_hv_call.c | 171 +++++
drivers/hv/mshv_root_main.c | 679 +++++++++++++++++-
drivers/hv/mshv_synic.c | 16 +-
include/asm-generic/mshyperv.h | 7 +
include/hyperv/hvgdk_mini.h | 31 +
include/hyperv/hvhdk.h | 124 +++-
include/hyperv/hvhdk_mini.h | 56 +-
include/linux/efi.h | 1 +
include/uapi/linux/mshv.h | 58 +-
23 files changed, 1834 insertions(+), 30 deletions(-)
create mode 100644 drivers/firmware/efi/libstub/arm64-efi-mshv.c
create mode 100644 drivers/firmware/efi/libstub/efi-mshv-common.c
create mode 100644 drivers/firmware/efi/libstub/efi-mshv.h
create mode 100644 drivers/firmware/efi/libstub/x86-efi-mshv.c
base-commit: f5098b6bae761e346ebcd9da7f95622c04733cff
--
2.43.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v1 01/13] mshv: add SEV-SNP UAPI definitions
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
@ 2026-08-07 13:51 ` Wei Hu
2026-08-07 13:51 ` [PATCH v1 02/13] mshv: add SEV-SNP PSP request hypercall Wei Hu
` (11 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 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 Hyper-V ABI definitions needed by the SEV-SNP
partition ioctls: the SNP isolation type, GPA host-access and
isolated-page request structures, PSP request structures, and the SEV
control register definitions.
Also add the SNP launch-data definitions (guest policy, ID block, ID
auth info and launch-finish data) that form the isolated-import
completion parameters, along with the SNP support-status enumeration.
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 | 55 +++++++++++++++++++++++++++++++++++++
4 files changed, 172 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 b4cb2fa26e9b..db83fedce243 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..4e7f3038f0c6 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,55 @@ 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;
+ __u64 guest_pfns[];
+};
+
+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
+};
+
+struct mshv_import_isolated_pages {
+ __u8 page_type;
+ __u8 rsvd[7];
+ __u64 page_count;
+ __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_complete_isolated_import {
+ union hv_partition_complete_isolated_import_data import_data;
+};
+
/**
* struct mshv_root_hvcall - arguments for MSHV_ROOT_HVCALL
* @code: Hypercall code (HVCALL_*)
@@ -254,6 +304,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] 14+ messages in thread
* [PATCH v1 02/13] mshv: add SEV-SNP PSP request hypercall
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
2026-08-07 13:51 ` [PATCH v1 01/13] mshv: add SEV-SNP UAPI definitions Wei Hu
@ 2026-08-07 13:51 ` Wei Hu
2026-08-07 13:51 ` [PATCH v1 03/13] mshv: add SEV-SNP isolated page hypercalls Wei Hu
` (10 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 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 a helper for HVCALL_ISSUE_SNP_PSP_GUEST_REQUEST so MSHV can
issue asynchronous PSP guest requests on behalf of encrypted
partitions.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
drivers/hv/mshv_root.h | 8 +++++++
drivers/hv/mshv_root_hv_call.c | 39 ++++++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+)
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 1f086dcb7aa1..8abb080938fd 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -378,4 +378,12 @@ 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);
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+int hv_call_issue_psp_guest_request(u64 partition_id, u64 req_pfn,
+ u64 rsp_pfn,
+ void (*completion_handler)(void *data,
+ u64 *status),
+ void *completion_data);
+#endif
+
#endif /* _MSHV_ROOT_H_ */
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index cb55d4d4be2e..48abe338c06f 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -1009,6 +1009,45 @@ int hv_unmap_stats_page(enum hv_stats_object_type type,
return ret;
}
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+int hv_call_issue_psp_guest_request(u64 partition_id, u64 req_pfn,
+ u64 rsp_pfn,
+ void (*completion_handler)(void *data,
+ u64 *status),
+ void *completion_data)
+{
+ struct hv_input_issue_psp_guest_request *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->request_page = req_pfn;
+ input->response_page = rsp_pfn;
+ status = hv_do_hypercall(HVCALL_ISSUE_SNP_PSP_GUEST_REQUEST, 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;
+}
+#endif
+
int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
u64 page_struct_count, u32 host_access,
u32 flags, u8 acquire)
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 03/13] mshv: add SEV-SNP isolated page hypercalls
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
2026-08-07 13:51 ` [PATCH v1 01/13] mshv: add SEV-SNP UAPI definitions Wei Hu
2026-08-07 13:51 ` [PATCH v1 02/13] mshv: add SEV-SNP PSP request hypercall Wei Hu
@ 2026-08-07 13:51 ` Wei Hu
2026-08-07 13:51 ` [PATCH v1 04/13] mshv: wire SEV-SNP partition ioctls Wei Hu
` (9 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 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 are used by SEV-SNP guest launch flows to transfer
isolated page state to the hypervisor.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
drivers/hv/mshv_root.h | 12 +++++
drivers/hv/mshv_root_hv_call.c | 97 ++++++++++++++++++++++++++++++++++
2 files changed, 109 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..9ddc8dd73da7 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,99 @@ 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_result_success(status)) {
+ pr_err("%s: completed %llu of %llu, %s\n", __func__,
+ num_pages - remaining, num_pages,
+ hv_result_to_string(status));
+ return hv_result_to_errno(status);
+ }
+
+ gpa += completed;
+ remaining -= completed;
+ }
+
+ 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] 14+ messages in thread
* [PATCH v1 04/13] mshv: wire SEV-SNP partition ioctls
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
` (2 preceding siblings ...)
2026-08-07 13:51 ` [PATCH v1 03/13] mshv: add SEV-SNP isolated page hypercalls Wei Hu
@ 2026-08-07 13:51 ` Wei Hu
2026-08-07 13:51 ` [PATCH v1 05/13] hyperv: fix hv_input_get_system_property layout for SNP status Wei Hu
` (8 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 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 SNP partition
teardown.
The GPA host-access ioctl converts the guest addresses to GFNs before
issuing the hypercall. The PSP guest-request path restores host access
to the request/response pages if the hypercall fails, so a failed request
does not leave guest pages inaccessible to the host.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
drivers/hv/mshv_root.h | 5 +
drivers/hv/mshv_root_hv_call.c | 35 +++
drivers/hv/mshv_root_main.c | 391 +++++++++++++++++++++++++++++++++
include/hyperv/hvgdk_mini.h | 12 +
4 files changed, 443 insertions(+)
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 69226179cfbf..0e6d4ce25112 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -378,6 +378,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 9ddc8dd73da7..bf5636a9ef6a 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(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,
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 146726cc4e9b..3590a8ea24b5 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -115,6 +115,9 @@ static u16 mshv_passthru_hvcalls[] = {
HVCALL_GET_PARTITION_PROPERTY,
HVCALL_GET_PARTITION_PROPERTY_EX,
HVCALL_SET_PARTITION_PROPERTY,
+ HVCALL_ISSUE_SNP_PSP_GUEST_REQUEST,
+ HVCALL_COMPLETE_ISOLATED_IMPORT,
+ HVCALL_IMPORT_ISOLATED_PAGES,
HVCALL_INSTALL_INTERCEPT,
HVCALL_GET_VP_REGISTERS,
HVCALL_SET_VP_REGISTERS,
@@ -142,6 +145,8 @@ static bool mshv_hvcall_is_async(u16 code)
{
switch (code) {
case HVCALL_SET_PARTITION_PROPERTY:
+ case HVCALL_IMPORT_ISOLATED_PAGES:
+ case HVCALL_ISSUE_SNP_PSP_GUEST_REQUEST:
return true;
default:
break;
@@ -641,6 +646,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.
@@ -1625,6 +1666,260 @@ 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 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 *gpfns;
+ u64 i;
+ u32 host_access = 0;
+ u32 flags = 0;
+ bool acquire;
+ long ret;
+
+ 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)
+ return -EINVAL;
+
+ gpfns = vmemdup_user((char __user *)user_args +
+ offsetof(struct mshv_modify_gpa_host_access,
+ guest_pfns),
+ size_mul(sizeof(*gpfns), args.page_count));
+ if (IS_ERR(gpfns))
+ return PTR_ERR(gpfns);
+
+ pages = kcalloc(args.page_count, sizeof(*pages), GFP_KERNEL);
+ if (!pages) {
+ ret = -ENOMEM;
+ goto free_gpfns;
+ }
+
+ for (i = 0; i < args.page_count; i++)
+ gpfns[i] = HVPFN_DOWN(gpfns[i]);
+
+ ret = mshv_gpfns_to_pages(partition, gpfns, args.page_count, pages);
+ if (ret)
+ goto free_pages;
+
+ 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);
+
+ ret = hv_call_modify_spa_host_access(partition->pt_id, pages,
+ args.page_count, host_access,
+ flags, acquire);
+
+free_pages:
+ kfree(pages);
+free_gpfns:
+ 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 *pages;
+ long ret;
+
+ if (copy_from_user(&args, user_args, sizeof(args)))
+ return -EFAULT;
+
+ if (args.page_type >= MSHV_ISOLATED_PAGE_COUNT ||
+ mshv_field_nonzero(args, rsvd) || !args.page_count)
+ return -EINVAL;
+
+ pages = vmemdup_user((char __user *)user_args +
+ offsetof(struct mshv_import_isolated_pages,
+ guest_pfns),
+ size_mul(sizeof(*pages), args.page_count));
+ if (IS_ERR(pages))
+ return PTR_ERR(pages);
+
+ ret = mshv_init_async_handler(partition);
+ if (ret)
+ goto out;
+
+ ret = hv_call_import_isolated_pages(partition->pt_id, pages,
+ args.page_count, args.page_type,
+ HV_ISOLATED_PAGE_SIZE_4KB,
+ mshv_async_hvcall_handler,
+ partition);
+
+out:
+ kvfree(pages);
+ return ret;
+}
+
+static long
+mshv_partition_ioctl_complete_isolated_import(struct mshv_partition *partition,
+ void __user *user_args)
+{
+ struct mshv_complete_isolated_import *args;
+ long ret;
+
+ args = memdup_user(user_args, sizeof(*args));
+ if (IS_ERR(args))
+ return PTR_ERR(args);
+
+ ret = mshv_init_async_handler(partition);
+ if (ret)
+ goto out;
+
+ ret = hv_call_complete_isolated_import(partition->pt_id,
+ &args->import_data,
+ mshv_async_hvcall_handler,
+ partition);
+ if (!ret)
+ partition->import_completed = true;
+
+out:
+ kfree(args);
+ 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)
+ return ret;
+
+ 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:
+ hv_call_modify_spa_host_access(partition->pt_id, pages,
+ ARRAY_SIZE(pages), host_access, 0, true);
+ 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)
{
@@ -1661,6 +1956,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;
}
@@ -1772,6 +2076,69 @@ 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
+
/*
* Tear down a partition and remove it from the list.
* Partition's refcount must be 0
@@ -1782,6 +2149,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,
@@ -1792,6 +2162,24 @@ static void destroy_partition(struct mshv_partition *partition)
trace_mshv_destroy_partition(partition->pt_id);
if (partition->pt_initialized) {
+#ifdef HV_SUPPORTS_SEV_SNP_GUESTS
+ if (mshv_partition_encrypted(partition)) {
+ hlist_for_each_entry_safe(region, n,
+ &partition->pt_mem_regions,
+ hnode) {
+ hlist_del(®ion->hnode);
+ mshv_region_put(region);
+ }
+
+ ret = destroy_snp_partition_state(partition);
+ if (ret) {
+ pt_err(partition,
+ "Failed to destroy SNP state: %d\n",
+ ret);
+ return;
+ }
+ }
+#endif
/*
* We only need to drain signals for root scheduler. This should be
* done before removing the partition from the partition list.
@@ -2016,6 +2404,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;
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] 14+ messages in thread
* [PATCH v1 05/13] hyperv: fix hv_input_get_system_property layout for SNP status
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
` (3 preceding siblings ...)
2026-08-07 13:51 ` [PATCH v1 04/13] mshv: wire SEV-SNP partition ioctls Wei Hu
@ 2026-08-07 13:51 ` Wei Hu
2026-08-07 13:51 ` [PATCH v1 06/13] mshv: detect and report SEV-SNP support at init Wei Hu
` (7 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 UTC (permalink / raw)
To: linux-hyperv
Cc: linux-kernel, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Wei Hu
From: Wei Hu <weh@microsoft.com>
The processor-feature selector must sit at offset 8 (property_id + u32
reserved), matching the Microsoft Hypervisor ABI. Upstream 7.1's leaner
struct placed it at offset 4, so HVCALL_GET_SYSTEM_PROPERTY(SNP_STATUS)
failed and mshv aborted (no /dev/mshv). Restore reserved + u64 union.
Signed-off-by: Wei Hu <weh@microsoft.com>
---
include/hyperv/hvhdk_mini.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h
index db83fedce243..dd84085c0aff 100644
--- a/include/hyperv/hvhdk_mini.h
+++ b/include/hyperv/hvhdk_mini.h
@@ -237,8 +237,9 @@ enum hv_dynamic_processor_feature_property {
struct hv_input_get_system_property {
u32 property_id; /* enum hv_system_property */
+ u32 reserved;
union {
- u32 as_uint32;
+ u64 as_uint64;
#if IS_ENABLED(CONFIG_X86)
/* enum hv_dynamic_processor_feature_property */
u32 hv_processor_feature;
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 06/13] mshv: detect and report SEV-SNP support at init
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
` (4 preceding siblings ...)
2026-08-07 13:51 ` [PATCH v1 05/13] hyperv: fix hv_input_get_system_property layout for SNP status Wei Hu
@ 2026-08-07 13:51 ` Wei Hu
2026-08-07 13:51 ` [PATCH v1 07/13] mshv: default to safe partition CPU features Wei Hu
` (6 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 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>
Query Hyper-V dynamic processor features during mshv initialization to
determine SEV-SNP availability, and report the result: on success log the
maximum number of encrypted partitions, otherwise log the decoded SNP
status string so misconfigurations are diagnosable. Cloud Hypervisor
expects the root driver to perform this capability check before SEV-SNP
child partition setup.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
drivers/hv/mshv_root_main.c | 107 ++++++++++++++++++++++++++++++++++++
1 file changed, 107 insertions(+)
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 3590a8ea24b5..cc38507570ac 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -2660,6 +2660,109 @@ static int mshv_root_scheduler_cleanup(unsigned int cpu)
}
/* Must be called after retrieving the scheduler type */
+#if defined(__x86_64__)
+static int mshv_print_max_sev_snp_partitions(struct device *dev)
+{
+ struct hv_input_get_system_property *input;
+ struct hv_output_get_system_property *output;
+ unsigned long flags;
+ u64 status;
+ __u64 snp_partition_count;
+
+ 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->property_id = HV_DYNAMIC_PROCESSOR_FEATURE_PROPERTY;
+ input->hv_processor_feature =
+ HV_X64_DYNAMIC_PROCESSOR_FEATURE_MAX_ENCRYPTED_PARTITIONS;
+
+ status = hv_do_hypercall(HVCALL_GET_SYSTEM_PROPERTY, input, output);
+ if (!hv_result_success(status)) {
+ local_irq_restore(flags);
+ dev_err(dev, "Failed to get max SNP partitions: %s\n",
+ hv_result_to_string(status));
+ return hv_result_to_errno(status);
+ }
+
+ snp_partition_count = output->hv_processor_feature_value;
+ local_irq_restore(flags);
+
+ dev_info(dev, "Maximum supported SEV-SNP partitions are: %llu\n",
+ snp_partition_count);
+ return 0;
+}
+#endif
+
+static const char *hv_snp_status_to_string(enum hv_snp_status status)
+{
+ switch (status) {
+ case HV_SNP_STATUS_NONE:
+ return "not available";
+ case HV_SNP_STATUS_AVAILABLE:
+ return "available";
+ case HV_SNP_STATUS_INCOMPATIBLE:
+ return "incompatible";
+ case HV_SNP_STATUS_PSP_UNAVAILABLE:
+ return "PSP unavailable";
+ case HV_SNP_STATUS_PSP_INIT_FAILED:
+ return "PSP init failed";
+ case HV_SNP_STATUS_PSP_BAD_FW_VERSION:
+ return "bad PSP firmware version";
+ case HV_SNP_STATUS_BAD_CONFIGURATION:
+ return "bad configuration";
+ case HV_SNP_STATUS_PSP_FW_UPDATE_IN_PROGRESS:
+ return "PSP firmware update in progress";
+ case HV_SNP_STATUS_PSP_RB_INIT_FAILED:
+ return "PSP ring buffer init failed";
+ case HV_SNP_STATUS_PSP_PLATFORM_STATUS_FAILED:
+ return "PSP platform status failed";
+ case HV_SNP_STATUS_PSP_INIT_LATE_FAILED:
+ return "PSP late init failed";
+ default:
+ return "unknown";
+ }
+}
+
+static int __init mshv_check_sev_snp_support(struct device *dev)
+{
+#if defined(__x86_64__)
+ struct hv_input_get_system_property *input;
+ struct hv_output_get_system_property *output;
+ unsigned long flags;
+ u64 status;
+ enum hv_snp_status snp_status;
+
+ 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->property_id = HV_DYNAMIC_PROCESSOR_FEATURE_PROPERTY;
+ input->hv_processor_feature = HV_X64_DYNAMIC_PROCESSOR_FEATURE_SNP_STATUS;
+
+ status = hv_do_hypercall(HVCALL_GET_SYSTEM_PROPERTY, input, output);
+ if (!hv_result_success(status)) {
+ local_irq_restore(flags);
+ dev_err(dev, "Failed to get SNP support: %s\n",
+ hv_result_to_string(status));
+ return hv_result_to_errno(status);
+ }
+
+ snp_status = output->hv_processor_feature_value;
+ local_irq_restore(flags);
+
+ dev_info(dev, "SEV-SNP support status: %s (%u)\n",
+ hv_snp_status_to_string(snp_status), snp_status);
+
+ if (snp_status == HV_SNP_STATUS_AVAILABLE)
+ return mshv_print_max_sev_snp_partitions(dev);
+#endif
+
+ return 0;
+}
+
static int
root_scheduler_init(struct device *dev)
{
@@ -2763,6 +2866,10 @@ static int __init mshv_parent_partition_init(void)
if (ret)
goto synic_cleanup;
+ ret = mshv_check_sev_snp_support(dev);
+ if (ret)
+ goto synic_cleanup;
+
ret = root_scheduler_init(dev);
if (ret)
goto synic_cleanup;
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 07/13] mshv: default to safe partition CPU features
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
` (5 preceding siblings ...)
2026-08-07 13:51 ` [PATCH v1 06/13] mshv: detect and report SEV-SNP support at init Wei Hu
@ 2026-08-07 13:51 ` Wei Hu
2026-08-07 13:51 ` [PATCH v1 08/13] mshv: accept partial CPU feature banks Wei Hu
` (5 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 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>
For legacy create-partition callers, disable all processor and xsave
features first, then enable only the default feature set known to be
supported. Passing an all-zero disabled feature mask can make SEV-SNP
partition creation fail on Hyper-V.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
drivers/hv/mshv_root_main.c | 90 ++++++++++++++++++++++++++++++++-----
1 file changed, 80 insertions(+), 10 deletions(-)
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index cc38507570ac..11d3c86050b4 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -2333,6 +2333,86 @@ static long mshv_ioctl_process_pt_flags(void __user *user_arg, u64 *pt_flags,
disabled_procs = &cr_props->disabled_processor_features;
disabled_xsave = &cr_props->disabled_processor_xsave_features;
+ /* Disable all processor features first. */
+ for (i = 0; i < HV_PARTITION_PROCESSOR_FEATURES_BANKS; i++)
+ disabled_procs->as_uint64[i] = U64_MAX;
+
+#if IS_ENABLED(CONFIG_X86_64)
+ /* Enable default features that are known to be supported. */
+ disabled_procs->sse3_support = 0;
+ disabled_procs->lahf_sahf_support = 0;
+ disabled_procs->ssse3_support = 0;
+ disabled_procs->sse4_1_support = 0;
+ disabled_procs->sse4_2_support = 0;
+ disabled_procs->sse4a_support = 0;
+ disabled_procs->xop_support = 0;
+ disabled_procs->pop_cnt_support = 0;
+ disabled_procs->cmpxchg16b_support = 0;
+ disabled_procs->altmovcr8_support = 0;
+ disabled_procs->lzcnt_support = 0;
+ disabled_procs->mis_align_sse_support = 0;
+ disabled_procs->mmx_ext_support = 0;
+ disabled_procs->amd3dnow_support = 0;
+ disabled_procs->extended_amd3dnow_support = 0;
+ disabled_procs->page_1gb_support = 0;
+ disabled_procs->aes_support = 0;
+ disabled_procs->pclmulqdq_support = 0;
+ disabled_procs->pcid_support = 0;
+ disabled_procs->fma4_support = 0;
+ disabled_procs->f16c_support = 0;
+ disabled_procs->rd_rand_support = 0;
+ disabled_procs->rd_wr_fs_gs_support = 0;
+ disabled_procs->smep_support = 0;
+ disabled_procs->enhanced_fast_string_support = 0;
+ disabled_procs->bmi1_support = 0;
+ disabled_procs->bmi2_support = 0;
+ disabled_procs->hle_support_deprecated = 0;
+ disabled_procs->rtm_support_deprecated = 0;
+ disabled_procs->movbe_support = 0;
+ disabled_procs->npiep1_support = 0;
+ disabled_procs->dep_x87_fpu_save_support = 0;
+ disabled_procs->rd_seed_support = 0;
+ disabled_procs->adx_support = 0;
+ disabled_procs->intel_prefetch_support = 0;
+ disabled_procs->smap_support = 0;
+ disabled_procs->hle_support = 0;
+ disabled_procs->rtm_support = 0;
+ disabled_procs->rdtscp_support = 0;
+ disabled_procs->clflushopt_support = 0;
+ disabled_procs->invpcid_support = 0;
+ disabled_procs->ibrs_support = 0;
+ disabled_procs->stibp_support = 0;
+ disabled_procs->ibpb_support = 0;
+ disabled_procs->unrestricted_guest_support = 0;
+ disabled_procs->mdd_support = 0;
+ disabled_procs->fast_short_rep_mov_support = 0;
+ disabled_procs->l1dcache_flush_support = 0;
+ disabled_procs->rdcl_no_support = 0;
+ disabled_procs->ibrs_all_support = 0;
+ disabled_procs->skip_l1df_support = 0;
+ disabled_procs->ssb_no_support = 0;
+ disabled_procs->rsb_a_no_support = 0;
+ disabled_procs->virt_spec_ctrl_support = 0;
+ disabled_procs->rd_pid_support = 0;
+ disabled_procs->umip_support = 0;
+ disabled_procs->mbs_no_support = 0;
+ disabled_procs->mb_clear_support = 0;
+ disabled_procs->taa_no_support = 0;
+ disabled_procs->tsx_ctrl_support = 0;
+ disabled_procs->tsc_invariant_support = 0;
+ disabled_procs->rdpru_support = 0;
+ disabled_procs->mbec_support = 0;
+ disabled_procs->psfd_support = 0;
+ disabled_procs->cet_ss_support = 0;
+ disabled_procs->cet_ibt_support = 0;
+ disabled_procs->vmx_exception_inject_support = 0;
+
+ disabled_xsave->as_uint64 = U64_MAX;
+ disabled_xsave->xsave_support = 0;
+ disabled_xsave->xsaveopt_support = 0;
+ disabled_xsave->avx_support = 0;
+#endif
+
/* Check if user provided newer struct with feature fields */
if (args.pt_flags & BIT_ULL(MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES)) {
if (copy_from_user(&args, user_arg, sizeof(args)))
@@ -2371,16 +2451,6 @@ static long mshv_ioctl_process_pt_flags(void __user *user_arg, u64 *pt_flags,
if (mshv_field_nonzero(args, pt_rsvd2))
return -EINVAL;
#endif
- } else {
- /*
- * v1 behavior: try to enable everything. The hypervisor will
- * disable features that are not supported. The banks can be
- * queried via the get partition property hypercall.
- */
- for (i = 0; i < HV_PARTITION_PROCESSOR_FEATURES_BANKS; i++)
- disabled_procs->as_uint64[i] = 0;
-
- disabled_xsave->as_uint64 = 0;
}
/* Only support EXO partitions */
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 08/13] mshv: accept partial CPU feature banks
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
` (6 preceding siblings ...)
2026-08-07 13:51 ` [PATCH v1 07/13] mshv: default to safe partition CPU features Wei Hu
@ 2026-08-07 13:51 ` Wei Hu
2026-08-07 13:51 ` [PATCH v1 09/13] mshv: define full processor and xsave feature masks Wei Hu
` (4 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 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>
Allow userspace to provide fewer than MSHV_NUM_CPU_FEATURES_BANKS banks
for the create-partition v2 ioctl. Preserve the default disabled-feature
mask for omitted banks instead of rejecting the ioctl or overwriting all
banks.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
drivers/hv/mshv_root_main.c | 12 ++----------
include/uapi/linux/mshv.h | 3 ++-
2 files changed, 4 insertions(+), 11 deletions(-)
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 11d3c86050b4..729916e1eacf 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -2423,20 +2423,12 @@ static long mshv_ioctl_process_pt_flags(void __user *user_arg, u64 *pt_flags,
args.pt_isolation >= MSHV_PT_ISOLATION_COUNT)
return -EINVAL;
- if (args.pt_num_cpu_fbanks != MSHV_NUM_CPU_FEATURES_BANKS ||
+ if (args.pt_num_cpu_fbanks > MSHV_NUM_CPU_FEATURES_BANKS ||
mshv_field_nonzero(args, pt_rsvd) ||
mshv_field_nonzero(args, pt_rsvd1))
return -EINVAL;
- /*
- * Note this assumes MSHV_NUM_CPU_FEATURES_BANKS will never
- * change and equals HV_PARTITION_PROCESSOR_FEATURES_BANKS
- * (i.e. 2).
- *
- * Further banks (index >= 2) will be modifiable as 'early'
- * properties via the set partition property hypercall.
- */
- for (i = 0; i < HV_PARTITION_PROCESSOR_FEATURES_BANKS; i++)
+ for (i = 0; i < args.pt_num_cpu_fbanks; i++)
disabled_procs->as_uint64[i] = args.pt_cpu_fbanks[i];
#if IS_ENABLED(CONFIG_X86_64)
diff --git a/include/uapi/linux/mshv.h b/include/uapi/linux/mshv.h
index 4e7f3038f0c6..6d6e0fda63bd 100644
--- a/include/uapi/linux/mshv.h
+++ b/include/uapi/linux/mshv.h
@@ -66,7 +66,8 @@ struct mshv_create_partition {
* This is extended version of the above initial MSHV_CREATE_PARTITION
* ioctl and allows for following additional parameters:
*
- * @pt_num_cpu_fbanks: Must be set to MSHV_NUM_CPU_FEATURES_BANKS.
+ * @pt_num_cpu_fbanks: Number of processor feature banks provided. Must not
+ * exceed MSHV_NUM_CPU_FEATURES_BANKS.
* @pt_cpu_fbanks: Disabled processor feature banks array.
* @pt_disabled_xsave: Disabled xsave feature bits.
*
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 09/13] mshv: define full processor and xsave feature masks
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
` (7 preceding siblings ...)
2026-08-07 13:51 ` [PATCH v1 08/13] mshv: accept partial CPU feature banks Wei Hu
@ 2026-08-07 13:51 ` Wei Hu
2026-08-07 13:51 ` [PATCH v1 10/13] mshv: unmap SNP memory before state teardown Wei Hu
` (3 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 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 missing processor and xsave feature bit definitions and use
them when constructing the default disabled-feature mask, so legacy
SNP partition creation applies a complete, well-defined feature mask.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
drivers/hv/mshv_root_main.c | 10 +++++
include/hyperv/hvhdk.h | 79 ++++++++++++++++++++++++++++++++++++-
2 files changed, 87 insertions(+), 2 deletions(-)
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 729916e1eacf..c81a8d0493c1 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -2406,11 +2406,21 @@ static long mshv_ioctl_process_pt_flags(void __user *user_arg, u64 *pt_flags,
disabled_procs->cet_ss_support = 0;
disabled_procs->cet_ibt_support = 0;
disabled_procs->vmx_exception_inject_support = 0;
+ disabled_procs->fb_clear_support = 0;
+ disabled_procs->gds_no_support = 0;
+ disabled_procs->bhi_no_support = 0;
+ disabled_procs->bhi_dis_support = 0;
+ disabled_procs->btc_no_support = 0;
+ disabled_procs->mitigation_ctrl_support = 0;
+ disabled_procs->rfds_no_support = 0;
+ disabled_procs->rfds_clear_support = 0;
disabled_xsave->as_uint64 = U64_MAX;
disabled_xsave->xsave_support = 0;
disabled_xsave->xsaveopt_support = 0;
disabled_xsave->avx_support = 0;
+ disabled_xsave->xsave_comp_support = 0;
+ disabled_xsave->xsave_supervisor_support = 0;
#endif
/* Check if user provided newer struct with feature fields */
diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h
index 691ac495a095..54b0e671a028 100644
--- a/include/hyperv/hvhdk.h
+++ b/include/hyperv/hvhdk.h
@@ -231,7 +231,47 @@ union hv_partition_processor_features {
u64 fzlrep_movsb : 1;
u64 fsrep_stosb : 1;
u64 fsrep_cmpsb : 1;
- u64 reserved_bank1 : 42;
+ u64 tsx_ld_trk_support : 1;
+ u64 vmx_ins_outs_exit_info_support : 1;
+ u64 hlat_support : 1;
+ u64 sbdr_ssdp_no_support : 1;
+ u64 fbsdp_no_support : 1;
+ u64 psdp_no_support : 1;
+ u64 fb_clear_support : 1;
+ u64 btc_no_support : 1;
+ u64 ibpb_rsb_flush_support : 1;
+ u64 stibp_always_on_support : 1;
+ u64 perf_global_ctrl_support : 1;
+ u64 npt_execute_only_support : 1;
+ u64 npt_ad_flags_support : 1;
+ u64 npt1_gb_page_support : 1;
+ u64 amd_processor_topology_node_id_support : 1;
+ u64 local_machine_check_support : 1;
+ u64 extended_topology_leaf_fp256_amd_support : 1;
+ u64 gds_no_support : 1;
+ u64 cmpccxadd_support : 1;
+ u64 tsc_aux_virtualization_support : 1;
+ u64 rmp_query_support : 1;
+ u64 bhi_no_support : 1;
+ u64 bhi_dis_support : 1;
+ u64 prefetch_i_support : 1;
+ u64 sha512_support : 1;
+ u64 mitigation_ctrl_support : 1;
+ u64 rfds_no_support : 1;
+ u64 rfds_clear_support : 1;
+ u64 sm3_support : 1;
+ u64 sm4_support : 1;
+ u64 secure_avic_support : 1;
+ u64 guest_intercept_ctrl_support : 1;
+ u64 sbpb_supported : 1;
+ u64 ibpb_br_type_supported : 1;
+ u64 srso_no_supported : 1;
+ u64 srso_user_kernel_no_supported : 1;
+ u64 vrew_clear_supported : 1;
+ u64 tsa_l1_no_supported : 1;
+ u64 tsa_sq_no_supported : 1;
+ u64 lass_support : 1;
+ u64 reserved_bank1 : 2;
} __packed;
};
@@ -240,7 +280,42 @@ union hv_partition_processor_xsave_features {
u64 xsave_support : 1;
u64 xsaveopt_support : 1;
u64 avx_support : 1;
- u64 reserved1 : 61;
+ u64 avx2_support : 1;
+ u64 fma_support : 1;
+ u64 mpx_support : 1;
+ u64 avx512_support : 1;
+ u64 avx512_dq_support : 1;
+ u64 avx512_cd_support : 1;
+ u64 avx512_bw_support : 1;
+ u64 avx512_vl_support : 1;
+ u64 xsave_comp_support : 1;
+ u64 xsave_supervisor_support : 1;
+ u64 xcr1_support : 1;
+ u64 avx512_bitalg_support : 1;
+ u64 avx512_i_fma_support : 1;
+ u64 avx512_v_bmi_support : 1;
+ u64 avx512_v_bmi2_support : 1;
+ u64 avx512_vnni_support : 1;
+ u64 gfni_support : 1;
+ u64 vaes_support : 1;
+ u64 avx512_v_popcntdq_support : 1;
+ u64 vpclmulqdq_support : 1;
+ u64 avx512_bf16_support : 1;
+ u64 avx512_vp2_intersect_support : 1;
+ u64 avx512_fp16_support : 1;
+ u64 xfd_support : 1;
+ u64 amx_tile_support : 1;
+ u64 amx_bf16_support : 1;
+ u64 amx_int8_support : 1;
+ u64 avx_vnni_support : 1;
+ u64 avx_ifma_support : 1;
+ u64 avx_ne_convert_support : 1;
+ u64 avx_vnni_int8_support : 1;
+ u64 avx_vnni_int16_support : 1;
+ u64 avx10_1_256_support : 1;
+ u64 avx10_1_512_support : 1;
+ u64 amx_fp16_support : 1;
+ u64 reserved1 : 26;
} __packed;
u64 as_uint64;
};
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 10/13] mshv: unmap SNP memory before state teardown
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
` (8 preceding siblings ...)
2026-08-07 13:51 ` [PATCH v1 09/13] mshv: define full processor and xsave feature masks Wei Hu
@ 2026-08-07 13:51 ` Wei Hu
2026-08-07 13:51 ` [PATCH v1 11/13] mshv: unlock SNP pages on panic for crashdump collection Wei Hu
` (2 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 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>
Unmap memory regions before destroying SNP partition state, and only
release regions after SNP state is torn down. Dropping regions first
attempts to regain host access while the partition is still secure and
can leave pages inaccessible.
Signed-off-by: Wei Liu <wei.liu@kernel.org>
---
drivers/hv/mshv_regions.c | 2 +-
drivers/hv/mshv_root.h | 1 +
drivers/hv/mshv_root_main.c | 6 ++----
3 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index 6d65e5b42152..62c49b4d2a33 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,
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 0e6d4ce25112..0423f4adb3ac 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -370,6 +370,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);
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index c81a8d0493c1..0fbd2158968d 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -2166,10 +2166,8 @@ static void destroy_partition(struct mshv_partition *partition)
if (mshv_partition_encrypted(partition)) {
hlist_for_each_entry_safe(region, n,
&partition->pt_mem_regions,
- hnode) {
- hlist_del(®ion->hnode);
- mshv_region_put(region);
- }
+ hnode)
+ mshv_region_unmap(region);
ret = destroy_snp_partition_state(partition);
if (ret) {
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 11/13] mshv: unlock SNP pages on panic for crashdump collection
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
` (9 preceding siblings ...)
2026-08-07 13:51 ` [PATCH v1 10/13] mshv: unmap SNP memory before state teardown Wei Hu
@ 2026-08-07 13:51 ` Wei Hu
2026-08-07 13:51 ` [PATCH v1 12/13] hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader) Wei Hu
2026-08-07 13:51 ` [PATCH v1 13/13] mshv: set up own SynIC registers on a nested root partition Wei Hu
12 siblings, 0 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 UTC (permalink / raw)
To: linux-hyperv
Cc: linux-kernel, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Wei Hu, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin
From: Wei Hu <weh@microsoft.com>
Add an SNP panic-path page-unlock feature so a root partition running
confidential (SEV-SNP) guests can still collect a Linux root vmcore via
kexec after a crash. On panic, guest pages of every encrypted partition
are unmapped and shared back to the host so kexec can read them.
Register a panic notifier that walks mshv_root.pt_htable and, for each
encrypted partition, unmaps each memory region and marks it shared again.
The notifier is only registered when the hypervisor's own crash path is
not active (hv_crash_enabled) and only built when CONFIG_CRASH_DUMP is
enabled. Declare hv_crash_enabled in asm/mshyperv.h alongside the other
MSHV crashdump symbols.
Tested on a SEV-SNP Dom0: the notifier only runs in its intended fallback
path, i.e. when the hypervisor's own kdump support is inactive
(hv_crash_enabled == false); when it is active the hypervisor stops the
CPUs and kexecs before the Linux panic_notifier_list runs. To exercise the
ported code that fallback was reproduced on a test kernel (hypervisor crash
init disabled so hv_crash_enabled == false). With a confidential guest
running, an intentional Dom0 panic showed the notifier fire, find the
encrypted partition, and unlock its regions with no errors ("SNP pages are
unlocked for panic", no "Unlock snp failed"), after which kdump collected a
valid, readable vmcore. The default active-hypervisor path was also
confirmed to boot cleanly with the notifier correctly not registered.
Signed-off-by: Wei Hu <weh@microsoft.com>
---
arch/x86/include/asm/mshyperv.h | 1 +
drivers/hv/mshv_root_main.c | 71 +++++++++++++++++++++++++++++++++
2 files changed, 72 insertions(+)
diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h
index f64393e853ee..052cc44ae1f0 100644
--- a/arch/x86/include/asm/mshyperv.h
+++ b/arch/x86/include/asm/mshyperv.h
@@ -240,6 +240,7 @@ static __always_inline u64 hv_raw_get_msr(unsigned int reg)
int hv_apicid_to_vp_index(u32 apic_id);
#if IS_ENABLED(CONFIG_MSHV_ROOT) && IS_ENABLED(CONFIG_CRASH_DUMP)
+extern bool hv_crash_enabled;
void hv_root_crash_init(void);
void hv_crash_asm32(void);
void hv_crash_asm64(void);
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 0fbd2158968d..4d08d547704e 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -2898,6 +2898,74 @@ static int __init mshv_init_vmm_caps(struct device *dev)
return 0;
}
+#if defined(CONFIG_X86_64) && IS_ENABLED(CONFIG_CRASH_DUMP)
+static void mshv_panic_unlock_snp(struct mshv_partition *vm)
+{
+ struct mshv_mem_region *memreg;
+ int ret;
+
+ hlist_for_each_entry(memreg, &vm->pt_mem_regions, hnode) {
+ mshv_region_unmap(memreg);
+ ret = mshv_region_share(memreg);
+ if (ret)
+ pt_err(vm, "Unlock snp failed. ret:0x%x gfn:%llx numpfns:%lld\n",
+ ret, memreg->start_gfn, memreg->nr_pages);
+ }
+}
+
+static int mshv_root_panic_cb(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ int i, done = 0;
+ struct mshv_partition *pt;
+ struct device *dev = NULL;
+
+ hash_for_each_rcu(mshv_root.pt_htable, i, pt, pt_hnode) {
+ if (!mshv_partition_encrypted(pt))
+ continue;
+
+ done = 1;
+ mshv_panic_unlock_snp(pt);
+ dev = pt->pt_module_dev;
+ }
+ if (done && dev)
+ dev_info(dev, "SNP pages are unlocked for panic\n");
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block mshv_root_panic_blk = {
+ .notifier_call = mshv_root_panic_cb,
+};
+
+/*
+ * If mshv devirt setup failed during boot, or the feature itself is not
+ * available, allow the system to at least collect linux root vmcore. For
+ * that, snp guest pages must be made readable in the panic path so kexec can
+ * collect them.
+ */
+static void mshv_crashdump_init(void)
+{
+ if (hv_crash_enabled)
+ return;
+
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &mshv_root_panic_blk);
+}
+
+static void mshv_crashdump_deinit(void)
+{
+ if (hv_crash_enabled)
+ return;
+
+ atomic_notifier_chain_unregister(&panic_notifier_list,
+ &mshv_root_panic_blk);
+}
+#else
+static void mshv_crashdump_init(void) {}
+static void mshv_crashdump_deinit(void) {}
+#endif
+
static int __init mshv_parent_partition_init(void)
{
int ret;
@@ -2957,6 +3025,8 @@ static int __init mshv_parent_partition_init(void)
hv_setup_mshv_handler(mshv_isr);
+ mshv_crashdump_init();
+
return 0;
exit_debugfs:
@@ -2972,6 +3042,7 @@ static int __init mshv_parent_partition_init(void)
static void __exit mshv_parent_partition_exit(void)
{
+ mshv_crashdump_deinit();
hv_setup_mshv_handler(NULL);
mshv_port_table_fini();
mshv_debugfs_exit();
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 12/13] hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader)
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
` (10 preceding siblings ...)
2026-08-07 13:51 ` [PATCH v1 11/13] mshv: unlock SNP pages on panic for crashdump collection Wei Hu
@ 2026-08-07 13:51 ` Wei Hu
2026-08-07 13:51 ` [PATCH v1 13/13] mshv: set up own SynIC registers on a nested root partition Wei Hu
12 siblings, 0 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 UTC (permalink / raw)
To: linux-hyperv
Cc: linux-kernel, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Wei Hu, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Ard Biesheuvel,
Ilias Apalodimas, Arnd Bergmann, linux-efi, linux-arch
From: Wei Hu <weh@microsoft.com>
Port the EFI HvLoader protocol handshake (efi-mshv stub + SETUP_MSHV) and
the hyperv_resvd_new memory reservation so an upstream kernel can boot as
a Microsoft Hypervisor (MSHV) root partition. Mainline lacks this
Linux-side enablement, so lxhvloader declines to hand off a stock kernel
as root.
Signed-off-by: Wei Hu <weh@microsoft.com>
---
MAINTAINERS | 2 +
arch/x86/hyperv/hv_init.c | 3 +
arch/x86/include/uapi/asm/setup_data.h | 3 +-
arch/x86/kernel/cpu/mshyperv.c | 80 ++++++-
drivers/firmware/efi/libstub/Makefile | 7 +
drivers/firmware/efi/libstub/arm64-efi-mshv.c | 41 ++++
.../firmware/efi/libstub/efi-mshv-common.c | 136 ++++++++++++
drivers/firmware/efi/libstub/efi-mshv.h | 118 ++++++++++
drivers/firmware/efi/libstub/x86-efi-mshv.c | 202 ++++++++++++++++++
drivers/firmware/efi/libstub/x86-stub.c | 18 +-
drivers/hv/hv_common.c | 82 +++++++
include/asm-generic/mshyperv.h | 7 +
include/linux/efi.h | 1 +
13 files changed, 697 insertions(+), 3 deletions(-)
create mode 100644 drivers/firmware/efi/libstub/arm64-efi-mshv.c
create mode 100644 drivers/firmware/efi/libstub/efi-mshv-common.c
create mode 100644 drivers/firmware/efi/libstub/efi-mshv.h
create mode 100644 drivers/firmware/efi/libstub/x86-efi-mshv.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 716acfc3d7c1..0da3c7929aef 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12098,6 +12098,8 @@ F: arch/x86/include/asm/mshyperv.h
F: arch/x86/include/asm/trace/hyperv.h
F: arch/x86/kernel/cpu/mshyperv.c
F: drivers/clocksource/hyperv_timer.c
+F: drivers/firmware/efi/libstub/*-efi-mshv.c
+F: drivers/firmware/efi/libstub/efi-mshv*
F: drivers/hid/hid-hyperv.c
F: drivers/hv/
F: drivers/input/serio/hyperv-keyboard.c
diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c
index 55a8b6de2865..4ee8035cb990 100644
--- a/arch/x86/hyperv/hv_init.c
+++ b/arch/x86/hyperv/hv_init.c
@@ -557,6 +557,9 @@ void __init hyperv_init(void)
hv_remap_tsc_clocksource();
hv_sleep_notifiers_register();
+
+ /* mark ram reserved for hypervisor as owned by hypervisor */
+ hv_mark_resources();
} else {
hypercall_msr.guest_physical_address = vmalloc_to_pfn(hv_hypercall_pg);
wrmsrq(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
diff --git a/arch/x86/include/uapi/asm/setup_data.h b/arch/x86/include/uapi/asm/setup_data.h
index 2671c4e1b3a0..e88f7bf709ab 100644
--- a/arch/x86/include/uapi/asm/setup_data.h
+++ b/arch/x86/include/uapi/asm/setup_data.h
@@ -14,7 +14,8 @@
#define SETUP_IMA 8
#define SETUP_RNG_SEED 9
#define SETUP_KEXEC_KHO 10
-#define SETUP_ENUM_MAX SETUP_KEXEC_KHO
+#define SETUP_MSHV 11
+#define SETUP_ENUM_MAX SETUP_MSHV
#define SETUP_INDIRECT (1<<31)
#define SETUP_TYPE_MAX (SETUP_ENUM_MAX | SETUP_INDIRECT)
diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
index 185d4f677ec0..46c3fca3d99d 100644
--- a/arch/x86/kernel/cpu/mshyperv.c
+++ b/arch/x86/kernel/cpu/mshyperv.c
@@ -13,6 +13,8 @@
#include <linux/export.h>
#include <linux/hardirq.h>
#include <linux/efi.h>
+#include <linux/memblock.h>
+#include <linux/crash_dump.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kexec.h>
@@ -22,6 +24,7 @@
#include <asm/cpuid/api.h>
#include <hyperv/hvhdk.h>
#include <asm/mshyperv.h>
+#include <asm/e820/api.h>
#include <asm/desc.h>
#include <asm/idtentry.h>
#include <asm/irq_regs.h>
@@ -496,6 +499,75 @@ EXPORT_SYMBOL_GPL(hv_get_hypervisor_version);
* Reserved vectors hard coded in the hypervisor. If used outside, the hypervisor
* will either crash or hang or attempt to break into debugger.
*/
+bool mshv_loader_new = true;
+
+static int hv_resvd_ranges[HV_MAX_RESVD_RANGES] = {
+ [0 ... HV_MAX_RESVD_RANGES - 1] = -1};
+
+/*
+ * Parse eg "hyperv_resvd=3,7,20" where 3, 7, and 20 are indexes into the e820
+ * table for ranges that are reserved by the loader for the hypervisor
+ */
+static int __init hv_parse_hyperv_resvd(char *arg)
+{
+ int idx, max = ARRAY_SIZE(hv_resvd_ranges);
+ int i = 0;
+
+ mshv_loader_new = false;
+
+ if (is_kdump_kernel())
+ return 0;
+
+ if (hv_resvd_ranges[0] != -1) {
+ pr_err("Hyper-V: multiple hyperv_resvd not supported\n");
+ return 0;
+ }
+
+ while (get_option(&arg, &idx)) {
+ if (i >= max) {
+ pr_err("Hyper-V: resvd ranges tbl full %d\n", idx);
+ break;
+ }
+
+ hv_resvd_ranges[i++] = idx;
+ }
+
+ return 0;
+}
+early_param("hyperv_resvd", hv_parse_hyperv_resvd);
+
+/*
+ * Reserve memory that the hypervisor is using early on. The ranges are marked
+ * reserved by a custom bootloader, change that to usable and reserve that
+ * range. Note, the bootloader sanitizes the e820 before passing on here.
+ */
+static void __init hv_resv_mshv_memory(void)
+{
+ u64 start, end, size;
+ int i, idx, max = ARRAY_SIZE(hv_resvd_ranges);
+
+ for (i = 0; i < max && hv_resvd_ranges[i] != -1; i++) {
+ idx = hv_resvd_ranges[i];
+ if (idx < 0 || idx >= e820_table->nr_entries) {
+ pr_info("Hyper-V: invalid resvd idx %d\n", idx);
+ continue;
+ }
+
+ start = e820_table->entries[idx].addr;
+ size = e820_table->entries[idx].size;
+ end = start + size - 1;
+
+ memblock_reserve(start, size);
+ e820_table->entries[idx].type = E820_TYPE_RAM;
+ pr_info("Hyper-V reserve [mem %#018Lx-%#018Lx]\n", start, end);
+
+ hv_mshv_res[i].name = "Hypervisor Code and Data";
+ hv_mshv_res[i].flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM;
+ hv_mshv_res[i].start = start;
+ hv_mshv_res[i].end = end;
+ }
+}
+
static void hv_reserve_irq_vectors(void)
{
#define HYPERV_DBG_FASTFAIL_VECTOR 0x29
@@ -547,8 +619,14 @@ static void __init ms_hyperv_init_platform(void)
hv_identify_partition_type();
- if (hv_root_partition())
+ if (hv_root_partition()) {
+ /* very first thing, reserve/log exclusive hypervisor memory */
+ if (mshv_loader_new)
+ hv_dump_mshv_memory();
+ else
+ hv_resv_mshv_memory();
hv_reserve_irq_vectors();
+ }
if (cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
ms_hyperv.hints |= HV_DEPRECATING_AEOI_RECOMMENDED;
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 77a2b2d74f3f..d116a6c99527 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -87,6 +87,13 @@ lib-$(CONFIG_X86) += x86-stub.o smbios.o
lib-$(CONFIG_X86_64) += x86-5lvl.o
lib-$(CONFIG_RISCV) += kaslr.o riscv.o riscv-stub.o
lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-stub.o
+lib-$(subst m,y,$(CONFIG_MSHV_ROOT)) += efi-mshv-common.o
+ifdef CONFIG_X86_64
+lib-$(CONFIG_MSHV_ROOT) += x86-efi-mshv.o
+endif
+ifdef CONFIG_ARM64
+lib-$(subst m,y,$(CONFIG_MSHV_ROOT)) += arm64-efi-mshv.o
+endif
CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
diff --git a/drivers/firmware/efi/libstub/arm64-efi-mshv.c b/drivers/firmware/efi/libstub/arm64-efi-mshv.c
new file mode 100644
index 000000000000..374ca1b41e03
--- /dev/null
+++ b/drivers/firmware/efi/libstub/arm64-efi-mshv.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/efi.h>
+#include <asm/efi.h>
+#include <asm/setup.h>
+
+#include "efistub.h"
+#include "efi-mshv.h"
+
+efi_status_t mshv_efi_setup(char **cmdline_ptr)
+{
+ efi_status_t status;
+ efi_memory_desc_t *mem_map;
+ unsigned long map_sz, desc_sz, new_cmdline_addr;
+
+ status = mshv_efi_init();
+ if (status == EFI_NOT_FOUND) // we are in a standard Linux boot
+ return EFI_SUCCESS;
+
+ map_sz = 0;
+ mshv_get_hv_ranges((void *)&mem_map, &map_sz, &desc_sz);
+
+ status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, COMMAND_LINE_SIZE,
+ (void **)&new_cmdline_addr);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to allocate space for cmdline with code %d",
+ status);
+
+ mshv_efi_update_cmdline(mem_map, map_sz, desc_sz,
+ *cmdline_ptr,
+ (char *)new_cmdline_addr, COMMAND_LINE_SIZE);
+
+ status = efi_bs_call(free_pool, *cmdline_ptr);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to free old cmdline with code %d",
+ status);
+
+ *cmdline_ptr = (char *)new_cmdline_addr;
+
+ return EFI_SUCCESS;
+}
diff --git a/drivers/firmware/efi/libstub/efi-mshv-common.c b/drivers/firmware/efi/libstub/efi-mshv-common.c
new file mode 100644
index 000000000000..2b631a9f50fc
--- /dev/null
+++ b/drivers/firmware/efi/libstub/efi-mshv-common.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "efistub.h"
+#include "efi-mshv.h"
+
+struct efi_hvloader_protocol *efi_mshv;
+
+efi_status_t mshv_efi_init(void)
+{
+ efi_status_t status;
+ static efi_guid_t hv_proto_guid = EFI_MSHV_MEDIA_PROTOCOL_GUID;
+
+ status = efi_bs_call(locate_protocol,
+ &hv_proto_guid, NULL, (void **)&efi_mshv);
+ if (status == EFI_NOT_FOUND) {
+ /* If the protocol is not installed we are in a standard Linux boot */
+ return status;
+ } else if (status != EFI_SUCCESS) {
+ mshv_efi_reboot("LocateProtocol failed unexpectedly with code %d",
+ status);
+ }
+
+ status = efi_mshv->get_loader_init_status();
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("mshv protocol installed but seems to have failed with code %d",
+ status);
+
+ return EFI_SUCCESS;
+}
+
+void mshv_get_hv_ranges(efi_memory_desc_t **mem_map, unsigned long *map_sz,
+ unsigned long *desc_sz)
+{
+ efi_status_t status;
+
+ status = efi_mshv->get_hv_ranges((void **)mem_map, map_sz, desc_sz);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to retrieve mshv ranges: error code %d",
+ status);
+}
+
+/*
+ * Concatenate the hypervisor reserved ranges to the command line.
+ *
+ * The reserved ranges are formatted as follows:
+ * 'hyperv_resvd_new=<size>!<address>,<size>!<address>,...'
+ *
+ * @mem_map: EFI memory map with the hypervisor reserved ranges
+ * @map_sz: size of the memory map
+ * @desc_sz: size of each descriptor in the memory map
+ * @old_cmdline: old command line
+ * @buf: buffer to hold the new command line
+ */
+void mshv_efi_update_cmdline(efi_memory_desc_t *mem_map,
+ unsigned long map_sz, unsigned long desc_sz, char *old_cmdline,
+ char *buf, unsigned long buf_sz)
+{
+ int i, cmdline_len, nr_desc;
+
+ cmdline_len = strlen(old_cmdline);
+ memcpy(buf, old_cmdline, cmdline_len + 1);
+
+ cmdline_len += snprintf(buf + cmdline_len,
+ buf_sz - cmdline_len,
+ " hyperv_resvd_new=");
+
+ nr_desc = map_sz / desc_sz;
+ for (i = 0; i < nr_desc; ++i) {
+ efi_memory_desc_t *d;
+ u64 start, end, sz;
+
+ d = efi_memdesc_ptr(mem_map, desc_sz, i);
+ start = d->phys_addr;
+ sz = d->num_pages << PAGE_SHIFT;
+ end = start + sz - 1;
+
+ cmdline_len += snprintf(buf + cmdline_len,
+ buf_sz - cmdline_len,
+ "%s0x%llx!0x%llx", i > 0 ? "," : "",
+ sz, start);
+ }
+}
+
+efi_status_t mshv_set_efi_rt_range(struct efi_boot_memmap *map)
+{
+ u32 nr_desc;
+ int i;
+ efi_status_t status;
+
+ if (!efi_mshv)
+ return EFI_SUCCESS;
+
+ nr_desc = map->map_size / map->desc_size;
+
+ for (i = 0; i < nr_desc; i++) {
+ efi_memory_desc_t *d;
+
+ d = efi_memdesc_ptr(map->map, map->desc_size, i);
+ switch (d->type) {
+ case EFI_RUNTIME_SERVICES_CODE:
+ case EFI_RUNTIME_SERVICES_DATA:
+ status = efi_mshv->register_range(d->phys_addr >> PAGE_SHIFT,
+ d->num_pages);
+ if (status != EFI_SUCCESS)
+ return status;
+ break;
+ default:
+ /* default case: range is not relevant to mshv */
+ break;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * Launch mshv, if enabled.
+ *
+ * If mshv reports a bad status at this point, abort the boot.
+ * To get more information about the failure, the HV loader's internal
+ * logging can be used, which is exposed via efi_hv->get_next_log_msg(...).
+ *
+ */
+efi_status_t mshv_launch(void)
+{
+ struct hvl_return_data ret;
+
+ if (!efi_mshv)
+ return EFI_SUCCESS;
+
+ efi_mshv->launch_hv(NULL, &ret);
+ /* TODO: Where/how do we dump the hv loader logs? */
+ if (ret.launch_data.launch_status != 0)
+ efi_rt_call(reset_system, EFI_RESET_COLD, EFI_ABORTED, 0, NULL);
+ return EFI_SUCCESS;
+}
diff --git a/drivers/firmware/efi/libstub/efi-mshv.h b/drivers/firmware/efi/libstub/efi-mshv.h
new file mode 100644
index 000000000000..38e10f457a6c
--- /dev/null
+++ b/drivers/firmware/efi/libstub/efi-mshv.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _DRIVERS_FIRMWARE_EFI_MSHV_H
+#define _DRIVERS_FIRMWARE_EFI_MSHV_H
+
+#include "efistub.h"
+
+#if IS_ENABLED(CONFIG_MSHV_ROOT)
+
+extern struct efi_hvloader_protocol *efi_mshv;
+
+static inline void mshv_efi_reboot(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ efi_printk(fmt, args);
+ va_end(args);
+
+ efi_bs_call(stall, 5 * EFI_USEC_PER_SEC);
+ efi_rt_call(reset_system, EFI_RESET_COLD, EFI_ABORTED, 0, NULL);
+}
+
+efi_status_t mshv_efi_init(void);
+void mshv_get_hv_ranges(efi_memory_desc_t **mem_map, unsigned long *map_sz,
+ unsigned long *desc_sz);
+void mshv_efi_update_cmdline(efi_memory_desc_t *mem_map,
+ unsigned long map_sz, unsigned long desc_sz, char *cmdline,
+ char *buf, unsigned long buf_sz);
+
+#ifdef CONFIG_X86_64
+efi_status_t mshv_efi_setup(struct boot_params *boot_params);
+#endif /* CONFIG_X86_64 */
+
+#ifdef CONFIG_ARM64
+efi_status_t mshv_efi_setup(char **cmdline_ptr);
+#endif /* CONFIG_ARM64 */
+
+efi_status_t mshv_set_efi_rt_range(struct efi_boot_memmap *map);
+efi_status_t mshv_launch(void);
+
+struct hvl_dbg_data {
+ u8 unused[552];
+} __packed;
+
+struct hvl_launch_data {
+ u64 launch_status;
+ u64 launch_substatus1;
+} __packed;
+
+struct hvl_load_data {
+ u32 is_unsafe_config:1;
+ u32 reserved:31;
+} __packed;
+
+struct hvl_return_data {
+ u32 crash_dump_area_page_count;
+ u32 unused;
+ u64 crashdump_area_spa;
+ union {
+ struct hvl_launch_data launch_data;
+ struct hvl_load_data load_data;
+ };
+ struct hvl_dbg_data debug_data;
+ void *spa_page_range_array;
+ u32 range_count;
+
+ struct {
+ u32 base_checksum;
+ u32 base_timestamp;
+ u32 patch_checksum;
+ u32 patch_timestamp;
+ u32 base_hpat_entries_used;
+ u32 patch_hpat_entries_used;
+ u32 patch_sequence_number;
+ } patch_details;
+} __packed;
+
+struct efi_hvloader_protocol {
+ void (__efiapi * launch_hv)(void *, struct hvl_return_data *);
+ efi_status_t (__efiapi * register_range)(u64, u64);
+ efi_status_t (__efiapi * get_memory_map)(unsigned long *, void *,
+ unsigned long *,
+ unsigned long *, u32 *);
+ efi_status_t (__efiapi * get_hv_ranges)(void **,
+ unsigned long *,
+ unsigned long *);
+ efi_status_t (__efiapi * get_loader_init_status)(void);
+ efi_char16_t *(__efiapi * get_next_log_msg)(size_t *);
+};
+
+#else /* CONFIG_MSHV_ROOT */
+#ifdef CONFIG_X86_64
+static inline efi_status_t mshv_efi_setup(struct boot_params *boot_params)
+{
+ return EFI_SUCCESS;
+}
+#endif /* CONFIG_X86_64 */
+
+#ifdef CONFIG_ARM64
+static inline efi_status_t mshv_efi_setup(char **cmdline_ptr)
+{
+ return EFI_SUCCESS;
+}
+#endif /* CONFIG_ARM64 */
+
+static inline efi_status_t mshv_set_efi_rt_range(struct efi_boot_memmap *map)
+{
+ return EFI_SUCCESS;
+}
+
+static inline efi_status_t mshv_launch(void)
+{
+ return EFI_SUCCESS;
+}
+#endif /* !CONFIG_MSHV_ROOT */
+
+#endif /* _DRIVERS_FIRMWARE_EFI_MSHV_H */
diff --git a/drivers/firmware/efi/libstub/x86-efi-mshv.c b/drivers/firmware/efi/libstub/x86-efi-mshv.c
new file mode 100644
index 000000000000..bd65c7d82556
--- /dev/null
+++ b/drivers/firmware/efi/libstub/x86-efi-mshv.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <asm/setup.h>
+
+#include "efistub.h"
+#include "efi-mshv.h"
+
+/* Initial number of MSHV reserved ranges, extended as needed */
+#define MSHV_RESERVED_RANGES_COUNT 16
+
+struct mshv_setup_data {
+ struct setup_data sd;
+ struct setup_indirect si;
+} __packed;
+
+static int mshv_realloc_ranges(struct resource **data,
+ unsigned long *data_sz, int nr_ranges)
+{
+ struct resource *new_data;
+ unsigned long new_sz;
+ int status;
+
+ new_sz = sizeof(struct resource) * nr_ranges;
+ status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, new_sz,
+ (void **)&new_data);
+ if (status != EFI_SUCCESS) {
+ efi_err("mshv failed to allocate setup_data\n");
+ return status;
+ }
+
+ memset(new_data, 0, new_sz);
+ if (*data) {
+ memcpy(new_data, *data, *data_sz);
+ efi_bs_call(free_pool, *data);
+ }
+
+ *data = new_data;
+ *data_sz = new_sz;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t mshv_populate_ranges(struct boot_params *boot_params,
+ efi_memory_desc_t *mem_map, unsigned long map_sz,
+ unsigned long desc_sz)
+{
+ unsigned long cmdline_ptr;
+ u32 cmdline_size;
+ static u8 mshv_cmdline[COMMAND_LINE_SIZE];
+
+ memset(mshv_cmdline, 0, sizeof(mshv_cmdline));
+
+ cmdline_ptr = boot_params->hdr.cmd_line_ptr;
+ cmdline_ptr |= (u64)boot_params->ext_cmd_line_ptr << 32;
+ cmdline_size = boot_params->hdr.cmdline_size;
+
+ mshv_efi_update_cmdline(mem_map, map_sz, desc_sz,
+ (char *)cmdline_ptr,
+ (char *)mshv_cmdline, COMMAND_LINE_SIZE);
+
+ boot_params->hdr.cmd_line_ptr = (u32)((unsigned long)mshv_cmdline);
+ boot_params->ext_cmd_line_ptr = (u32)((unsigned long)mshv_cmdline >> 32);
+ boot_params->hdr.cmdline_size = sizeof(mshv_cmdline);
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * Prepare for running as root partition with mshv.
+ * - Open the hypervisor loader EFI protocol, used for launching mshv after
+ * 'exit boot services'.
+ * - Get mshv reserved memory ranges from the loader, and populates those
+ * via a command line parameter 'hyperv_resvd_new'.
+ * If mshv_efi_setup() fails, boot continues as a bare-metal boot.
+ */
+efi_status_t mshv_efi_setup(struct boot_params *boot_params)
+{
+ struct setup_data **setup_data_itr;
+ struct mshv_setup_data *sd_block;
+ efi_memory_desc_t *mem_map;
+ unsigned long map_sz, key, desc_sz, setup_data_sz;
+ u32 desc_ver;
+ u64 start, end;
+ struct resource *mshv_range, *prev;
+ struct resource *mshv_reserved;
+ unsigned long mshv_reserved_sz;
+ u32 nr_desc;
+ int i, nr_ranges, max_ranges;
+ efi_status_t status;
+
+ mem_map = NULL;
+ mshv_reserved = NULL;
+
+ status = mshv_efi_init();
+ if (status == EFI_NOT_FOUND) {
+ /*
+ * If the protocol is not installed
+ * we are in a standard Linux boot
+ */
+ return EFI_SUCCESS;
+ }
+
+ /*
+ * Get mshv memory map to figure out mshv reserved ranges.
+ */
+
+ map_sz = 0;
+ mshv_get_hv_ranges((void *)&mem_map, &map_sz, &desc_sz);
+
+ /*
+ * Build an array of kernel 'struct resource' objects that contain mshv
+ * reserved ranges. This array is populated via a command line parameter
+ * called 'hyperv_resvd_new'.
+ */
+
+ status = mshv_realloc_ranges(&mshv_reserved,
+ &mshv_reserved_sz,
+ MSHV_RESERVED_RANGES_COUNT);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to allocate space for hv ranges with code %d",
+ status);
+
+ max_ranges = MSHV_RESERVED_RANGES_COUNT;
+ mshv_range = mshv_reserved;
+ prev = NULL;
+ nr_desc = map_sz / desc_sz;
+ for (i = 0, nr_ranges = 0; i < nr_desc; i++) {
+ efi_memory_desc_t *d;
+
+ d = efi_memdesc_ptr(mem_map, desc_sz, i);
+
+ /* Merge adjacent ranges */
+ if (prev && ((prev->end + 1) == d->phys_addr)) {
+ prev->end += (d->num_pages << PAGE_SHIFT);
+ continue;
+ }
+
+ mshv_range->name = "Hypervisor Code and Data";
+ mshv_range->flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM;
+ mshv_range->start = d->phys_addr;
+ mshv_range->end = d->phys_addr + (d->num_pages << PAGE_SHIFT) - 1;
+
+ prev = mshv_range++;
+ nr_ranges++;
+ if (nr_ranges >= max_ranges) {
+ /* Extend the array to accommodate more ranges */
+ max_ranges += MSHV_RESERVED_RANGES_COUNT;
+ status = mshv_realloc_ranges(&mshv_reserved, &mshv_reserved_sz,
+ max_ranges);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to allocate hv ranges: %d", status);
+
+ prev = &mshv_reserved[nr_ranges - 1];
+ mshv_range = prev + 1;
+ }
+ }
+
+ status = mshv_populate_ranges(boot_params, mem_map, map_sz, desc_sz);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to allocate space for hv ranges with code %d",
+ status);
+
+ /* Build an indirect setup_data for each mshv reserved range. */
+ status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
+ nr_ranges * sizeof(struct mshv_setup_data),
+ (void **)&sd_block);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to allocate space for hv ranges: error code %d", status);
+
+ memset((void *)sd_block, 0, nr_ranges * sizeof(struct mshv_setup_data));
+ setup_data_itr = (struct setup_data **)&boot_params->hdr.setup_data;
+
+ while (*setup_data_itr && (*setup_data_itr)->next)
+ setup_data_itr = (struct setup_data **)&(*setup_data_itr)->next;
+
+ *setup_data_itr = (struct setup_data *)sd_block;
+
+ for (i = 0; i < nr_ranges; i++) {
+ start = mshv_reserved[i].start;
+ end = mshv_reserved[i].end;
+
+ sd_block[i].sd.type = SETUP_INDIRECT;
+ sd_block[i].sd.len = sizeof(struct setup_indirect);
+ sd_block[i].sd.next = (__u64)&sd_block[i + 1];
+
+ sd_block[i].si.type = SETUP_MSHV;
+ sd_block[i].si.reserved = 0;
+ sd_block[i].si.len = end - start + 1;
+ sd_block[i].si.addr = start;
+ }
+
+ /*
+ * Remove the trailing 'next' pointer which is currently
+ * outside of the struct mshv_setup_data buffer.
+ */
+
+ sd_block[nr_ranges - 1].sd.next = 0;
+
+ efi_bs_call(free_pool, mem_map);
+
+ return EFI_SUCCESS;
+}
diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c
index cef32e2c82d8..eab2539488ee 100644
--- a/drivers/firmware/efi/libstub/x86-stub.c
+++ b/drivers/firmware/efi/libstub/x86-stub.c
@@ -20,6 +20,7 @@
#include "efistub.h"
#include "x86-stub.h"
+#include "efi-mshv.h"
extern char _bss[], _ebss[];
@@ -737,6 +738,7 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map,
void *priv)
{
const char *signature;
+ efi_status_t status;
struct exit_boot_struct *p = priv;
signature = efi_is_64bit() ? EFI64_LOADER_SIGNATURE
@@ -751,6 +753,11 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map,
&p->efi->efi_memmap, &p->efi->efi_memmap_hi);
p->efi->efi_memmap_size = map->map_size;
+ /* Notify hypervisor of efi runtime services pages */
+ status = mshv_set_efi_rt_range(map);
+ if (status != EFI_SUCCESS)
+ return status;
+
return EFI_SUCCESS;
}
@@ -918,7 +925,7 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
const struct linux_efi_initrd *initrd = NULL;
unsigned long kernel_entry;
struct setup_header *hdr;
- efi_status_t status;
+ efi_status_t status, mshv_status;
efi_system_table = sys_table_arg;
/* Check if we were booted by the EFI firmware */
@@ -1011,6 +1018,8 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
/* Ask the firmware to clear memory on unclean shutdown */
efi_enable_reset_attack_mitigation();
+ mshv_status = mshv_efi_setup(boot_params);
+
efi_random_get_seed();
efi_retrieve_eventlog();
@@ -1035,6 +1044,13 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
*/
sev_enable(boot_params);
+ /*
+ * Launch the hypervisor before switching to 5 level paging.
+ * The hypervisor does not support being launched with LA57 enabled.
+ */
+ if (mshv_status == EFI_SUCCESS)
+ mshv_status = mshv_launch();
+
efi_5level_switch();
enter_kernel(kernel_entry, boot_params);
diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
index 6b67ac616789..a642c110cf5d 100644
--- a/drivers/hv/hv_common.c
+++ b/drivers/hv/hv_common.c
@@ -26,6 +26,10 @@
#include <linux/kmsg_dump.h>
#include <linux/sizes.h>
#include <linux/slab.h>
+#include <linux/memblock.h>
+#include <linux/crash_dump.h>
+#include <linux/kstrtox.h>
+#include <linux/string.h>
#include <linux/dma-map-ops.h>
#include <linux/set_memory.h>
#include <hyperv/hvhdk.h>
@@ -863,3 +867,81 @@ const char *hv_result_to_string(u64 status)
return "Unknown";
}
EXPORT_SYMBOL_GPL(hv_result_to_string);
+
+struct resource hv_mshv_res[HV_MAX_RESVD_RANGES];
+u32 ranges_nr;
+
+/*
+ * Parse "hyperv_resvd_new=<size>!<address>,<size>!<address>,...", specifying a
+ * list of memory ranges that are reserved by the loader for the hypervisor.
+ */
+static int __init hv_parse_hyperv_resvd_new(char *arg)
+{
+ int i = 0;
+
+ if (is_kdump_kernel())
+ return 0;
+
+ while (arg && *arg) {
+ unsigned long long region_start, region_sz;
+ char *range, *addr_str;
+
+ if (i >= HV_MAX_RESVD_RANGES) {
+ pr_err("Hyper-V: too many hyperv_resvd_new ranges specified\n");
+ return 0;
+ }
+
+ /* Each range is "<size>!<address>", ranges separated by ','. */
+ range = strsep(&arg, ",");
+ addr_str = strchr(range, '!');
+ if (!addr_str) {
+ pr_err("Hyper-V: invalid format for hyperv_resvd_new: %s\n", range);
+ return 0;
+ }
+ *addr_str++ = '\0';
+
+ if (kstrtoull(range, 16, ®ion_sz) || !region_sz ||
+ kstrtoull(addr_str, 16, ®ion_start) || !region_start) {
+ pr_err("Hyper-V: invalid format for hyperv_resvd_new\n");
+ return 0;
+ }
+
+ memblock_reserve(region_start, region_sz);
+
+ hv_mshv_res[i].name = "Hypervisor Code and Data";
+ hv_mshv_res[i].flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM;
+ hv_mshv_res[i].start = region_start;
+ hv_mshv_res[i].end = region_start + region_sz - 1;
+
+ ++i;
+ }
+
+ ranges_nr = i;
+
+ return 0;
+}
+early_param("hyperv_resvd_new", hv_parse_hyperv_resvd_new);
+
+/*
+ * Log memory ranges that the hypervisor uses. The ranges are marked
+ * by a custom bootloader.
+ */
+void __init hv_dump_mshv_memory(void)
+{
+ u64 start, end;
+ int i;
+
+ for (i = 0; i < ranges_nr; i++) {
+ start = hv_mshv_res[i].start;
+ end = hv_mshv_res[i].end;
+ pr_info("Hyper-V reserve [mem %#018Lx-%#018Lx]\n", start, end);
+ }
+}
+
+void __init hv_mark_resources(void)
+{
+ int i, max = ARRAY_SIZE(hv_mshv_res);
+
+ for (i = 0; i < max && hv_mshv_res[i].end; i++)
+ insert_resource(&iomem_resource, &hv_mshv_res[i]);
+}
diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
index bf601d67cecb..83e0dff5371a 100644
--- a/include/asm-generic/mshyperv.h
+++ b/include/asm-generic/mshyperv.h
@@ -212,6 +212,13 @@ int hv_common_cpu_init(unsigned int cpu);
int hv_common_cpu_die(unsigned int cpu);
void hv_identify_partition_type(void);
+#define HV_MAX_RESVD_RANGES 32
+extern struct resource hv_mshv_res[HV_MAX_RESVD_RANGES];
+extern u32 ranges_nr;
+
+void __init hv_dump_mshv_memory(void);
+void __init hv_mark_resources(void);
+
/**
* hv_cpu_number_to_vp_number() - Map CPU to VP.
* @cpu_number: CPU number in Linux terms
diff --git a/include/linux/efi.h b/include/linux/efi.h
index ccbc35479684..be3fb83194b9 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -391,6 +391,7 @@ void efi_native_runtime_setup(void);
#define EFI_LOAD_FILE_PROTOCOL_GUID EFI_GUID(0x56ec3091, 0x954c, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
#define EFI_LOAD_FILE2_PROTOCOL_GUID EFI_GUID(0x4006c0c1, 0xfcb3, 0x403e, 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d)
#define EFI_RT_PROPERTIES_TABLE_GUID EFI_GUID(0xeb66918a, 0x7eef, 0x402a, 0x84, 0x2e, 0x93, 0x1d, 0x21, 0xc3, 0x8a, 0xe9)
+#define EFI_MSHV_MEDIA_PROTOCOL_GUID EFI_GUID(0x098d423a, 0x6ca5, 0x4ad4, 0x90, 0xfa, 0x72, 0xc3, 0xce, 0x22, 0xc8, 0xd0)
#define EFI_DXE_SERVICES_TABLE_GUID EFI_GUID(0x05ad34ba, 0x6f02, 0x4214, 0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9)
#define EFI_SMBIOS_PROTOCOL_GUID EFI_GUID(0x03583ff6, 0xcb36, 0x4940, 0x94, 0x7e, 0xb9, 0xb3, 0x9f, 0x4a, 0xfa, 0xf7)
#define EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID EFI_GUID(0xf4560cf6, 0x40ec, 0x4b4a, 0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89)
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 13/13] mshv: set up own SynIC registers on a nested root partition
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
` (11 preceding siblings ...)
2026-08-07 13:51 ` [PATCH v1 12/13] hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader) Wei Hu
@ 2026-08-07 13:51 ` Wei Hu
12 siblings, 0 replies; 14+ messages in thread
From: Wei Hu @ 2026-08-07 13:51 UTC (permalink / raw)
To: linux-hyperv
Cc: linux-kernel, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Wei Hu
From: Wei Hu <weh@microsoft.com>
Upstream mshv_synic_cpu_init()/_exit() skip programming the SynIC
SIMP/SIEFP/SCONTROL registers when VMBus is active (hv_vmbus_exists()),
assuming VMBus's hv_hyp_synic_enable_regs() already provisioned them.
That assumption is wrong for a *nested* root partition. There, VMBus
programs the *nested* SynIC MSRs (HV_X64_MSR_NESTED_SIMP, ...) via
hv_set_msr()'s nested remap, while mshv_synic reads/writes the
*non-nested* SynIC MSRs (hv_get/set_non_nested_msr). As a result mshv
maps the wrong message page, never receives the SYNIC intercept message
that carries the async hypercall completion, and mshv_async_hvcall_handler
blocks forever -- hanging SEV-SNP guest creation from user space.
Gate the "VMBus owns the SynIC registers" optimization on !hv_nested so
that a nested root partition programs its own non-nested SynIC registers,
restoring correct intercept/async-hypercall delivery. No change for a
non-nested (bare-metal) root.
Signed-off-by: Wei Hu <weh@microsoft.com>
---
drivers/hv/mshv_synic.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c
index 7c168e5a740d..7d714ee5d226 100644
--- a/drivers/hv/mshv_synic.c
+++ b/drivers/hv/mshv_synic.c
@@ -472,7 +472,13 @@ static int mshv_synic_cpu_init(unsigned int cpu)
* VMBus owns SIMP/SIEFP/SCONTROL when it is active.
* See hv_hyp_synic_enable_regs() for that initialization.
*/
- bool vmbus_active = hv_vmbus_exists();
+ /*
+ * On a nested root partition VMBus programs the *nested* SynIC MSRs
+ * (SIMP/SIEFP/SCONTROL) via hv_set_msr()'s nested remap, whereas the
+ * code below reads/writes the non-nested SynIC MSRs. So when nested,
+ * do not defer SynIC setup to VMBus -- set up our own registers.
+ */
+ bool vmbus_active = hv_vmbus_exists() && !hv_nested;
/*
* Map the SYNIC message page. When VMBus is not active the
@@ -593,7 +599,13 @@ static int mshv_synic_cpu_exit(unsigned int cpu)
struct hv_synic_event_ring_page **event_ring_page =
&spages->synic_event_ring_page;
/* VMBus owns SIMP/SIEFP/SCONTROL when it is active */
- bool vmbus_active = hv_vmbus_exists();
+ /*
+ * On a nested root partition VMBus programs the *nested* SynIC MSRs
+ * (SIMP/SIEFP/SCONTROL) via hv_set_msr()'s nested remap, whereas the
+ * code below reads/writes the non-nested SynIC MSRs. So when nested,
+ * do not defer SynIC setup to VMBus -- set up our own registers.
+ */
+ bool vmbus_active = hv_vmbus_exists() && !hv_nested;
/* Disable the interrupt */
sint.as_uint64 = hv_get_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX);
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-08-07 13:52 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
2026-08-07 13:51 ` [PATCH v1 01/13] mshv: add SEV-SNP UAPI definitions Wei Hu
2026-08-07 13:51 ` [PATCH v1 02/13] mshv: add SEV-SNP PSP request hypercall Wei Hu
2026-08-07 13:51 ` [PATCH v1 03/13] mshv: add SEV-SNP isolated page hypercalls Wei Hu
2026-08-07 13:51 ` [PATCH v1 04/13] mshv: wire SEV-SNP partition ioctls Wei Hu
2026-08-07 13:51 ` [PATCH v1 05/13] hyperv: fix hv_input_get_system_property layout for SNP status Wei Hu
2026-08-07 13:51 ` [PATCH v1 06/13] mshv: detect and report SEV-SNP support at init Wei Hu
2026-08-07 13:51 ` [PATCH v1 07/13] mshv: default to safe partition CPU features Wei Hu
2026-08-07 13:51 ` [PATCH v1 08/13] mshv: accept partial CPU feature banks Wei Hu
2026-08-07 13:51 ` [PATCH v1 09/13] mshv: define full processor and xsave feature masks Wei Hu
2026-08-07 13:51 ` [PATCH v1 10/13] mshv: unmap SNP memory before state teardown Wei Hu
2026-08-07 13:51 ` [PATCH v1 11/13] mshv: unlock SNP pages on panic for crashdump collection Wei Hu
2026-08-07 13:51 ` [PATCH v1 12/13] hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader) Wei Hu
2026-08-07 13:51 ` [PATCH v1 13/13] mshv: set up own SynIC registers on a nested root partition Wei Hu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox