Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
37 changes: 37 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,32 @@ 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 = (void *)__get_free_pages(
GFP_KERNEL | __GFP_ZERO,
get_order(num_possible_cpus() * PAGE_SIZE));
if (hv_vp_early_input_arg) {
ret = set_memory_decrypted((u64)hv_vp_early_input_arg,
num_possible_cpus());
if (ret) {
free_pages((unsigned long)hv_vp_early_input_arg,
get_order(num_possible_cpus() * PAGE_SIZE));
hv_vp_early_input_arg = NULL;
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 +546,16 @@ void __init hyperv_init(void)
free_vp_assist_page:
kfree(hv_vp_assist_page);
hv_vp_assist_page = NULL;
free_vp_early_input_arg:
if (hv_vp_early_input_arg) {
set_memory_encrypted((u64)hv_vp_early_input_arg,
num_possible_cpus());
free_pages((unsigned long)hv_vp_early_input_arg,
get_order(num_possible_cpus() * PAGE_SIZE));
hv_vp_early_input_arg = NULL;
}
common_free:
hv_common_free();
}

/*
Expand Down
39 changes: 39 additions & 0 deletions arch/x86/hyperv/ivm.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,45 @@ 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 =
(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);

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
16 changes: 16 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,20 @@ 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.
*/
#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
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.
#endif

#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
30 changes: 24 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,16 @@ static void savic_setup(void)
void *ap = this_cpu_ptr(savic_page);
enum es_result res;
unsigned long gpa;
unsigned long gfn;

/*
* 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 +375,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())
res = hv_set_savic_backing_page(gfn);
else
res = 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