Commit 23378295 authored by Dexuan Cui's avatar Dexuan Cui Committed by Wei Liu

Drivers: hv: vmbus: Bring the post_msg_page back for TDX VMs with the paravisor

The post_msg_page was removed in
commit 9a6b1a17 ("Drivers: hv: vmbus: Remove the per-CPU post_msg_page")

However, it turns out that we need to bring it back, but only for a TDX VM
with the paravisor: in such a VM, the hyperv_pcpu_input_arg is not decrypted,
but the HVCALL_POST_MESSAGE in such a VM needs a decrypted page as the
hypercall input page: see the comments in hyperv_init() for a detailed
explanation.

Except for HVCALL_POST_MESSAGE and HVCALL_SIGNAL_EVENT, the other hypercalls
in a TDX VM with the paravisor still use hv_hypercall_pg and must use the
hyperv_pcpu_input_arg (which is encrypted in such a VM), when a hypercall
input page is used.
Signed-off-by: default avatarDexuan Cui <decui@microsoft.com>
Reviewed-by: default avatarTianyu Lan <tiala@microsoft.com>
Reviewed-by: default avatarMichael Kelley <mikelley@microsoft.com>
Signed-off-by: default avatarWei Liu <wei.liu@kernel.org>
Link: https://lore.kernel.org/r/20230824080712.30327-8-decui@microsoft.com
parent d3a9d7e4
...@@ -480,6 +480,22 @@ void __init hyperv_init(void) ...@@ -480,6 +480,22 @@ void __init hyperv_init(void)
* Setup the hypercall page and enable hypercalls. * Setup the hypercall page and enable hypercalls.
* 1. Register the guest ID * 1. Register the guest ID
* 2. Enable the hypercall and register the hypercall page * 2. Enable the hypercall and register the hypercall page
*
* A TDX VM with no paravisor only uses TDX GHCI rather than hv_hypercall_pg:
* when the hypercall input is a page, such a VM must pass a decrypted
* page to Hyper-V, e.g. hv_post_message() uses the per-CPU page
* hyperv_pcpu_input_arg, which is decrypted if no paravisor is present.
*
* A TDX VM with the paravisor uses hv_hypercall_pg for most hypercalls,
* which are handled by the paravisor and the VM must use an encrypted
* input page: in such a VM, the hyperv_pcpu_input_arg is encrypted and
* used in the hypercalls, e.g. see hv_mark_gpa_visibility() and
* hv_arch_irq_unmask(). Such a VM uses TDX GHCI for two hypercalls:
* 1. HVCALL_SIGNAL_EVENT: see vmbus_set_event() and _hv_do_fast_hypercall8().
* 2. HVCALL_POST_MESSAGE: the input page must be a decrypted page, i.e.
* hv_post_message() in such a VM can't use the encrypted hyperv_pcpu_input_arg;
* instead, hv_post_message() uses the post_msg_page, which is decrypted
* in such a VM and is only used in such a VM.
*/ */
guest_id = hv_generate_guest_id(LINUX_VERSION_CODE); guest_id = hv_generate_guest_id(LINUX_VERSION_CODE);
wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id); wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id);
...@@ -487,8 +503,8 @@ void __init hyperv_init(void) ...@@ -487,8 +503,8 @@ void __init hyperv_init(void)
/* Hyper-V requires to write guest os id via ghcb in SNP IVM. */ /* Hyper-V requires to write guest os id via ghcb in SNP IVM. */
hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, guest_id); hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, guest_id);
/* A TDX guest uses the GHCI call rather than hv_hypercall_pg. */ /* A TDX VM with no paravisor only uses TDX GHCI rather than hv_hypercall_pg */
if (hv_isolation_type_tdx()) if (hv_isolation_type_tdx() && !ms_hyperv.paravisor_present)
goto skip_hypercall_pg_init; goto skip_hypercall_pg_init;
hv_hypercall_pg = __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, hv_hypercall_pg = __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START,
......
...@@ -57,20 +57,37 @@ int hv_post_message(union hv_connection_id connection_id, ...@@ -57,20 +57,37 @@ int hv_post_message(union hv_connection_id connection_id,
local_irq_save(flags); local_irq_save(flags);
aligned_msg = *this_cpu_ptr(hyperv_pcpu_input_arg); /*
* A TDX VM with the paravisor must use the decrypted post_msg_page: see
* the comment in struct hv_per_cpu_context. A SNP VM with the paravisor
* can use the encrypted hyperv_pcpu_input_arg because it copies the
* input into the GHCB page, which has been decrypted by the paravisor.
*/
if (hv_isolation_type_tdx() && ms_hyperv.paravisor_present)
aligned_msg = this_cpu_ptr(hv_context.cpu_context)->post_msg_page;
else
aligned_msg = *this_cpu_ptr(hyperv_pcpu_input_arg);
aligned_msg->connectionid = connection_id; aligned_msg->connectionid = connection_id;
aligned_msg->reserved = 0; aligned_msg->reserved = 0;
aligned_msg->message_type = message_type; aligned_msg->message_type = message_type;
aligned_msg->payload_size = payload_size; aligned_msg->payload_size = payload_size;
memcpy((void *)aligned_msg->payload, payload, payload_size); memcpy((void *)aligned_msg->payload, payload, payload_size);
if (hv_isolation_type_snp()) if (ms_hyperv.paravisor_present) {
status = hv_ghcb_hypercall(HVCALL_POST_MESSAGE, if (hv_isolation_type_tdx())
(void *)aligned_msg, NULL, status = hv_tdx_hypercall(HVCALL_POST_MESSAGE,
sizeof(*aligned_msg)); virt_to_phys(aligned_msg), 0);
else else if (hv_isolation_type_snp())
status = hv_ghcb_hypercall(HVCALL_POST_MESSAGE,
aligned_msg, NULL,
sizeof(*aligned_msg));
else
status = HV_STATUS_INVALID_PARAMETER;
} else {
status = hv_do_hypercall(HVCALL_POST_MESSAGE, status = hv_do_hypercall(HVCALL_POST_MESSAGE,
aligned_msg, NULL); aligned_msg, NULL);
}
local_irq_restore(flags); local_irq_restore(flags);
...@@ -105,6 +122,24 @@ int hv_synic_alloc(void) ...@@ -105,6 +122,24 @@ int hv_synic_alloc(void)
tasklet_init(&hv_cpu->msg_dpc, tasklet_init(&hv_cpu->msg_dpc,
vmbus_on_msg_dpc, (unsigned long) hv_cpu); vmbus_on_msg_dpc, (unsigned long) hv_cpu);
if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) {
hv_cpu->post_msg_page = (void *)get_zeroed_page(GFP_ATOMIC);
if (hv_cpu->post_msg_page == NULL) {
pr_err("Unable to allocate post msg page\n");
goto err;
}
ret = set_memory_decrypted((unsigned long)hv_cpu->post_msg_page, 1);
if (ret) {
pr_err("Failed to decrypt post msg page: %d\n", ret);
/* Just leak the page, as it's unsafe to free the page. */
hv_cpu->post_msg_page = NULL;
goto err;
}
memset(hv_cpu->post_msg_page, 0, PAGE_SIZE);
}
/* /*
* Synic message and event pages are allocated by paravisor. * Synic message and event pages are allocated by paravisor.
* Skip these pages allocation here. * Skip these pages allocation here.
...@@ -178,6 +213,17 @@ void hv_synic_free(void) ...@@ -178,6 +213,17 @@ void hv_synic_free(void)
= per_cpu_ptr(hv_context.cpu_context, cpu); = per_cpu_ptr(hv_context.cpu_context, cpu);
/* It's better to leak the page if the encryption fails. */ /* It's better to leak the page if the encryption fails. */
if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) {
if (hv_cpu->post_msg_page) {
ret = set_memory_encrypted((unsigned long)
hv_cpu->post_msg_page, 1);
if (ret) {
pr_err("Failed to encrypt post msg page: %d\n", ret);
hv_cpu->post_msg_page = NULL;
}
}
}
if (!ms_hyperv.paravisor_present && if (!ms_hyperv.paravisor_present &&
(hv_isolation_type_en_snp() || hv_isolation_type_tdx())) { (hv_isolation_type_en_snp() || hv_isolation_type_tdx())) {
if (hv_cpu->synic_message_page) { if (hv_cpu->synic_message_page) {
...@@ -199,6 +245,7 @@ void hv_synic_free(void) ...@@ -199,6 +245,7 @@ void hv_synic_free(void)
} }
} }
free_page((unsigned long)hv_cpu->post_msg_page);
free_page((unsigned long)hv_cpu->synic_event_page); free_page((unsigned long)hv_cpu->synic_event_page);
free_page((unsigned long)hv_cpu->synic_message_page); free_page((unsigned long)hv_cpu->synic_message_page);
} }
......
...@@ -123,6 +123,17 @@ struct hv_per_cpu_context { ...@@ -123,6 +123,17 @@ struct hv_per_cpu_context {
void *synic_message_page; void *synic_message_page;
void *synic_event_page; void *synic_event_page;
/*
* The page is only used in hv_post_message() for a TDX VM (with the
* paravisor) to post a messages to Hyper-V: when such a VM calls
* HVCALL_POST_MESSAGE, it can't use the hyperv_pcpu_input_arg (which
* is encrypted in such a VM) as the hypercall input page, because
* the input page for HVCALL_POST_MESSAGE must be decrypted in such a
* VM, so post_msg_page (which is decrypted in hv_synic_alloc()) is
* introduced for this purpose. See hyperv_init() for more comments.
*/
void *post_msg_page;
/* /*
* Starting with win8, we can take channel interrupts on any CPU; * Starting with win8, we can take channel interrupts on any CPU;
* we will manage the tasklet that handles events messages on a per CPU * we will manage the tasklet that handles events messages on a per CPU
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment