x86/hyperv: Add ghcb hvcall support for SNP VM

hyperv provides ghcb hvcall to handle VMBus
HVCALL_SIGNAL_EVENT and HVCALL_POST_MESSAGE
msg in SNP Isolation VM. Add such support.

Reviewed-by: Michael Kelley <mikelley@microsoft.com>
Signed-off-by: Tianyu Lan <Tianyu.Lan@microsoft.com>
Link: https://lore.kernel.org/r/20211025122116.264793-8-ltykernel@gmail.com
Signed-off-by: Wei Liu <wei.liu@kernel.org>
This commit is contained in:
Tianyu Lan 2021-10-25 08:21:12 -04:00 committed by Wei Liu
parent faff44069f
commit 20c89a559e
5 changed files with 94 additions and 2 deletions

View File

@ -19,10 +19,85 @@
#include <asm/hypervisor.h>
#ifdef CONFIG_AMD_MEM_ENCRYPT
#define GHCB_USAGE_HYPERV_CALL 1
union hv_ghcb {
struct ghcb ghcb;
struct {
u64 hypercalldata[509];
u64 outputgpa;
union {
union {
struct {
u32 callcode : 16;
u32 isfast : 1;
u32 reserved1 : 14;
u32 isnested : 1;
u32 countofelements : 12;
u32 reserved2 : 4;
u32 repstartindex : 12;
u32 reserved3 : 4;
};
u64 asuint64;
} hypercallinput;
union {
struct {
u16 callstatus;
u16 reserved1;
u32 elementsprocessed : 12;
u32 reserved2 : 20;
};
u64 asunit64;
} hypercalloutput;
};
u64 reserved2;
} hypercall;
} __packed __aligned(HV_HYP_PAGE_SIZE);
u64 hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_size)
{
union hv_ghcb *hv_ghcb;
void **ghcb_base;
unsigned long flags;
u64 status;
if (!hv_ghcb_pg)
return -EFAULT;
WARN_ON(in_nmi());
local_irq_save(flags);
ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
hv_ghcb = (union hv_ghcb *)*ghcb_base;
if (!hv_ghcb) {
local_irq_restore(flags);
return -EFAULT;
}
hv_ghcb->ghcb.protocol_version = GHCB_PROTOCOL_MAX;
hv_ghcb->ghcb.ghcb_usage = GHCB_USAGE_HYPERV_CALL;
hv_ghcb->hypercall.outputgpa = (u64)output;
hv_ghcb->hypercall.hypercallinput.asuint64 = 0;
hv_ghcb->hypercall.hypercallinput.callcode = control;
if (input_size)
memcpy(hv_ghcb->hypercall.hypercalldata, input, input_size);
VMGEXIT();
hv_ghcb->ghcb.ghcb_usage = 0xffffffff;
memset(hv_ghcb->ghcb.save.valid_bitmap, 0,
sizeof(hv_ghcb->ghcb.save.valid_bitmap));
status = hv_ghcb->hypercall.hypercalloutput.callstatus;
local_irq_restore(flags);
return status;
}
void hv_ghcb_msr_write(u64 msr, u64 value)
{
union hv_ghcb *hv_ghcb;

View File

@ -447,6 +447,10 @@ void vmbus_set_event(struct vmbus_channel *channel)
++channel->sig_events;
hv_do_fast_hypercall8(HVCALL_SIGNAL_EVENT, channel->sig_event);
if (hv_isolation_type_snp())
hv_ghcb_hypercall(HVCALL_SIGNAL_EVENT, &channel->sig_event,
NULL, sizeof(channel->sig_event));
else
hv_do_fast_hypercall8(HVCALL_SIGNAL_EVENT, channel->sig_event);
}
EXPORT_SYMBOL_GPL(vmbus_set_event);

View File

@ -98,7 +98,13 @@ int hv_post_message(union hv_connection_id connection_id,
aligned_msg->payload_size = payload_size;
memcpy((void *)aligned_msg->payload, payload, payload_size);
status = hv_do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL);
if (hv_isolation_type_snp())
status = hv_ghcb_hypercall(HVCALL_POST_MESSAGE,
(void *)aligned_msg, NULL,
sizeof(*aligned_msg));
else
status = hv_do_hypercall(HVCALL_POST_MESSAGE,
aligned_msg, NULL);
/* Preemption must remain disabled until after the hypercall
* so some other thread can't get scheduled onto this cpu and

View File

@ -289,3 +289,9 @@ void __weak hyperv_cleanup(void)
{
}
EXPORT_SYMBOL_GPL(hyperv_cleanup);
u64 __weak hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_size)
{
return HV_STATUS_INVALID_PARAMETER;
}
EXPORT_SYMBOL_GPL(hv_ghcb_hypercall);

View File

@ -266,6 +266,7 @@ bool hv_is_hibernation_supported(void);
enum hv_isolation_type hv_get_isolation_type(void);
bool hv_is_isolation_supported(void);
bool hv_isolation_type_snp(void);
u64 hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_size);
void hyperv_cleanup(void);
bool hv_query_ext_cap(u64 cap_query);
#else /* CONFIG_HYPERV */