TDX supports a PAGE.RELEASE feature, when configured, host can only remove a private page until guest releases it and puts it in a PENDING state through TDG.MEM.PAGE.RELEASE.
When TDX PAGE.RELEASE is supported, release private memory pages before converting them to shared state, this ensures pages transition from accepted to pending state. The release operation helps handle scenarios where the hypervisor may retain old private pages during conversion. Without proper release, subsequent shared->private conversions could encounter re-acceptance errors when attempting to accept pages that are still in accepted state. If the release operation fails, abort the conversion to prevent inconsistent memory state. Note that if tdx_map_gpa() fails after successful release, we cannot safely rollback because the GPA mapping may have partially succeeded, creating a mix of shared and private pages that cannot be reliably tracked or recovered. Co-developed-by: Xu Yilun <[email protected]> Signed-off-by: Xu Yilun <[email protected]> Signed-off-by: Zhenzhong Duan <[email protected]> --- arch/x86/coco/tdx/tdx.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 0abfb3505093..ecee6df92395 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -1121,7 +1121,25 @@ static bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool enc) { phys_addr_t start = __pa(vaddr); phys_addr_t end = __pa(vaddr + numpages * PAGE_SIZE); + bool release_required = !enc && tdx_page_release_supported; + /* + * For private->shared conversion, release memory pages first. + * This transitions pages from accepted to pending state to be + * more robust with buggy VMM, e.g., VMM may keep old pages, + * when converting back to private, re-accept error triggers. + */ + if (release_required && !tdx_release_memory(start, end)) + return false; + + /* + * Update the GPA mapping state. If this fails, we cannot rollback + * by calling tdx_accept_memory() because tdx_map_gpa() may have + * partially succeeded, creating a mix of shared and private pages. + * Attempting to accept the entire range would fail on pages that + * are still in shared state, and we have no way to determine which + * pages are in which state after partial failure. + */ if (!tdx_map_gpa(start, end, enc)) return false; -- 2.52.0

