Skip to content

Commit 310fd8b

Browse files
tialaBrian Perkins
authored andcommitted
x86/Hyper-V: Add Hyper-V specific hvcall to set backing page
Secure AVIC provides backing page to aid the guest in limiting which interrupt vectors can be injected into the guest. Hyper-V has specific hvcall to set backing page and call it in Secure AVIC driver. Signed-off-by: Tianyu Lan <tiala@microsoft.com>
1 parent e3e0b6b commit 310fd8b

5 files changed

Lines changed: 132 additions & 2 deletions

File tree

arch/x86/hyperv/hv_init.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ static inline void hv_set_hypercall_pg(void *ptr)
7676
EXPORT_SYMBOL_GPL(hv_hypercall_pg);
7777
#endif
7878

79+
void *hv_vp_early_input_arg;
7980
union hv_ghcb * __percpu *hv_ghcb_pg;
8081

8182
/* Storage to save the hypercall page temporarily for hibernation */
@@ -382,13 +383,32 @@ void __init hyperv_init(void)
382383
u64 guest_id;
383384
union hv_x64_msr_hypercall_contents hypercall_msr;
384385
int cpuhp;
386+
int ret;
385387

386388
if (x86_hyper_type != X86_HYPER_MS_HYPERV)
387389
return;
388390

389391
if (hv_common_init())
390392
return;
391393

394+
if (cc_platform_has(CC_ATTR_SNP_SECURE_AVIC)) {
395+
hv_vp_early_input_arg = (void *)__get_free_pages(
396+
GFP_KERNEL | __GFP_ZERO,
397+
get_order(num_possible_cpus() * PAGE_SIZE));
398+
if (hv_vp_early_input_arg) {
399+
ret = set_memory_decrypted((u64)hv_vp_early_input_arg,
400+
num_possible_cpus());
401+
if (ret) {
402+
free_pages((unsigned long)hv_vp_early_input_arg,
403+
get_order(num_possible_cpus() * PAGE_SIZE));
404+
hv_vp_early_input_arg = NULL;
405+
goto common_free;
406+
}
407+
} else {
408+
goto common_free;
409+
}
410+
}
411+
392412
if (ms_hyperv.paravisor_present && hv_isolation_type_snp()) {
393413
/* Negotiate GHCB Version. */
394414
if (!hv_ghcb_negotiate_protocol())
@@ -526,6 +546,16 @@ void __init hyperv_init(void)
526546
free_vp_assist_page:
527547
kfree(hv_vp_assist_page);
528548
hv_vp_assist_page = NULL;
549+
free_vp_early_input_arg:
550+
if (hv_vp_early_input_arg) {
551+
set_memory_encrypted((u64)hv_vp_early_input_arg,
552+
num_possible_cpus());
553+
free_pages((unsigned long)hv_vp_early_input_arg,
554+
get_order(num_possible_cpus() * PAGE_SIZE));
555+
hv_vp_early_input_arg = NULL;
556+
}
557+
common_free:
558+
hv_common_free();
529559
}
530560

531561
/*

arch/x86/hyperv/ivm.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,45 @@ static void snp_cleanup_vmsa(struct sev_es_save_area *vmsa)
291291
free_page((unsigned long)vmsa);
292292
}
293293

294+
enum es_result hv_set_savic_backing_page(u64 gfn)
295+
{
296+
u64 control = HV_HYPERCALL_REP_COMP_1 | HVCALL_SET_VP_REGISTERS;
297+
struct hv_set_vp_registers_input *input =
298+
(struct hv_set_vp_registers_input *)
299+
((u8 *)hv_vp_early_input_arg + smp_processor_id() * PAGE_SIZE);
300+
union hv_x64_register_sev_gpa_page value;
301+
unsigned long flags;
302+
int retry = 5;
303+
u64 ret;
304+
305+
local_irq_save(flags);
306+
307+
value.enabled = 1;
308+
value.reserved = 0;
309+
value.pagenumber = gfn;
310+
311+
memset(input, 0, struct_size(input, element, 1));
312+
input->header.partitionid = HV_PARTITION_ID_SELF;
313+
input->header.vpindex = HV_VP_INDEX_SELF;
314+
input->header.inputvtl = ms_hyperv.vtl;
315+
input->element[0].name = HV_X64_REGISTER_SEV_AVIC_GPA;
316+
input->element[0].value.reg64 = value.u64;
317+
318+
do {
319+
ret = hv_do_hypercall(control, input, NULL);
320+
} while (ret == HV_STATUS_TIME_OUT && retry--);
321+
322+
if (!hv_result_success(ret))
323+
pr_err("Failed to set secure AVIC backing page %llx.\n", ret);
324+
325+
local_irq_restore(flags);
326+
327+
if (hv_result_success(ret))
328+
return ES_OK;
329+
else
330+
return ES_VMM_ERROR;
331+
}
332+
294333
int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip, unsigned int cpu)
295334
{
296335
struct sev_es_save_area *vmsa = (struct sev_es_save_area *)

arch/x86/include/asm/mshyperv.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ extern u64 hv_std_hypercall(u64 control, u64 param1, u64 param2);
4444

4545
#if IS_ENABLED(CONFIG_HYPERV)
4646
extern void *hv_hypercall_pg;
47+
extern void *hv_vp_early_input_arg;
4748

4849
extern union hv_ghcb * __percpu *hv_ghcb_pg;
4950

@@ -197,6 +198,7 @@ int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry);
197198
bool hv_ghcb_negotiate_protocol(void);
198199
void __noreturn hv_ghcb_terminate(unsigned int set, unsigned int reason);
199200
int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip, unsigned int cpu);
201+
enum es_result hv_set_savic_backing_page(u64 gfn);
200202
#else
201203
static inline bool hv_ghcb_negotiate_protocol(void) { return false; }
202204
static inline void hv_ghcb_terminate(unsigned int set, unsigned int reason) {}
@@ -300,6 +302,20 @@ static inline void hv_vtl_idle(void)
300302
native_safe_halt();
301303
}
302304

305+
/*
306+
* Registers are only accessible via HVCALL_GET_VP_REGISTERS hvcall and
307+
* there is not associated MSR address.
308+
*/
309+
#ifndef HV_X64_REGISTER_VSM_VP_STATUS
310+
#define HV_X64_REGISTER_VSM_VP_STATUS 0x000D0003
311+
#endif
312+
#ifndef HV_X64_VTL_MASK
313+
#define HV_X64_VTL_MASK GENMASK(3, 0)
314+
#endif
315+
#ifndef HV_X64_REGISTER_SEV_AVIC_GPA
316+
#define HV_X64_REGISTER_SEV_AVIC_GPA 0x00090043
317+
#endif
318+
303319
#ifdef CONFIG_HYPERV_VTL_MODE
304320
void __init hv_vtl_init_platform(void);
305321
int __init hv_vtl_early_init(void);

