From bdf5eb306c4d07a3bfdd4e2ab8f2acc7ff824940 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Wed, 4 Mar 2026 15:41:12 -0800 Subject: [PATCH 01/14] x86/topology: Add missing struct declaration and attribute dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prototypes for get_topology_cpu_type_name() and get_topology_cpu_type() take a pointer to struct cpuinfo_x86, but asm/topology.h neither includes nor forward-declares the structure. Including asm/topology.h, directly or indirectly, without including asm/processor.h triggers a warning: ./arch/x86/include/asm/topology.h:159:47: error: ‘struct cpuinfo_x86’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror] 159 | const char *get_topology_cpu_type_name(struct cpuinfo_x86 *c); | ^~~~~~~~~~~ Since only a pointer is needed, add a forward declaration of struct cpuinfo_x86. Additionally, sysctl_sched_itmt_enabled is declared in asm/topology.h with the __read_mostly attribute, but the header does not include linux/cache.h. This causes a build failure when including asm/topology.h but not linux/ cache.h: ./arch/x86/include/asm/topology.h:264:27: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘sysctl_sched_itmt_enabled’ 264 | extern bool __read_mostly sysctl_sched_itmt_enabled; | ^~~~~~~~~~~~~~~~~~~~~~~~~ Include the required header. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202511181954.UMxCeTV1-lkp@intel.com/ Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202511190008.AA0NTn3G-lkp@intel.com/ Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/include/asm/topology.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h index 0ba9bdb99871..8fb61d2465eb 100644 --- a/arch/x86/include/asm/topology.h +++ b/arch/x86/include/asm/topology.h @@ -157,6 +157,8 @@ extern unsigned int __num_threads_per_package; extern unsigned int __num_cores_per_package; extern unsigned int __num_nodes_per_package; +struct cpuinfo_x86; + const char *get_topology_cpu_type_name(struct cpuinfo_x86 *c); enum x86_topology_cpu_type get_topology_cpu_type(struct cpuinfo_x86 *c); @@ -265,6 +267,7 @@ extern bool x86_topology_update; #ifdef CONFIG_SCHED_MC_PRIO #include +#include DECLARE_PER_CPU_READ_MOSTLY(int, sched_core_priority); extern bool __read_mostly sysctl_sched_itmt_enabled; From a746607df24b4b01fef380390c0b499b84fc7863 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Wed, 4 Mar 2026 15:41:13 -0800 Subject: [PATCH 02/14] x86/acpi: Add functions to setup and access the wakeup mailbox Systems that describe hardware using DeviceTree graphs may enumerate and implement the wakeup mailbox as defined in the ACPI specification but do not otherwise depend on ACPI. Expose functions to setup and access the location of the wakeup mailbox from outside ACPI code. The function acpi_setup_mp_wakeup_mailbox() stores the physical address of the mailbox and updates the wakeup_secondary_cpu_64() APIC callback. The function acpi_madt_multiproc_wakeup_mailbox() returns a pointer to the mailbox. Acked-by: Rafael J. Wysocki (Intel) Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/include/asm/acpi.h | 10 ++++++++++ arch/x86/kernel/acpi/madt_wakeup.c | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index a03aa6f999d1..820df375df79 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -182,6 +182,9 @@ void __iomem *x86_acpi_os_ioremap(acpi_physical_address phys, acpi_size size); #define acpi_os_ioremap acpi_os_ioremap #endif +void acpi_setup_mp_wakeup_mailbox(u64 addr); +struct acpi_madt_multiproc_wakeup_mailbox *acpi_get_mp_wakeup_mailbox(void); + #else /* !CONFIG_ACPI */ #define acpi_lapic 0 @@ -200,6 +203,13 @@ static inline u64 x86_default_get_root_pointer(void) return 0; } +static inline void acpi_setup_mp_wakeup_mailbox(u64 addr) { } + +static inline struct acpi_madt_multiproc_wakeup_mailbox *acpi_get_mp_wakeup_mailbox(void) +{ + return NULL; +} + #endif /* !CONFIG_ACPI */ #define ARCH_HAS_POWER_INIT 1 diff --git a/arch/x86/kernel/acpi/madt_wakeup.c b/arch/x86/kernel/acpi/madt_wakeup.c index 6d7603511f52..82caf44b45e3 100644 --- a/arch/x86/kernel/acpi/madt_wakeup.c +++ b/arch/x86/kernel/acpi/madt_wakeup.c @@ -247,3 +247,14 @@ int __init acpi_parse_mp_wake(union acpi_subtable_headers *header, return 0; } + +void __init acpi_setup_mp_wakeup_mailbox(u64 mailbox_paddr) +{ + acpi_mp_wake_mailbox_paddr = mailbox_paddr; + apic_update_callback(wakeup_secondary_cpu_64, acpi_wakeup_cpu); +} + +struct acpi_madt_multiproc_wakeup_mailbox *acpi_get_mp_wakeup_mailbox(void) +{ + return acpi_mp_wake_mailbox; +} From b7c8992aea5e0aca6f5c3d1e57ed568eeddfe9a0 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Wed, 4 Mar 2026 15:41:14 -0800 Subject: [PATCH 03/14] dt-bindings: reserved-memory: Wakeup Mailbox for Intel processors Add DeviceTree bindings to enumerate the wakeup mailbox used in platform firmware for Intel processors. x86 platforms commonly boot secondary CPUs using an INIT assert, de-assert followed by Start-Up IPI messages. The wakeup mailbox can be used when this mechanism is unavailable. The wakeup mailbox offers more control to the operating system to boot secondary CPUs than a spin-table. It allows the reuse of the same wakeup vector for all CPUs while maintaining control over which CPUs to boot and when. While it is possible to achieve the same level of control using a spin-table, it would require specifying a separate `cpu-release-addr` for each secondary CPU. The operation and structure of the mailbox are described in the Multiprocessor Wakeup Structure defined in the ACPI specification. Note that this structure does not specify how to publish the mailbox to the operating system (ACPI-based platform firmware uses a separate table). No ACPI table is needed in DeviceTree-based firmware to enumerate the mailbox. Nodes that want to refer to the reserved memory usually define a `memory-region` property. /cpus/cpu* nodes would want to refer to the mailbox, but they do not have such property defined in the DeviceTree specification. Moreover, it would imply that there is a memory region per CPU. Instead, add a `compatible` property that the operating system can use to discover the mailbox. Reviewed-by: Dexuan Cui Reviewed-by: Rob Herring (Arm) Acked-by: Rafael J. Wysocki (Intel) Co-developed-by: Yunhong Jiang Signed-off-by: Yunhong Jiang Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- .../reserved-memory/intel,wakeup-mailbox.yaml | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Documentation/devicetree/bindings/reserved-memory/intel,wakeup-mailbox.yaml diff --git a/Documentation/devicetree/bindings/reserved-memory/intel,wakeup-mailbox.yaml b/Documentation/devicetree/bindings/reserved-memory/intel,wakeup-mailbox.yaml new file mode 100644 index 000000000000..4362bc058df4 --- /dev/null +++ b/Documentation/devicetree/bindings/reserved-memory/intel,wakeup-mailbox.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reserved-memory/intel,wakeup-mailbox.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Wakeup Mailbox for Intel processors + +description: | + The Wakeup Mailbox provides a mechanism for the operating system to wake up + secondary CPUs on Intel processors. It is an alternative to the INIT-!INIT- + SIPI sequence used on most x86 systems. + + The structure and operation of the mailbox is described in the Multiprocessor + Wakeup Structure of the ACPI specification version 6.6 section 5.2.12.19 [1]. + + The implementation of the mailbox in platform firmware is described in the + Intel TDX Virtual Firmware Design Guide section 4.3.5 [2]. + + 1: https://uefi.org/specs/ACPI/6.6/05_ACPI_Software_Programming_Model.html#multiprocessor-wakeup-structure + 2: https://www.intel.com/content/www/us/en/content-details/733585/intel-tdx-virtual-firmware-design-guide.html + +maintainers: + - Ricardo Neri + +allOf: + - $ref: reserved-memory.yaml + +properties: + compatible: + const: intel,wakeup-mailbox + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + reserved-memory { + #address-cells = <2>; + #size-cells = <1>; + + wakeup-mailbox@ffff0000 { + compatible = "intel,wakeup-mailbox"; + reg = <0x0 0xffff0000 0x1000>; + }; + }; From 12d58799c19572b98ec84c12ec34a228fe3e8c5c Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Wed, 4 Mar 2026 15:41:15 -0800 Subject: [PATCH 04/14] x86/dt: Parse the Wakeup Mailbox for Intel processors The Wakeup Mailbox is a mechanism to boot secondary CPUs on systems that do not want or cannot use the INIT + StartUp IPI messages. The platform firmware is expected to implement the mailbox as described in the Multiprocessor Wakeup Structure of the ACPI specification. It is also expected to publish the mailbox to the operating system as described in the corresponding DeviceTree schema that accompanies the documentation of the Linux kernel. Reuse the existing functionality to set the memory location of the mailbox and update the wakeup_secondary_cpu_64() APIC callback. Make this functionality available to DeviceTree-based systems by making CONFIG_X86_ MAILBOX_WAKEUP depend on either CONFIG_OF or CONFIG_ACPI_MADT_WAKEUP. do_boot_cpu() uses wakeup_secondary_cpu_64() when set. It will be set if a wakeup mailbox is enumerated via an ACPI table or a DeviceTree node. For cases in which this behavior is not desired, this APIC callback can be updated later during boot using platform-specific hooks. Reviewed-by: Dexuan Cui Co-developed-by: Yunhong Jiang Signed-off-by: Yunhong Jiang Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/kernel/devicetree.c | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c index dd8748c45529..318acaecb5ca 100644 --- a/arch/x86/kernel/devicetree.c +++ b/arch/x86/kernel/devicetree.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -125,6 +126,51 @@ static void __init dtb_setup_hpet(void) #endif } +#if defined(CONFIG_X86_64) && defined(CONFIG_SMP) + +#define WAKEUP_MAILBOX_SIZE 0x1000 +#define WAKEUP_MAILBOX_ALIGN 0x1000 + +/** dtb_wakeup_mailbox_setup() - Parse the wakeup mailbox from the device tree + * + * Look for the presence of a wakeup mailbox in the DeviceTree. The mailbox is + * expected to follow the structure and operation described in the Multiprocessor + * Wakeup Structure of the ACPI specification. + */ +static void __init dtb_wakeup_mailbox_setup(void) +{ + struct device_node *node; + struct resource res; + + node = of_find_compatible_node(NULL, NULL, "intel,wakeup-mailbox"); + if (!node) + return; + + if (of_address_to_resource(node, 0, &res)) + goto done; + + /* The mailbox is a 4KB-aligned region.*/ + if (res.start & (WAKEUP_MAILBOX_ALIGN - 1)) + goto done; + + /* The mailbox has a size of 4KB. */ + if (res.end - res.start + 1 != WAKEUP_MAILBOX_SIZE) + goto done; + + /* Not supported when the mailbox is used. */ + cpu_hotplug_disable_offlining(); + + acpi_setup_mp_wakeup_mailbox(res.start); +done: + of_node_put(node); +} +#else /* !CONFIG_X86_64 || !CONFIG_SMP */ +static inline int dtb_wakeup_mailbox_setup(void) +{ + return -EOPNOTSUPP; +} +#endif /* CONFIG_X86_64 && CONFIG_SMP */ + #ifdef CONFIG_X86_LOCAL_APIC static void __init dtb_cpu_setup(void) @@ -287,6 +333,7 @@ static void __init x86_dtb_parse_smp_config(void) dtb_setup_hpet(); dtb_apic_setup(); + dtb_wakeup_mailbox_setup(); } void __init x86_flattree_get_config(void) From 7e4c083ece42ed6c90e4071772847db595081e0e Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Wed, 4 Mar 2026 15:41:16 -0800 Subject: [PATCH 05/14] x86/hyperv/vtl: Set real_mode_header in hv_vtl_init_platform() Hyper-V VTL clears x86_platform.realmode_{init(), reserve()} in hv_vtl_init_platform() whereas it sets real_mode_header later in hv_vtl_early_init(). There is no need to deal with the settings of real mode memory in two places. Also, both functions are called much earlier than x86_platform.realmode_init() (via an early_initcall), where the real_mode_header is needed. Set real_mode_header in hv_vtl_init_platform() to keep all code dealing with memory for the real mode trampoline in one place. Besides making the code more readable, it prepares it for a subsequent changeset in which the behavior needs to change to support Hyper-V VTL guests in a TDX environment. Reviewed-by: Dexuan Cui Reviewed-by: Michael Kelley Suggested-by: Thomas Gleixner Signed-off-by: Yunhong Jiang Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/hyperv/hv_vtl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/hyperv/hv_vtl.c b/arch/x86/hyperv/hv_vtl.c index 9b6a9bc4ab76..e045bf3a2c59 100644 --- a/arch/x86/hyperv/hv_vtl.c +++ b/arch/x86/hyperv/hv_vtl.c @@ -70,6 +70,7 @@ void __init hv_vtl_init_platform(void) x86_platform.realmode_reserve = x86_init_noop; x86_platform.realmode_init = x86_init_noop; + real_mode_header = &hv_vtl_real_mode_header; x86_init.irqs.pre_vector_init = x86_init_noop; x86_init.timers.timer_init = x86_init_noop; x86_init.resources.probe_roms = x86_init_noop; @@ -251,7 +252,6 @@ int __init hv_vtl_early_init(void) panic("XSAVE has to be disabled as it is not supported by this module.\n" "Please add 'noxsave' to the kernel command line.\n"); - real_mode_header = &hv_vtl_real_mode_header; apic_update_callback(wakeup_secondary_cpu_64, hv_vtl_wakeup_secondary_cpu); return 0; From a7ac1ea1f06314f6690ac772e9a31d6e2db977fa Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Wed, 4 Mar 2026 15:41:17 -0800 Subject: [PATCH 06/14] x86/realmode: Make the location of the trampoline configurable x86 CPUs boot in real mode. This mode uses a 1MB address space. The trampoline must reside below this 1MB memory boundary. There are platforms in which the firmware boots the secondary CPUs, switches them to long mode and transfers control to the kernel. An example of such a mechanism is the ACPI Multiprocessor Wakeup Structure. In this scenario there is no restriction on locating the trampoline under 1MB memory. Moreover, certain platforms (for example, Hyper-V VTL guests) may not have memory available for allocation below 1MB. Add a new member to struct x86_init_resources to specify the upper bound for the location of the trampoline memory. Preserve the default upper bound of 1MB to conserve the current behavior. Reviewed-by: Dexuan Cui Reviewed-by: Michael Kelley Originally-by: Thomas Gleixner Signed-off-by: Yunhong Jiang Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/include/asm/x86_init.h | 3 +++ arch/x86/kernel/x86_init.c | 3 +++ arch/x86/realmode/init.c | 7 +++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h index 6c8a6ead84f6..953d3199408a 100644 --- a/arch/x86/include/asm/x86_init.h +++ b/arch/x86/include/asm/x86_init.h @@ -31,12 +31,15 @@ struct x86_init_mpparse { * platform * @memory_setup: platform specific memory setup * @dmi_setup: platform specific DMI setup + * @realmode_limit: platform specific address limit for the real mode trampoline + * (default 1M) */ struct x86_init_resources { void (*probe_roms)(void); void (*reserve_resources)(void); char *(*memory_setup)(void); void (*dmi_setup)(void); + unsigned long realmode_limit; }; /** diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c index ebefb77c37bb..252c5827d063 100644 --- a/arch/x86/kernel/x86_init.c +++ b/arch/x86/kernel/x86_init.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -69,6 +70,8 @@ struct x86_init_ops x86_init __initdata = { .reserve_resources = reserve_standard_io_resources, .memory_setup = e820__memory_setup_default, .dmi_setup = dmi_setup, + /* Has to be under 1M so we can execute real-mode AP code. */ + .realmode_limit = SZ_1M, }, .mpparse = { diff --git a/arch/x86/realmode/init.c b/arch/x86/realmode/init.c index 88be32026768..694d80a5c68e 100644 --- a/arch/x86/realmode/init.c +++ b/arch/x86/realmode/init.c @@ -46,7 +46,7 @@ void load_trampoline_pgtable(void) void __init reserve_real_mode(void) { - phys_addr_t mem; + phys_addr_t mem, limit = x86_init.resources.realmode_limit; size_t size = real_mode_size_needed(); if (!size) @@ -54,10 +54,9 @@ void __init reserve_real_mode(void) WARN_ON(slab_is_available()); - /* Has to be under 1M so we can execute real-mode AP code. */ - mem = memblock_phys_alloc_range(size, PAGE_SIZE, 0, 1<<20); + mem = memblock_phys_alloc_range(size, PAGE_SIZE, 0, limit); if (!mem) - pr_info("No sub-1M memory is available for the trampoline\n"); + pr_info("No memory below %pa for the real-mode trampoline\n", &limit); else set_real_mode_mem(mem); From aa7719039bd9e378c8c134b8ce5fa5f4e13cda73 Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Wed, 4 Mar 2026 15:41:18 -0800 Subject: [PATCH 07/14] x86/hyperv/vtl: Setup the 64-bit trampoline for TDX guests The hypervisor is an untrusted entity for TDX guests. It cannot be used to boot secondary CPUs - neither via hypercalls nor the INIT assert, de-assert, plus Start-Up IPI messages. Instead, the platform virtual firmware boots the secondary CPUs and puts them in a state to transfer control to the kernel. This mechanism uses the wakeup mailbox described in the Multiprocessor Wakeup Structure of the ACPI specification. The entry point to the kernel is trampoline_start64. Allocate and setup the trampoline using the default x86_platform callbacks. The platform firmware configures the secondary CPUs in long mode. It is no longer necessary to locate the trampoline under 1MB memory. After handoff from firmware, the trampoline code switches briefly to 32-bit addressing mode, which has an addressing limit of 4GB. Set the upper bound of the trampoline memory accordingly. Reviewed-by: Dexuan Cui Reviewed-by: Michael Kelley Signed-off-by: Yunhong Jiang Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/hyperv/hv_vtl.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/x86/hyperv/hv_vtl.c b/arch/x86/hyperv/hv_vtl.c index e045bf3a2c59..b94fffa67312 100644 --- a/arch/x86/hyperv/hv_vtl.c +++ b/arch/x86/hyperv/hv_vtl.c @@ -68,9 +68,14 @@ void __init hv_vtl_init_platform(void) */ pr_info("Linux runs in Hyper-V Virtual Trust Level %d\n", ms_hyperv.vtl); - x86_platform.realmode_reserve = x86_init_noop; - x86_platform.realmode_init = x86_init_noop; - real_mode_header = &hv_vtl_real_mode_header; + /* There is no paravisor present if we are here. */ + if (hv_isolation_type_tdx()) { + x86_init.resources.realmode_limit = SZ_4G; + } else { + x86_platform.realmode_reserve = x86_init_noop; + x86_platform.realmode_init = x86_init_noop; + real_mode_header = &hv_vtl_real_mode_header; + } x86_init.irqs.pre_vector_init = x86_init_noop; x86_init.timers.timer_init = x86_init_noop; x86_init.resources.probe_roms = x86_init_noop; From 12584a89c9172ed5c2718612314a2d6f87260896 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Wed, 4 Mar 2026 15:41:19 -0800 Subject: [PATCH 08/14] x86/acpi: Add a helper to get the address of the wakeup mailbox A Hyper-V VTL level 2 guest in a TDX environment needs to map the physical page of the ACPI Multiprocessor Wakeup Structure as private (encrypted). It needs to know the physical address of this structure. Add a helper function to retrieve the address. Suggested-by: Michael Kelley Acked-by: Rafael J. Wysocki (Intel) Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/include/asm/acpi.h | 6 ++++++ arch/x86/kernel/acpi/madt_wakeup.c | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index 820df375df79..c4e6459bd56b 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -184,6 +184,7 @@ void __iomem *x86_acpi_os_ioremap(acpi_physical_address phys, acpi_size size); void acpi_setup_mp_wakeup_mailbox(u64 addr); struct acpi_madt_multiproc_wakeup_mailbox *acpi_get_mp_wakeup_mailbox(void); +u64 acpi_get_mp_wakeup_mailbox_paddr(void); #else /* !CONFIG_ACPI */ @@ -210,6 +211,11 @@ static inline struct acpi_madt_multiproc_wakeup_mailbox *acpi_get_mp_wakeup_mail return NULL; } +static inline u64 acpi_get_mp_wakeup_mailbox_paddr(void) +{ + return 0; +} + #endif /* !CONFIG_ACPI */ #define ARCH_HAS_POWER_INIT 1 diff --git a/arch/x86/kernel/acpi/madt_wakeup.c b/arch/x86/kernel/acpi/madt_wakeup.c index 82caf44b45e3..48734e4a6e8f 100644 --- a/arch/x86/kernel/acpi/madt_wakeup.c +++ b/arch/x86/kernel/acpi/madt_wakeup.c @@ -258,3 +258,8 @@ struct acpi_madt_multiproc_wakeup_mailbox *acpi_get_mp_wakeup_mailbox(void) { return acpi_mp_wake_mailbox; } + +u64 acpi_get_mp_wakeup_mailbox_paddr(void) +{ + return acpi_mp_wake_mailbox_paddr; +} From 80200341197a799fe39da4e9da61bde4d23ec7ec Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Wed, 4 Mar 2026 15:41:20 -0800 Subject: [PATCH 09/14] x86/hyperv/vtl: Mark the wakeup mailbox page as private The current code maps MMIO devices as shared (decrypted) by default in a confidential computing VM. In a TDX environment, secondary CPUs are booted using the Multiprocessor Wakeup Structure defined in the ACPI specification. The virtual firmware and the operating system function in the guest context, without intervention from the VMM. Map the physical memory of the mailbox as private. Use the is_private_mmio() callback. Signed-off-by: Yunhong Jiang Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/hyperv/hv_vtl.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/x86/hyperv/hv_vtl.c b/arch/x86/hyperv/hv_vtl.c index b94fffa67312..1e2f5b3ea772 100644 --- a/arch/x86/hyperv/hv_vtl.c +++ b/arch/x86/hyperv/hv_vtl.c @@ -6,6 +6,9 @@ * Saurabh Sengar */ +#include + +#include #include #include #include @@ -59,6 +62,18 @@ static void __noreturn hv_vtl_restart(char __maybe_unused *cmd) hv_vtl_emergency_restart(); } +static inline bool within_page(u64 addr, u64 start) +{ + return addr >= start && addr < (start + PAGE_SIZE); +} + +static bool hv_vtl_is_private_mmio_tdx(u64 addr) +{ + u64 mb_addr = acpi_get_mp_wakeup_mailbox_paddr(); + + return mb_addr && within_page(addr, mb_addr); +} + void __init hv_vtl_init_platform(void) { /* @@ -71,6 +86,8 @@ void __init hv_vtl_init_platform(void) /* There is no paravisor present if we are here. */ if (hv_isolation_type_tdx()) { x86_init.resources.realmode_limit = SZ_4G; + x86_platform.hyper.is_private_mmio = hv_vtl_is_private_mmio_tdx; + } else { x86_platform.realmode_reserve = x86_init_noop; x86_platform.realmode_init = x86_init_noop; From 7a035678fc2bdee81881170764ef08a91a076147 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Wed, 4 Mar 2026 15:41:21 -0800 Subject: [PATCH 10/14] x86/hyperv/vtl: Use the wakeup mailbox to boot secondary CPUs The hypervisor is an untrusted entity for TDX guests. It cannot be used to boot secondary CPUs. The function hv_vtl_wakeup_secondary_cpu() cannot be used. Instead, the virtual firmware boots the secondary CPUs and places them in a state to transfer control to the kernel using the wakeup mailbox. The firmware enumerates the mailbox via either an ACPI table or a DeviceTree node. If the wakeup mailbox is present, the kernel updates the APIC callback wakeup_secondary_cpu_64() to use it. Reviewed-by: Dexuan Cui Reviewed-by: Michael Kelley Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/hyperv/hv_vtl.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/x86/hyperv/hv_vtl.c b/arch/x86/hyperv/hv_vtl.c index 1e2f5b3ea772..07fac3d687c3 100644 --- a/arch/x86/hyperv/hv_vtl.c +++ b/arch/x86/hyperv/hv_vtl.c @@ -274,7 +274,15 @@ int __init hv_vtl_early_init(void) panic("XSAVE has to be disabled as it is not supported by this module.\n" "Please add 'noxsave' to the kernel command line.\n"); - apic_update_callback(wakeup_secondary_cpu_64, hv_vtl_wakeup_secondary_cpu); + /* + * TDX confidential VMs do not trust the hypervisor and cannot use it to + * boot secondary CPUs. Instead, they will be booted using the wakeup + * mailbox if detected during boot. See setup_arch(). + * + * There is no paravisor present if we are here. + */ + if (!hv_isolation_type_tdx()) + apic_update_callback(wakeup_secondary_cpu_64, hv_vtl_wakeup_secondary_cpu); return 0; } From c14efec1452098ec97d30423e4120e4fa90e0663 Mon Sep 17 00:00:00 2001 From: Mukesh R Date: Wed, 3 Jun 2026 15:50:10 -0700 Subject: [PATCH 11/14] iommu/hyperv: Create hyperv subdirectory under drivers/iommu Create hyperv subdirectory under drivers/iommu in anticipation of more Hyper-V related files from upcoming PCI passthrough and PV-IOMMU patches. Also, the current file hyperv-iommu.c actually implements irq remapping on x86, so rename to more appropriate hv-irq-remap-x86.c and move it under the new hyperv subdirectory. Since this file implements irq_remap_ops exposed by drivers/iommu/irq_remapping.h, it cannot be relocated to the irq directory. This is in sync with other backend directories like amd and intel there. Lastly, this file should not be tied to CONFIG_HYPERV_IOMMU, but to CONFIG_HYPERV and CONFIG_IRQ_REMAP. Signed-off-by: Mukesh R Reviewed-by: Jacob Pan Signed-off-by: Wei Liu --- MAINTAINERS | 2 +- drivers/iommu/Kconfig | 9 --------- drivers/iommu/Makefile | 2 +- drivers/iommu/hyperv/Makefile | 2 ++ .../iommu/{hyperv-iommu.c => hyperv/hv-irq-remap-x86.c} | 6 +----- drivers/iommu/irq_remapping.c | 2 +- 6 files changed, 6 insertions(+), 17 deletions(-) create mode 100644 drivers/iommu/hyperv/Makefile rename drivers/iommu/{hyperv-iommu.c => hyperv/hv-irq-remap-x86.c} (99%) diff --git a/MAINTAINERS b/MAINTAINERS index b539be153f6a..93a7105e9cef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11988,7 +11988,7 @@ F: drivers/clocksource/hyperv_timer.c F: drivers/hid/hid-hyperv.c F: drivers/hv/ F: drivers/input/serio/hyperv-keyboard.c -F: drivers/iommu/hyperv-iommu.c +F: drivers/iommu/hyperv/ F: drivers/net/ethernet/microsoft/ F: drivers/net/hyperv/ F: drivers/pci/controller/pci-hyperv-intf.c diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index f86262b11416..1becc0f20222 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -351,15 +351,6 @@ config MTK_IOMMU_V1 if unsure, say N here. -config HYPERV_IOMMU - bool "Hyper-V IRQ Handling" - depends on HYPERV && X86 - select IOMMU_API - default HYPERV - help - Stub IOMMU driver to handle IRQs to support Hyper-V Linux - guest and root partitions. - config VIRTIO_IOMMU tristate "Virtio IOMMU driver" depends on VIRTIO diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 0275821f4ef9..d9683422aecb 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_AMD_IOMMU) += amd/ obj-$(CONFIG_INTEL_IOMMU) += intel/ obj-$(CONFIG_RISCV_IOMMU) += riscv/ obj-$(CONFIG_GENERIC_PT) += generic_pt/fmt/ +obj-$(CONFIG_HYPERV) += hyperv/ obj-$(CONFIG_IOMMU_API) += iommu.o obj-$(CONFIG_IOMMU_SUPPORT) += iommu-pages.o obj-$(CONFIG_IOMMU_API) += iommu-traces.o @@ -30,7 +31,6 @@ obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o obj-$(CONFIG_S390_IOMMU) += s390-iommu.o -obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o obj-$(CONFIG_IOMMU_IOPF) += io-pgfault.o diff --git a/drivers/iommu/hyperv/Makefile b/drivers/iommu/hyperv/Makefile new file mode 100644 index 000000000000..6ef0ef97f3dd --- /dev/null +++ b/drivers/iommu/hyperv/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_IRQ_REMAP) += hv-irq-remap-x86.o diff --git a/drivers/iommu/hyperv-iommu.c b/drivers/iommu/hyperv/hv-irq-remap-x86.c similarity index 99% rename from drivers/iommu/hyperv-iommu.c rename to drivers/iommu/hyperv/hv-irq-remap-x86.c index 479103261ae6..4963c9ce05dc 100644 --- a/drivers/iommu/hyperv-iommu.c +++ b/drivers/iommu/hyperv/hv-irq-remap-x86.c @@ -22,9 +22,7 @@ #include #include -#include "irq_remapping.h" - -#ifdef CONFIG_IRQ_REMAP +#include "../irq_remapping.h" /* * According 82093AA IO-APIC spec , IO APIC has a 24-entry Interrupt @@ -330,5 +328,3 @@ static const struct irq_domain_ops hyperv_root_ir_domain_ops = { .alloc = hyperv_root_irq_remapping_alloc, .free = hyperv_root_irq_remapping_free, }; - -#endif diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c index c2443659812a..41bf65e4ea88 100644 --- a/drivers/iommu/irq_remapping.c +++ b/drivers/iommu/irq_remapping.c @@ -108,7 +108,7 @@ int __init irq_remapping_prepare(void) else if (IS_ENABLED(CONFIG_AMD_IOMMU) && amd_iommu_irq_ops.prepare() == 0) remap_ops = &amd_iommu_irq_ops; - else if (IS_ENABLED(CONFIG_HYPERV_IOMMU) && + else if (IS_ENABLED(CONFIG_HYPERV) && hyperv_irq_remap_ops.prepare() == 0) remap_ops = &hyperv_irq_remap_ops; else From 982cb55bec4a8aa73569dba4739a3de1ee2c25d9 Mon Sep 17 00:00:00 2001 From: Mukesh R Date: Mon, 1 Jun 2026 15:51:16 -0700 Subject: [PATCH 12/14] x86/hyperv: Cosmetic changes in irqdomain.c for readability Make cosmetic changes: o Rename struct pci_dev *dev to *pdev since there are cases of struct device *dev in the file and all over the kernel o Rename hv_build_pci_dev_id to hv_build_devid_type_pci in anticipation of building different types of device ids o Fix checkpatch.pl issues with return and extraneous printk o Replace spaces with tabs o Rename struct hv_devid *xxx to struct hv_devid *hv_devid given code paths involve many types of device ids o Fix indentation in a large if block by using goto. There are no functional changes. Reviewed-by: Souradeep Chakrabarti Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Mukesh R Signed-off-by: Wei Liu --- arch/x86/hyperv/irqdomain.c | 198 +++++++++++++++++++----------------- 1 file changed, 104 insertions(+), 94 deletions(-) diff --git a/arch/x86/hyperv/irqdomain.c b/arch/x86/hyperv/irqdomain.c index 365e364268d9..b3ad50a874dc 100644 --- a/arch/x86/hyperv/irqdomain.c +++ b/arch/x86/hyperv/irqdomain.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 - /* * Irqdomain for Linux to run as the root partition on Microsoft Hypervisor. * @@ -14,8 +13,8 @@ #include #include -static int hv_map_interrupt(union hv_device_id device_id, bool level, - int cpu, int vector, struct hv_interrupt_entry *entry) +static int hv_map_interrupt(union hv_device_id hv_devid, bool level, + int cpu, int vector, struct hv_interrupt_entry *ret_entry) { struct hv_input_map_device_interrupt *input; struct hv_output_map_device_interrupt *output; @@ -32,7 +31,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level, intr_desc = &input->interrupt_descriptor; memset(input, 0, sizeof(*input)); input->partition_id = hv_current_partition_id; - input->device_id = device_id.as_uint64; + input->device_id = hv_devid.as_uint64; intr_desc->interrupt_type = HV_X64_INTERRUPT_TYPE_FIXED; intr_desc->vector_count = 1; intr_desc->target.vector = vector; @@ -44,7 +43,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level, intr_desc->target.vp_set.valid_bank_mask = 0; intr_desc->target.vp_set.format = HV_GENERIC_SET_SPARSE_4K; - nr_bank = cpumask_to_vpset(&(intr_desc->target.vp_set), cpumask_of(cpu)); + nr_bank = cpumask_to_vpset(&intr_desc->target.vp_set, cpumask_of(cpu)); if (nr_bank < 0) { local_irq_restore(flags); pr_err("%s: unable to generate VP set\n", __func__); @@ -61,7 +60,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level, status = hv_do_rep_hypercall(HVCALL_MAP_DEVICE_INTERRUPT, 0, var_size, input, output); - *entry = output->interrupt_entry; + *ret_entry = output->interrupt_entry; local_irq_restore(flags); @@ -71,21 +70,19 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level, return hv_result_to_errno(status); } -static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *old_entry) +static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *irq_entry) { unsigned long flags; struct hv_input_unmap_device_interrupt *input; - struct hv_interrupt_entry *intr_entry; u64 status; local_irq_save(flags); input = *this_cpu_ptr(hyperv_pcpu_input_arg); memset(input, 0, sizeof(*input)); - intr_entry = &input->interrupt_entry; input->partition_id = hv_current_partition_id; input->device_id = id; - *intr_entry = *old_entry; + input->interrupt_entry = *irq_entry; status = hv_do_hypercall(HVCALL_UNMAP_DEVICE_INTERRUPT, input, NULL); local_irq_restore(flags); @@ -115,67 +112,71 @@ static int get_rid_cb(struct pci_dev *pdev, u16 alias, void *data) return 0; } -static union hv_device_id hv_build_pci_dev_id(struct pci_dev *dev) +static union hv_device_id hv_build_devid_type_pci(struct pci_dev *pdev) { - union hv_device_id dev_id; + int pos; + union hv_device_id hv_devid; struct rid_data data = { .bridge = NULL, - .rid = PCI_DEVID(dev->bus->number, dev->devfn) + .rid = PCI_DEVID(pdev->bus->number, pdev->devfn) }; - pci_for_each_dma_alias(dev, get_rid_cb, &data); + pci_for_each_dma_alias(pdev, get_rid_cb, &data); - dev_id.as_uint64 = 0; - dev_id.device_type = HV_DEVICE_TYPE_PCI; - dev_id.pci.segment = pci_domain_nr(dev->bus); + hv_devid.as_uint64 = 0; + hv_devid.device_type = HV_DEVICE_TYPE_PCI; + hv_devid.pci.segment = pci_domain_nr(pdev->bus); - dev_id.pci.bdf.bus = PCI_BUS_NUM(data.rid); - dev_id.pci.bdf.device = PCI_SLOT(data.rid); - dev_id.pci.bdf.function = PCI_FUNC(data.rid); - dev_id.pci.source_shadow = HV_SOURCE_SHADOW_NONE; + hv_devid.pci.bdf.bus = PCI_BUS_NUM(data.rid); + hv_devid.pci.bdf.device = PCI_SLOT(data.rid); + hv_devid.pci.bdf.function = PCI_FUNC(data.rid); + hv_devid.pci.source_shadow = HV_SOURCE_SHADOW_NONE; - if (data.bridge) { - int pos; + if (data.bridge == NULL) + goto out; - /* - * Microsoft Hypervisor requires a bus range when the bridge is - * running in PCI-X mode. - * - * To distinguish conventional vs PCI-X bridge, we can check - * the bridge's PCI-X Secondary Status Register, Secondary Bus - * Mode and Frequency bits. See PCI Express to PCI/PCI-X Bridge - * Specification Revision 1.0 5.2.2.1.3. - * - * Value zero means it is in conventional mode, otherwise it is - * in PCI-X mode. - */ + /* + * Microsoft Hypervisor requires a bus range when the bridge is + * running in PCI-X mode. + * + * To distinguish conventional vs PCI-X bridge, we can check + * the bridge's PCI-X Secondary Status Register, Secondary Bus + * Mode and Frequency bits. See PCI Express to PCI/PCI-X Bridge + * Specification Revision 1.0 5.2.2.1.3. + * + * Value zero means it is in conventional mode, otherwise it is + * in PCI-X mode. + */ - pos = pci_find_capability(data.bridge, PCI_CAP_ID_PCIX); - if (pos) { - u16 status; + pos = pci_find_capability(data.bridge, PCI_CAP_ID_PCIX); + if (pos) { + u16 status; - pci_read_config_word(data.bridge, pos + - PCI_X_BRIDGE_SSTATUS, &status); + pci_read_config_word(data.bridge, pos + PCI_X_BRIDGE_SSTATUS, + &status); - if (status & PCI_X_SSTATUS_FREQ) { - /* Non-zero, PCI-X mode */ - u8 sec_bus, sub_bus; + if (status & PCI_X_SSTATUS_FREQ) { + /* Non-zero, PCI-X mode */ + u8 sec_bus, sub_bus; - dev_id.pci.source_shadow = HV_SOURCE_SHADOW_BRIDGE_BUS_RANGE; + hv_devid.pci.source_shadow = + HV_SOURCE_SHADOW_BRIDGE_BUS_RANGE; - pci_read_config_byte(data.bridge, PCI_SECONDARY_BUS, &sec_bus); - dev_id.pci.shadow_bus_range.secondary_bus = sec_bus; - pci_read_config_byte(data.bridge, PCI_SUBORDINATE_BUS, &sub_bus); - dev_id.pci.shadow_bus_range.subordinate_bus = sub_bus; - } + pci_read_config_byte(data.bridge, PCI_SECONDARY_BUS, + &sec_bus); + hv_devid.pci.shadow_bus_range.secondary_bus = sec_bus; + pci_read_config_byte(data.bridge, PCI_SUBORDINATE_BUS, + &sub_bus); + hv_devid.pci.shadow_bus_range.subordinate_bus = sub_bus; } } - return dev_id; +out: + return hv_devid; } -/** - * hv_map_msi_interrupt() - "Map" the MSI IRQ in the hypervisor. +/* + * hv_map_msi_interrupt() - Map the MSI IRQ in the hypervisor. * @data: Describes the IRQ * @out_entry: Hypervisor (MSI) interrupt entry (can be NULL) * @@ -188,22 +189,23 @@ int hv_map_msi_interrupt(struct irq_data *data, { struct irq_cfg *cfg = irqd_cfg(data); struct hv_interrupt_entry dummy; - union hv_device_id device_id; + union hv_device_id hv_devid; struct msi_desc *msidesc; - struct pci_dev *dev; + struct pci_dev *pdev; int cpu; msidesc = irq_data_get_msi_desc(data); - dev = msi_desc_to_pci_dev(msidesc); - device_id = hv_build_pci_dev_id(dev); + pdev = msi_desc_to_pci_dev(msidesc); + hv_devid = hv_build_devid_type_pci(pdev); cpu = cpumask_first(irq_data_get_effective_affinity_mask(data)); - return hv_map_interrupt(device_id, false, cpu, cfg->vector, + return hv_map_interrupt(hv_devid, false, cpu, cfg->vector, out_entry ? out_entry : &dummy); } EXPORT_SYMBOL_GPL(hv_map_msi_interrupt); -static inline void entry_to_msi_msg(struct hv_interrupt_entry *entry, struct msi_msg *msg) +static void entry_to_msi_msg(struct hv_interrupt_entry *entry, + struct msi_msg *msg) { /* High address is always 0 */ msg->address_hi = 0; @@ -211,17 +213,19 @@ static inline void entry_to_msi_msg(struct hv_interrupt_entry *entry, struct msi msg->data = entry->msi_entry.data.as_uint32; } -static int hv_unmap_msi_interrupt(struct pci_dev *dev, struct hv_interrupt_entry *old_entry); +static int hv_unmap_msi_interrupt(struct pci_dev *pdev, + struct hv_interrupt_entry *irq_entry); + static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) { struct hv_interrupt_entry *stored_entry; struct irq_cfg *cfg = irqd_cfg(data); struct msi_desc *msidesc; - struct pci_dev *dev; + struct pci_dev *pdev; int ret; msidesc = irq_data_get_msi_desc(data); - dev = msi_desc_to_pci_dev(msidesc); + pdev = msi_desc_to_pci_dev(msidesc); if (!cfg) { pr_debug("%s: cfg is NULL", __func__); @@ -240,7 +244,7 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) stored_entry = data->chip_data; data->chip_data = NULL; - ret = hv_unmap_msi_interrupt(dev, stored_entry); + ret = hv_unmap_msi_interrupt(pdev, stored_entry); kfree(stored_entry); @@ -249,10 +253,8 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) } stored_entry = kzalloc_obj(*stored_entry, GFP_ATOMIC); - if (!stored_entry) { - pr_debug("%s: failed to allocate chip data\n", __func__); + if (!stored_entry) return; - } ret = hv_map_msi_interrupt(data, stored_entry); if (ret) { @@ -262,18 +264,21 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) data->chip_data = stored_entry; entry_to_msi_msg(data->chip_data, msg); - - return; } -static int hv_unmap_msi_interrupt(struct pci_dev *dev, struct hv_interrupt_entry *old_entry) +static int hv_unmap_msi_interrupt(struct pci_dev *pdev, + struct hv_interrupt_entry *irq_entry) { - return hv_unmap_interrupt(hv_build_pci_dev_id(dev).as_uint64, old_entry); + union hv_device_id hv_devid; + + hv_devid = hv_build_devid_type_pci(pdev); + return hv_unmap_interrupt(hv_devid.as_uint64, irq_entry); } -static void hv_teardown_msi_irq(struct pci_dev *dev, struct irq_data *irqd) +/* NB: during map, hv_interrupt_entry is saved via data->chip_data */ +static void hv_teardown_msi_irq(struct pci_dev *pdev, struct irq_data *irqd) { - struct hv_interrupt_entry old_entry; + struct hv_interrupt_entry irq_entry; struct msi_msg msg; if (!irqd->chip_data) { @@ -281,13 +286,13 @@ static void hv_teardown_msi_irq(struct pci_dev *dev, struct irq_data *irqd) return; } - old_entry = *(struct hv_interrupt_entry *)irqd->chip_data; - entry_to_msi_msg(&old_entry, &msg); + irq_entry = *(struct hv_interrupt_entry *)irqd->chip_data; + entry_to_msi_msg(&irq_entry, &msg); kfree(irqd->chip_data); irqd->chip_data = NULL; - (void)hv_unmap_msi_interrupt(dev, &old_entry); + (void)hv_unmap_msi_interrupt(pdev, &irq_entry); } /* @@ -302,7 +307,8 @@ static struct irq_chip hv_pci_msi_controller = { }; static bool hv_init_dev_msi_info(struct device *dev, struct irq_domain *domain, - struct irq_domain *real_parent, struct msi_domain_info *info) + struct irq_domain *real_parent, + struct msi_domain_info *info) { struct irq_chip *chip = info->chip; @@ -317,7 +323,8 @@ static bool hv_init_dev_msi_info(struct device *dev, struct irq_domain *domain, } #define HV_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | MSI_FLAG_PCI_MSIX) -#define HV_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS) +#define HV_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS) static struct msi_parent_ops hv_msi_parent_ops = { .supported_flags = HV_MSI_FLAGS_SUPPORTED, @@ -329,14 +336,14 @@ static struct msi_parent_ops hv_msi_parent_ops = { .init_dev_msi_info = hv_init_dev_msi_info, }; -static int hv_msi_domain_alloc(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs, - void *arg) +/* Allocate nr_irqs IRQs for the given irq domain */ +static int hv_msi_domain_alloc(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs, void *arg) { /* - * TODO: The allocation bits of hv_irq_compose_msi_msg(), i.e. everything except - * entry_to_msi_msg() should be in here. + * TODO: The allocation bits of hv_irq_compose_msi_msg(), i.e. + * everything except entry_to_msi_msg() should be in here. */ - int ret; ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, arg); @@ -344,13 +351,15 @@ static int hv_msi_domain_alloc(struct irq_domain *d, unsigned int virq, unsigned return ret; for (int i = 0; i < nr_irqs; ++i) { - irq_domain_set_info(d, virq + i, 0, &hv_pci_msi_controller, NULL, - handle_edge_irq, NULL, "edge"); + irq_domain_set_info(d, virq + i, 0, &hv_pci_msi_controller, + NULL, handle_edge_irq, NULL, "edge"); } + return 0; } -static void hv_msi_domain_free(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs) +static void hv_msi_domain_free(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs) { for (int i = 0; i < nr_irqs; ++i) { struct irq_data *irqd = irq_domain_get_irq_data(d, virq); @@ -362,6 +371,7 @@ static void hv_msi_domain_free(struct irq_domain *d, unsigned int virq, unsigned hv_teardown_msi_irq(to_pci_dev(desc->dev), irqd); } + irq_domain_free_irqs_top(d, virq, nr_irqs); } @@ -394,25 +404,25 @@ struct irq_domain * __init hv_create_pci_msi_domain(void) int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry) { - union hv_device_id device_id; + union hv_device_id hv_devid; - device_id.as_uint64 = 0; - device_id.device_type = HV_DEVICE_TYPE_IOAPIC; - device_id.ioapic.ioapic_id = (u8)ioapic_id; + hv_devid.as_uint64 = 0; + hv_devid.device_type = HV_DEVICE_TYPE_IOAPIC; + hv_devid.ioapic.ioapic_id = (u8)ioapic_id; - return hv_unmap_interrupt(device_id.as_uint64, entry); + return hv_unmap_interrupt(hv_devid.as_uint64, entry); } EXPORT_SYMBOL_GPL(hv_unmap_ioapic_interrupt); int hv_map_ioapic_interrupt(int ioapic_id, bool level, int cpu, int vector, struct hv_interrupt_entry *entry) { - union hv_device_id device_id; + union hv_device_id hv_devid; - device_id.as_uint64 = 0; - device_id.device_type = HV_DEVICE_TYPE_IOAPIC; - device_id.ioapic.ioapic_id = (u8)ioapic_id; + hv_devid.as_uint64 = 0; + hv_devid.device_type = HV_DEVICE_TYPE_IOAPIC; + hv_devid.ioapic.ioapic_id = (u8)ioapic_id; - return hv_map_interrupt(device_id, level, cpu, vector, entry); + return hv_map_interrupt(hv_devid, level, cpu, vector, entry); } EXPORT_SYMBOL_GPL(hv_map_ioapic_interrupt); From 145613eb15eaed537825363a59523a0dcfa2d0f3 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 5 Jun 2026 14:44:54 +0200 Subject: [PATCH 13/14] hv_balloon: Simplify data output in hv_balloon_debug_show() Move the specification for a line break from a seq_puts() call to a seq_printf() call. The source code was transformed by using the Coccinelle software. Signed-off-by: Markus Elfring Reviewed-by: Sahil Chandna Signed-off-by: Wei Liu --- drivers/hv/hv_balloon.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 9a55f5c43307..42ce27be344d 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1862,9 +1862,7 @@ static int hv_balloon_debug_show(struct seq_file *f, void *offset) if (hot_add_enabled()) seq_puts(f, " hot_add"); - seq_puts(f, "\n"); - - seq_printf(f, "%-22s: %u", "state", dm->state); + seq_printf(f, "\n%-22s: %u", "state", dm->state); switch (dm->state) { case DM_INITIALIZING: sname = "Initializing"; From a4ffc59238be84dd1c26bf1c001543e832674fc6 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Thu, 16 Apr 2026 22:18:05 +0800 Subject: [PATCH 14/14] mshv: add bounds check on vp_index in mshv_intercept_isr() mshv_intercept_isr() extracts vp_index from the hypervisor message payload and uses it directly to index into pt_vp_array without validation. handle_bitset_message() and handle_pair_message() already validate vp_index against MSHV_MAX_VPS before array access. Add the same MSHV_MAX_VPS bounds check for consistency with the other message handlers. Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Reported-by: Yuhao Jiang Signed-off-by: Junrui Luo Signed-off-by: Wei Liu --- drivers/hv/mshv_synic.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c index e2288a726fec..fe591d159766 100644 --- a/drivers/hv/mshv_synic.c +++ b/drivers/hv/mshv_synic.c @@ -383,6 +383,11 @@ mshv_intercept_isr(struct hv_message *msg) */ vp_index = ((struct hv_opaque_intercept_message *)msg->u.payload)->vp_index; + /* This shouldn't happen, but just in case. */ + if (unlikely(vp_index >= MSHV_MAX_VPS)) { + pr_debug("VP index %u out of bounds\n", vp_index); + goto unlock_out; + } vp = partition->pt_vp_array[vp_index]; if (unlikely(!vp)) { pr_debug("failed to find VP %u\n", vp_index);