* [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; 25+ 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] 25+ 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 14:03 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 02/13] mshv: add SEV-SNP PSP request hypercall Wei Hu
` (11 subsequent siblings)
12 siblings, 1 reply; 25+ 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] 25+ 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; 25+ 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] 25+ 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 14:21 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 04/13] mshv: wire SEV-SNP partition ioctls Wei Hu
` (9 subsequent siblings)
12 siblings, 1 reply; 25+ 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] 25+ 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 14:45 ` sashiko-bot
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, 1 reply; 25+ 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] 25+ 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; 25+ 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] 25+ 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 15:04 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 07/13] mshv: default to safe partition CPU features Wei Hu
` (6 subsequent siblings)
12 siblings, 1 reply; 25+ 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] 25+ 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 15:15 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 08/13] mshv: accept partial CPU feature banks Wei Hu
` (5 subsequent siblings)
12 siblings, 1 reply; 25+ 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] 25+ 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 15:30 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 09/13] mshv: define full processor and xsave feature masks Wei Hu
` (4 subsequent siblings)
12 siblings, 1 reply; 25+ 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] 25+ 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 15:42 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 10/13] mshv: unmap SNP memory before state teardown Wei Hu
` (3 subsequent siblings)
12 siblings, 1 reply; 25+ 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] 25+ 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 15:53 ` sashiko-bot
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, 1 reply; 25+ 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] 25+ 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 16:11 ` sashiko-bot
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, 1 reply; 25+ 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] 25+ 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 16:22 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 13/13] mshv: set up own SynIC registers on a nested root partition Wei Hu
12 siblings, 1 reply; 25+ 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] 25+ 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
2026-08-07 16:36 ` sashiko-bot
12 siblings, 1 reply; 25+ 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] 25+ messages in thread
* Re: [PATCH v1 01/13] mshv: add SEV-SNP UAPI definitions
2026-08-07 13:51 ` [PATCH v1 01/13] mshv: add SEV-SNP UAPI definitions Wei Hu
@ 2026-08-07 14:03 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-07 14:03 UTC (permalink / raw)
To: Wei Hu; +Cc: linux-hyperv
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The newly added UAPI structure `struct mshv_complete_isolated_import` embeds `union hv_partition_complete_isolated_import_data` directly by value. However, this union is defined in `include/hyperv/hvhdk_mini.h`, which is a kernel-internal header and not exported to userspace. Because userspace headers cannot access this internal definition, the union evaluates to an undeclared, incomplete type. Any userspace application including `<linux/mshv.h>` will now fail to compile with a hard compiler error, completely breaking the UAPI.
--
--- Patch [1]: [PATCH v1 01/13] mshv: add SEV-SNP UAPI definitions ---
commit 0c72e15c0e7fd5f724d97fa58e436ee572659cfa
Author: Wei Liu <wei.liu@kernel.org>
mshv: add SEV-SNP UAPI definitions
This adds the MSHV UAPI and Hyper-V ABI definitions needed by the SEV-SNP
partition ioctls. It includes structures for SNP isolation type, GPA
host-access, isolated-page requests, and SEV control register definitions.
> diff --git a/include/uapi/linux/mshv.h b/include/uapi/linux/mshv.h
> index 32ff92b6342b2..4e7f3038f0c67 100644
> --- a/include/uapi/linux/mshv.h
> +++ b/include/uapi/linux/mshv.h
[ ... ]
> @@ -219,6 +220,55 @@ struct mshv_gpap_access_bitmap {
[ ... ]
> +struct mshv_complete_isolated_import {
> + union hv_partition_complete_isolated_import_data import_data;
> +};
[Severity: High]
Will this cause a compilation failure for userspace applications including
<linux/mshv.h>?
It appears union hv_partition_complete_isolated_import_data is defined in
include/hyperv/hvhdk_mini.h, which is a kernel-internal header and not exported
to userspace.
Because userspace headers cannot access this internal definition, embedding the
union directly by value here will evaluate to an undeclared, incomplete type
for any userspace application trying to use the UAPI.
[ ... ]
> @@ -254,6 +304,11 @@ struct mshv_root_hvcall {
[ ... ]
> +#define MSHV_COMPLETE_ISOLATED_IMPORT _IOW(MSHV_IOCTL, 0xF4, struct mshv_complete_isolated_import)
[Severity: High]
Does this ioctl macro expansion also fail in userspace for the same reason?
The _IOW macro implicitly requires the size of the incomplete type during
expansion, which will also break compilation when the underlying struct relies
on the kernel-internal union.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260807135134.303943-1-weh@linux.microsoft.com?part=1
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v1 03/13] mshv: add SEV-SNP isolated page hypercalls
2026-08-07 13:51 ` [PATCH v1 03/13] mshv: add SEV-SNP isolated page hypercalls Wei Hu
@ 2026-08-07 14:21 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-07 14:21 UTC (permalink / raw)
To: Wei Hu; +Cc: linux-hyperv
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The `completed` variable is evaluated from `status` before the asynchronous completion handler updates it, leading to a stale completion count and an infinite loop.
--
--- Patch [3]: [PATCH v1 03/13] mshv: add SEV-SNP isolated page hypercalls ---
commit 3c19c21fc983589586a3386b97c714ce13aec72c
Author: Wei Liu <wei.liu@kernel.org>
mshv: add SEV-SNP isolated page hypercalls
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.
> diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
> index 48abe338c06f8..9ddc8dd73da7a 100644
> --- a/drivers/hv/mshv_root_hv_call.c
> +++ b/drivers/hv/mshv_root_hv_call.c
[ ... ]
> @@ -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)
> +{
[ ... ]
> + 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);
[Severity: High]
If the hypercall returns HV_STATUS_CALL_PENDING, the completion_handler updates
the status variable, but the completed variable was already evaluated from the
initial status.
Could this lead to completed being 0 (or a stale value), which would then
prevent gpa and remaining from advancing correctly?
> +
> + 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;
[Severity: High]
If completed is not updated after the completion handler executes, does this
result in an infinite loop since remaining will not decrease?
> + }
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260807135134.303943-1-weh@linux.microsoft.com?part=3
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v1 04/13] mshv: wire SEV-SNP partition ioctls
2026-08-07 13:51 ` [PATCH v1 04/13] mshv: wire SEV-SNP partition ioctls Wei Hu
@ 2026-08-07 14:45 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-07 14:45 UTC (permalink / raw)
To: Wei Hu; +Cc: linux-hyperv
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
New issues:
- [Critical] The `MSHV_MODIFY_GPA_HOST_ACCESS` ioctl lacks physical contiguity validation when `MSHV_GPA_HOST_ACCESS_BIT_LARGE_PAGE` is specified, allowing a malicious guest to alter host access for arbitrary physical memory.
- [Critical] Time-of-Check to Time-of-Use (TOCTOU) race condition in `mshv_gpfns_to_pages()` drops the region lock without pinning pages, allowing use-after-free of `struct page` pointers.
- [High] A failure in `destroy_snp_partition_state()` aborts `destroy_partition()` early, permanently leaking the partition struct, its virtual processors (VPs), memory regions, and hypervisor resources.
- [High] Unbounded `kcalloc` of user-controlled `page_count` in `mshv_partition_ioctl_modify_gpa_host_access` can trigger `MAX_ORDER` kernel warnings and unexpected allocation failures.
- [High] Stale `completed` rep count in `hv_call_import_isolated_pages()` and `mshv_ioctl_passthru_hvcall()` causes an infinite loop during asynchronous rep hypercalls.
Pre-existing issues:
- [Critical] Missing `local_irq_restore` on the error path in `hv_call_modify_spa_host_access()` permanently disables interrupts.
- [High] A flawed completion check in `mshv_init_async_handler()` permanently bricks the partition after the first async hypercall completes.
- [High] The passthrough hypercall ioctl (`mshv_ioctl_passthru_hvcall`) leaks uninitialized kernel memory to userspace due to allocating output pages without `__GFP_ZERO`.
--
--- Patch [4]: [PATCH v1 04/13] mshv: wire SEV-SNP partition ioctls ---
commit bad2199bf3a355afbf48396c10943374ce6f9e11
Author: Wei Liu <wei.liu@kernel.org>
Subject: mshv: wire SEV-SNP partition ioctls
Wire the SEV-SNP partition ioctls for GPA host-access changes,
isolated-page import, PSP guest requests, AP creation, and SNP partition
teardown.
> diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
> --- 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(...)
[ ... ]
> +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)
> +{
[ ... ]
> + while (remaining) {
[ ... ]
> + 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;
> + }
[Severity: High]
Will this cause an infinite loop during asynchronous rep hypercalls?
When the hypercall returns HV_STATUS_CALL_PENDING, completed is evaluated
as 0 before the completion_handler is invoked. After the handler updates
the status with the final result, completed is never recalculated.
This means remaining -= completed subtracts 0, causing the loop to spin
indefinitely and hanging the kernel thread.
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -641,6 +646,42 @@ mshv_partition_region_by_gfn_get(struct mshv_partition *p, u64 gfn)
> +static int mshv_gpfns_to_pages(struct mshv_partition *partition,
> + const u64 *gpfns, u64 page_count,
> + struct page **pages)
> +{
[ ... ]
> + 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);
> + }
[Severity: Critical]
Does this drop the region lock without pinning the returned pages?
The struct page pointers are copied into the pages array under the region
mutex, but the lock is dropped without elevating the page reference count
with get_page().
A concurrent thread or user action like munmap could trigger the MMU
interval notifier, unmap the pages, and return them to the allocator.
Later, when hv_call_modify_spa_host_access() processes these pointers via
page_to_pfn(), it might trick the hypervisor into revoking host access to
memory that has already been reallocated to another process or the kernel.
> @@ -1625,6 +1666,260 @@ mshv_partition_ioctl_initialize(struct mshv_partition *partition)
> +static long
> +mshv_partition_ioctl_modify_gpa_host_access(struct mshv_partition *partition,
> + void __user *user_args)
> +{
[ ... ]
> + 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;
> + }
[Severity: High]
Could this unbounded allocation trigger kernel warnings or unexpected failures?
The args.page_count parameter is user-controlled and directly passed to
kcalloc with GFP_KERNEL. Since kcalloc relies on contiguous physical memory
allocation, passing an excessively large count could exceed the MAX_ORDER
limit.
This would trigger a loud kernel warning stack trace and fail the request.
Should this cap the page count or fallback to kvcalloc instead?
> + 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);
[Severity: Critical]
Can a malicious guest exploit the large page flag to alter host access for
arbitrary physical memory?
When MSHV_GPA_HOST_ACCESS_BIT_LARGE_PAGE is passed,
hv_call_modify_spa_host_access() iterates in 512-page chunks, extracts the
PFN from only the first struct page, and passes it to the hypervisor as the
base of a contiguous 2MB large page:
drivers/hv/mshv_root_hv_call.c:hv_call_modify_spa_host_access()
for (i = 0; i < rep_count; i++) {
u64 index = (done + i) << large_shift;
if (index >= page_struct_count)
return -EINVAL;
input_page->spa_page_list[i] =
page_to_pfn(pages[index]);
}
However, mshv_gpfns_to_pages() resolves the user-provided GFNs into struct
page pointers completely independently. If a user provides 512 unrelated
GFNs, the hypervisor might grant or revoke permissions on the 2MB physical
block starting at the first page's PFN, bypassing validation.
[Severity: Critical]
This is a pre-existing issue, but wiring up this ioctl exposes a pre-existing
error path that leaves local interrupts disabled.
In the underlying hv_call_modify_spa_host_access() function, local IRQs are
disabled via local_irq_save(), but an early return on invalid indices fails
to restore them:
drivers/hv/mshv_root_hv_call.c:hv_call_modify_spa_host_access()
local_irq_save(irq_flags);
...
for (i = 0; i < rep_count; i++) {
u64 index = (done + i) << large_shift;
if (index >= page_struct_count)
return -EINVAL;
...
}
...
local_irq_restore(irq_flags);
Returning -EINVAL without calling local_irq_restore() will permanently
disable hardware interrupts on the executing CPU.
> +static long
> +mshv_partition_ioctl_import_isolated_pages(struct mshv_partition *partition,
> + void __user *user_args)
> +{
[ ... ]
> + ret = mshv_init_async_handler(partition);
> + if (ret)
> + goto out;
[Severity: High]
This is a pre-existing issue, but will this permanently block async
hypercalls after the very first one completes?
The mshv_init_async_handler() function uses completion_done() to check if
an async hypercall is in progress:
drivers/hv/mshv_root_main.c:mshv_init_async_handler()
if (completion_done(&partition->async_hypercall)) {
pt_err(partition,
"Cannot issue async hypercall while another one in progress!\n");
return -EPERM;
}
However, completion_done() returns true if a completion has already finished
successfully (count > 0). If any previous async hypercall has completed,
this will always evaluate to true, returning -EPERM for all future async
hypercalls and breaking the teardown path for the partition.
> @@ -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;
[Severity: High]
This is a pre-existing issue, but could this passthrough ioctl leak
uninitialized kernel memory to userspace?
The mshv_ioctl_passthru_hvcall() function allocates the output page without
GFP_ZERO:
drivers/hv/mshv_root_main.c:mshv_ioctl_passthru_hvcall()
pages_order = args.out_ptr ? 1 : 0;
page = alloc_pages(GFP_KERNEL, pages_order);
...
if (!ret && output_pg &&
copy_to_user((void __user *)args.out_ptr, output_pg, args.out_sz))
ret = -EFAULT;
If a user requests a large out_sz, and the hypervisor writes fewer bytes to
the output buffer, the remaining uninitialized bytes from the kernel
allocation are copied to userspace.
> @@ -1782,6 +2149,9 @@ static void destroy_partition(struct mshv_partition *partition)
[ ... ]
> 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
[Severity: High]
Does this early return permanently leak the partition and its resources?
If destroy_snp_partition_state() fails and returns a non-zero code, this
triggers an early return. The partition's reference count is already 0 at
this point.
Because the function returns early, the rest of destroy_partition() is
skipped entirely. The virtual processors are never removed, state pages
remain mapped, and the partition is never deleted from the hypervisor,
leaving the resources permanently leaked.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260807135134.303943-1-weh@linux.microsoft.com?part=4
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v1 06/13] mshv: detect and report SEV-SNP support at init
2026-08-07 13:51 ` [PATCH v1 06/13] mshv: detect and report SEV-SNP support at init Wei Hu
@ 2026-08-07 15:04 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-07 15:04 UTC (permalink / raw)
To: Wei Hu; +Cc: linux-hyperv
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Failing to query optional SEV-SNP support aborts the entire `mshv` module initialization, preventing non-SNP virtual machines from running on unsupported platforms.
- [Low] The helper function `hv_snp_status_to_string` is defined unconditionally but only used inside an x86-specific block, causing a build failure on non-x86 architectures.
--
--- Patch [6]: [PATCH v1 06/13] mshv: detect and report SEV-SNP support at init ---
commit e3193a7357d34307ce8275f28a3eb193fd3183cf
Author: Wei Liu <wei.liu@kernel.org>
mshv: detect and report SEV-SNP support at init
Query Hyper-V dynamic processor features during mshv initialization to
determine SEV-SNP availability, and report the result.
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index 3590a8ea24b5f..cc38507570ac4 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)
> +{
[ ... ]
> + 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);
> + }
[Severity: High]
Does propagating this error cause driver initialization to abort on platforms
that do not support SEV-SNP?
If the hypervisor does not support this dynamic processor feature, returning
an error here will cause the entire mshv module to fail to load because it
is propagated up to mshv_parent_partition_init().
[ ... ]
> +static const char *hv_snp_status_to_string(enum hv_snp_status status)
> +{
> + switch (status) {
> + case HV_SNP_STATUS_NONE:
> + return "not available";
[Severity: Low]
This function is defined unconditionally, but it is only called from within
an `#if defined(__x86_64__)` block in mshv_check_sev_snp_support().
Does this cause an unused function warning on non-x86 architectures (such as
ARM64) that could break the build when CONFIG_WERROR is enabled?
[ ... ]
> +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);
> + }
[Severity: High]
Similar to above, does returning an error here prevent the mshv module from
loading on systems without SEV-SNP support (like Intel CPUs) or on older
Hyper-V versions?
It appears this will abort module initialization if the hypercall fails.
> +
> + 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;
> +
[Severity: High]
This is where the error from the informational SEV-SNP checks causes module
initialization to abort. Should these functions handle unsupported features
gracefully instead of failing the init?
> ret = root_scheduler_init(dev);
> if (ret)
> goto synic_cleanup;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260807135134.303943-1-weh@linux.microsoft.com?part=6
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v1 07/13] mshv: default to safe partition CPU features
2026-08-07 13:51 ` [PATCH v1 07/13] mshv: default to safe partition CPU features Wei Hu
@ 2026-08-07 15:15 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-07 15:15 UTC (permalink / raw)
To: Wei Hu; +Cc: linux-hyperv
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Functional regression and uninitialized stack memory use on ARM64 for legacy partition creation.
Pre-existing issues:
- [Medium] TOCTOU double-fetch race allows validation and use of flag-gated parameters outside their flag gate.
--
--- Patch [7]: [PATCH v1 07/13] mshv: default to safe partition CPU features ---
commit 85299e9eb98a0db0daae1adea69f8e739d17d8db
Author: Wei Liu <wei.liu@kernel.org>
mshv: default to safe partition CPU features
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.
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index cc38507570ac4..11d3c86050b45 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_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)))
> return -EFAULT;
[Severity: Medium]
This is a pre-existing issue, but does this second fetch from userspace
introduce a race condition?
If a concurrent userspace thread clears the MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES
flag between the first and second fetch, args.pt_flags will be overwritten
with the new value. The code does not re-verify the flag before proceeding to
validate and use extended v2 fields like args.pt_num_cpu_fbanks.
This could allow the kernel to operate on uninitialized userspace garbage.
[ ... ]
> @@ -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 */
[Severity: High]
Does removing this fallback initialization break legacy v1 callers on ARM64?
With this code removed, disabled_procs->as_uint64[i] is unconditionally set
to U64_MAX earlier in mshv_ioctl_process_pt_flags(), which disables all
processor features. Because the new default initializations are inside an
#if IS_ENABLED(CONFIG_X86_64) block, ARM64 callers never get those defaults
nor the old 0 initialization.
Additionally, disabled_xsave points to the disabled_processor_xsave_features
field of cr_props (which is on the stack in mshv_ioctl_create_partition).
Since it is no longer initialized to 0 here for non-x86 v1 callers, will it
send uninitialized stack memory to the hypervisor?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260807135134.303943-1-weh@linux.microsoft.com?part=7
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v1 08/13] mshv: accept partial CPU feature banks
2026-08-07 13:51 ` [PATCH v1 08/13] mshv: accept partial CPU feature banks Wei Hu
@ 2026-08-07 15:30 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-07 15:30 UTC (permalink / raw)
To: Wei Hu; +Cc: linux-hyperv
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Kernel stack memory leak to the hypervisor on ARM64 when the MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES flag is omitted.
--
--- Patch [8]: [PATCH v1 08/13] mshv: accept partial CPU feature banks ---
commit f76ad6e0838cf1ac9e5affb070dfe61ab6bea40d
Author: Wei Liu <wei.liu@kernel.org>
mshv: accept partial CPU feature banks
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.
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index 11d3c86050b45..729916e1eacff 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)
[Severity: Medium]
Does this code leak uninitialized kernel stack memory to the hypervisor on
ARM64?
When a user calls the MSHV_CREATE_PARTITION ioctl without the
MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES flag, the initialization of
disabled_processor_xsave_features appears to be skipped on ARM64.
In mshv_ioctl_create_partition(), creation_properties is allocated on the
kernel stack uninitialized:
struct hv_partition_creation_properties creation_properties;
If the MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES flag is omitted, the block below
is skipped in mshv_ioctl_process_pt_flags():
if (args.pt_flags & BIT_ULL(MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES)) {
The default initialization of disabled_processor_xsave_features is
conditionally excluded via #if IS_ENABLED(CONFIG_X86_64), leaving it
uninitialized on ARM64.
Later, hv_call_create_partition() copies the entire structure to the
hypercall input page:
memcpy(&input->partition_creation_properties, &creation_properties,
sizeof(creation_properties));
Could this send 8 bytes of uninitialized kernel stack memory to the
hypervisor, potentially causing it to disable random XSAVE features for the
guest VM?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260807135134.303943-1-weh@linux.microsoft.com?part=8
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v1 09/13] mshv: define full processor and xsave feature masks
2026-08-07 13:51 ` [PATCH v1 09/13] mshv: define full processor and xsave feature masks Wei Hu
@ 2026-08-07 15:42 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-07 15:42 UTC (permalink / raw)
To: Wei Hu; +Cc: linux-hyperv
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Uninitialized kernel stack memory is leaked to the hypervisor on non-x86 architectures when the legacy v1 ioctl is used.
- [Low] Typo in the bitfield name `vrew_clear_supported` (should be `verw_clear_supported`).
--
--- Patch [9]: [PATCH v1 09/13] mshv: define full processor and xsave feature masks ---
commit 758f4dc3eb51bb86e9e77dff7703639f92529960
Author: Wei Liu <wei.liu@kernel.org>
mshv: define full processor and xsave feature masks
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.
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index 729916e1eacff..c81a8d0493c19 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_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 */
[Severity: Medium]
Does this leave disabled_xsave uninitialized on non-x86 architectures when
using the legacy v1 ioctl?
When MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES is not set on ARM64, the fallback
initialization that clears disabled_xsave->as_uint64 is skipped because it is
nested inside the v2 struct check block further down in
mshv_ioctl_process_pt_flags().
This leaves 8 bytes of uninitialized kernel stack in
creation_properties.disabled_processor_xsave_features (allocated in
mshv_ioctl_create_partition()), which is then passed to the hypervisor via
hv_call_create_partition().
This appears to have been introduced earlier in this patch series in the commit
"mshv: default to safe partition CPU features" when the original else block
clearing the legacy features was removed.
> diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h
> index 691ac495a095d..54b0e671a028f 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 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;
[Severity: Low]
This isn't a bug, but there is a typo in the bitfield name vrew_clear_supported,
it should be verw_clear_supported.
> + u64 tsa_l1_no_supported : 1;
> + u64 tsa_sq_no_supported : 1;
> + u64 lass_support : 1;
> + u64 reserved_bank1 : 2;
> } __packed;
> };
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260807135134.303943-1-weh@linux.microsoft.com?part=9
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v1 10/13] mshv: unmap SNP memory before state teardown
2026-08-07 13:51 ` [PATCH v1 10/13] mshv: unmap SNP memory before state teardown Wei Hu
@ 2026-08-07 15:53 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-07 15:53 UTC (permalink / raw)
To: Wei Hu; +Cc: linux-hyperv
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Userspace can trigger a kernel memory leak or DoS by unmapping memory regions of an active encrypted partition.
--
--- Patch [10]: [PATCH v1 10/13] mshv: unmap SNP memory before state teardown ---
commit f9f61905fec1fff131a4505ade5ecacaee8647a1
Author: Wei Liu <wei.liu@kernel.org>
mshv: unmap SNP memory before state teardown
This commit updates the partition teardown process to unmap memory regions
before destroying the SNP partition state. The regions are only released
after the SNP state is fully torn down, which allows the host to regain
access while the partition is still secure.
[ ... ]
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index c81a8d0493c19..0fbd2158968da 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) {
[Severity: High]
This is a pre-existing issue, but does a similar problem occur when userspace
unmaps memory via the MSHV_SET_GUEST_MEMORY ioctl?
When a userspace process unmaps memory from an active SEV-SNP partition,
mshv_unmap_user_memory() drops the region unconditionally without checking the
SNP state:
drivers/hv/mshv_root_main.c:mshv_unmap_user_memory() {
...
region = mshv_partition_region_by_gfn(partition, mem->guest_pfn);
...
hlist_del(®ion->hnode);
...
mshv_region_put(region);
}
Because the partition's SNP state is still active, could mshv_region_share()
fail to regain host access during teardown?
drivers/hv/mshv_regions.c:mshv_region_destroy() {
...
if (mshv_partition_encrypted(partition)) {
ret = mshv_region_share(region);
if (ret) {
pt_err(partition,
"Failed to regain access to memory...\n",
ret);
return;
}
}
}
If it returns early here, does this result in a permanent leak of the pinned
host pages and kernel structures?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260807135134.303943-1-weh@linux.microsoft.com?part=10
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v1 11/13] mshv: unlock SNP pages on panic for crashdump collection
2026-08-07 13:51 ` [PATCH v1 11/13] mshv: unlock SNP pages on panic for crashdump collection Wei Hu
@ 2026-08-07 16:11 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-07 16:11 UTC (permalink / raw)
To: Wei Hu; +Cc: linux-hyperv
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Lockless iteration of a non-RCU list in a panic notifier exposes the system to poison pointer dereferences, causing a nested panic and preventing crashdump collection.
--
--- Patch [11]: [PATCH v1 11/13] mshv: unlock SNP pages on panic for crashdump collection ---
commit da7cefab011e489222d854cd80ab1f7f2ab3f297
Author: Wei Hu <weh@microsoft.com>
mshv: unlock SNP pages on panic for crashdump collection
This commit adds a panic notifier to unmap and share SEV-SNP guest pages
back to the host, allowing kexec to collect a valid root vmcore after a
crash. The notifier walks the partition table and unlocks memory regions
if the hypervisor's own crashdump support is inactive.
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index 0fbd2158968da..4d08d547704e4 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) {
[Severity: High]
Is it safe to iterate pt_mem_regions locklessly here?
Since panic notifiers can run at any time, they might interrupt a concurrent
hlist_del() operation on this list. If that happens, the list removal poisons
the node pointers. This hlist_for_each_entry() traversal would then
dereference a poisoned pointer, causing a recursive panic.
A recursive panic would instantly halt the machine and abort the kexec
crashdump process, defeating the purpose of this fallback path.
Does this list need to be converted to use RCU to safely traverse it during
a panic, or is there another way to safely identify these regions without
risking a page fault?
> + 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);
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260807135134.303943-1-weh@linux.microsoft.com?part=11
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v1 12/13] hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader)
2026-08-07 13:51 ` [PATCH v1 12/13] hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader) Wei Hu
@ 2026-08-07 16:22 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-07 16:22 UTC (permalink / raw)
To: Wei Hu; +Cc: linux-hyperv
Thank you for your contribution! Sashiko AI review found 9 potential issue(s) to consider:
- [Critical] The x86 EFI stub stores the command line in a `.bss` static buffer which is wiped by the kernel decompressor.
- [High] Unconditional increment of `cmdline_len` with `snprintf` return value leads to buffer overflow.
- [High] Incorrect linked list traversal causes truncation of the existing `setup_data` chain.
- [Medium] Missing NULL pointer check before calling `strlen` on the command line.
- [Medium] Skipping invalid e820 indices leaves uninitialized holes in `hv_mshv_res`, breaking resource registration.
- [Medium] MSHV root partition boot enablement is completely unreachable on ARM64.
- [Low] Memory leak of `mshv_reserved` array in x86 EFI stub.
- [Low] Memory leak of `mem_map` in ARM64 EFI stub.
- [Low] Parsing error in `hyperv_resvd_new` bypasses state updates, silencing previous valid ranges.
--
--- Patch [12]: [PATCH v1 12/13] hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader) ---
commit fcbf57de7557f7a5bf335c4003de65861d33a7c5
Author: Wei Hu <weh@microsoft.com>
hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader)
This commit ports the EFI HvLoader protocol handshake and the hyperv_resvd_new
memory reservation. It provides the Linux-side enablement necessary to boot an
upstream kernel as a Microsoft Hypervisor (MSHV) root partition.
> --- a/arch/x86/kernel/cpu/mshyperv.c
> +++ b/arch/x86/kernel/cpu/mshyperv.c
[ ... ]
> @@ -496,6 +499,75 @@ EXPORT_SYMBOL_GPL(hv_get_hypervisor_version);
> +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;
[Severity: Medium]
Does skipping the invalid index here leave the hv_mshv_res array
zero-initialized for this slot?
Later on, hv_mark_resources() iterates over this array using:
for (i = 0; i < max && hv_mshv_res[i].end; i++)
Could this cause the registration loop to terminate prematurely when it hits
the hole, silently dropping all subsequent valid memory ranges?
> + }
> +
> + start = e820_table->entries[idx].addr;
> --- /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)
> +{
[Severity: Medium]
Is this function ever invoked? It appears to be defined here but never hooked
into the ARM64 EFI stub entry point, making the ARM64 MSHV boot enablement
unreachable code.
> + 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);
[Severity: Low]
Does this code leak the mem_map pool allocation? Unlike the x86 implementation
in this patch, it doesn't appear to be freed before returning EFI_SUCCESS.
> + if (status != EFI_SUCCESS)
> --- /dev/null
> +++ b/drivers/firmware/efi/libstub/efi-mshv-common.c
[ ... ]
> +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);
[Severity: Medium]
Can old_cmdline be NULL if the kernel is booted without a command line?
If so, passing it directly to strlen() would lead to a NULL pointer
dereference.
> + 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);
[Severity: High]
Could this result in a buffer overflow?
If the appended string exceeds buf_sz, snprintf returns the number of
characters that would have been written, not the number actually written.
Adding this value to cmdline_len could cause buf_sz - cmdline_len to underflow
into a very large unsigned integer.
Will the next loop iteration then write out of bounds via buf + cmdline_len?
> + }
> --- /dev/null
> +++ b/drivers/firmware/efi/libstub/x86-efi-mshv.c
[ ... ]
> +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];
[Severity: Critical]
Will this static buffer be placed in the .bss segment and subsequently
destroyed?
When the EFI stub hands over control to the kernel decompressor
(e.g. startup_64), the decompressor zeroes its .bss segment before the command
line is parsed. Using a static buffer here might cause the entire kernel
command line to be lost.
> +
> + 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);
[ ... ]
> +efi_status_t mshv_efi_setup(struct boot_params *boot_params)
> +{
[ ... ]
> + /*
> + * 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);
[Severity: Low]
Does this code leak the mshv_reserved array memory? It is dynamically
allocated here and possibly reallocated during the loop, but it doesn't appear
to be freed at the end of the function.
[ ... ]
> + 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;
[Severity: High]
Does this loop terminate one node early?
Because it checks (*setup_data_itr)->next in the while condition, it exits
when pointing to the last node. Overwriting *setup_data_itr here seems to
overwrite the pointer to the last element rather than appending to it, which
could drop critical boot parameters passed by the bootloader.
> +
> + for (i = 0; i < nr_ranges; i++) {
> --- a/drivers/hv/hv_common.c
> +++ b/drivers/hv/hv_common.c
[ ... ]
> @@ -863,3 +867,81 @@ const char *hv_result_to_string(u64 status)
> +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;
[Severity: Low]
If an invalid format is encountered, does this early return bypass updating
the global ranges_nr counter?
By returning early instead of breaking the loop, ranges_nr = i is skipped,
which might cause any previously parsed valid memory ranges to be silently
ignored by hv_dump_mshv_memory() later.
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260807135134.303943-1-weh@linux.microsoft.com?part=12
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v1 13/13] mshv: set up own SynIC registers on a nested root partition
2026-08-07 13:51 ` [PATCH v1 13/13] mshv: set up own SynIC registers on a nested root partition Wei Hu
@ 2026-08-07 16:36 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-07 16:36 UTC (permalink / raw)
To: Wei Hu; +Cc: linux-hyperv
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Use-after-free in `mshv_portid_lookup()` due to dereferencing an RCU-protected pointer outside the RCU read-side critical section.
- [High] Use-after-free of `mshv_vp` in `mshv_intercept_isr` and `mshv_scheduler_isr` due to premature `kfree(vp)` in `destroy_partition`.
--
--- Patch [13]: [PATCH v1 13/13] mshv: set up own SynIC registers on a nested root partition ---
commit e9472d7549df2c05d78706a10839e3ca1d79d52c
Author: Wei Hu <weh@microsoft.com>
mshv: set up own SynIC registers on a nested root partition
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.
> diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c
> index 7c168e5a740dd..7d714ee5d2267 100644
> --- a/drivers/hv/mshv_synic.c
> +++ b/drivers/hv/mshv_synic.c
[ ... ]
> @@ -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);
[Severity: High]
This isn't a bug introduced by this patch, but I noticed a pre-existing
use-after-free pattern regarding mshv_vp in mshv_intercept_isr() and
mshv_scheduler_isr().
When userspace initiates partition destruction, destroy_partition() frees
virtual processors before removing the partition from the RCU-protected hash
list:
drivers/hv/mshv_root_main.c:destroy_partition() {
...
kfree(vp);
partition->pt_vp_array[i] = NULL;
}
...
}
Concurrently, hypervisor messages can trigger mshv_intercept_isr() which
reads the VP pointer while holding the rcu_read_lock:
drivers/hv/mshv_synic.c:mshv_intercept_isr() {
...
vp = partition->pt_vp_array[vp_index];
if (unlikely(!vp)) {
pr_debug("failed to find VP %u\n", vp_index);
goto unlock_out;
}
kick_vp(vp);
...
}
Is it possible for kick_vp() to write to freed memory if the read happens
just before kfree(vp) in destroy_partition()?
[Severity: Critical]
This is a pre-existing issue, but does mshv_portid_lookup() have a
use-after-free if it drops the RCU lock before dereferencing the protected
pointer?
drivers/hv/mshv_portid_table.c:mshv_portid_lookup() {
...
rcu_read_lock();
_info = idr_find(&port_table_idr, port_id);
rcu_read_unlock();
if (_info) {
*info = *_info;
...
}
Concurrently, mshv_portid_free() can remove the port ID, call
synchronize_rcu(), and free the memory:
drivers/hv/mshv_portid_table.c:mshv_portid_free() {
...
idr_unlock(&port_table_idr);
synchronize_rcu();
kfree(info);
...
}
Since rcu_read_unlock() is called prematurely, synchronize_rcu() wouldn't
wait for the dereference.
This is concerning because mshv_doorbell_isr() uses this returned struct to
invoke a callback:
drivers/hv/mshv_synic.c:mshv_doorbell_isr() {
...
/* Invoke the callback */
ptinfo.hv_port_doorbell.doorbell_cb(port,
ptinfo.hv_port_doorbell.data);
...
}
Could an attacker trigger port freeing to race with the doorbell interrupt
and control the function pointer?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260807135134.303943-1-weh@linux.microsoft.com?part=13
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2026-08-07 16:36 UTC | newest]
Thread overview: 25+ 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 14:03 ` sashiko-bot
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 14:21 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 04/13] mshv: wire SEV-SNP partition ioctls Wei Hu
2026-08-07 14:45 ` sashiko-bot
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 15:04 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 07/13] mshv: default to safe partition CPU features Wei Hu
2026-08-07 15:15 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 08/13] mshv: accept partial CPU feature banks Wei Hu
2026-08-07 15:30 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 09/13] mshv: define full processor and xsave feature masks Wei Hu
2026-08-07 15:42 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 10/13] mshv: unmap SNP memory before state teardown Wei Hu
2026-08-07 15:53 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 11/13] mshv: unlock SNP pages on panic for crashdump collection Wei Hu
2026-08-07 16:11 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 12/13] hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader) Wei Hu
2026-08-07 16:22 ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 13/13] mshv: set up own SynIC registers on a nested root partition Wei Hu
2026-08-07 16:36 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).