drivers: hv: Support for SNP Secure AVIC#130
Conversation
When Secure AVIC is enabled, VMBus driver should call x2apic Secure AVIC interface to allow Hyper-V to inject VMBus message interrupt. Reviewed-by: Michael Kelley <mhklinux@outlook.com> Reviewed-by: Neeraj Upadhyay <Neeraj.Upadhyay@amd.com> Signed-off-by: Tianyu Lan <tiala@microsoft.com>
When Secure AVIC is enabled, call Secure AVIC function to allow Hyper-V to inject STIMER0 interrupt. Reviewed-by: Neeraj Upadhyay <Neeraj.Upadhyay@amd.com> Reviewed-by: Michael Kelley <mhklinux@outlook.com> Signed-off-by: Tianyu Lan <tiala@microsoft.com>
When Secure AVIC is available, the AMD x2apic Secure AVIC driver will be selected. In that case, have hv_apic_init() return immediately without doing anything. Reviewed-by: Neeraj Upadhyay <Neeraj.Upadhyay@amd.com> Reviewed-by: Michael Kelley <mhklinux@outlook.com> Signed-off-by: Tianyu Lan <tiala@microsoft.com>
Hyper-V doesn't support auto-eoi with Secure AVIC. So set the HV_DEPRECATING_AEOI_RECOMMENDED flag to force writing the EOI register after handling an interrupt. Reviewed-by: Michael Kelley <mhklinux@outlook.com> Reviewed-by: Neeraj Upadhyay <Neeraj.Upadhyay@amd.com> Signed-off-by: Tianyu Lan <tiala@microsoft.com>
Secure AVIC provides backing page to aid the guest in limiting which interrupt vectors can be injected into the guest. Hyper-V has specific hvcall to set backing page and call it in Secure AVIC driver. Signed-off-by: Tianyu Lan <tiala@microsoft.com>
Signed-off-by: Roman Kisel <romank@linux.microsoft.com> Signed-off-by: Tianyu Lan <tiala@microsoft.com>
There was a problem hiding this comment.
Pull request overview
Adds Hyper-V guest/kernel plumbing to support AMD SEV-SNP Secure AVIC, primarily to accelerate interrupt delivery/handling for isolated VMs (SNP, plus some refactoring shared with TDX) and to expose required backing-page details to MSHV/VTL userspace.
Changes:
- Introduces new Hyper-V register/message structures and a hypercall path to configure the Secure AVIC backing page.
- Extends
mshv_vtlto allocate/manage a Secure AVIC backing page and adds an ioctl to retrieve its PFN for VTL0. - Refactors/extends in-kernel interrupt/ICR handling to cover both TDX and SNP fast paths, and wires up vector enabling for CoCo interrupt injection.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| include/uapi/linux/mshv.h | Adds new VTL ioctl for Secure AVIC PFN retrieval. |
| include/hyperv/hvhdk.h | Adds x64 exception intercept message definitions used by SNP intercept handling. |
| include/hyperv/hvgdk_mini.h | Adds new VP register IDs and hypercall input struct/union for setting Secure AVIC GPA. |
| include/asm-generic/mshyperv.h | Declares hv_enable_coco_interrupt() hook used by SynIC enable/disable. |
| drivers/hv/mshv_vtl_main.c | Allocates Secure AVIC page for SNP, adds fast-path exit/intercept handling, adds ioctl and generic APIC-page plumbing. |
| drivers/hv/hv_common.c | Adds weak stub/export for hv_enable_coco_interrupt(). |
| drivers/hv/hv.c | Calls hv_enable_coco_interrupt() when enabling/disabling SynIC regs. |
| arch/x86/kernel/cpu/mshyperv.c | Sets Hyper-V hint to recommend deprecating AEOI when Secure AVIC is present. |
| arch/x86/kernel/apic/x2apic_savic.c | Initializes backing page and routes SNP+Hyper-V through new hv_set_savic_backing_page(). |
| arch/x86/include/asm/svm.h | Adds VMCB control bits used by SNP exit handling. |
| arch/x86/include/asm/sev.h | Adds RMPADJUST permission bit defines used for Secure AVIC page permissions. |
| arch/x86/include/asm/mshyperv.h | Adds hv_vp_early_input_arg and declares hv_set_savic_backing_page(). |
| arch/x86/include/asm/apic.h | Exposes x2apic_savic_init_backing_page() for reuse by mshv_vtl. |
| arch/x86/hyperv/ivm.c | Implements hv_set_savic_backing_page() via HVCALL_SET_VP_REGISTERS. |
| arch/x86/hyperv/hv_init.c | Allocates/decrypts per-CPU early input pages and updates APIC vectors for direct stimer injection. |
| arch/x86/hyperv/hv_apic.c | Implements hv_enable_coco_interrupt() and skips hv-apic init when Secure AVIC is enabled. |
| Microsoft/hcl-x64.config | Updates HCL kernel config snapshot to match new expectations/options. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| __u16 exception_vector; | ||
| union hv_x64_exception_info exception_info; | ||
| __u8 instruction_byte_count; | ||
| __u32 error_code; | ||
| __u64 exception_parameter; | ||
| __u64 reserved; | ||
| __u8 instruction_bytes[16]; | ||
| struct hv_x64_segment_register ds_segment; | ||
| struct hv_x64_segment_register ss_segment; | ||
| __u64 rax; | ||
| __u64 rcx; | ||
| __u64 rdx; | ||
| __u64 rbx; | ||
| __u64 rsp; | ||
| __u64 rbp; | ||
| __u64 rsi; | ||
| __u64 rdi; | ||
| __u64 r8; | ||
| __u64 r9; | ||
| __u64 r10; | ||
| __u64 r11; | ||
| __u64 r12; | ||
| __u64 r13; | ||
| __u64 r14; | ||
| __u64 r15; |
There was a problem hiding this comment.
This new exception-intercept struct mixes __u* types with the surrounding code in this header, which consistently uses u8/u16/u32/u64. For consistency (and to avoid subtle header-style issues), switch these fields to u16/u8/u32/u64 like the existing intercept message structs.
| __u16 exception_vector; | |
| union hv_x64_exception_info exception_info; | |
| __u8 instruction_byte_count; | |
| __u32 error_code; | |
| __u64 exception_parameter; | |
| __u64 reserved; | |
| __u8 instruction_bytes[16]; | |
| struct hv_x64_segment_register ds_segment; | |
| struct hv_x64_segment_register ss_segment; | |
| __u64 rax; | |
| __u64 rcx; | |
| __u64 rdx; | |
| __u64 rbx; | |
| __u64 rsp; | |
| __u64 rbp; | |
| __u64 rsi; | |
| __u64 rdi; | |
| __u64 r8; | |
| __u64 r9; | |
| __u64 r10; | |
| __u64 r11; | |
| __u64 r12; | |
| __u64 r13; | |
| __u64 r14; | |
| __u64 r15; | |
| u16 exception_vector; | |
| union hv_x64_exception_info exception_info; | |
| u8 instruction_byte_count; | |
| u32 error_code; | |
| u64 exception_parameter; | |
| u64 reserved; | |
| u8 instruction_bytes[16]; | |
| struct hv_x64_segment_register ds_segment; | |
| struct hv_x64_segment_register ss_segment; | |
| u64 rax; | |
| u64 rcx; | |
| u64 rdx; | |
| u64 rbx; | |
| u64 rsp; | |
| u64 rbp; | |
| u64 rsi; | |
| u64 rdi; | |
| u64 r8; | |
| u64 r9; | |
| u64 r10; | |
| u64 r11; | |
| u64 r12; | |
| u64 r13; | |
| u64 r14; | |
| u64 r15; |
| snp_secure_avic_page = alloc_page(GFP_KERNEL | __GFP_ZERO); | ||
| if (!snp_secure_avic_page) | ||
| return -ENOMEM; | ||
|
|
||
| /* VMPL 2 for the VTL0 */ | ||
| ret = rmpadjust((unsigned long)page_address(snp_secure_avic_page), | ||
| RMP_PG_SIZE_4K, | ||
| 2 | RMPADJUST_ENABLE_READ | RMPADJUST_ENABLE_WRITE); | ||
| if (ret) { | ||
| pr_err("failed to adjust RMP for the secure AVIC page: %d\n", ret); | ||
| __free_page(snp_secure_avic_page); | ||
| return -EINVAL; | ||
| } |
There was a problem hiding this comment.
In the SNP allocation path, failures after per_cpu->run and/or per_cpu->vmsa_page have been allocated (e.g. alloc_page() failure or rmpadjust() failure for the secure AVIC page) return immediately without freeing the already-allocated pages. Add a cleanup path (e.g. goto err) that frees per_cpu->run, per_cpu->vmsa_page, and any partially-initialized snp_secure_avic_page before returning an error.
| * the APIC backing page is mapped in NPT. | ||
| */ | ||
| res = savic_register_gpa(gpa); | ||
| if (hv_isolation_type_snp()) |
There was a problem hiding this comment.
savic_setup() now calls hv_isolation_type_snp() / hv_set_savic_backing_page() without guarding for CONFIG_HYPERV. CONFIG_AMD_SECURE_AVIC only depends on AMD_MEM_ENCRYPT, so a build with Secure AVIC enabled but Hyper-V disabled will end up with unresolved Hyper-V symbols. Gate the Hyper-V path with IS_ENABLED(CONFIG_HYPERV) (or provide stubs) so non-Hyper-V kernels still build and use savic_register_gpa().
| if (hv_isolation_type_snp()) | |
| if (IS_ENABLED(CONFIG_HYPERV) && hv_isolation_type_snp()) |
| u64 control = HV_HYPERCALL_REP_COMP_1 | HVCALL_SET_VP_REGISTERS; | ||
| struct hv_set_vp_registers_input *input = | ||
| (struct hv_set_vp_registers_input *) | ||
| ((u8 *)hv_vp_early_input_arg + smp_processor_id() * PAGE_SIZE); | ||
| union hv_x64_register_sev_gpa_page value; | ||
| unsigned long flags; | ||
| int retry = 5; | ||
| u64 ret; | ||
|
|
||
| local_irq_save(flags); | ||
|
|
There was a problem hiding this comment.
hv_set_savic_backing_page() indexes hv_vp_early_input_arg using smp_processor_id() while only IRQs are disabled. This can trigger smp_processor_id() debug warnings / CPU migration issues on preemptible kernels. Use raw_smp_processor_id() or bracket with get_cpu()/put_cpu() (or preempt_disable()/preempt_enable()) to make the CPU ID stable for the duration of the access.
|
|
||
| #define HV_X64_REGISTER_VSM_VP_STATUS 0x000D0003 | ||
| #define HV_X64_VTL_MASK GENMASK(3, 0) |
There was a problem hiding this comment.
HV_X64_REGISTER_VSM_VP_STATUS and HV_X64_VTL_MASK are defined unconditionally here, but are also defined (with #ifndef guards) in arch/x86/include/asm/mshyperv.h. If both headers are included, this can produce macro redefinition warnings. Add #ifndef guards here as well (or remove one of the definitions) to keep the definitions single-sourced.
| #define HV_X64_REGISTER_VSM_VP_STATUS 0x000D0003 | |
| #define HV_X64_VTL_MASK GENMASK(3, 0) | |
| #ifndef HV_X64_REGISTER_VSM_VP_STATUS | |
| #define HV_X64_REGISTER_VSM_VP_STATUS 0x000D0003 | |
| #endif | |
| #ifndef HV_X64_VTL_MASK | |
| #define HV_X64_VTL_MASK GENMASK(3, 0) | |
| #endif |
| u64 u64; | ||
| struct { | ||
| u64 enabled:1; | ||
| u64 reserved:11; | ||
| u64 pagenumber:52; | ||
| }; |
There was a problem hiding this comment.
union hv_x64_register_sev_gpa_page uses a member named u64 u64; and an anonymous bitfield struct. This is hard to read and easy to misuse. Consider renaming the raw member to something like as_u64 and naming/packing the bitfield struct explicitly to make the layout and intent clearer.
| u64 u64; | |
| struct { | |
| u64 enabled:1; | |
| u64 reserved:11; | |
| u64 pagenumber:52; | |
| }; | |
| u64 as_u64; | |
| struct { | |
| u64 enabled:1; | |
| u64 reserved:11; | |
| u64 pagenumber:52; | |
| } bits __packed; |
No description provided.