Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions arch/x86/hyperv/hv_apic.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <linux/clockchips.h>
#include <linux/slab.h>
#include <linux/cpuhotplug.h>
#include <linux/cc_platform.h>
#include <asm/hypervisor.h>
#include <asm/mshyperv.h>
#include <asm/apic.h>
Expand Down Expand Up @@ -53,6 +54,11 @@ static void hv_apic_icr_write(u32 low, u32 id)
wrmsrq(HV_X64_MSR_ICR, reg_val);
}

void hv_enable_coco_interrupt(unsigned int cpu, unsigned int vector, bool set)
{
apic_update_vector(cpu, vector, set);
}

static u32 hv_apic_read(u32 reg)
{
u32 reg_val, hi;
Expand Down Expand Up @@ -293,6 +299,9 @@ static void hv_send_ipi_self(int vector)

void __init hv_apic_init(void)
{
if (cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
return;

if (ms_hyperv.hints & HV_X64_CLUSTER_IPI_RECOMMENDED) {
pr_info("Hyper-V: Using IPI hypercalls\n");
/*
Expand Down
31 changes: 31 additions & 0 deletions arch/x86/hyperv/hv_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ static inline void hv_set_hypercall_pg(void *ptr)
EXPORT_SYMBOL_GPL(hv_hypercall_pg);
#endif

void *hv_vp_early_input_arg;
union hv_ghcb * __percpu *hv_ghcb_pg;

/* Storage to save the hypercall page temporarily for hibernation */
Expand Down Expand Up @@ -120,6 +121,10 @@ static int hv_cpu_init(unsigned int cpu)
if (ret)
return ret;

/* Allow Hyper-V stimer vector to be injected from Hypervisor. */
if (ms_hyperv.misc_features & HV_STIMER_DIRECT_MODE_AVAILABLE)
apic_update_vector(cpu, HYPERV_STIMER0_VECTOR, true);

return hyperv_init_ghcb();
}

Expand Down Expand Up @@ -227,6 +232,9 @@ static int hv_cpu_die(unsigned int cpu)
*ghcb_va = NULL;
}

if (ms_hyperv.misc_features & HV_STIMER_DIRECT_MODE_AVAILABLE)
apic_update_vector(cpu, HYPERV_STIMER0_VECTOR, false);

hv_common_cpu_die(cpu);

if (hv_reenlightenment_cb == NULL)
Expand Down Expand Up @@ -375,13 +383,30 @@ void __init hyperv_init(void)
u64 guest_id;
union hv_x64_msr_hypercall_contents hypercall_msr;
int cpuhp;
int ret;

if (x86_hyper_type != X86_HYPER_MS_HYPERV)
return;

if (hv_common_init())
return;

if (cc_platform_has(CC_ATTR_SNP_SECURE_AVIC)) {
hv_vp_early_input_arg = kcalloc(num_possible_cpus(),
PAGE_SIZE,
GFP_KERNEL);
if (hv_vp_early_input_arg) {
ret = set_memory_decrypted((u64)hv_vp_early_input_arg,
num_possible_cpus());
if (ret) {
kfree(hv_vp_early_input_arg);
goto common_free;
}
} else {
goto common_free;
}
Comment on lines +394 to +409
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hv_vp_early_input_arg is allocated with kcalloc() and then passed to set_memory_decrypted(). set_memory_decrypted() warns and rounds down misaligned addresses, so this needs a PAGE_SIZE-aligned allocation to avoid converting the wrong pages. Consider allocating with a page-aligned API (e.g., __get_free_pages()/alloc_pages_exact()) rather than kcalloc().

Copilot uses AI. Check for mistakes.
}

if (ms_hyperv.paravisor_present && hv_isolation_type_snp()) {
/* Negotiate GHCB Version. */
if (!hv_ghcb_negotiate_protocol())
Expand Down Expand Up @@ -519,6 +544,12 @@ void __init hyperv_init(void)
free_vp_assist_page:
kfree(hv_vp_assist_page);
hv_vp_assist_page = NULL;
free_vp_early_input_arg:
set_memory_encrypted((u64)hv_vp_early_input_arg, num_possible_cpus());
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The free_vp_early_input_arg: error path unconditionally calls set_memory_encrypted() on hv_vp_early_input_arg. If Secure AVIC isn’t enabled (or allocation failed before setting the pointer), this can end up converting address 0 / a NULL pointer. Guard the conversion with if (hv_vp_early_input_arg) (and only attempt set_memory_encrypted() when it was successfully decrypted earlier).

Suggested change
set_memory_encrypted((u64)hv_vp_early_input_arg, num_possible_cpus());
if (hv_vp_early_input_arg)
set_memory_encrypted((u64)hv_vp_early_input_arg,
num_possible_cpus());

Copilot uses AI. Check for mistakes.
kfree(hv_vp_early_input_arg);
hv_vp_early_input_arg = NULL;
common_free:
hv_common_free();
}

/*
Expand Down
38 changes: 38 additions & 0 deletions arch/x86/hyperv/ivm.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,44 @@ static void snp_cleanup_vmsa(struct sev_es_save_area *vmsa)
free_page((unsigned long)vmsa);
}

enum es_result hv_set_savic_backing_page(u64 gfn)
{
u64 control = HV_HYPERCALL_REP_COMP_1 | HVCALL_SET_VP_REGISTERS;
struct hv_set_vp_registers_input *input
= hv_vp_early_input_arg + smp_processor_id() * PAGE_SIZE;
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hv_set_savic_backing_page() does pointer arithmetic on hv_vp_early_input_arg even though it’s declared as void * (hv_vp_early_input_arg + smp_processor_id() * PAGE_SIZE). This trips -Wpointer-arith on Clang/GCC and is non-standard C. Cast to a byte pointer for the arithmetic (or change the global’s type to u8 *) before adding the per-CPU offset.

Suggested change
struct hv_set_vp_registers_input *input
= hv_vp_early_input_arg + smp_processor_id() * PAGE_SIZE;
struct hv_set_vp_registers_input *input =
(struct hv_set_vp_registers_input *)
((u8 *)hv_vp_early_input_arg + smp_processor_id() * PAGE_SIZE);

Copilot uses AI. Check for mistakes.
union hv_x64_register_sev_gpa_page value;
unsigned long flags;
int retry = 5;
u64 ret;

local_irq_save(flags);

value.enabled = 1;
value.reserved = 0;
value.pagenumber = gfn;

memset(input, 0, struct_size(input, element, 1));
input->header.partitionid = HV_PARTITION_ID_SELF;
input->header.vpindex = HV_VP_INDEX_SELF;
input->header.inputvtl = ms_hyperv.vtl;
input->element[0].name = HV_X64_REGISTER_SEV_AVIC_GPA;
input->element[0].value.reg64 = value.u64;

do {
ret = hv_do_hypercall(control, input, NULL);
} while (ret == HV_STATUS_TIME_OUT && retry--);

if (!hv_result_success(ret))
pr_err("Failed to set secure AVIC backing page %llx.\n", ret);

local_irq_restore(flags);

if (hv_result_success(ret))
return ES_OK;
else
return ES_VMM_ERROR;
}

int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip, unsigned int cpu)
{
struct sev_es_save_area *vmsa = (struct sev_es_save_area *)
Expand Down
6 changes: 6 additions & 0 deletions arch/x86/include/asm/apic.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ static inline u64 native_x2apic_icr_read(void)
return val;
}

#if defined(CONFIG_AMD_SECURE_AVIC)
extern void x2apic_savic_init_backing_page(void *backing_page);
#else
static inline void x2apic_savic_init_backing_page(void *backing_page) {}
#endif

extern int x2apic_mode;
extern int x2apic_phys;
extern void __init x2apic_set_max_apicid(u32 apicid);
Expand Down
10 changes: 10 additions & 0 deletions arch/x86/include/asm/mshyperv.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ extern u64 hv_std_hypercall(u64 control, u64 param1, u64 param2);

#if IS_ENABLED(CONFIG_HYPERV)
extern void *hv_hypercall_pg;
extern void *hv_vp_early_input_arg;

extern union hv_ghcb * __percpu *hv_ghcb_pg;

Expand Down Expand Up @@ -197,6 +198,7 @@ int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry);
bool hv_ghcb_negotiate_protocol(void);
void __noreturn hv_ghcb_terminate(unsigned int set, unsigned int reason);
int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip, unsigned int cpu);
enum es_result hv_set_savic_backing_page(u64 gfn);
#else
static inline bool hv_ghcb_negotiate_protocol(void) { return false; }
static inline void hv_ghcb_terminate(unsigned int set, unsigned int reason) {}
Expand Down Expand Up @@ -300,6 +302,14 @@ static inline void hv_vtl_idle(void)
native_safe_halt();
}

/*
* Registers are only accessible via HVCALL_GET_VP_REGISTERS hvcall and
* there is not associated MSR address.
*/
#define HV_X64_REGISTER_VSM_VP_STATUS 0x000D0003
#define HV_X64_VTL_MASK GENMASK(3, 0)
#define HV_X64_REGISTER_SEV_AVIC_GPA 0x00090043
Comment on lines +308 to +316
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new #define constants are duplicates of ones already provided via <hyperv/hvhdk.h>hvgdk_mini.h (e.g., HV_X64_REGISTER_VSM_VP_STATUS / HV_X64_VTL_MASK). Because this header includes hvhdk.h before these #defines, this can trigger macro redefinition warnings (notably with Clang). Prefer to define them in only one place, or wrap with #ifndef guards here.

Suggested change
*/
#define HV_X64_REGISTER_VSM_VP_STATUS 0x000D0003
#define HV_X64_VTL_MASK GENMASK(3, 0)
#define HV_X64_REGISTER_SEV_AVIC_GPA 0x00090043
*/
#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
#ifndef HV_X64_REGISTER_SEV_AVIC_GPA
#define HV_X64_REGISTER_SEV_AVIC_GPA 0x00090043
#endif

Copilot uses AI. Check for mistakes.

#ifdef CONFIG_HYPERV_VTL_MODE
void __init hv_vtl_init_platform(void);
int __init hv_vtl_early_init(void);
Expand Down
7 changes: 6 additions & 1 deletion arch/x86/include/asm/sev.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,12 @@ struct rmp_state {
u32 asid;
} __packed;

#define RMPADJUST_VMSA_PAGE_BIT BIT(16)
/* Target VMPL takes the first byte */
#define RMPADJUST_ENABLE_READ BIT(8)
#define RMPADJUST_ENABLE_WRITE BIT(9)
#define RMPADJUST_USER_EXECUTE BIT(10)
#define RMPADJUST_KERNEL_EXECUTE BIT(11)
#define RMPADJUST_VMSA_PAGE_BIT BIT(16)

/* SNP Guest message request */
struct snp_req_data {
Expand Down
6 changes: 6 additions & 0 deletions arch/x86/include/asm/svm.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
#define V_GIF_SHIFT 9
#define V_GIF_MASK (1 << V_GIF_SHIFT)

#define V_INT_SHADOW_SHIFT 10
#define V_INT_SHADOW_MASK (1 << V_INT_SHADOW_SHIFT)

#define V_NMI_PENDING_SHIFT 11
#define V_NMI_PENDING_MASK (1 << V_NMI_PENDING_SHIFT)

Expand All @@ -202,6 +205,9 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
#define V_IGN_TPR_SHIFT 20
#define V_IGN_TPR_MASK (1 << V_IGN_TPR_SHIFT)

#define V_GUEST_BUSY_SHIFT 63
#define V_GUEST_BUSY_MASK (1ULL << V_GUEST_BUSY_SHIFT)

#define V_IRQ_INJECTION_BITS_MASK (V_IRQ_MASK | V_INTR_PRIO_MASK | V_IGN_TPR_MASK)

#define V_INTR_MASKING_SHIFT 24
Expand Down
31 changes: 25 additions & 6 deletions arch/x86/kernel/apic/x2apic_savic.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include <asm/apic.h>
#include <asm/sev.h>
#include <asm/mshyperv.h>

#include "local.h"

Expand Down Expand Up @@ -330,6 +331,18 @@ static void savic_eoi(void)
}
}

void x2apic_savic_init_backing_page(void *ap)
{
u32 apic_id;

/*
* Before Secure AVIC is enabled, APIC msr reads are intercepted.
* APIC_ID msr read returns the value from the Hypervisor.
*/
apic_id = native_apic_msr_read(APIC_ID);
apic_set_reg(ap, APIC_ID, apic_id);
}

static void savic_teardown(void)
{
/* Disable Secure AVIC */
Expand All @@ -342,15 +355,17 @@ static void savic_setup(void)
void *ap = this_cpu_ptr(savic_page);
enum es_result res;
unsigned long gpa;
unsigned long gfn;
int ret;

/*
* Before Secure AVIC is enabled, APIC MSR reads are intercepted.
* APIC_ID MSR read returns the value from the hypervisor.
*/
apic_set_reg(ap, APIC_ID, native_apic_msr_read(APIC_ID));
if (!cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
return;

x2apic_savic_init_backing_page(ap);
gpa = __pa(ap);

gfn = gpa >> PAGE_SHIFT;

/*
* The NPT entry for a vCPU's APIC backing page must always be
* present when the vCPU is running in order for Secure AVIC to
Expand All @@ -361,7 +376,11 @@ static void savic_setup(void)
* VMRUN, the hypervisor makes use of this information to make sure
* the APIC backing page is mapped in NPT.
*/
res = savic_register_gpa(gpa);
if (hv_isolation_type_snp())
ret = hv_set_savic_backing_page(gfn);
else
ret = savic_register_gpa(gpa);

if (res != ES_OK)
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
Comment on lines 355 to 384
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In savic_setup(), enum es_result res is never assigned after switching to ret = hv_set_savic_backing_page() / ret = savic_register_gpa(), but the code still checks if (res != ES_OK). This leaves res uninitialized and will trigger incorrect termination; also the return types now mismatch (hv_set_savic_backing_page returns enum es_result). Use a single enum es_result res result variable and assign/check it consistently (or update the check to use ret and convert types appropriately).

Copilot uses AI. Check for mistakes.

Expand Down
3 changes: 3 additions & 0 deletions arch/x86/kernel/cpu/mshyperv.c
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,9 @@ static void __init ms_hyperv_init_platform(void)

hv_identify_partition_type();

if (cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
ms_hyperv.hints |= HV_DEPRECATING_AEOI_RECOMMENDED;

if (ms_hyperv.hints & HV_X64_HYPERV_NESTED) {
hv_nested = true;
pr_info("Hyper-V: running on a nested hypervisor\n");
Expand Down
2 changes: 2 additions & 0 deletions drivers/hv/hv.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ void hv_synic_enable_regs(unsigned int cpu)
}

hv_set_msr(HV_MSR_SIEFP, siefp.as_uint64);
hv_enable_coco_interrupt(cpu, vmbus_interrupt, true);

/* Setup the shared SINT. */
if (vmbus_irq != -1)
Expand Down Expand Up @@ -353,6 +354,7 @@ void hv_synic_disable_regs(unsigned int cpu)
/* Need to correctly cleanup in the case of SMP!!! */
/* Disable the interrupt */
hv_set_msr(HV_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
hv_enable_coco_interrupt(cpu, vmbus_interrupt, false);

simp.as_uint64 = hv_get_msr(HV_MSR_SIMP);
/*
Expand Down
5 changes: 5 additions & 0 deletions drivers/hv/hv_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,11 @@ u64 __weak hv_tdx_hypercall(u64 control, u64 param1, u64 param2)
}
EXPORT_SYMBOL_GPL(hv_tdx_hypercall);

void __weak hv_enable_coco_interrupt(unsigned int cpu, unsigned int vector, bool set)
{
}
EXPORT_SYMBOL_GPL(hv_enable_coco_interrupt);

void hv_identify_partition_type(void)
{
/* Assume guest role */
Expand Down
Loading
Loading