Skip to content

Commit 2337829

Browse files
dcuiliuw
authored andcommitted
Drivers: hv: vmbus: Bring the post_msg_page back for TDX VMs with the paravisor
The post_msg_page was removed in commit 9a6b1a1 ("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: Dexuan Cui <decui@microsoft.com> Reviewed-by: Tianyu Lan <tiala@microsoft.com> Reviewed-by: Michael Kelley <mikelley@microsoft.com> Signed-off-by: Wei Liu <wei.liu@kernel.org> Link: https://lore.kernel.org/r/20230824080712.30327-8-decui@microsoft.com
1 parent d3a9d7e commit 2337829

File tree

3 files changed

+82
-8
lines changed

3 files changed

+82
-8
lines changed

arch/x86/hyperv/hv_init.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,15 +480,31 @@ void __init hyperv_init(void)
480480
* Setup the hypercall page and enable hypercalls.
481481
* 1. Register the guest ID
482482
* 2. Enable the hypercall and register the hypercall page
483+
*
484+
* A TDX VM with no paravisor only uses TDX GHCI rather than hv_hypercall_pg:
485+
* when the hypercall input is a page, such a VM must pass a decrypted
486+
* page to Hyper-V, e.g. hv_post_message() uses the per-CPU page
487+
* hyperv_pcpu_input_arg, which is decrypted if no paravisor is present.
488+
*
489+
* A TDX VM with the paravisor uses hv_hypercall_pg for most hypercalls,
490+
* which are handled by the paravisor and the VM must use an encrypted
491+
* input page: in such a VM, the hyperv_pcpu_input_arg is encrypted and
492+
* used in the hypercalls, e.g. see hv_mark_gpa_visibility() and
493+
* hv_arch_irq_unmask(). Such a VM uses TDX GHCI for two hypercalls:
494+
* 1. HVCALL_SIGNAL_EVENT: see vmbus_set_event() and _hv_do_fast_hypercall8().
495+
* 2. HVCALL_POST_MESSAGE: the input page must be a decrypted page, i.e.
496+
* hv_post_message() in such a VM can't use the encrypted hyperv_pcpu_input_arg;
497+
* instead, hv_post_message() uses the post_msg_page, which is decrypted
498+
* in such a VM and is only used in such a VM.
483499
*/
484500
guest_id = hv_generate_guest_id(LINUX_VERSION_CODE);
485501
wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id);
486502

487503
/* Hyper-V requires to write guest os id via ghcb in SNP IVM. */
488504
hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, guest_id);
489505

490-
/* A TDX guest uses the GHCI call rather than hv_hypercall_pg. */
491-
if (hv_isolation_type_tdx())
506+
/* A TDX VM with no paravisor only uses TDX GHCI rather than hv_hypercall_pg */
507+
if (hv_isolation_type_tdx() && !ms_hyperv.paravisor_present)
492508
goto skip_hypercall_pg_init;
493509

494510
hv_hypercall_pg = __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START,

