From d33e796ec9869ebf212f278617f08b0915b143f3 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Tue, 18 Feb 2025 17:48:19 -0800 Subject: [PATCH 01/10] hv: mshv_vtl: Introduce the MAP_REDIRECTED_DEVICE_INTERRUPT IOCTL The proxy interrupts that the VTL2 kernel relays to the VTL0 guest are handled via the single interrupt vector of the VMBus synthetic interrupt controller. On TDX guests in particular, Hyper-V does not use posted interrupts for these interrupts. This results in a TDEXIT for every interrupt and consequent performance degradation. To recoup performance, updated versions of Hyper-V will feature support the posting of VTL0 interrupts directly to the VTL2 guest. In such setup, the VTL2 guest still relays interrupts to the VTL0 guest, but uses a dedicated VTL2 interrupt vector for each VTL0 interrupt. Since user space in the VTL2 guest is responsible of requesting such redirection, implement the new MAP_REDIRECTED_DEVICE_INTERRUPT IOCTL. User space provides the vector of the VTL0 guest that wants the VTL2 kernel to redirect. On success, return the corresponding VTL2 interrupt vector on the payload of the IOCTL. Reuse the same IOCTL to also request the removal of the redirection when needed. When removing a redirection user space must provide the number of the hardware vector. Provide weak stub functions. Subsequent changsets will implement actual functionality for x86. Signed-off-by: Ricardo Neri --- Changes since v1: * Assigned 0x39 to MAP_REDIRECTED_DEVICE_INTERRUPT as 0x38 was already taken. (Roman) * Fixed error handling for copy_to_user(). (Roman) * Moved comment about unmapping above the conditional statement. (Roman) --- drivers/hv/hv_common.c | 12 ++++++++++++ drivers/hv/mshv_vtl_main.c | 33 +++++++++++++++++++++++++++++++++ include/asm-generic/mshyperv.h | 3 +++ include/uapi/linux/mshv.h | 8 +++++++- 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index 502d90a5b322..ae7537b81189 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -871,6 +871,18 @@ u64 __weak hv_tdx_hypercall(u64 control, u64 param1, u64 param2) } EXPORT_SYMBOL_GPL(hv_tdx_hypercall); +int __weak mshv_vtl_map_redirected_intr(u32 vector) +{ + return -ENODEV; +} +EXPORT_SYMBOL_GPL(mshv_vtl_map_redirected_intr); + +int __weak mshv_vtl_unmap_redirected_intr(u32 vector) +{ + return -ENODEV; +} +EXPORT_SYMBOL_GPL(mshv_vtl_unmap_redirected_intr); + int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags) { struct hv_create_vp *input; diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c index ec7a2403f841..e2164b918cc9 100644 --- a/drivers/hv/mshv_vtl_main.c +++ b/drivers/hv/mshv_vtl_main.c @@ -1930,6 +1930,36 @@ static inline long mshv_vtl_ioctl_kick_cpu(void __user *user_arg) return 0; } +static long mshv_vtl_ioctl_setup_redirected_intr(void __user *user_arg) +{ + struct mshv_map_device_intr intr_data; + int ret; + + if (copy_from_user(&intr_data, user_arg, sizeof(intr_data))) + return (long)-EFAULT; + + /* User space provides the hardware vector to unmap. */ + if (!intr_data.create_mapping) + return (long)mshv_vtl_unmap_redirected_intr(intr_data.vector); + + /* + * User space provides the proxy vector it wants to map to a hardware + * vector. + */ + ret = mshv_vtl_map_redirected_intr(intr_data.vector); + if (ret < 0) + return (long)ret; + + /* + * The return value is the hardware vector to which the proxy vector + * is mapped. + */ + intr_data.vector = ret; + ret = copy_to_user(user_arg, &intr_data, sizeof(intr_data)) ? -EFAULT : 0; + + return (long)ret; +} + static long mshv_vtl_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -1982,6 +2012,9 @@ mshv_vtl_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) ret = mshv_vtl_ioctl_guest_vsm_vmsa_pfn((void __user *)arg); break; #endif + case MSHV_VTL_MAP_REDIRECTED_DEVICE_INTERRUPT: + ret = mshv_vtl_ioctl_setup_redirected_intr((void __user *)arg); + break; case MSHV_VTL_KICK_CPU: ret = mshv_vtl_ioctl_kick_cpu((void __user *)arg); diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index fad13d0dd17d..6e23fdc90575 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -207,6 +207,9 @@ void hv_remove_kexec_handler(void); void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs)); void hv_remove_crash_handler(void); +int mshv_vtl_map_redirected_intr(u32 proxy_vector); +int mshv_vtl_unmap_redirected_intr(u32 hw_vector); + extern int vmbus_interrupt; extern int vmbus_irq; diff --git a/include/uapi/linux/mshv.h b/include/uapi/linux/mshv.h index 7ba3a3f24989..75f29f684973 100644 --- a/include/uapi/linux/mshv.h +++ b/include/uapi/linux/mshv.h @@ -298,9 +298,13 @@ struct mshv_kick_cpus { __u64 flags; } __packed; +struct mshv_map_device_intr { + __u32 vector; + bool create_mapping; +} __packed; + #define MSHV_KICK_CPUS_FLAG_WAIT_FOR_CPUS (1 << 0) #define MSHV_KICK_CPUS_FLAG_CANCEL_CPU_RUN (1 << 1) - #define MSHV_IOCTL 0xB8 /* mshv device */ @@ -359,6 +363,8 @@ struct mshv_kick_cpus { /* For x86-64 TDX only */ #define MSHV_VTL_TDCALL _IOWR(MSHV_IOCTL, 0x32, struct mshv_tdcall) #define MSHV_VTL_READ_VMX_CR4_FIXED1 _IOR(MSHV_IOCTL, 0x33, __u64) +#define MSHV_VTL_MAP_REDIRECTED_DEVICE_INTERRUPT _IOWR(MSHV_IOCTL, 0x39, \ + struct mshv_map_device_intr) /* For x86-64 only */ #define MSHV_VTL_GUEST_VSM_VMSA_PFN _IOWR(MSHV_IOCTL, 0x34, __u64) From 1dac486713b77cd938d32d9a3dab7c2a22e7d3b7 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Fri, 30 May 2025 13:02:51 -0700 Subject: [PATCH 02/10] hv: mshv_vtl: Optimize computation of the proxy interrupt bit The computation of the proxy interrupt needs to be computed each time it needs to be flagged. Optimize the computation using bit shifts and masks. While here, fix a minor issue on a comment. Suggested-by: Roman Kisel Signed-off-by: Ricardo Neri --- Changes since v1: * Introduced this patch. --- drivers/hv/mshv_vtl_main.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c index e2164b918cc9..e23e65d56219 100644 --- a/drivers/hv/mshv_vtl_main.c +++ b/drivers/hv/mshv_vtl_main.c @@ -467,11 +467,15 @@ static void mshv_vtl_scan_proxy_interrupts(struct hv_per_cpu_context *per_cpu) } else { /* A malicious hypervisor might set a vector > 255. */ vector = READ_ONCE(proxy->u.asserted_vector) & 0xff; - const u32 bank = vector / 32; - const u32 masked_irr = BIT(vector % 32) & + /* + * See mshv_tdx_handle_simple_icr_write() on how the bank and bit are + * computed. + */ + const u32 bank = vector >> 5; + const u32 masked_irr = BIT(vector & 0x1f) & ~READ_ONCE(run->proxy_irr_blocked[bank]); - /* nb atomic_t cast: See comment in mshv_tdx_handle_simple_icr_write */ + /* nb atomic_t cast: See comment in mshv_tdx_handle_simple_icr_write() */ atomic_or(masked_irr, (atomic_t *)&run->proxy_irr[bank]); } @@ -1120,8 +1124,8 @@ static int mshv_tdx_handle_simple_icr_write(struct tdx_vp_context *context) const u32 dest = context->l2_enter_guest_state.rdx; const u8 shorthand = (icr_lo >> 18) & 0b11; const u8 vector = icr_lo; - const u64 bank = vector / 32; - const u32 mask = BIT(vector % 32); + const u64 bank = vector >> 5; /* Each bank is 32 bits. Divide by 32 to find the bank. */ + const u32 mask = BIT(vector & 0x1f); /* Bit in the bank is the remainder of the division. */ const u32 self = smp_processor_id(); bool send_ipi = false; From 66fbded1849b50c0120fe23202cc67140c9d1661 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Wed, 19 Feb 2025 08:41:22 -0800 Subject: [PATCH 03/10] hv: mshv_vtl: Add a helper function to assert single proxy interrupts Proxy interrupts can be asserted in group or individually via the vector of the VMBus synthetic interrupt controller. They can also be asserted individually if the proxy interrupt has been redirected to a hardware interrupt vector in the VTL2 guest. Add a helper function to handle single proxy interrupts that both cases can use. No functional change intended. Signed-off-by: Ricardo Neri --- Changes since v1: * Rebased on the previous changeset that optimizes the calculation of the proxy interrupt bit. --- drivers/hv/mshv_vtl_main.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c index e23e65d56219..a3b313c1a4ca 100644 --- a/drivers/hv/mshv_vtl_main.c +++ b/drivers/hv/mshv_vtl_main.c @@ -430,13 +430,22 @@ static int mshv_vtl_get_vsm_regs(void) return ret; } +static void do_assert_single_proxy_intr(const u32 vector, struct mshv_vtl_run *run) +{ + /* See mshv_tdx_handle_simple_icr_write() on how the bank and bit are computed. */ + const u32 bank = vector >> 5; + const u32 masked_irr = BIT(vector & 0x1f) & ~READ_ONCE(run->proxy_irr_blocked[bank]); + + /* nb atomic_t cast: See comment in mshv_tdx_handle_simple_icr_write */ + atomic_or(masked_irr, (atomic_t *)&run->proxy_irr[bank]); +} + static void mshv_vtl_scan_proxy_interrupts(struct hv_per_cpu_context *per_cpu) { struct hv_message *msg; u32 message_type; struct hv_x64_proxy_interrupt_message_payload *proxy; struct mshv_vtl_run *run; - u32 vector; msg = (struct hv_message *)per_cpu->synic_message_page + HV_SYNIC_INTERCEPTION_SINT_INDEX; for (;;) { @@ -466,17 +475,9 @@ static void mshv_vtl_scan_proxy_interrupts(struct hv_per_cpu_context *per_cpu) } } else { /* A malicious hypervisor might set a vector > 255. */ - vector = READ_ONCE(proxy->u.asserted_vector) & 0xff; - /* - * See mshv_tdx_handle_simple_icr_write() on how the bank and bit are - * computed. - */ - const u32 bank = vector >> 5; - const u32 masked_irr = BIT(vector & 0x1f) & - ~READ_ONCE(run->proxy_irr_blocked[bank]); + const u32 vector = READ_ONCE(proxy->u.asserted_vector) & 0xff; - /* nb atomic_t cast: See comment in mshv_tdx_handle_simple_icr_write() */ - atomic_or(masked_irr, (atomic_t *)&run->proxy_irr[bank]); + do_assert_single_proxy_intr(vector, run); } WRITE_ONCE(run->scan_proxy_irr, 1); From a78b206cca6c88d936f56d870bed6ffb70ec835a Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Tue, 22 Apr 2025 12:16:17 -0700 Subject: [PATCH 04/10] hv: mshv_vtl: Add a handler for redirected proxy interrupts The Hyper-V VTL driver owns all the data structures of proxy interrupts. Add an architecture-independent handler to assert those that are redirected (one proxy interrupt is redirected to one hardware interrupt). The servicing of hardware interrupts is implemented in architecture- specific code that is usually compiled as built-in. The Hyper-V driver can be a module. The handler is out of scope of built-in code at build time. It needs to be registered at runtime when the Hyper-V driver is loaded. Since the registration of the handler is architecture-specific, add weak stub functions that architectures can use to implement such registration. Signed-off-by: Ricardo Neri --- Changes since v1: * None --- drivers/hv/hv_common.c | 10 ++++++++++ drivers/hv/mshv_vtl_main.c | 12 ++++++++++++ include/asm-generic/mshyperv.h | 2 ++ 3 files changed, 24 insertions(+) diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index ae7537b81189..61bb07381202 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -883,6 +883,16 @@ int __weak mshv_vtl_unmap_redirected_intr(u32 vector) } EXPORT_SYMBOL_GPL(mshv_vtl_unmap_redirected_intr); +void __weak hv_setup_redirected_proxy_intr_handler(void (*handler)(u32 vector)) +{ +} +EXPORT_SYMBOL_GPL(hv_setup_redirected_proxy_intr_handler); + +void __weak hv_remove_redirected_proxy_intr_handler(void) +{ +} +EXPORT_SYMBOL_GPL(hv_remove_redirected_proxy_intr_handler); + int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags) { struct hv_create_vp *input; diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c index a3b313c1a4ca..eb49db0f5239 100644 --- a/drivers/hv/mshv_vtl_main.c +++ b/drivers/hv/mshv_vtl_main.c @@ -440,6 +440,15 @@ static void do_assert_single_proxy_intr(const u32 vector, struct mshv_vtl_run *r atomic_or(masked_irr, (atomic_t *)&run->proxy_irr[bank]); } +static void assert_single_proxy_intr(const u32 vector) +{ + struct mshv_vtl_run *run = mshv_vtl_this_run(); + + do_assert_single_proxy_intr(vector, run); + + WRITE_ONCE(run->scan_proxy_irr, 1); +} + static void mshv_vtl_scan_proxy_interrupts(struct hv_per_cpu_context *per_cpu) { struct hv_message *msg; @@ -2723,6 +2732,8 @@ static int __init mshv_vtl_init(void) goto free_sidecar; } + hv_setup_redirected_proxy_intr_handler(assert_single_proxy_intr); + mutex_init(&mshv_vtl_poll_file_lock); device_initialize(mem_dev); @@ -2762,6 +2773,7 @@ static int __init mshv_vtl_init(void) static void __exit mshv_vtl_exit(void) { mshv_setup_vtl_func(NULL, NULL, NULL); + hv_remove_redirected_proxy_intr_handler(); mshv_tdx_free_apicid_to_cpuid_mapping(); misc_deregister(&mshv_vtl_sint_dev); misc_deregister(&mshv_vtl_hvcall); diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index 6e23fdc90575..e203bdaa9b4a 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -209,6 +209,8 @@ void hv_remove_crash_handler(void); int mshv_vtl_map_redirected_intr(u32 proxy_vector); int mshv_vtl_unmap_redirected_intr(u32 hw_vector); +void hv_setup_redirected_proxy_intr_handler(void (*handler)(u32 vector)); +void hv_remove_redirected_proxy_intr_handler(void); extern int vmbus_interrupt; extern int vmbus_irq; From 09013825dbfebe39777cb1e0de9d570c89b85505 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Tue, 18 Feb 2025 15:11:41 -0800 Subject: [PATCH 05/10] x86/vector: Reserve vectors for Hyper-V redirected interrupts Recent versions of Hyper-V hosts use IOMMU posted interrupts to deliver the interrupts of VTL0 passthrough devices directly the underlying VTL2 guest. These interrupts are hardware vectors in the VTL2 guest and need an interrupt gate in the Interrupt Descriptor Table. Reserve a block of 32 interrupt vectors immediately below the FIRST_ SYSTEM_VECTOR. This can be regarded as an expansion of the system vectors at the expense of external vectors. This should be acceptable since not many external vectors are used in the normal operation of the VTL2 kernel. Moreover, the proxy interrupts that the VTL2 guest handles come from devices. Signed-off-by: Ricardo Neri --- Changes since v1: * None --- arch/x86/include/asm/irq_vectors.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/irq_vectors.h b/arch/x86/include/asm/irq_vectors.h index 47051871b436..0ebfd4c54351 100644 --- a/arch/x86/include/asm/irq_vectors.h +++ b/arch/x86/include/asm/irq_vectors.h @@ -18,7 +18,9 @@ * Vectors 0 ... 31 : system traps and exceptions - hardcoded events * Vectors 32 ... 127 : device interrupts * Vector 128 : legacy int80 syscall interface - * Vectors 129 ... FIRST_SYSTEM_VECTOR-1 : device interrupts + * Vectors 129 ... FIRST_HV_VTL_REDIRECTED_VECTOR-1: more device interrupts + * Vectors FIRST_HV_VTL_REDIRECTED_VECTOR ... FIRST_SYSTEM_VECTOR-1 + * : HyperV redirected device interrupts * Vectors FIRST_SYSTEM_VECTOR ... 255 : special interrupts * * 64-bit x86 has per CPU IDT tables, 32-bit has one shared IDT table. @@ -91,6 +93,7 @@ #define MANAGED_IRQ_SHUTDOWN_VECTOR 0xef #if IS_ENABLED(CONFIG_HYPERV) +#define FIRST_HV_VTL_REDIRECTED_VECTOR 0xcc #define HYPERV_REENLIGHTENMENT_VECTOR 0xee #define HYPERV_STIMER0_VECTOR 0xed #endif @@ -111,7 +114,12 @@ #define FIRST_SYSTEM_VECTOR NR_VECTORS #endif +#ifdef CONFIG_HYPERV_VTL_MODE +#define NR_EXTERNAL_VECTORS (FIRST_HV_VTL_REDIRECTED_VECTOR - FIRST_EXTERNAL_VECTOR) +#define NR_HV_VTL_REDIRECTED_VECTORS (FIRST_SYSTEM_VECTOR - FIRST_HV_VTL_REDIRECTED_VECTOR) +#else #define NR_EXTERNAL_VECTORS (FIRST_SYSTEM_VECTOR - FIRST_EXTERNAL_VECTOR) +#endif #define NR_SYSTEM_VECTORS (NR_VECTORS - FIRST_SYSTEM_VECTOR) /* From 0252b0cae7267c7a4c784a3aa2ffaafdf5a38006 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Sat, 19 Apr 2025 18:46:09 -0700 Subject: [PATCH 06/10] x86/idtentry: Add a macro to create IDT entry stubs The symbols irq_entries_start and spurious_entries_start are tables of IDT entry stubs that are subsequently used to populate the interrupt descriptor table. The code for both stubs tables is identical. Wrap it in a macro. Signed-off-by: Ricardo Neri --- Changes since v1: * None --- arch/x86/include/asm/idtentry.h | 45 +++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/arch/x86/include/asm/idtentry.h b/arch/x86/include/asm/idtentry.h index ad5c68f0509d..2b87c0e075a1 100644 --- a/arch/x86/include/asm/idtentry.h +++ b/arch/x86/include/asm/idtentry.h @@ -547,36 +547,43 @@ static inline void fred_install_sysvec(unsigned int vector, const idtentry_t fun * point is to mask off the bits above bit 7 because the push is sign * extending. */ +.macro __idtentry_stubs name, vector_nr_start, vector_nr, isr .align IDT_ALIGN -SYM_CODE_START(irq_entries_start) - vector=FIRST_EXTERNAL_VECTOR - .rept NR_EXTERNAL_VECTORS +SYM_CODE_START(\name) + vector=\vector_nr_start + .rept (\vector_nr) UNWIND_HINT_IRET_REGS 0 : ENDBR .byte 0x6a, vector - jmp asm_common_interrupt + jmp asm_\isr /* Ensure that the above is IDT_ALIGN bytes max */ .fill 0b + IDT_ALIGN - ., 1, 0xcc vector = vector+1 + .endr -SYM_CODE_END(irq_entries_start) + +SYM_CODE_END(\name) +.endm + +/** + * DEFINE_IDTENTRY_STUBS - DECLARE_IDTENTRY_DF - Define a block of IDT entries + * @name: Name of the entry point + * @vector_nr_start: First vector number + * @vector_nr: Number of vectors to emit + * @isr: Name of the C handler called from ASM entry point + * + * Emit a block of @vector_nr IDT entry stubs packed into IDT_ALIGN bytes. + */ +#define DEFINE_IDTENTRY_STUBS(name, vector_nr_start, vector_nr, isr) \ + __idtentry_stubs name, (vector_nr_start), (vector_nr), isr + +DEFINE_IDTENTRY_STUBS(irq_entries_start, FIRST_EXTERNAL_VECTOR, + NR_EXTERNAL_VECTORS, common_interrupt) #ifdef CONFIG_X86_LOCAL_APIC - .align IDT_ALIGN -SYM_CODE_START(spurious_entries_start) - vector=FIRST_SYSTEM_VECTOR - .rept NR_SYSTEM_VECTORS - UNWIND_HINT_IRET_REGS -0 : - ENDBR - .byte 0x6a, vector - jmp asm_spurious_interrupt - /* Ensure that the above is IDT_ALIGN bytes max */ - .fill 0b + IDT_ALIGN - ., 1, 0xcc - vector = vector+1 - .endr -SYM_CODE_END(spurious_entries_start) +DEFINE_IDTENTRY_STUBS(spurious_entries_start, FIRST_SYSTEM_VECTOR, + NR_SYSTEM_VECTORS, spurious_interrupt) #endif #endif /* __ASSEMBLY__ */ From 17e7171a184247510da1503b0090c952be284624 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Tue, 18 Feb 2025 16:28:20 -0800 Subject: [PATCH 07/10] x86/idt/hyperv: Create a block interrupt gates for redirected interrupts Create a block interrupt gates that start at FIRST_HV_VTL_REDIRECTED_ VECTOR. Service this interrupt with the function hv_vtl_redirected_ interrupt(). Only tell the APIC that the interrupt has been handled. Subsequent changesets will use these interrupt vectors to deliver their corresponding proxy interrupts to a VTL0 guest. Signed-off-by: Ricardo Neri --- Changes since v1: * None --- arch/x86/include/asm/hw_irq.h | 4 ++++ arch/x86/include/asm/idtentry.h | 10 ++++++++++ arch/x86/kernel/cpu/mshyperv.c | 7 +++++++ arch/x86/kernel/idt.c | 16 ++++++++++++++++ 4 files changed, 37 insertions(+) diff --git a/arch/x86/include/asm/hw_irq.h b/arch/x86/include/asm/hw_irq.h index edebf1020e04..9d2f06b795e4 100644 --- a/arch/x86/include/asm/hw_irq.h +++ b/arch/x86/include/asm/hw_irq.h @@ -121,6 +121,10 @@ extern char irq_entries_start[]; extern char spurious_entries_start[]; +#ifdef CONFIG_HYPERV_VTL_MODE +extern char hv_vtl_redirected_entries_start[]; +#endif + #define VECTOR_UNUSED NULL #define VECTOR_SHUTDOWN ((void *)-1L) #define VECTOR_RETRIGGERED ((void *)-2L) diff --git a/arch/x86/include/asm/idtentry.h b/arch/x86/include/asm/idtentry.h index 2b87c0e075a1..5f12dbcd198b 100644 --- a/arch/x86/include/asm/idtentry.h +++ b/arch/x86/include/asm/idtentry.h @@ -586,6 +586,13 @@ DEFINE_IDTENTRY_STUBS(spurious_entries_start, FIRST_SYSTEM_VECTOR, NR_SYSTEM_VECTORS, spurious_interrupt) #endif +#ifdef CONFIG_HYPERV_VTL_MODE +DEFINE_IDTENTRY_STUBS(hv_vtl_redirected_entries_start, + FIRST_HV_VTL_REDIRECTED_VECTOR, + NR_HV_VTL_REDIRECTED_VECTORS, + hv_vtl_redirected_interrupt) +#endif + #endif /* __ASSEMBLY__ */ /* @@ -768,6 +775,9 @@ DECLARE_IDTENTRY_SYSVEC(POSTED_MSI_NOTIFICATION_VECTOR, sysvec_posted_msi_notifi DECLARE_IDTENTRY_SYSVEC(HYPERVISOR_CALLBACK_VECTOR, sysvec_hyperv_callback); DECLARE_IDTENTRY_SYSVEC(HYPERV_REENLIGHTENMENT_VECTOR, sysvec_hyperv_reenlightenment); DECLARE_IDTENTRY_SYSVEC(HYPERV_STIMER0_VECTOR, sysvec_hyperv_stimer0); +#ifdef CONFIG_HYPERV_VTL_MODE +DECLARE_IDTENTRY_IRQ(X86_TRAP_OTHER, hv_vtl_redirected_interrupt); +#endif #endif #if IS_ENABLED(CONFIG_ACRN_GUEST) diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index c6797cf9f37c..ad38c386293b 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -165,6 +165,13 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_stimer0) set_irq_regs(old_regs); } +#ifdef CONFIG_HYPERV_VTL_MODE +DEFINE_IDTENTRY_IRQ(hv_vtl_redirected_interrupt) +{ + apic_eoi(); +} +#endif + /* For x86/x64, override weak placeholders in hyperv_timer.c */ void hv_setup_stimer0_handler(void (*handler)(void)) { diff --git a/arch/x86/kernel/idt.c b/arch/x86/kernel/idt.c index f445bec516a0..d3fc5856ebcc 100644 --- a/arch/x86/kernel/idt.c +++ b/arch/x86/kernel/idt.c @@ -278,6 +278,20 @@ static void __init idt_map_in_cea(void) idt_descr.address = CPU_ENTRY_AREA_RO_IDT; } +static void __init idt_setup_hv_vtl_redirected_vectors(void) +{ +#ifdef CONFIG_HYPERV_VTL_MODE + int i = FIRST_HV_VTL_REDIRECTED_VECTOR; + + for (i = FIRST_HV_VTL_REDIRECTED_VECTOR; i < FIRST_SYSTEM_VECTOR; i++) { + void *entry = hv_vtl_redirected_entries_start + + IDT_ALIGN * (i - FIRST_HV_VTL_REDIRECTED_VECTOR); + if (!WARN_ON(test_and_set_bit(i, system_vectors))) + set_intr_gate(i, entry); + } +#endif +} + /** * idt_setup_apic_and_irq_gates - Setup APIC/SMP and normal interrupt gates */ @@ -288,6 +302,8 @@ void __init idt_setup_apic_and_irq_gates(void) idt_setup_from_table(idt_table, apic_idts, ARRAY_SIZE(apic_idts), true); + idt_setup_hv_vtl_redirected_vectors(); + for_each_clear_bit_from(i, system_vectors, FIRST_SYSTEM_VECTOR) { entry = irq_entries_start + IDT_ALIGN * (i - FIRST_EXTERNAL_VECTOR); set_intr_gate(i, entry); From 9857c554118f40784f4646b334712caaa569f67e Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Mon, 21 Apr 2025 19:06:26 -0700 Subject: [PATCH 08/10] x86/hv/mshv_vtl: Implement MAP_REDIRECTED_DEVICE_INTERRUPT The MAP_REDIRECTED_DEVICE_INTERRUPT IOCTL maps an interrupt of a VTL0 guest to a hadware vector interrupt in the VTL2 kernel. On x86, we reserve a block of 32 vectors starting at FIRST_HV_VTL_ REDIRECTED_VECTOR for this mapping. Keep track of this redirection using an array hardware interrupt vectors and their mapping to a proxy interrupt, if any. Note that the array starts at zero but the first hardware vector is FIRST_HV_VTL_REDIRECTED_VECTOR. Account for this offset as needed. The array of mapped proxy interrupts is only updated when processing the IOCTL. It is only read during interrupts. Serialize access using a seqcount. This allows lockless read access for cases in which multiple CPUs want to access the mapping data structures. Since we are using a seqcount, writers cannot be preempted. Protect write access using a spinlock. Signed-off-by: Ricardo Neri --- Changes since v1: * Used a seqcount for serialization. --- arch/x86/kernel/cpu/mshyperv.c | 87 ++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index ad38c386293b..5cf395bcce00 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -166,6 +167,15 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_stimer0) } #ifdef CONFIG_HYPERV_VTL_MODE +#define REDIRECTED_VECTOR_UNDEF 0xffff +static u32 redirected_proxy_intr[NR_HV_VTL_REDIRECTED_VECTORS] = { + [0 ... NR_HV_VTL_REDIRECTED_VECTORS - 1] = REDIRECTED_VECTOR_UNDEF }; + +static DEFINE_RAW_SPINLOCK(redirected_proxy_intr_lock); +static seqcount_raw_spinlock_t redirected_proxy_intr_seq = + SEQCNT_RAW_SPINLOCK_ZERO(redirected_proxy_intr_seq, + &redirected_proxy_intr_lock); + DEFINE_IDTENTRY_IRQ(hv_vtl_redirected_interrupt) { apic_eoi(); @@ -715,6 +725,83 @@ static bool hv_sev_es_hcall_finish(struct ghcb *ghcb, struct pt_regs *regs) } #endif +#ifdef CONFIG_HYPERV_VTL_MODE +static int find_redirected_proxy_intr(u32 vector) +{ + int v; + + for (v = 0; v < ARRAY_SIZE(redirected_proxy_intr); v++) { + if (redirected_proxy_intr[v] == vector) + return v; + } + + return -ENOENT; +} + +static void redirected_proxy_intr_write_lock(unsigned long *flags) +{ + raw_spin_lock_irqsave(&redirected_proxy_intr_lock, *flags); + write_seqcount_begin(&redirected_proxy_intr_seq); +} + +static void redirected_proxy_intr_write_unlock(unsigned long *flags) +{ + write_seqcount_end(&redirected_proxy_intr_seq); + raw_spin_unlock_irqrestore(&redirected_proxy_intr_lock, *flags); +} + +int mshv_vtl_map_redirected_intr(u32 proxy_vector) +{ + int vec_idx; + unsigned long flags; + + if (proxy_vector >= NR_VECTORS) + return -EINVAL; + + redirected_proxy_intr_write_lock(&flags); + + /* Check if the vector is already redirected. */ + vec_idx = find_redirected_proxy_intr(proxy_vector); + if (vec_idx >= 0) { + redirected_proxy_intr_write_unlock(&flags); + /* Caller expects the hardware vector, not the array index. */ + return vec_idx + FIRST_HV_VTL_REDIRECTED_VECTOR; + } + + /* Now that we know it is not redirected, find a free slot. */ + vec_idx = find_redirected_proxy_intr(REDIRECTED_VECTOR_UNDEF); + if (vec_idx < 0) { + redirected_proxy_intr_write_unlock(&flags); + return -EBUSY; + } + + redirected_proxy_intr[vec_idx] = proxy_vector; + + redirected_proxy_intr_write_unlock(&flags); + + /* Caller expects the hardware vector, not the array index. */ + return vec_idx + FIRST_HV_VTL_REDIRECTED_VECTOR; +} + +int mshv_vtl_unmap_redirected_intr(u32 hw_vector) +{ + unsigned long flags; + int vec_idx = hw_vector - FIRST_HV_VTL_REDIRECTED_VECTOR; + + if (vec_idx < 0 || vec_idx >= ARRAY_SIZE(redirected_proxy_intr)) + return -EINVAL; + + if (redirected_proxy_intr[vec_idx] == REDIRECTED_VECTOR_UNDEF) + return -ENOENT; + + redirected_proxy_intr_write_lock(&flags); + redirected_proxy_intr[vec_idx] = REDIRECTED_VECTOR_UNDEF; + redirected_proxy_intr_write_unlock(&flags); + + return 0; +} +#endif /* CONFIG_HYPERV_VTL_MODE */ + const __initconst struct hypervisor_x86 x86_hyper_ms_hyperv = { .name = "Microsoft Hyper-V", .detect = ms_hyperv_platform, From f6d968be0e32955a7c443e7725647af3a22e9d46 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Tue, 22 Apr 2025 12:35:28 -0700 Subject: [PATCH 09/10] x86/hyperv: Register the handler of redirected proxy interrupts Implement the hv_{setup, remove}__redirected_proxy_intr_handler() function. Store a pointer to the handler that the Hyper-V VTL driver provides. It will be needed when dispatching hardware interrupt vectors that have been mapped to proxy interrupts. Signed-off-by: Ricardo Neri --- Changes since v1: * None --- arch/x86/kernel/cpu/mshyperv.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index 5cf395bcce00..e5c117295571 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -115,6 +115,9 @@ static void (*vmbus_handler)(void); static void (*hv_stimer0_handler)(void); static void (*hv_kexec_handler)(void); static void (*hv_crash_handler)(struct pt_regs *regs); +#ifdef CONFIG_HYPERV_VTL_MODE +static void (*hv_redirected_proxy_intr_handler)(u32 vector); +#endif DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_callback) { @@ -214,6 +217,23 @@ void hv_remove_crash_handler(void) hv_crash_handler = NULL; } +#ifdef CONFIG_HYPERV_VTL_MODE +void hv_setup_redirected_proxy_intr_handler(void (*handler)(u32 vector)) +{ + hv_redirected_proxy_intr_handler = handler; +} + +void hv_remove_redirected_proxy_intr_handler(void) +{ + /* + * The interrupt gate is still allocated. Interrupts will + * continue to be acked with out a handler they cannot be + * proper processed. + */ + hv_redirected_proxy_intr_handler = NULL; +} +#endif + #ifdef CONFIG_KEXEC_CORE static void hv_machine_shutdown(void) { From 9fe424daf78e4fe9d1786b3544fd0f2e8751802b Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Tue, 18 Feb 2025 17:13:27 -0800 Subject: [PATCH 10/10] x86/hyperv: Dispatch redirected proxy interrupts Once a redirected proxy interrupt has been configured, the IOMMU at the host will post it directly to a hardware vector in the VTL2 guest. Calculate the index of the proxy interrupt based on the hardware interrupt vector and indicate that it needs to be asserted. We only read the redirected data structures during interrupts. Protect access using the provided seqcount. This allows lockless access for readers. Signed-off-by: Ricardo Neri --- Changes since v1: * Used a seqcount for serialization --- arch/x86/kernel/cpu/mshyperv.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index e5c117295571..e7977c00b276 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -181,6 +181,32 @@ static seqcount_raw_spinlock_t redirected_proxy_intr_seq = DEFINE_IDTENTRY_IRQ(hv_vtl_redirected_interrupt) { + unsigned int seq; + u32 proxy_intr; + + if (!hv_redirected_proxy_intr_handler) { + pr_err_ratelimited("redirected intr handler not defined. hw intr %u not handled\n", vector); + goto out; + } + + if (vector < FIRST_HV_VTL_REDIRECTED_VECTOR || vector > FIRST_SYSTEM_VECTOR) { + pr_err_ratelimited("redirected hw vector %u is invalid\n", vector); + goto out; + } + + do { + seq = read_seqcount_begin(&redirected_proxy_intr_seq); + proxy_intr = redirected_proxy_intr[vector - FIRST_HV_VTL_REDIRECTED_VECTOR]; + } while (read_seqcount_retry(&redirected_proxy_intr_seq, seq)); + + if (proxy_intr == REDIRECTED_VECTOR_UNDEF) { + pr_err_ratelimited("hw vector %u not redictected to proxy interrupt\n", vector); + goto out; + } + + hv_redirected_proxy_intr_handler(proxy_intr); + +out: apic_eoi(); } #endif