Skip to content

Commit 019b383

Browse files
dcuihansendc
authored andcommitted
x86/tdx: Retry partially-completed page conversion hypercalls
TDX guest memory is private by default and the VMM may not access it. However, in cases where the guest needs to share data with the VMM, the guest and the VMM can coordinate to make memory shared between them. The guest side of this protocol includes the "MapGPA" hypercall. This call takes a guest physical address range. The hypercall spec (aka. the GHCI) says that the MapGPA call is allowed to return partial progress in mapping this range and indicate that fact with a special error code. A guest that sees such partial progress is expected to retry the operation for the portion of the address range that was not completed. Hyper-V does this partial completion dance when set_memory_decrypted() is called to "decrypt" swiotlb bounce buffers that can be up to 1GB in size. It is evidently the only VMM that does this, which is why nobody noticed this until now. [ dhansen: rewrite changelog ] Signed-off-by: Dexuan Cui <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Reviewed-by: Michael Kelley <[email protected]> Reviewed-by: Kuppuswamy Sathyanarayanan <[email protected]> Acked-by: Kirill A. Shutemov <[email protected]> Link: https://lore.kernel.org/all/20230811021246.821-2-decui%40microsoft.com
1 parent 0bb80ec commit 019b383

File tree

2 files changed

+54
-12
lines changed

2 files changed

+54
-12
lines changed

arch/x86/coco/tdx/tdx.c

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -703,27 +703,67 @@ static bool tdx_cache_flush_required(void)
703703
}
704704

705705
/*
706-
* Inform the VMM of the guest's intent for this physical page: shared with
707-
* the VMM or private to the guest. The VMM is expected to change its mapping
708-
* of the page in response.
706+
* Notify the VMM about page mapping conversion. More info about ABI
707+
* can be found in TDX Guest-Host-Communication Interface (GHCI),
708+
* section "TDG.VP.VMCALL<MapGPA>".
709709
*/
710-
static bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool enc)
710+
static bool tdx_map_gpa(phys_addr_t start, phys_addr_t end, bool enc)
711711
{
712-
phys_addr_t start = __pa(vaddr);
713-
phys_addr_t end = __pa(vaddr + numpages * PAGE_SIZE);
712+
/* Retrying the hypercall a second time should succeed; use 3 just in case */
713+
const int max_retries_per_page = 3;
714+
int retry_count = 0;
714715

715716
if (!enc) {
716717
/* Set the shared (decrypted) bits: */
717718
start |= cc_mkdec(0);
718719
end |= cc_mkdec(0);
719720
}
720721

721-
/*
722-
* Notify the VMM about page mapping conversion. More info about ABI
723-
* can be found in TDX Guest-Host-Communication Interface (GHCI),
724-
* section "TDG.VP.VMCALL<MapGPA>"
725-
*/
726-
if (_tdx_hypercall(TDVMCALL_MAP_GPA, start, end - start, 0, 0))
722+
while (retry_count < max_retries_per_page) {
723+
struct tdx_hypercall_args args = {
724+
.r10 = TDX_HYPERCALL_STANDARD,
725+
.r11 = TDVMCALL_MAP_GPA,
726+
.r12 = start,
727+
.r13 = end - start };
728+
729+
u64 map_fail_paddr;
730+
u64 ret = __tdx_hypercall_ret(&args);
731+
732+
if (ret != TDVMCALL_STATUS_RETRY)
733+
return !ret;
734+
/*
735+
* The guest must retry the operation for the pages in the
736+
* region starting at the GPA specified in R11. R11 comes
737+
* from the untrusted VMM. Sanity check it.
738+
*/
739+
map_fail_paddr = args.r11;
740+
if (map_fail_paddr < start || map_fail_paddr >= end)
741+
return false;
742+
743+
/* "Consume" a retry without forward progress */
744+
if (map_fail_paddr == start) {
745+
retry_count++;
746+
continue;
747+
}
748+
749+
start = map_fail_paddr;
750+
retry_count = 0;
751+
}
752+
753+
return false;
754+
}
755+
756+
/*
757+
* Inform the VMM of the guest's intent for this physical page: shared with
758+
* the VMM or private to the guest. The VMM is expected to change its mapping
759+
* of the page in response.
760+
*/
761+
static bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool enc)
762+
{
763+
phys_addr_t start = __pa(vaddr);
764+
phys_addr_t end = __pa(vaddr + numpages * PAGE_SIZE);
765+
766+
if (!tdx_map_gpa(start, end, enc))
727767
return false;
728768

729769
/* shared->private conversion requires memory to be accepted before use */

arch/x86/include/asm/shared/tdx.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#define TDVMCALL_MAP_GPA 0x10001
2525
#define TDVMCALL_REPORT_FATAL_ERROR 0x10003
2626

27+
#define TDVMCALL_STATUS_RETRY 1
28+
2729
#ifndef __ASSEMBLY__
2830

2931
/*

0 commit comments

Comments
 (0)