arch/x86/kernel/apic/x2apic_savic.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include <asm/apic.h>
1616
#include <asm/sev.h>
17+
#include <asm/mshyperv.h>
1718

1819
#include "local.h"
1920

@@ -342,6 +343,10 @@ static void savic_setup(void)
342343
void *ap = this_cpu_ptr(savic_page);
343344
enum es_result res;
344345
unsigned long gpa;
346+
unsigned long gfn;
347+
348+
if (!cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
349+
return;
345350

346351
/*
347352
* Before Secure AVIC is enabled, APIC MSR reads are intercepted.
@@ -351,6 +356,8 @@ static void savic_setup(void)
351356

352357
gpa = __pa(ap);
353358

359+
gfn = gpa >> PAGE_SHIFT;
360+
354361
/*
355362
* The NPT entry for a vCPU's APIC backing page must always be
356363
* present when the vCPU is running in order for Secure AVIC to
@@ -361,7 +368,11 @@ static void savic_setup(void)
361368
* VMRUN, the hypervisor makes use of this information to make sure
362369
* the APIC backing page is mapped in NPT.
363370
*/
364-
res = savic_register_gpa(gpa);
371+
if (hv_isolation_type_snp())
372+
res = hv_set_savic_backing_page(gfn);
373+
else
374+
res = savic_register_gpa(gpa);
375+
365376
if (res != ES_OK)
366377
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
367378

include/hyperv/hvgdk_mini.h

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1141,11 +1141,15 @@ enum hv_register_name {
11411141

11421142
/* AMD SEV SNP configuration register */
11431143
HV_X64_REGISTER_SEV_CONTROL = 0x00090040,
1144+
HV_X64_REGISTER_SEV_AVIC_GPA = 0x00090043,
11441145
#endif
1145-
HV_REGISTER_REG_PAGE = 0x0009001C,
1146+
HV_REGISTER_REG_PAGE = 0x0009001C,
11461147

11471148
};
11481149

1150+
#define HV_X64_REGISTER_VSM_VP_STATUS 0x000D0003
1151+
#define HV_X64_VTL_MASK GENMASK(3, 0)
1152+
11491153
/*
11501154
* Arch compatibility regs for use with hv_set/get_register
11511155
*/
@@ -1371,6 +1375,36 @@ struct hv_input_get_vp_registers {
13711375
u32 names[];
13721376
} __packed;
13731377

1378+
union hv_x64_register_sev_gpa_page {
1379+
u64 u64;
1380+
struct {
1381+
u64 enabled:1;
1382+
u64 reserved:11;
1383+
u64 pagenumber:52;
1384+
};
1385+
} __packed;
1386+
1387+
struct hv_set_vp_registers_input {
1388+
struct {
1389+
u64 partitionid;
1390+
u32 vpindex;
1391+
u8 inputvtl;
1392+
u8 padding[3];
1393+
} header;
1394+
struct {
1395+
u32 name;
1396+
u32 padding1;
1397+
u64 padding2;
1398+
union {
1399+
union hv_register_value value;
1400+
struct {
1401+
u64 valuelow;
1402+
u64 valuehigh;
1403+
};
1404+
};
1405+
} element[];
1406+
} __packed;
1407+
13741408
struct hv_input_set_vp_registers {
13751409
u64 partition_id;
13761410
u32 vp_index;

0 commit comments

Comments
 (0)