drivers/hv/hv.c

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,37 @@ int hv_post_message(union hv_connection_id connection_id,
5757

5858
local_irq_save(flags);
5959

60-
aligned_msg = *this_cpu_ptr(hyperv_pcpu_input_arg);
60+
/*
61+
* A TDX VM with the paravisor must use the decrypted post_msg_page: see
62+
* the comment in struct hv_per_cpu_context. A SNP VM with the paravisor
63+
* can use the encrypted hyperv_pcpu_input_arg because it copies the
64+
* input into the GHCB page, which has been decrypted by the paravisor.
65+
*/
66+
if (hv_isolation_type_tdx() && ms_hyperv.paravisor_present)
67+
aligned_msg = this_cpu_ptr(hv_context.cpu_context)->post_msg_page;
68+
else
69+
aligned_msg = *this_cpu_ptr(hyperv_pcpu_input_arg);
70+
6171
aligned_msg->connectionid = connection_id;
6272
aligned_msg->reserved = 0;
6373
aligned_msg->message_type = message_type;
6474
aligned_msg->payload_size = payload_size;
6575
memcpy((void *)aligned_msg->payload, payload, payload_size);
6676

67-
if (hv_isolation_type_snp())
68-
status = hv_ghcb_hypercall(HVCALL_POST_MESSAGE,
69-
(void *)aligned_msg, NULL,
70-
sizeof(*aligned_msg));
71-
else
77+
if (ms_hyperv.paravisor_present) {
78+
if (hv_isolation_type_tdx())
79+
status = hv_tdx_hypercall(HVCALL_POST_MESSAGE,
80+
virt_to_phys(aligned_msg), 0);
81+
else if (hv_isolation_type_snp())
82+
status = hv_ghcb_hypercall(HVCALL_POST_MESSAGE,
83+
aligned_msg, NULL,
84+
sizeof(*aligned_msg));
85+
else
86+
status = HV_STATUS_INVALID_PARAMETER;
87+
} else {
7288
status = hv_do_hypercall(HVCALL_POST_MESSAGE,
7389
aligned_msg, NULL);
90+
}
7491

7592
local_irq_restore(flags);
7693

@@ -105,6 +122,24 @@ int hv_synic_alloc(void)
105122
tasklet_init(&hv_cpu->msg_dpc,
106123
vmbus_on_msg_dpc, (unsigned long) hv_cpu);
107124

125+
if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) {
126+
hv_cpu->post_msg_page = (void *)get_zeroed_page(GFP_ATOMIC);
127+
if (hv_cpu->post_msg_page == NULL) {
128+
pr_err("Unable to allocate post msg page\n");
129+
goto err;
130+
}
131+
132+
ret = set_memory_decrypted((unsigned long)hv_cpu->post_msg_page, 1);
133+
if (ret) {
134+
pr_err("Failed to decrypt post msg page: %d\n", ret);
135+
/* Just leak the page, as it's unsafe to free the page. */
136+
hv_cpu->post_msg_page = NULL;
137+
goto err;
138+
}
139+
140+
memset(hv_cpu->post_msg_page, 0, PAGE_SIZE);
141+
}
142+
108143
/*
109144
* Synic message and event pages are allocated by paravisor.
110145
* Skip these pages allocation here.
@@ -178,6 +213,17 @@ void hv_synic_free(void)
178213
= per_cpu_ptr(hv_context.cpu_context, cpu);
179214

180215
/* It's better to leak the page if the encryption fails. */
216+
if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) {
217+
if (hv_cpu->post_msg_page) {
218+
ret = set_memory_encrypted((unsigned long)
219+
hv_cpu->post_msg_page, 1);
220+
if (ret) {
221+
pr_err("Failed to encrypt post msg page: %d\n", ret);
222+
hv_cpu->post_msg_page = NULL;
223+
}
224+
}
225+
}
226+
181227
if (!ms_hyperv.paravisor_present &&
182228
(hv_isolation_type_en_snp() || hv_isolation_type_tdx())) {
183229
if (hv_cpu->synic_message_page) {
@@ -199,6 +245,7 @@ void hv_synic_free(void)
199245
}
200246
}
201247

248+
free_page((unsigned long)hv_cpu->post_msg_page);
202249
free_page((unsigned long)hv_cpu->synic_event_page);
203250
free_page((unsigned long)hv_cpu->synic_message_page);
204251
}

drivers/hv/hyperv_vmbus.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,17 @@ struct hv_per_cpu_context {
123123
void *synic_message_page;
124124
void *synic_event_page;
125125

126+
/*
127+
* The page is only used in hv_post_message() for a TDX VM (with the
128+
* paravisor) to post a messages to Hyper-V: when such a VM calls
129+
* HVCALL_POST_MESSAGE, it can't use the hyperv_pcpu_input_arg (which
130+
* is encrypted in such a VM) as the hypercall input page, because
131+
* the input page for HVCALL_POST_MESSAGE must be decrypted in such a
132+
* VM, so post_msg_page (which is decrypted in hv_synic_alloc()) is
133+
* introduced for this purpose. See hyperv_init() for more comments.
134+
*/
135+
void *post_msg_page;
136+
126137
/*
127138
* Starting with win8, we can take channel interrupts on any CPU;
128139
* we will manage the tasklet that handles events messages on a per CPU

0 commit comments

Comments
 (0)