From patchwork Wed Jun 11 21:16:29 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Ryan Afranji X-Patchwork-Id: 895639 Received: from mail-pg1-f202.google.com (mail-pg1-f202.google.com [209.85.215.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7F15C2441A0 for ; Wed, 11 Jun 2025 21:16:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749676621; cv=none; b=mYCVlNq1DDj1o7hxWN/2IBe2ffuUofI/JaryrCtL/wna2kTgvlAQ0TyStfDsqKFsG+GIiEKmKpwmkD7foFfdtXSf3PqeJ5wdMY/nIOJ/lf8zIqa2eCS4WN7qmZZuattKdG3uFkA6lfQFtDcM1HdIuPyFrgwJI4RKBNmW1Kg70co= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749676621; c=relaxed/simple; bh=b4VA2RGdpApRaSDkLmIRrxzmznFdKL/sDPXrbgY9dp8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=j4QYeJPk+S31B2lkeeDneAHFp0nuaio14o9b+PlSFyXw9ujpjiWsbcbTEk76CD2dXsTweeVlrPf4ASo90v0Kpe1YRWEwqLJnoyZGz3nAgmr8lM8mGUQISRbqHZn3RYDobegl7kF6N+XsrDIWXIMR6zpxwVSuxw1qW613MpuaU/0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--afranji.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=cm1llxK3; arc=none smtp.client-ip=209.85.215.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--afranji.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="cm1llxK3" Received: by mail-pg1-f202.google.com with SMTP id 41be03b00d2f7-b2fa1a84568so128094a12.1 for ; Wed, 11 Jun 2025 14:16:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1749676619; x=1750281419; darn=vger.kernel.org; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=zir2Z2BYD9TVMhA71zOuRqts4XDUJGWDctYBYtUWNnI=; b=cm1llxK3nvYST9jiAVqkkidlRCmrAXeSYqwy5PPrNaWKYbh+8JqYpvcGN3lgsTf/Wc ozOIWGOnoc3hfQTF0B6lXe3+itn5k30LD3dLFa4w4HfC5LGDLbWXOVRs+IXdnlE1Z98Q mJ6ntD92WvBTZ/ut1U/0QlZcolXoKaZa8e641SZ+cebYF2uEvRFg7j2FaC4+JXDpMKbp HBqnC5tMXLi16unbvBxg/+CzaOhV09X5+Or0itEx7ufRKimATNBtoHrmhh7F+viWJSz8 FrO+aSNBa/wQGiIXEyLzUIFtM3fVraFwQk2o3P65osGSVPAnfM3WFKUENx0utPSoZV5j flYw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1749676619; x=1750281419; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=zir2Z2BYD9TVMhA71zOuRqts4XDUJGWDctYBYtUWNnI=; b=kt4tg4s3qfdZf5Na8QgFVGF9cV+jX/MAYfbJokN0SkjmYBU9qHo939jBFsDU8Eb69V h2QeYepeuPu7InEBaSUpQQ0JIbD3WSohRkUMWcTmy5FGZWTpiZYHVeDbjDl/LRZIfEw+ IiEbKiW/EzzVfQqJld/yX8TqYZy4A0nTxJ7WlFgXQhcueT5XDCkd6BPA7uukR/1WGJxp GDUGW7nZhQRF+lyeNGCabS1HvFfT5ISsfkGRfyn1i7/b8qQgtayIBcSMhnVQXIvdIcmI TEzw07Y2c5A1hYdevklXxWzLpqch+fS9irYVfZl5MGtx4CCXPQfIcOizhZM1auJ2ZrwR L5Hw== X-Forwarded-Encrypted: i=1; AJvYcCXxhqHsMYPB0QnHkQMQ2sFNJbvJvQAOLkQj+GR4/z6KQY7tZ/o8kIylWn42c1PDiNDZXZkOMwn00KsaLJHSfts=@vger.kernel.org X-Gm-Message-State: AOJu0YzMhSoyWwsNDhz0kcerrw6GmO5r32JE4C0BLmRwzpyT3nxHHxMR 9d5wC195ni+VESgqEdyhnfYyVzwe7QcrydxZ4njiKk/JPizf2Nh6ii8CR+LxutYVd8n1d2iWVz6 r0/cfhGU3ng== X-Google-Smtp-Source: AGHT+IFEdzwI0hLX7jOjgrsvuEtZ1pf59QC5BkheGfaBuI5bKPz0+MMRvkX5eBM8K7fT56stpLxx97K0L1P1 X-Received: from pjbof7.prod.google.com ([2002:a17:90b:39c7:b0:312:e5dd:9248]) (user=afranji job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:5866:b0:311:b5ac:6f7d with SMTP id 98e67ed59e1d1-313bfd90b31mr1285824a91.6.1749676618837; Wed, 11 Jun 2025 14:16:58 -0700 (PDT) Date: Wed, 11 Jun 2025 21:16:29 +0000 In-Reply-To: Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: X-Mailer: git-send-email 2.50.0.rc1.591.g9c95f17f64-goog Message-ID: Subject: [RFC PATCH v2 02/10] KVM: x86: Adjust locking order in move_enc_context_from From: Ryan Afranji To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org, x86@kernel.org Cc: sagis@google.com, bp@alien8.de, chao.p.peng@linux.intel.com, dave.hansen@linux.intel.com, dmatlack@google.com, erdemaktas@google.com, isaku.yamahata@intel.com, kai.huang@intel.com, mingo@redhat.com, pbonzini@redhat.com, seanjc@google.com, tglx@linutronix.de, zhi.wang.linux@gmail.com, ackerleytng@google.com, andrew.jones@linux.dev, david@redhat.com, hpa@zytor.com, kirill.shutemov@linux.intel.com, linux-kselftest@vger.kernel.org, tabba@google.com, vannapurve@google.com, yan.y.zhao@intel.com, rick.p.edgecombe@intel.com, Ryan Afranji Previously, the order for acquiring the locks required for the migration function move_enc_context_from() was: 1) memslot lock 2) vCPU lock. This can trigger a deadlock warning because a vCPU IOCTL modifying memslots will acquire the locks in reverse order: 1) vCPU lock 2) memslot lock. This patch adjusts move_enc_context_from() to match vCPU IOCTL’s locking order to prevent deadlock warnings. Signed-off-by: Ryan Afranji --- arch/x86/kvm/svm/sev.c | 13 +------------ arch/x86/kvm/x86.c | 14 +++++++++++++- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 402543994b0b..380d5951f8dd 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -1961,26 +1961,15 @@ int sev_vm_move_enc_context_from(struct kvm *kvm, struct kvm *source_kvm) charged = true; } - ret = kvm_lock_all_vcpus(kvm); - if (ret) - goto out_dst_cgroup; - ret = kvm_lock_all_vcpus(source_kvm); - if (ret) - goto out_dst_vcpu; - ret = sev_check_source_vcpus(kvm, source_kvm); if (ret) - goto out_source_vcpu; + goto out_dst_cgroup; sev_migrate_from(kvm, source_kvm); kvm_vm_dead(source_kvm); cg_cleanup_sev = src_sev; ret = 0; -out_source_vcpu: - kvm_unlock_all_vcpus(source_kvm); -out_dst_vcpu: - kvm_unlock_all_vcpus(kvm); out_dst_cgroup: /* Operates on the source on success, on the destination on failure. */ if (charged) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index b1672379a16b..c28fa28a8e42 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6743,10 +6743,18 @@ static int kvm_vm_move_enc_context_from(struct kvm *kvm, unsigned int source_fd) if (r) goto out_mark_migration_done; - r = kvm_lock_vm_memslots(kvm, source_kvm); + r = kvm_lock_all_vcpus(kvm); if (r) goto out_unlock; + r = kvm_lock_all_vcpus(source_kvm); + if (r) + goto out_unlock_vcpus; + + r = kvm_lock_vm_memslots(kvm, source_kvm); + if (r) + goto out_unlock_source_vcpus; + r = kvm_move_memory_ctxt_from(kvm, source_kvm); if (r) goto out_unlock_memslots; @@ -6762,6 +6770,10 @@ static int kvm_vm_move_enc_context_from(struct kvm *kvm, unsigned int source_fd) out_unlock_memslots: kvm_unlock_vm_memslots(kvm, source_kvm); +out_unlock_source_vcpus: + kvm_unlock_all_vcpus(source_kvm); +out_unlock_vcpus: + kvm_unlock_all_vcpus(kvm); out_unlock: kvm_unlock_two_vms(kvm, source_kvm); out_mark_migration_done: From patchwork Wed Jun 11 21:16:31 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ryan Afranji X-Patchwork-Id: 895638 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C659F2472B6 for ; Wed, 11 Jun 2025 21:17:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749676624; cv=none; b=a+rJsDDClzviOeeaaOJHxGnwk8fHG6eEaCYlk7XXmrQVWxr3A/oxhDvKDniJ3Aocadj01gAtEF4GWdSxJIFC5bfi9OuoJQQhDXUn2SOdiEMEFWBtRJBFSHMPoMQyrHIN3hwA9Q73XGbElPzLMH/R1A7LdQyi7m7W16Wm2TvD5OE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749676624; c=relaxed/simple; bh=23eEzMfMOXF7V+bWkGGPvYl3Tz385egAD6gxjflsMOg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=XxCHLxCUI5NiZYWkOuF2XilotOeFKi9MW137Odvf1NyRdDFaLdOQxqwAJimAUCAE2rZBr6K+eQK9kG63PF5DYAaRhOm/lmnqBdI+zEDOcJtU5LPH51PEXO79rnAJNDtaYUDka+tyllPPnSnAR+AzXVSLr40v+U2wlIgGNbcXn5I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--afranji.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=XtB1acE6; arc=none smtp.client-ip=209.85.214.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--afranji.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="XtB1acE6" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-2356ce55d33so3387545ad.0 for ; Wed, 11 Jun 2025 14:17:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1749676622; x=1750281422; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=eeeVbKpBN3tx4HaouiCrmDPq5kK6kn8OwuY8SLpOHLs=; b=XtB1acE6yjL3I9NFyGCi6BYqW5VF0TzWICdBJCaWrTIweyMGIEavO6DhZQhBZkW8GN 786v4e9GuoOBRbsWWukwctYitSaiboVz/g9ey/7nvWZdUf9A73r1NzG0OuTsaWze0ci/ 2uSGrvblLTyRME7FgeaE70AdV0c/+zbTK0ogfcXscUemWAkRubi0wEqZIu8Uw+Sd7rDJ HhAxEe5kGoJD0ew12TqXBWIL3UNK9RJj+Bd4YRSFavUK/CClwPizZHgDeoEz9a59jur0 hbrwGNn7dJNWbzqe390gjZCLv8yuzvmcraRo5R12sd3BxZK6AGaOo/rcnIISN1D3RTAZ H3fg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1749676622; x=1750281422; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=eeeVbKpBN3tx4HaouiCrmDPq5kK6kn8OwuY8SLpOHLs=; b=UYQFkEo6pPK4kDw7vbU7aDHC3/Vki9p4Gh5fu8DArFv6/4j4CYM2H66VgRLc0s7ywZ Mkl/Kj1ynsW+zZVtggg0uo72HAgApXsOPNXNu5o0JheWbxiGoJdf5tO4q7dp6elXh3yU gEQ56VKsiD8sI4/aH6V3g07R2SWyWai8yjOfU39jns31n4gHVa+JB0357VGFiGF6BTzU cNDLjTk41jXhhj0be+Xg0pLmRmLgfov7oKYJYnrv0+DkCz94P/bGG27Fs7swcH7/nFy5 zZXAOvKJcnp7M9vEmtlKqAnMmhaitGp6Q4VkZEy4xRW5wYPz7oG21j6v7+wM/TLE9oSe cUWQ== X-Forwarded-Encrypted: i=1; AJvYcCWstujiZdm1X40EuxezobCBysz3xbLsp25TAo1y+zCmtERamr4AmfXMA8pQMUOcq1Q0Ojgqy0HRpti7Jqhjy3o=@vger.kernel.org X-Gm-Message-State: AOJu0Yw3khL/7QYyjx1oqBC69noCvD4juef5rSMUt2jyTr4yt1o9X7SX g9gXNTOkhhivsHxAOQjYfm/Sakjz7O3ar4pVmvDqqkK+++7Uv7vqFBH8K12pdnmNTZl0E3SQVUc lLH+I0DWgHg== X-Google-Smtp-Source: AGHT+IE5WV553GbkZv8fO1P4XOmH7RTOhAm37uG1sjPoDpJHdYSQZ79D7+wj8i1Ji8ZCsjPC1Fa02a37Bbim X-Received: from pjbqo13.prod.google.com ([2002:a17:90b:3dcd:b0:311:a4ee:7c3d]) (user=afranji job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:46c5:b0:234:8ec1:4ad3 with SMTP id d9443c01a7336-23641b291acmr69887485ad.40.1749676622084; Wed, 11 Jun 2025 14:17:02 -0700 (PDT) Date: Wed, 11 Jun 2025 21:16:31 +0000 In-Reply-To: Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: X-Mailer: git-send-email 2.50.0.rc1.591.g9c95f17f64-goog Message-ID: Subject: [RFC PATCH v2 04/10] KVM: TDX: Implement moving mirror pages between 2 TDs From: Ryan Afranji To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org, x86@kernel.org Cc: sagis@google.com, bp@alien8.de, chao.p.peng@linux.intel.com, dave.hansen@linux.intel.com, dmatlack@google.com, erdemaktas@google.com, isaku.yamahata@intel.com, kai.huang@intel.com, mingo@redhat.com, pbonzini@redhat.com, seanjc@google.com, tglx@linutronix.de, zhi.wang.linux@gmail.com, ackerleytng@google.com, andrew.jones@linux.dev, david@redhat.com, hpa@zytor.com, kirill.shutemov@linux.intel.com, linux-kselftest@vger.kernel.org, tabba@google.com, vannapurve@google.com, yan.y.zhao@intel.com, rick.p.edgecombe@intel.com, Ryan Afranji From: Sagi Shahar Added functionality for moving the mirror EPT table from one TD to a new one. This function moves the root of the mirror EPT table and overwrites the root of the destination. Signed-off-by: Sagi Shahar Signed-off-by: Ryan Afranji --- arch/x86/kvm/mmu.h | 2 ++ arch/x86/kvm/mmu/mmu.c | 66 ++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/mmu/tdp_mmu.c | 61 ++++++++++++++++++++++++++++++++--- arch/x86/kvm/mmu/tdp_mmu.h | 6 ++++ 4 files changed, 130 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index b4b6860ab971..b43d770daa05 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -102,6 +102,8 @@ void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu); void kvm_mmu_sync_prev_roots(struct kvm_vcpu *vcpu); void kvm_mmu_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new, int bytes); +int kvm_mmu_move_mirror_pages_from(struct kvm_vcpu *vcpu, + struct kvm_vcpu *src_vcpu); static inline int kvm_mmu_reload(struct kvm_vcpu *vcpu) { diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index cbc84c6abc2e..09c1892e0ac1 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -3943,6 +3943,72 @@ static int mmu_first_shadow_root_alloc(struct kvm *kvm) return r; } +int kvm_mmu_move_mirror_pages_from(struct kvm_vcpu *vcpu, + struct kvm_vcpu *src_vcpu) +{ + struct kvm_mmu *mmu = vcpu->arch.mmu; + struct kvm_mmu *src_mmu = src_vcpu->arch.mmu; + gfn_t gfn_shared = kvm_gfn_direct_bits(vcpu->kvm); + hpa_t mirror_root_hpa; + int r = -EINVAL; + + if (!gfn_shared) + return r; + + r = mmu_topup_memory_caches(vcpu, !vcpu->arch.mmu->root_role.direct); + if (r) + return r; + + /* Hold locks for both src and dst. Always take the src lock first. */ + read_lock(&src_vcpu->kvm->mmu_lock); + write_lock_nested(&vcpu->kvm->mmu_lock, SINGLE_DEPTH_NESTING); + + WARN_ON_ONCE(!is_tdp_mmu_active(vcpu)); + WARN_ON_ONCE(!is_tdp_mmu_active(src_vcpu)); + + /* + * The mirror root is moved from the src to the dst and is marked as + * invalid in the src. + */ + mirror_root_hpa = kvm_tdp_mmu_move_mirror_pages_from(vcpu, src_vcpu); + if (mirror_root_hpa == INVALID_PAGE) { + struct kvm_mmu_page *mirror_root; + union kvm_mmu_page_role role = vcpu->arch.mmu->root_role; + + /* + * This likely means that the mirror root was already moved by + * another vCPU. + */ + role.is_mirror = true; + mirror_root = kvm_tdp_mmu_get_vcpu_root(vcpu, role); + if (!mirror_root) { + r = -EINVAL; + goto out_unlock; + } + mirror_root_hpa = __pa(mirror_root->spt); + } + + mmu->mirror_root_hpa = mirror_root_hpa; + mmu_free_root_page(src_vcpu->kvm, &src_mmu->mirror_root_hpa, NULL); + write_unlock(&vcpu->kvm->mmu_lock); + read_unlock(&src_vcpu->kvm->mmu_lock); + + /* The direct root is allocated normally and is not moved from src. */ + kvm_tdp_mmu_alloc_root(vcpu, false); + + kvm_mmu_load_pgd(vcpu); + kvm_x86_call(flush_tlb_current)(vcpu); + + return r; + +out_unlock: + write_unlock(&vcpu->kvm->mmu_lock); + read_unlock(&src_vcpu->kvm->mmu_lock); + + return r; +} +EXPORT_SYMBOL(kvm_mmu_move_mirror_pages_from); + static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu) { struct kvm_mmu *mmu = vcpu->arch.mmu; diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 115af5e4c5ed..212716ab7e8b 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -251,6 +251,22 @@ static void tdp_mmu_init_child_sp(struct kvm_mmu_page *child_sp, tdp_mmu_init_sp(child_sp, iter->sptep, iter->gfn, role); } +struct kvm_mmu_page * +kvm_tdp_mmu_get_vcpu_root(struct kvm_vcpu *vcpu, + union kvm_mmu_page_role role) +{ + struct kvm *kvm = vcpu->kvm; + struct kvm_mmu_page *root; + + lockdep_assert_held(&kvm->mmu_lock); + list_for_each_entry(root, &kvm->arch.tdp_mmu_roots, link) { + if (root->role.word == role.word && + !WARN_ON_ONCE(!kvm_tdp_mmu_get_root(root))) + return root; + } + return NULL; +} + void kvm_tdp_mmu_alloc_root(struct kvm_vcpu *vcpu, bool mirror) { struct kvm_mmu *mmu = vcpu->arch.mmu; @@ -285,11 +301,9 @@ void kvm_tdp_mmu_alloc_root(struct kvm_vcpu *vcpu, bool mirror) * fails, as the last reference to a root can only be put *after* the * root has been invalidated, which requires holding mmu_lock for write. */ - list_for_each_entry(root, &kvm->arch.tdp_mmu_roots, link) { - if (root->role.word == role.word && - !WARN_ON_ONCE(!kvm_tdp_mmu_get_root(root))) - goto out_spin_unlock; - } + root = kvm_tdp_mmu_get_vcpu_root(vcpu, role); + if (!!root) + goto out_spin_unlock; root = tdp_mmu_alloc_sp(vcpu); tdp_mmu_init_sp(root, NULL, 0, role); @@ -321,6 +335,43 @@ void kvm_tdp_mmu_alloc_root(struct kvm_vcpu *vcpu, bool mirror) } } +hpa_t kvm_tdp_mmu_move_mirror_pages_from(struct kvm_vcpu *vcpu, + struct kvm_vcpu *src_vcpu) +{ + union kvm_mmu_page_role role = vcpu->arch.mmu->root_role; + struct kvm *kvm = vcpu->kvm; + struct kvm *src_kvm = src_vcpu->kvm; + struct kvm_mmu_page *mirror_root = NULL; + s64 num_mirror_pages, old; + + lockdep_assert_held_read(&src_vcpu->kvm->mmu_lock); + lockdep_assert_held_write(&vcpu->kvm->mmu_lock); + + /* Find the mirror root of the source. */ + role.is_mirror = true; + mirror_root = kvm_tdp_mmu_get_vcpu_root(src_vcpu, role); + if (!mirror_root) + return INVALID_PAGE; + + /* Remove the mirror root from the src kvm and add it to dst kvm. */ + spin_lock(&src_vcpu->kvm->arch.tdp_mmu_pages_lock); + list_del_rcu(&mirror_root->link); + spin_unlock(&src_vcpu->kvm->arch.tdp_mmu_pages_lock); + + /* The destination holds a write lock so no spin_lock required. */ + list_add_rcu(&mirror_root->link, &kvm->arch.tdp_mmu_roots); + +#ifdef CONFIG_KVM_PROVE_MMU + num_mirror_pages = atomic64_read(&src_kvm->arch.tdp_mirror_mmu_pages); + old = atomic64_cmpxchg(&kvm->arch.tdp_mirror_mmu_pages, 0, + num_mirror_pages); + /* The destination VM should have no mirror pages at this point. */ + WARN_ON(old); + atomic64_set(&src_kvm->arch.tdp_mirror_mmu_pages, 0); +#endif + return __pa(mirror_root->spt); +} + static void handle_changed_spte(struct kvm *kvm, int as_id, gfn_t gfn, u64 old_spte, u64 new_spte, int level, bool shared); diff --git a/arch/x86/kvm/mmu/tdp_mmu.h b/arch/x86/kvm/mmu/tdp_mmu.h index 52acf99d40a0..abb1a84d8b1c 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.h +++ b/arch/x86/kvm/mmu/tdp_mmu.h @@ -63,6 +63,12 @@ static inline struct kvm_mmu_page *tdp_mmu_get_root(struct kvm_vcpu *vcpu, return root_to_sp(vcpu->arch.mmu->root.hpa); } +struct kvm_mmu_page * +kvm_tdp_mmu_get_vcpu_root(struct kvm_vcpu *vcpu, + union kvm_mmu_page_role role); +hpa_t kvm_tdp_mmu_move_mirror_pages_from(struct kvm_vcpu *vcpu, + struct kvm_vcpu *src_vcpu); + bool kvm_tdp_mmu_zap_leafs(struct kvm *kvm, gfn_t start, gfn_t end, bool flush); bool kvm_tdp_mmu_zap_sp(struct kvm *kvm, struct kvm_mmu_page *sp); void kvm_tdp_mmu_zap_all(struct kvm *kvm); From patchwork Wed Jun 11 21:16:33 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ryan Afranji X-Patchwork-Id: 895637 Received: from mail-pj1-f73.google.com (mail-pj1-f73.google.com [209.85.216.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 39FB724A06B for ; Wed, 11 Jun 2025 21:17:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749676627; cv=none; b=hS2vPqo+nLsJnlPy7RtaKcA9+TdcYWV/bO7aK4ZO1QvmRhSmy3RYxLEuSVBeUFVoWp5Ce4BUfYwJgke3utXYQ+nw11foJ00jhkTTz9qRIxtRrn6uaXqeI65vaBYmRquldVYYf2tvSsUXaRL6T1v3SrDXwvKIOcL+3pWjhFTM6kM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749676627; c=relaxed/simple; bh=x3k8bhvleSPHVSZM+Hu9CVZnDZNGJdDQHXfon2q0JNg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=EVtLbMp/bxBH0JF/zlsZi93ZKJeI/w+xYXB1yJjN9wfhgn66htpA+F590KBJB/U+IAQv2WHK9ogIbh0QpkBKADlNz+FhFnLOKkIrFLNzlESzBrQcxTsbmLuukJ+6HprSi5a5IsInlpm2NzTEK9XJ9dEQGc2lzkSbONR0NLP9G30= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--afranji.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=VHjZDJc/; arc=none smtp.client-ip=209.85.216.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--afranji.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="VHjZDJc/" Received: by mail-pj1-f73.google.com with SMTP id 98e67ed59e1d1-3139c0001b5so264758a91.2 for ; Wed, 11 Jun 2025 14:17:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1749676625; x=1750281425; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=sUa6MDoTcZ9fiqDL9aF0ZXv2kKdfYnDn1hf7TqaAmz0=; b=VHjZDJc/Y9l5PmFrsPGNW0CrFoJrgplwW4NKLlbpdBvuuXcmBYULslJjEF+GoMewGQ +Aqu1AO26tF6a17cxBGSyLHESlIfPqxO2+B6yJjo1PSTb9EOXv94hPs2SVhHIVDL4dBk yXb6sfISfTN3+Manx9zRaE19yAaY9YxLzfVnhQFcNe30Qqt6FRGJ0JIgjgmDGghUVFtL m91vgS/E1MecHtVe9iffuR88v6ItkxopCZ1hp5t4og2R/HLp4Ty3Fbj8p96UMonnTbeq gnY+w3WuT3Ef/tHVjwO36pgQqhLaFfJr1fdeR7lVfaQVDZWghD8sMEIqshDktCe6tply pMvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1749676625; x=1750281425; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=sUa6MDoTcZ9fiqDL9aF0ZXv2kKdfYnDn1hf7TqaAmz0=; b=QEkl4UozWBL2reHy4cT1ERN58dcwCQxvnlBL3KKx81NDcH40D90fMjJe7hWUzTQ3Jw E9sYYYvFZ0n6afDr7bh68CYV0BA8o2Om58KsiRkdj8foWp7q75eGv8dG+7QpMLFZqTyN DwPhNHInKeJ+Rx0zjuaHpgtBXyQxdT8FMIc8wZotKUam86a87w7GTw1c6+HFExi2xfTq i7lOeQ4i+Le3FLdxaNNahk4cvIzUi5c2s7D3UhcR+cZmMBxMM8CAihO9+xNViT+iS2LP +JSMYqy7QFxK5cUN+deSsVYXf+hvR9qKXSN6XmqW6tXJT0Ihz16YjZ2e87D8WLtWnaBI 9M0w== X-Forwarded-Encrypted: i=1; AJvYcCVe+LJF07ubj+gR1KT/r1nOnUNlYGSq8WC5wi3sD+8f22by/HbpR+giDT11DvWQ04rm3L2G7PMf82zdGA9pzJw=@vger.kernel.org X-Gm-Message-State: AOJu0Yz1w/BnfFcd32iz/BtqU7yjAHrp19oizbtStwGnWQ+hgli08fdL r6LqAQ/Q6sDqh7uRmSOeNn/6mtkiksHFAmQOq5WSWLkvkFcPmRlf68IQB/bx6s4+ouK1Od4Ov5j oNT40qTl2nA== X-Google-Smtp-Source: AGHT+IFvw3e8kSlOft2Aa67tR4tbWriXJAz6qMW5rx2kfDteHiCjVlYUYiTe4hevdIc3/x6NCe31w22UVNJM X-Received: from pjoo4.prod.google.com ([2002:a17:90b:5824:b0:30a:7da4:f075]) (user=afranji job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:4b87:b0:311:d05c:936 with SMTP id 98e67ed59e1d1-313bfbf521fmr1449542a91.17.1749676625477; Wed, 11 Jun 2025 14:17:05 -0700 (PDT) Date: Wed, 11 Jun 2025 21:16:33 +0000 In-Reply-To: Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: X-Mailer: git-send-email 2.50.0.rc1.591.g9c95f17f64-goog Message-ID: Subject: [RFC PATCH v2 06/10] KVM: TDX: Add core logic for TDX intra-host migration From: Ryan Afranji To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org, x86@kernel.org Cc: sagis@google.com, bp@alien8.de, chao.p.peng@linux.intel.com, dave.hansen@linux.intel.com, dmatlack@google.com, erdemaktas@google.com, isaku.yamahata@intel.com, kai.huang@intel.com, mingo@redhat.com, pbonzini@redhat.com, seanjc@google.com, tglx@linutronix.de, zhi.wang.linux@gmail.com, ackerleytng@google.com, andrew.jones@linux.dev, david@redhat.com, hpa@zytor.com, kirill.shutemov@linux.intel.com, linux-kselftest@vger.kernel.org, tabba@google.com, vannapurve@google.com, yan.y.zhao@intel.com, rick.p.edgecombe@intel.com, Ryan Afranji From: Sagi Shahar Adds the core logic for transferring state between source and destination TDs during intra-host migration. Signed-off-by: Sagi Shahar Co-developed-by: Ryan Afranji Signed-off-by: Ryan Afranji --- arch/x86/kvm/vmx/tdx.c | 193 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 192 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 4582f94175b7..268aca28d878 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -3534,9 +3534,200 @@ static __always_inline bool tdx_finalized(struct kvm *kvm) return tdx_kvm->state == TD_STATE_RUNNABLE; } +#define MAX_APIC_VECTOR 256 + +static int tdx_migrate_vcpus(struct kvm *dst, struct kvm *src) +{ + struct kvm_vcpu *src_vcpu; + struct kvm_tdx *dst_tdx; + unsigned long i; + + dst_tdx = to_kvm_tdx(dst); + + kvm_for_each_vcpu(i, src_vcpu, src) + tdx_flush_vp_on_cpu(src_vcpu); + + /* Copy per-vCPU state. */ + kvm_for_each_vcpu(i, src_vcpu, src) { + struct vcpu_tdx *dst_tdx_vcpu, *src_tdx_vcpu; + struct kvm_lapic_state src_lapic_state; + struct kvm_vcpu *dst_vcpu; + u64 apic_base; + u32 vector; + int ret; + + src_tdx_vcpu = to_tdx(src_vcpu); + dst_vcpu = kvm_get_vcpu(dst, i); + dst_tdx_vcpu = to_tdx(dst_vcpu); + + dst_vcpu->cpu = -1; + + /* Destination vCPU initialization skipped so do it here. */ + apic_base = APIC_DEFAULT_PHYS_BASE | LAPIC_MODE_X2APIC | + (kvm_vcpu_is_reset_bsp(dst_vcpu) ? + MSR_IA32_APICBASE_BSP : 0); + if (kvm_apic_set_base(dst_vcpu, apic_base, true)) + return -EINVAL; + + /* Copy lapic state. */ + ret = kvm_apic_get_state(src_vcpu, &src_lapic_state); + if (ret) + return -EINVAL; + + ret = kvm_apic_set_state(dst_vcpu, &src_lapic_state); + if (ret) + return -EINVAL; + + /* + * pi_desc stores state of posted interrupts for VMs which are + * processed by pcpu during VM entry/runtime. For + * non-confidential VMs, this storage is synchronized to vcpu + * state using set_lapic_state(sync_pir_to_virr). + * + * For TDX VMs, KVM doesn't have access to virtual lapic page, + * so in order to preserve the interrupt state, copy over + * pi_desc contents to destination VM during copyless migration. + */ + dst_tdx_vcpu->vt = src_tdx_vcpu->vt; + for (vector = 0; vector < MAX_APIC_VECTOR; vector++) { + if (pi_test_pir(vector, &src_tdx_vcpu->vt.pi_desc)) { + __vmx_deliver_posted_interrupt( + dst_vcpu, + &dst_tdx_vcpu->vt.pi_desc, + vector); + } + } + + /* Copy non-TDX vCPU state. */ + memcpy(dst_vcpu->arch.regs, src_vcpu->arch.regs, + NR_VCPU_REGS * sizeof(src_vcpu->arch.regs[0])); + + dst_vcpu->arch.regs_avail = src_vcpu->arch.regs_avail; + dst_vcpu->arch.regs_dirty = src_vcpu->arch.regs_dirty; + dst_vcpu->arch.tsc_offset = dst_tdx->tsc_offset; + dst_vcpu->arch.guest_state_protected = + src_vcpu->arch.guest_state_protected; + dst_vcpu->arch.xfd_no_write_intercept = + src_vcpu->arch.xfd_no_write_intercept; + dst_vcpu->arch.tsc_offset = dst_tdx->tsc_offset; + + /* Copy TD structures. */ + dst_tdx_vcpu->vp.tdvpr_page = src_tdx_vcpu->vp.tdvpr_page; + dst_tdx_vcpu->vp.tdcx_pages = src_tdx_vcpu->vp.tdcx_pages; + + td_vmcs_write64(dst_tdx_vcpu, POSTED_INTR_DESC_ADDR, + __pa(&dst_tdx_vcpu->vt.pi_desc)); + + /* Copy current vCPU status. */ + dst_tdx_vcpu->ext_exit_qualification = + src_tdx_vcpu->ext_exit_qualification; + dst_tdx_vcpu->exit_gpa = src_tdx_vcpu->exit_gpa; + dst_tdx_vcpu->vp_enter_args = src_tdx_vcpu->vp_enter_args; + dst_tdx_vcpu->vp_enter_ret = src_tdx_vcpu->vp_enter_ret; + dst_tdx_vcpu->guest_entered = src_tdx_vcpu->guest_entered; + dst_tdx_vcpu->map_gpa_next = src_tdx_vcpu->map_gpa_next; + dst_tdx_vcpu->map_gpa_end = src_tdx_vcpu->map_gpa_end; + + /* Copy mirror EPT tables. */ + vcpu_load(dst_vcpu); + if (kvm_mmu_move_mirror_pages_from(dst_vcpu, src_vcpu)) { + vcpu_put(dst_vcpu); + return -EINVAL; + } + vcpu_put(dst_vcpu); + + dst_vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE; + dst_tdx_vcpu->state = VCPU_TD_STATE_INITIALIZED; + + /* + * Set these source's vCPU migrated structures to NULL to avoid + * freeing them during source VM shutdown. + */ + src_tdx_vcpu->vp.tdvpr_page = NULL; + src_tdx_vcpu->vp.tdcx_pages = NULL; + } + + return 0; +} + static int tdx_migrate_from(struct kvm *dst, struct kvm *src) { - return -EINVAL; + struct kvm_tdx *src_tdx, *dst_tdx; + bool charged = false; + int ret; + + src_tdx = to_kvm_tdx(src); + dst_tdx = to_kvm_tdx(dst); + + ret = -EINVAL; + + if (src_tdx->state != TD_STATE_RUNNABLE) { + pr_warn("Cannot migrate from a non finalized VM\n"); + goto abort; + } + + /* Transfer miscellaneous cgroup. */ + dst_tdx->misc_cg = get_current_misc_cg(); + if (dst_tdx->misc_cg != src_tdx->misc_cg) { + ret = misc_cg_try_charge(MISC_CG_RES_TDX, dst_tdx->misc_cg, 1); + if (ret) + goto abort_dst_cgroup; + charged = true; + } + + dst_tdx->hkid = src_tdx->hkid; + + /* Copy VM data. */ + dst_tdx->attributes = src_tdx->attributes; + dst_tdx->xfam = src_tdx->xfam; + dst_tdx->tsc_offset = src_tdx->tsc_offset; + dst_tdx->tsc_multiplier = src_tdx->tsc_multiplier; + dst_tdx->nr_premapped = src_tdx->nr_premapped; + dst_tdx->wait_for_sept_zap = src_tdx->wait_for_sept_zap; + dst_tdx->kvm.arch.gfn_direct_bits = src_tdx->kvm.arch.gfn_direct_bits; + + /* Copy TD structures. */ + dst_tdx->td.tdcs_nr_pages = src_tdx->td.tdcs_nr_pages; + dst_tdx->td.tdcx_nr_pages = src_tdx->td.tdcx_nr_pages; + dst_tdx->td.tdr_page = src_tdx->td.tdr_page; + dst_tdx->td.tdcs_pages = src_tdx->td.tdcs_pages; + + /* Copy per-vCPU state. */ + ret = tdx_migrate_vcpus(dst, src); + if (ret) + goto late_abort; + + dst->mem_attr_array.xa_head = src->mem_attr_array.xa_head; + src->mem_attr_array.xa_head = NULL; + + dst_tdx->state = TD_STATE_RUNNABLE; + + /* + * Set these source's vCPU migrated structures to NULL to avoid + * freeing them during source VM shutdown. + */ + src_tdx->hkid = -1; + src_tdx->td.tdr_page = NULL; + src_tdx->td.tdcs_pages = NULL; + + return 0; + +late_abort: + /* + * If we aborted after the state transfer already started, the src VM + * is no longer valid. + */ + kvm_vm_dead(src); + +abort_dst_cgroup: + if (charged) + misc_cg_uncharge(MISC_CG_RES_TDX, dst_tdx->misc_cg, 1); + put_misc_cg(dst_tdx->misc_cg); + dst_tdx->misc_cg = NULL; +abort: + dst_tdx->hkid = -1; + dst_tdx->td.tdr_page = 0; + return ret; } int tdx_vm_move_enc_context_from(struct kvm *kvm, struct kvm *src_kvm) From patchwork Wed Jun 11 21:16:35 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ryan Afranji X-Patchwork-Id: 895636 Received: from mail-pf1-f201.google.com (mail-pf1-f201.google.com [209.85.210.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8C9CF24EA85 for ; Wed, 11 Jun 2025 21:17:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749676631; cv=none; b=qQhDYMt+SAkrS7mZH4EyIkfP2T7RO5XHer2pkCyWde33vUYRqGkjnDH3rzFACZTFfQmx0ZvDoXdnNFBpdzlRqh66AYn7ZTjPzSB8A0NQ+E3r7X2DmxttpnDrwCcrHNHFdOalVYEeAAB9FlgAjbJ5ZK8oPGJHgjrC6OkqzOl71eY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749676631; c=relaxed/simple; bh=9bHODc+L3jY8CU4AGA0f9Uj2uodDYKTbx2LQsdLuFe8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=dup8Ihed6FsBPIoLCha9zQPpfIpgaOJzLtg6x7aEczECqfrSLCYH1Ua/rpodQ+K5IiH7YaBIUsUX2xc+WxHs8v69Edvx4qyvAmu2cV4pIG3+Z+5+7Uf02jNMwCkBnMmY9+3UpeeywYhFzGy/X9TaPcDTgxWJmKdz8qfw4p8irwo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--afranji.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=yW4OwMA5; arc=none smtp.client-ip=209.85.210.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--afranji.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="yW4OwMA5" Received: by mail-pf1-f201.google.com with SMTP id d2e1a72fcca58-747d84fe5f8so149235b3a.3 for ; Wed, 11 Jun 2025 14:17:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1749676629; x=1750281429; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=ssjnbQ4kJflk7fXBHBaf7ALVkd5j+ovjxWVLHw4vo40=; b=yW4OwMA5+E3hWIoxPhcBzfrFkWa/ZxvxscuiJyItpBbf32NStZuTgoNAket1hu0HCN RNTOg6fWhNGCtoMKBt9MvSWs3Bht0wsj8KGvGBPY8BiWyrtrnfzC0iRG7fKOLmGUalVm 5OaSu4i7OIE91w8ILEbFbXzKSSfWDSQX3PHijTAjLurasKbzC2D0vkvXL6Du9WFBhs/x H22bKbu2IjNZzP6HQRFC7dED+uD+7bxucl3nM5J06l+FKzhWYmPkvkFzXgpJAHNrkAZ/ OXBbQ3bgenhidqd7qFJOVVag7ptjyke2Mpzh+MTH3boyjXIOiPR9s0kOjUFqxbooCvyp nSvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1749676629; x=1750281429; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=ssjnbQ4kJflk7fXBHBaf7ALVkd5j+ovjxWVLHw4vo40=; b=Wqen3TH2KQe7dJ12sVybVr85YuWZHpoXSsbvBjSu/md7Jybir0PVMrmjkI2cVzHMYT 0JdxwbqbL40oaKCP/35ssTajWthGZD/cvfF6RTIgQYBMGPxThTxKYGERkHlB9g/DtR3J QCjkXAxLtSnuakqgjz6Kgwa1eig4tFBuSo0pYFraSyaRb8t1ZXu3To9c1HD4yZs58WGu 64Spyy+XMOp6AAiWigAuLqNcaS7e7P3xU5dbvHsKbHVebYz+fAnENLVXvQfiKHJU8AnI hKGz+mLDJRTOcYv6bwJvxLoJc0l7HM/jrkAcr65V9NGbro05ypizea1P4wUtux5cQ7Qx rYXQ== X-Forwarded-Encrypted: i=1; AJvYcCWxofTmaFg28chBQndD257i33vE/G+rTfEfOTiJkqeKsU+/5/M4y4L3xI6tcC/v5y8JPjdfzae4D43qm5rUsaY=@vger.kernel.org X-Gm-Message-State: AOJu0YxnB4tzFweyGkOUCbcWcUseHXz56goWL+94s4QtXb8yQ+s7wjRu GmovxO5dkgviJEr3rO4V8FjYX6B/r2WSc2hdJHxoLfiYl1RZmtEBqUA5w67bIps8vLXrulpHUp5 f3Ade4HU4QA== X-Google-Smtp-Source: AGHT+IGt8itXpNhAIFBj62DiJ64U9JmDFLzjGyRqSV6OdShtMpiACjneNt3RcQvG2dGMamb53/SKPyXvNeW+ X-Received: from pfbfr17.prod.google.com ([2002:a05:6a00:8111:b0:73e:665:360]) (user=afranji job=prod-delivery.src-stubby-dispatcher) by 2002:aa7:8882:0:b0:73d:ff02:8d83 with SMTP id d2e1a72fcca58-7486cb21c08mr7425055b3a.3.1749676628808; Wed, 11 Jun 2025 14:17:08 -0700 (PDT) Date: Wed, 11 Jun 2025 21:16:35 +0000 In-Reply-To: Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: X-Mailer: git-send-email 2.50.0.rc1.591.g9c95f17f64-goog Message-ID: <92f22ace98238b79c25bd8759c75a1143d82a741.1749672978.git.afranji@google.com> Subject: [RFC PATCH v2 08/10] KVM: selftests: TDX: Add tests for TDX in-place migration From: Ryan Afranji To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org, x86@kernel.org Cc: sagis@google.com, bp@alien8.de, chao.p.peng@linux.intel.com, dave.hansen@linux.intel.com, dmatlack@google.com, erdemaktas@google.com, isaku.yamahata@intel.com, kai.huang@intel.com, mingo@redhat.com, pbonzini@redhat.com, seanjc@google.com, tglx@linutronix.de, zhi.wang.linux@gmail.com, ackerleytng@google.com, andrew.jones@linux.dev, david@redhat.com, hpa@zytor.com, kirill.shutemov@linux.intel.com, linux-kselftest@vger.kernel.org, tabba@google.com, vannapurve@google.com, yan.y.zhao@intel.com, rick.p.edgecombe@intel.com, Ryan Afranji From: Sagi Shahar Adds selftests for TDX in-place migration. Signed-off-by: Ryan Afranji Signed-off-by: Sagi Shahar --- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../testing/selftests/kvm/include/kvm_util.h | 20 + .../selftests/kvm/include/x86/tdx/tdx_util.h | 1 + tools/testing/selftests/kvm/lib/kvm_util.c | 50 ++- .../selftests/kvm/lib/x86/tdx/tdx_util.c | 3 +- .../selftests/kvm/x86/tdx_migrate_tests.c | 358 ++++++++++++++++++ 6 files changed, 429 insertions(+), 4 deletions(-) create mode 100644 tools/testing/selftests/kvm/x86/tdx_migrate_tests.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 1c7ea61e9031..d4c8cfb5910f 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -155,6 +155,7 @@ TEST_GEN_PROGS_x86 += pre_fault_memory_test TEST_GEN_PROGS_x86 += x86/tdx_vm_test TEST_GEN_PROGS_x86 += x86/tdx_shared_mem_test TEST_GEN_PROGS_x86 += x86/tdx_upm_test +TEST_GEN_PROGS_x86 += x86/tdx_migrate_tests # Compiled outputs used by test targets TEST_GEN_PROGS_EXTENDED_x86 += x86/nx_huge_pages_test diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 267f78f3f16f..1b6489081e74 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -110,6 +110,9 @@ struct kvm_vm { struct kvm_binary_stats stats; + /* VM was migrated using KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM */ + bool enc_migrated; + /* * KVM region slots. These are the default memslots used by page * allocators, e.g., lib/elf uses the memslots[MEM_REGION_CODE] @@ -673,6 +676,7 @@ static inline bool vm_arch_has_protected_memory(struct kvm_vm *vm) void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags); void vm_mem_region_move(struct kvm_vm *vm, uint32_t slot, uint64_t new_gpa); +void vm_migrate_mem_regions(struct kvm_vm *dst_vm, struct kvm_vm *src_vm); void vm_mem_region_delete(struct kvm_vm *vm, uint32_t slot); struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id); void vm_populate_vaddr_bitmap(struct kvm_vm *vm); @@ -1132,6 +1136,22 @@ static inline struct kvm_vcpu *vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, return vcpu; } +/* + * Adds a vCPU with no defaults. This vcpu will be used for migration + * + * Input Args: + * vm - Virtual Machine + * vcpu_id - The id of the VCPU to add to the VM. + */ +struct kvm_vcpu *vm_arch_vcpu_add_for_migration(struct kvm_vm *vm, + uint32_t vcpu_id); + +static inline struct kvm_vcpu *vm_vcpu_add_for_migration(struct kvm_vm *vm, + uint32_t vcpu_id) +{ + return vm_arch_vcpu_add_for_migration(vm, vcpu_id); +} + /* Re-create a vCPU after restarting a VM, e.g. for state save/restore tests. */ struct kvm_vcpu *vm_arch_vcpu_recreate(struct kvm_vm *vm, uint32_t vcpu_id); diff --git a/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h index ae39b78aa4af..9b495e621225 100644 --- a/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +++ b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h @@ -9,6 +9,7 @@ extern uint64_t tdx_s_bit; void tdx_filter_cpuid(struct kvm_vm *vm, struct kvm_cpuid2 *cpuid_data); void __tdx_mask_cpuid_features(struct kvm_cpuid_entry2 *entry); +void tdx_enable_capabilities(struct kvm_vm *vm); struct kvm_vcpu *td_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, void *guest_code); diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 3c131718b81a..9dc3c7bf0443 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -805,8 +805,10 @@ static void __vm_mem_region_delete(struct kvm_vm *vm, sparsebit_free(®ion->unused_phy_pages); sparsebit_free(®ion->protected_phy_pages); - ret = munmap(region->mmap_start, region->mmap_size); - TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret)); + if (!vm->enc_migrated) { + ret = munmap(region->mmap_start, region->mmap_size); + TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret)); + } if (region->fd >= 0) { /* There's an extra map when using shared memory. */ ret = munmap(region->mmap_alias, region->mmap_size); @@ -1287,6 +1289,50 @@ void vm_mem_region_move(struct kvm_vm *vm, uint32_t slot, uint64_t new_gpa) ret, errno, slot, new_gpa); } +static void vm_migrate_mem_region(struct kvm_vm *dst_vm, struct kvm_vm *src_vm, + struct userspace_mem_region *src_region) +{ + struct userspace_mem_region *dst_region; + int dst_guest_memfd; + + dst_guest_memfd = + vm_link_guest_memfd(dst_vm, src_region->region.guest_memfd, 0); + + dst_region = vm_mem_region_alloc( + dst_vm, src_region->region.guest_phys_addr, + src_region->region.slot, + src_region->region.memory_size / src_vm->page_size, + src_region->region.flags); + + dst_region->mmap_size = src_region->mmap_size; + dst_region->mmap_start = src_region->mmap_start; + dst_region->host_mem = src_region->host_mem; + + src_region->mmap_start = 0; + src_region->host_mem = 0; + + dst_region->region.guest_memfd = dst_guest_memfd; + dst_region->region.guest_memfd_offset = + src_region->region.guest_memfd_offset; + + userspace_mem_region_commit(dst_vm, dst_region); +} + +void vm_migrate_mem_regions(struct kvm_vm *dst_vm, struct kvm_vm *src_vm) +{ + int bkt; + struct hlist_node *node; + struct userspace_mem_region *region; + + hash_for_each_safe(src_vm->regions.slot_hash, bkt, node, region, + slot_node) { + TEST_ASSERT(region->region.guest_memfd >= 0, + "Migrating mem regions is only supported for GUEST_MEMFD"); + + vm_migrate_mem_region(dst_vm, src_vm, region); + } +} + /* * VM Memory Region Delete * diff --git a/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c b/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c index c5bee67099c5..ef03d42f58d0 100644 --- a/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +++ b/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c @@ -344,7 +344,7 @@ static void register_encrypted_memory_region(struct kvm_vm *vm, * TD creation/setup/finalization */ -static void tdx_enable_capabilities(struct kvm_vm *vm) +void tdx_enable_capabilities(struct kvm_vm *vm) { int rc; @@ -574,7 +574,6 @@ void td_initialize(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type, uint64_t nr_pages_required; tdx_enable_capabilities(vm); - tdx_td_init(vm, attributes); nr_pages_required = vm_nr_pages_required(VM_MODE_DEFAULT, 1, 0); diff --git a/tools/testing/selftests/kvm/x86/tdx_migrate_tests.c b/tools/testing/selftests/kvm/x86/tdx_migrate_tests.c new file mode 100644 index 000000000000..e15da2aa0437 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/tdx_migrate_tests.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "tdx/tdcall.h" +#include "tdx/tdx.h" +#include "tdx/tdx_util.h" +#include "tdx/test_util.h" +#include +#include + +#define NR_MIGRATE_TEST_VMS 10 +#define TDX_IOEXIT_TEST_PORT 0x50 + +static int __tdx_migrate_from(int dst_fd, int src_fd) +{ + struct kvm_enable_cap cap = { + .cap = KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM, + .args = { src_fd } + }; + + return ioctl(dst_fd, KVM_ENABLE_CAP, &cap); +} + + +static void tdx_migrate_from(struct kvm_vm *dst_vm, struct kvm_vm *src_vm) +{ + int ret; + + vm_migrate_mem_regions(dst_vm, src_vm); + ret = __tdx_migrate_from(dst_vm->fd, src_vm->fd); + TEST_ASSERT(!ret, "Migration failed, ret: %d, errno: %d\n", ret, errno); + src_vm->enc_migrated = true; +} + +void guest_code(void) +{ + int ret; + uint64_t data; + + data = 1; + ret = tdg_vp_vmcall_instruction_io(TDX_IOEXIT_TEST_PORT, 1, + PORT_WRITE, + &data); + if (ret) + tdx_test_fatal_with_data(ret, __LINE__); + + data++; + ret = tdg_vp_vmcall_instruction_io(TDX_IOEXIT_TEST_PORT, 1, + PORT_WRITE, + &data); + if (ret) + tdx_test_fatal_with_data(ret, __LINE__); + + tdx_test_success(); +} + +static void test_tdx_migrate_vm_with_private_memory(void) +{ + struct kvm_vm *src_vm; + struct kvm_vm *dst_vm; + struct kvm_vcpu *dst_vcpu; + uint32_t data; + + printf("Verifying migration of VM with private memory:\n"); + + src_vm = td_create(); + td_initialize(src_vm, VM_MEM_SRC_ANONYMOUS, 0); + td_vcpu_add(src_vm, 0, guest_code); + td_finalize(src_vm); + + dst_vm = td_create(); + tdx_enable_capabilities(dst_vm); + dst_vcpu = vm_vcpu_recreate(dst_vm, 0); + + tdx_migrate_from(dst_vm, src_vm); + + kvm_vm_free(src_vm); + + tdx_run(dst_vcpu); + tdx_test_assert_io(dst_vcpu, TDX_IOEXIT_TEST_PORT, 1, + PORT_WRITE); + data = *(uint8_t *)((void *)dst_vcpu->run + + dst_vcpu->run->io.data_offset); + TEST_ASSERT_EQ(data, 1); + + tdx_run(dst_vcpu); + tdx_test_assert_io(dst_vcpu, TDX_IOEXIT_TEST_PORT, 1, + PORT_WRITE); + data = *(uint8_t *)((void *)dst_vcpu->run + + dst_vcpu->run->io.data_offset); + TEST_ASSERT_EQ(data, 2); + + tdx_run(dst_vcpu); + tdx_test_assert_success(dst_vcpu); + + kvm_vm_free(dst_vm); + + printf("\t ... PASSED\n"); +} + +static void test_tdx_migrate_running_vm(void) +{ + struct kvm_vm *src_vm; + struct kvm_vm *dst_vm; + struct kvm_vcpu *src_vcpu; + struct kvm_vcpu *dst_vcpu; + uint32_t data; + + printf("Verifying migration of a running VM:\n"); + + src_vm = td_create(); + td_initialize(src_vm, VM_MEM_SRC_ANONYMOUS, 0); + src_vcpu = td_vcpu_add(src_vm, 0, guest_code); + td_finalize(src_vm); + + dst_vm = td_create(); + tdx_enable_capabilities(dst_vm); + dst_vcpu = vm_vcpu_recreate(dst_vm, 0); + + tdx_run(src_vcpu); + tdx_test_assert_io(src_vcpu, TDX_IOEXIT_TEST_PORT, 1, + PORT_WRITE); + data = *(uint8_t *)((void *)src_vcpu->run + + src_vcpu->run->io.data_offset); + TEST_ASSERT_EQ(data, 1); + + tdx_migrate_from(dst_vm, src_vm); + + kvm_vm_free(src_vm); + + tdx_run(dst_vcpu); + tdx_test_assert_io(dst_vcpu, TDX_IOEXIT_TEST_PORT, 1, + PORT_WRITE); + data = *(uint8_t *)((void *)dst_vcpu->run + + dst_vcpu->run->io.data_offset); + TEST_ASSERT_EQ(data, 2); + + tdx_run(dst_vcpu); + tdx_test_assert_success(dst_vcpu); + + kvm_vm_free(dst_vm); + + printf("\t ... PASSED\n"); +} + +#define TDX_SHARED_MEM_TEST_PRIVATE_GVA (0x80000000) +#define TDX_SHARED_MEM_TEST_VADDR_SHARED_MASK BIT_ULL(30) +#define TDX_SHARED_MEM_TEST_SHARED_GVA \ + (TDX_SHARED_MEM_TEST_PRIVATE_GVA | \ + TDX_SHARED_MEM_TEST_VADDR_SHARED_MASK) + +#define TDX_SHARED_MEM_TEST_PRIVATE_VALUE (100) +#define TDX_SHARED_MEM_TEST_SHARED_VALUE (200) +#define TDX_SHARED_MEM_TEST_DIFF_VALUE (1) + + +static uint64_t test_mem_private_gpa; +static uint64_t test_mem_shared_gpa; + +void guest_with_shared_mem(void) +{ + uint64_t *test_mem_shared_gva = + (uint64_t *)TDX_SHARED_MEM_TEST_SHARED_GVA; + + uint64_t *private_data, *shared_data; + uint64_t placeholder; + uint64_t failed_gpa; + uint64_t data; + int ret; + + /* Map gpa as shared */ + tdg_vp_vmcall_map_gpa(test_mem_shared_gpa, PAGE_SIZE, + &failed_gpa); + + shared_data = test_mem_shared_gva; + private_data = &data; + + *private_data = TDX_SHARED_MEM_TEST_PRIVATE_VALUE; + *shared_data = TDX_SHARED_MEM_TEST_SHARED_VALUE; + + ret = tdg_vp_vmcall_instruction_io(TDX_IOEXIT_TEST_PORT, 4, + PORT_WRITE, + private_data); + if (ret) + tdx_test_fatal_with_data(ret, __LINE__); + + /* Exit so host can read shared value */ + ret = tdg_vp_vmcall_instruction_io(TDX_IOEXIT_TEST_PORT, 4, + PORT_WRITE, + &placeholder); + if (ret) + tdx_test_fatal_with_data(ret, __LINE__); + + *private_data += TDX_SHARED_MEM_TEST_DIFF_VALUE; + *shared_data += TDX_SHARED_MEM_TEST_DIFF_VALUE; + + ret = tdg_vp_vmcall_instruction_io(TDX_IOEXIT_TEST_PORT, 4, + PORT_WRITE, + private_data); + if (ret) + tdx_test_fatal_with_data(ret, __LINE__); + + /* Exit so host can read shared value */ + ret = tdg_vp_vmcall_instruction_io(TDX_IOEXIT_TEST_PORT, 4, + PORT_WRITE, + &placeholder); + if (ret) + tdx_test_fatal_with_data(ret, __LINE__); + + tdx_test_success(); +} + +static void test_tdx_migrate_vm_with_shared_mem(void) +{ + uint32_t private_data; + vm_vaddr_t test_mem_private_gva; + uint32_t *test_mem_hva; + struct kvm_vm *src_vm; + struct kvm_vm *dst_vm; + struct kvm_vcpu *src_vcpu; + struct kvm_vcpu *dst_vcpu; + + printf("Verifying migration of a VM with shared memory:\n"); + + src_vm = td_create(); + td_initialize(src_vm, VM_MEM_SRC_ANONYMOUS, 0); + src_vcpu = td_vcpu_add(src_vm, 0, guest_with_shared_mem); + + /* + * Set up shared memory page for testing by first allocating as private + * and then mapping the same GPA again as shared. This way, the TD does + * not have to remap its page tables at runtime. + */ + test_mem_private_gva = vm_vaddr_alloc(src_vm, src_vm->page_size, + TDX_SHARED_MEM_TEST_PRIVATE_GVA); + TEST_ASSERT_EQ(test_mem_private_gva, TDX_SHARED_MEM_TEST_PRIVATE_GVA); + + test_mem_hva = addr_gva2hva(src_vm, test_mem_private_gva); + TEST_ASSERT(test_mem_hva != NULL, + "Guest address not found in guest memory regions\n"); + + test_mem_private_gpa = addr_gva2gpa(src_vm, test_mem_private_gva); + virt_map_shared(src_vm, TDX_SHARED_MEM_TEST_SHARED_GVA, + test_mem_private_gpa, 1); + + test_mem_shared_gpa = test_mem_private_gpa | src_vm->arch.s_bit; + sync_global_to_guest(src_vm, test_mem_shared_gpa); + + td_finalize(src_vm); + + dst_vm = td_create(); + tdx_enable_capabilities(dst_vm); + dst_vcpu = vm_vcpu_recreate(dst_vm, 0); + + vm_enable_cap(src_vm, KVM_CAP_EXIT_HYPERCALL, + BIT_ULL(KVM_HC_MAP_GPA_RANGE)); + + printf("Verifying shared memory accesses for TDX\n"); + + /* Begin guest execution; guest writes to shared memory. */ + printf("\t ... Starting guest execution\n"); + + /* Handle map gpa as shared */ + tdx_run(src_vcpu); + + tdx_run(src_vcpu); + tdx_test_assert_io(src_vcpu, TDX_IOEXIT_TEST_PORT, 4, PORT_WRITE); + TEST_ASSERT_EQ(*(uint32_t *)((void *)src_vcpu->run + + src_vcpu->run->io.data_offset), + TDX_SHARED_MEM_TEST_PRIVATE_VALUE); + + tdx_run(src_vcpu); + tdx_test_assert_io(src_vcpu, TDX_IOEXIT_TEST_PORT, 4, PORT_WRITE); + TEST_ASSERT_EQ(*test_mem_hva, TDX_SHARED_MEM_TEST_SHARED_VALUE); + + tdx_migrate_from(dst_vm, src_vm); + + kvm_vm_free(src_vm); + + tdx_run(dst_vcpu); + tdx_test_assert_io(dst_vcpu, TDX_IOEXIT_TEST_PORT, 4, + PORT_WRITE); + private_data = *(uint32_t *)((void *)dst_vcpu->run + + dst_vcpu->run->io.data_offset); + TEST_ASSERT_EQ(private_data, TDX_SHARED_MEM_TEST_PRIVATE_VALUE + + TDX_SHARED_MEM_TEST_DIFF_VALUE); + + tdx_run(dst_vcpu); + tdx_test_assert_io(dst_vcpu, TDX_IOEXIT_TEST_PORT, 4, + PORT_WRITE); + TEST_ASSERT_EQ(*test_mem_hva, TDX_SHARED_MEM_TEST_SHARED_VALUE + + TDX_SHARED_MEM_TEST_DIFF_VALUE); + + tdx_run(dst_vcpu); + tdx_test_assert_success(dst_vcpu); + + kvm_vm_free(dst_vm); + + printf("\t ... PASSED\n"); +} + +void guest_code_empty(void) +{ + tdx_test_success(); +} + +static void test_tdx_migrate_multiple_vms(void) +{ + struct kvm_vm *src_vm; + struct kvm_vm *dst_vms[NR_MIGRATE_TEST_VMS]; + int i, ret; + + printf("Verifying migration between multiple VMs:\n"); + + src_vm = td_create(); + td_initialize(src_vm, VM_MEM_SRC_ANONYMOUS, 0); + td_vcpu_add(src_vm, 0, guest_code_empty); + td_finalize(src_vm); + + for (i = 0; i < NR_MIGRATE_TEST_VMS; ++i) { + dst_vms[i] = td_create(); + tdx_enable_capabilities(dst_vms[i]); + vm_vcpu_recreate(dst_vms[i], 0); + } + + /* Initial migration from the src to the first dst. */ + tdx_migrate_from(dst_vms[0], src_vm); + + for (i = 1; i < NR_MIGRATE_TEST_VMS; i++) + tdx_migrate_from(dst_vms[i], dst_vms[i - 1]); + + /* Migrate the guest back to the original VM. */ + ret = __tdx_migrate_from(src_vm->fd, + dst_vms[NR_MIGRATE_TEST_VMS - 1]->fd); + TEST_ASSERT(ret == -1 && errno == EIO, + "VM that was migrated from should be dead. ret %d, errno: %d\n", + ret, errno); + + kvm_vm_free(src_vm); + for (i = 0; i < NR_MIGRATE_TEST_VMS; ++i) + kvm_vm_free(dst_vms[i]); + + printf("\t ... PASSED\n"); +} + +int main(int argc, char *argv[]) +{ + if (!is_tdx_enabled()) { + print_skip("TDX is not supported by the KVM"); + exit(KSFT_SKIP); + } + + run_in_new_process(&test_tdx_migrate_vm_with_private_memory); + run_in_new_process(&test_tdx_migrate_running_vm); + run_in_new_process(&test_tdx_migrate_vm_with_shared_mem); + run_in_new_process(&test_tdx_migrate_multiple_vms); + + return 0; +} From patchwork Wed Jun 11 21:16:37 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ryan Afranji X-Patchwork-Id: 895635 Received: from mail-pg1-f201.google.com (mail-pg1-f201.google.com [209.85.215.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C57FA2571DF for ; Wed, 11 Jun 2025 21:17:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749676635; cv=none; b=kPHQG7tweQsmy0y38zrnVppcG65oDO7oi5RQxFJ0l1TcZ9eq1taSVES1SNkvBe65gWVRW6MeRXzScyNH6v8qCi8K+rEDpa4WxfTXT8w3jk7lTIttMNyorWuHfJHOK2b1/Zt3qUwRG6Uphw8ENdtlTmPt02fx2N2tDTRqDX3V4EI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749676635; c=relaxed/simple; bh=lE6nUOIrKb4G9fRr4Fu+box3ulN6PQJB0SdDXJ5axmc=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=d+RrCMazLYeWB1KdfkgI/LQ4OmyIx+2D5Mpl8tTyN1hcQrK0E6BS7AKWASBlvetF3YLxz/MxgZ/Dt/jxe7wubzZFg4g4e3pHS39yaJLx0o/Fc2ZJj5JHbGRGg9mh2exMSq+RG4gG/CHTwqqyWe6b8XjMFtVKenKUQR2EIgk36G0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--afranji.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=clHW15g6; arc=none smtp.client-ip=209.85.215.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--afranji.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="clHW15g6" Received: by mail-pg1-f201.google.com with SMTP id 41be03b00d2f7-b26db9af463so199556a12.2 for ; Wed, 11 Jun 2025 14:17:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1749676632; x=1750281432; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=J6jtteubntBQN2p3kF5NvnYpZ0T18nDw91+DVALzqP8=; b=clHW15g6nMAIEe05bx4UKcajDHkPzhzaLIH8m1Lm3JVQKS8sNI3HtPuOqAHJ2q6lez rexYKQfTofnvda8gQCrwhgpqE+e7mv+TVAaGpAgKbYl/ImUv8m1xDNRCLqya2+BaFqJG fvNCLzhE1dVeUNbr+AZcHZuNijms3Z06GkDDk8YU4mfzQfZzT+b8jfHptiAZ/agPC2Qj b/GzOkzlE87s62fozUN6ZQjIoOO1peBlw7rHoRo1H0tw5gNafCRFVMqGX3S+XNW2LLzx q+xgnbzrb661auxG5942JE8jJKQ+HZpFGTLtvK25Xbshr0bqhTCqZ30Qmu+k7fNLfypU RRUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1749676632; x=1750281432; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=J6jtteubntBQN2p3kF5NvnYpZ0T18nDw91+DVALzqP8=; b=X5S5XkIkXw8wWSKCTKkN446ooC+x7g32c19xOv2Q6fr1pTWK+EYsRbFl8Z6yCSa4Za E2iNwS7HefCyaY1IA0WRBm7yYZaI2TPCtnGpmufSDz6W4IQ5OqjfhFeXNtjX2sNCAlOc yafpxh2QH36kze8Gdv50pchWkdYCIoGpCBQjG65/Qmyr7C86Bzn/E+hm8BP4PTpqSsz/ wU+JoIpJymWkCgTJq5fOJvoF7mmN2JnXCN79iqpc2USbRMnJGZBaEZsxhQMi9zYNkhUp rRODrsSRIkcz0mICBSTseatVsPgrjk8bmryFteYxutNaD54vH6fLULl9x2e7EM+2NJrF 02yA== X-Forwarded-Encrypted: i=1; AJvYcCW85QGVdMwfGyhKMXhe4j9JieYivDxorg7aZlVSkKNwb6ikZAdb2+hLX8Wj/OMhvC0gg/pm0R4TfVSolISxE1A=@vger.kernel.org X-Gm-Message-State: AOJu0YwvYVLzzLt2HBVMpsslLG41033O5ojRrze9vrGEDtouAR0LIjTC iH+f+G00eAd3mKNwrGaoEqUp/G1flX43WL7z/CtBCmot91Q9LynGYBTPRz3zPG0+auTyUEyMnpT vD8g15B38HA== X-Google-Smtp-Source: AGHT+IEr8iX/cIgcvsGYjAWSEZKLzLBYLe9qYqD9d7H++gZpo2ukhpqL6kw+SW8ooyNprJAB1kFZlg6/m6pQ X-Received: from pgdu24.prod.google.com ([2002:a05:6a02:2f58:b0:b2f:b096:8b5a]) (user=afranji job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a20:431c:b0:201:85f4:ade6 with SMTP id adf61e73a8af0-21f9b8f4694mr394232637.27.1749676632082; Wed, 11 Jun 2025 14:17:12 -0700 (PDT) Date: Wed, 11 Jun 2025 21:16:37 +0000 In-Reply-To: Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: X-Mailer: git-send-email 2.50.0.rc1.591.g9c95f17f64-goog Message-ID: Subject: [RFC PATCH v2 10/10] KVM: selftests: Add irqfd/interrupts test for TDX with migration From: Ryan Afranji To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org, x86@kernel.org Cc: sagis@google.com, bp@alien8.de, chao.p.peng@linux.intel.com, dave.hansen@linux.intel.com, dmatlack@google.com, erdemaktas@google.com, isaku.yamahata@intel.com, kai.huang@intel.com, mingo@redhat.com, pbonzini@redhat.com, seanjc@google.com, tglx@linutronix.de, zhi.wang.linux@gmail.com, ackerleytng@google.com, andrew.jones@linux.dev, david@redhat.com, hpa@zytor.com, kirill.shutemov@linux.intel.com, linux-kselftest@vger.kernel.org, tabba@google.com, vannapurve@google.com, yan.y.zhao@intel.com, rick.p.edgecombe@intel.com, Ryan Afranji From: Ackerley Tng Adds a selftest to verify interrupts sent to a TDX VM before migration are successfully handled by the migrated VM. Co-developed-by: Ryan Afranji Signed-off-by: Ryan Afranji Signed-off-by: Ackerley Tng --- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../testing/selftests/kvm/include/kvm_util.h | 4 + .../selftests/kvm/include/x86/tdx/tdx_util.h | 2 + .../selftests/kvm/include/x86/tdx/test_util.h | 5 + tools/testing/selftests/kvm/lib/kvm_util.c | 35 ++- .../selftests/kvm/lib/x86/tdx/tdx_util.c | 20 ++ .../selftests/kvm/lib/x86/tdx/test_util.c | 17 ++ .../kvm/x86/tdx_irqfd_migrate_test.c | 264 ++++++++++++++++++ .../selftests/kvm/x86/tdx_migrate_tests.c | 21 -- 9 files changed, 343 insertions(+), 26 deletions(-) create mode 100644 tools/testing/selftests/kvm/x86/tdx_irqfd_migrate_test.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index d4c8cfb5910f..4ae0d105c2a7 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -156,6 +156,7 @@ TEST_GEN_PROGS_x86 += x86/tdx_vm_test TEST_GEN_PROGS_x86 += x86/tdx_shared_mem_test TEST_GEN_PROGS_x86 += x86/tdx_upm_test TEST_GEN_PROGS_x86 += x86/tdx_migrate_tests +TEST_GEN_PROGS_x86 += x86/tdx_irqfd_migrate_test # Compiled outputs used by test targets TEST_GEN_PROGS_EXTENDED_x86 += x86/nx_huge_pages_test diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 8b252a668c78..f93ac2b9b0ff 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -80,6 +80,7 @@ enum kvm_mem_region_type { MEM_REGION_PT, MEM_REGION_TEST_DATA, MEM_REGION_TDX_BOOT_PARAMS, + MEM_REGION_TDX_SHARED_DATA, MEM_REGION_UCALL, NR_MEM_REGIONS, }; @@ -958,6 +959,9 @@ int _kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level); struct kvm_irq_routing *kvm_gsi_routing_create(void); void kvm_gsi_routing_irqchip_add(struct kvm_irq_routing *routing, uint32_t gsi, uint32_t pin); +void kvm_gsi_routing_msi_add(struct kvm_irq_routing *routing, uint32_t gsi, + uint32_t address_lo, uint32_t address_hi, + uint32_t data); int _kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing); void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing); diff --git a/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h index 9b495e621225..4393c8649718 100644 --- a/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h +++ b/tools/testing/selftests/kvm/include/x86/tdx/tdx_util.h @@ -10,6 +10,8 @@ extern uint64_t tdx_s_bit; void tdx_filter_cpuid(struct kvm_vm *vm, struct kvm_cpuid2 *cpuid_data); void __tdx_mask_cpuid_features(struct kvm_cpuid_entry2 *entry); void tdx_enable_capabilities(struct kvm_vm *vm); +int __tdx_migrate_from(int dst_fd, int src_fd); +void tdx_migrate_from(struct kvm_vm *dst_vm, struct kvm_vm *src_vm); struct kvm_vcpu *td_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, void *guest_code); diff --git a/tools/testing/selftests/kvm/include/x86/tdx/test_util.h b/tools/testing/selftests/kvm/include/x86/tdx/test_util.h index 3330d5a54698..0dd859974cb3 100644 --- a/tools/testing/selftests/kvm/include/x86/tdx/test_util.h +++ b/tools/testing/selftests/kvm/include/x86/tdx/test_util.h @@ -130,4 +130,9 @@ uint64_t tdx_test_read_64bit(struct kvm_vcpu *vcpu, uint64_t port); */ uint64_t tdx_test_read_64bit_report_from_guest(struct kvm_vcpu *vcpu); +/* + * Enables X2APIC for TDX guests. + */ +void tdx_guest_x2apic_enable(void); + #endif // SELFTEST_TDX_TEST_UTIL_H diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 9dc3c7bf0443..bbb489635064 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -1293,10 +1293,12 @@ static void vm_migrate_mem_region(struct kvm_vm *dst_vm, struct kvm_vm *src_vm, struct userspace_mem_region *src_region) { struct userspace_mem_region *dst_region; - int dst_guest_memfd; + int dst_guest_memfd = -1; - dst_guest_memfd = - vm_link_guest_memfd(dst_vm, src_region->region.guest_memfd, 0); + if (src_region->region.guest_memfd != -1) + dst_guest_memfd = vm_link_guest_memfd(dst_vm, + src_region->region.guest_memfd, + 0); dst_region = vm_mem_region_alloc( dst_vm, src_region->region.guest_phys_addr, @@ -1312,8 +1314,12 @@ static void vm_migrate_mem_region(struct kvm_vm *dst_vm, struct kvm_vm *src_vm, src_region->host_mem = 0; dst_region->region.guest_memfd = dst_guest_memfd; - dst_region->region.guest_memfd_offset = - src_region->region.guest_memfd_offset; + if (src_region->region.guest_memfd == -1) { + dst_region->fd = src_region->fd; + } else { + dst_region->region.guest_memfd_offset = + src_region->region.guest_memfd_offset; + } userspace_mem_region_commit(dst_vm, dst_region); } @@ -2057,6 +2063,25 @@ void kvm_gsi_routing_irqchip_add(struct kvm_irq_routing *routing, routing->nr++; } +void kvm_gsi_routing_msi_add(struct kvm_irq_routing *routing, uint32_t gsi, + uint32_t address_lo, uint32_t address_hi, + uint32_t data) +{ + int i; + + assert(routing); + assert(routing->nr < KVM_MAX_IRQ_ROUTES); + + i = routing->nr; + routing->entries[i].gsi = gsi; + routing->entries[i].type = KVM_IRQ_ROUTING_MSI; + routing->entries[i].flags = 0; + routing->entries[i].u.msi.address_lo = address_lo; + routing->entries[i].u.msi.address_hi = address_hi; + routing->entries[i].u.msi.data = data; + routing->nr++; +} + int _kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing) { int ret; diff --git a/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c b/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c index a3612bf187a0..8216a778474a 100644 --- a/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c +++ b/tools/testing/selftests/kvm/lib/x86/tdx/tdx_util.c @@ -372,6 +372,26 @@ static void tdx_apply_cr4_restrictions(struct kvm_sregs *sregs) sregs->cr4 &= ~(X86_CR4_VMXE | X86_CR4_SMXE); } +int __tdx_migrate_from(int dst_fd, int src_fd) +{ + struct kvm_enable_cap cap = { + .cap = KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM, + .args = { src_fd } + }; + + return ioctl(dst_fd, KVM_ENABLE_CAP, &cap); +} + +void tdx_migrate_from(struct kvm_vm *dst_vm, struct kvm_vm *src_vm) +{ + int ret; + + vm_migrate_mem_regions(dst_vm, src_vm); + ret = __tdx_migrate_from(dst_vm->fd, src_vm->fd); + TEST_ASSERT(!ret, "Migration failed, ret: %d, errno: %d\n", ret, errno); + src_vm->enc_migrated = true; +} + static void load_td_boot_code(struct kvm_vm *vm) { void *boot_code_hva = addr_gpa2hva(vm, FOUR_GIGABYTES_GPA - TD_BOOT_CODE_SIZE); diff --git a/tools/testing/selftests/kvm/lib/x86/tdx/test_util.c b/tools/testing/selftests/kvm/lib/x86/tdx/test_util.c index f92ddda2d1ac..7b622ccb2433 100644 --- a/tools/testing/selftests/kvm/lib/x86/tdx/test_util.c +++ b/tools/testing/selftests/kvm/lib/x86/tdx/test_util.c @@ -6,6 +6,7 @@ #include #include +#include "apic.h" #include "kvm_util.h" #include "tdx/tdcall.h" #include "tdx/tdx.h" @@ -185,3 +186,19 @@ uint64_t tdx_test_read_64bit_report_from_guest(struct kvm_vcpu *vcpu) { return tdx_test_read_64bit(vcpu, TDX_TEST_REPORT_PORT); } + +void tdx_guest_x2apic_enable(void) +{ + uint64_t x2apic_spiv = APIC_BASE_MSR + (APIC_SPIV >> 4); + uint64_t value, ret; + + /* + * x2apic does not have to be enabled for TDs, TDs already have x2apic + * enabled, and must use x2apic. Hence, we just soft-enable APIC. + */ + ret = tdg_vp_vmcall_instruction_rdmsr(x2apic_spiv, &value); + GUEST_ASSERT_EQ(ret, 0); + ret = tdg_vp_vmcall_instruction_wrmsr(x2apic_spiv, + value | APIC_SPIV_APIC_ENABLED); + GUEST_ASSERT_EQ(ret, 0); +} diff --git a/tools/testing/selftests/kvm/x86/tdx_irqfd_migrate_test.c b/tools/testing/selftests/kvm/x86/tdx_irqfd_migrate_test.c new file mode 100644 index 000000000000..d80cc204bd67 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/tdx_irqfd_migrate_test.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include + +#include "apic.h" +#include "kvm_util.h" +#include "processor.h" +#include "tdx/tdcall.h" +#include "tdx/tdx.h" +#include "tdx/tdx_util.h" +#include "tdx/test_util.h" +#include "test_util.h" +#include "ucall_common.h" + +#define TEST_IRQ_PIN 24 + +#define NUM_INTERRUPTS 256 +#define INTERRUPT_COUNT_GPA 0x100000000ULL +#define INTERRUPT_COUNT_MEMSLOT 5 + +#define MIGRATION_LOOPS 10 + +static uint32_t (*interrupt_count_per_vector)[NUM_INTERRUPTS]; + +static void interrupt_handler_increment_count(struct ex_regs *regs) +{ + (*interrupt_count_per_vector)[regs->vector]++; + x2apic_write_reg(APIC_EOI, 0); +} + +static void guest_code(void) +{ + uint32_t sync_count = 0; + + tdx_guest_x2apic_enable(); + + /* Enable interrupts which are disabled by default. */ + asm volatile("sti"); + + /* Keep guest runnable by continuously looping. */ + while (true) + GUEST_SYNC(++sync_count); +} + +/** + * gsi_route_add - Used to add a GSI route. + * + * @msi_redir_hint: Look up "Message Address Register Format" in Intel SDM + * @dest_mode: Look up "Message Address Register Format" in Intel SDM + * Use false for DM=0 and true for DM=1 + * @trig_mode: Look up "Message Data Register Format" in Intel SDM + * Use false for edge sensitive and true for level sensitive + * @delivery_mode: A 3 bit code: look up "Message Data Register Format" + * + * Add a route by building up the routing information in address_hi, address_lo + * and data according to how it is used in struct kvm_lapic_irq. For full + * details, look up how fields in struct kvm_lapic_irq are used. + * + * Return: None + */ +static void gsi_route_add(struct kvm_irq_routing *table, uint32_t gsi, + bool use_x2apic_format, uint32_t dest_id, + uint8_t vector, bool msi_redir_hint, bool dest_mode, + bool trig_mode, uint8_t delivery_mode) +{ + union { + struct { + u32 vector : 8, delivery_mode : 3, + dest_mode_logical : 1, reserved : 2, + active_low : 1, is_level : 1; + }; + uint32_t as_uint32; + } data = { 0 }; + union { + struct { + u32 reserved_0 : 2, dest_mode_logical : 1, + redirect_hint : 1, reserved_1 : 1, + virt_destid_8_14 : 7, destid_0_7 : 8, + base_address : 12; + }; + uint32_t as_uint32; + } address_lo = { 0 }; + union { + struct { + u32 reserved : 8, destid_8_31 : 24; + }; + uint32_t as_uint32; + } address_hi = { 0 }; + + /* Fixed 0xfee (see Intel SDM "Message Address Register Format") */ + address_lo.base_address = 0xfee; + + address_lo.destid_0_7 = dest_id & 0xff; + if (use_x2apic_format) + address_hi.destid_8_31 = (dest_id & 0xffffff00) >> 8; + + data.vector = vector; + address_lo.dest_mode_logical = dest_mode; + data.is_level = trig_mode; + data.delivery_mode = delivery_mode & 0b111; + address_lo.redirect_hint = msi_redir_hint; + + kvm_gsi_routing_msi_add(table, gsi, address_lo.as_uint32, + address_hi.as_uint32, data.as_uint32); +} + +/** + * Sets up KVM irqfd in @vm + * + * @gsi: irqchip pin toggled by this event + */ +static void set_irqfd(struct kvm_vm *vm, int fd, uint32_t gsi, bool assign) +{ + struct kvm_irqfd ifd = { + .fd = fd, + .gsi = gsi, + .flags = assign ? 0 : KVM_IRQFD_FLAG_DEASSIGN, + .resamplefd = 0, + }; + + vm_ioctl(vm, KVM_IRQFD, &ifd); +} + +static void setup_interrupt_count_per_vector(struct kvm_vm *vm) +{ + vm_vaddr_t gva; + int npages; + + npages = round_up(sizeof(*interrupt_count_per_vector), PAGE_SIZE); + vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, + INTERRUPT_COUNT_GPA, + INTERRUPT_COUNT_MEMSLOT, npages, 0); + vm->memslots[MEM_REGION_TDX_SHARED_DATA] = INTERRUPT_COUNT_MEMSLOT; + + gva = vm_vaddr_alloc_shared(vm, sizeof(*interrupt_count_per_vector), + KVM_UTIL_MIN_VADDR, + MEM_REGION_TDX_SHARED_DATA); + + interrupt_count_per_vector = addr_gva2hva(vm, gva); + memset(interrupt_count_per_vector, 0, + sizeof(*interrupt_count_per_vector)); + + write_guest_global(vm, interrupt_count_per_vector, + (uint32_t(*)[NUM_INTERRUPTS])gva); +} + +static void handle_vcpu_exit(struct kvm_vcpu *vcpu) +{ + struct ucall uc; + + switch (get_ucall(vcpu, &uc)) { + case UCALL_SYNC: + break; + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + default: + TEST_FAIL("Unexpected exit: %s", + exit_reason_str(vcpu->run->exit_reason)); + } +} + +void map_gsis_to_vectors(struct kvm_vm *vm, struct kvm_vcpu *vcpu, int *eventfds) +{ + struct kvm_irq_routing *table; + uint32_t vector_and_gsi; + int efd; + + /* Flush table first. */ + table = kvm_gsi_routing_create(); + kvm_gsi_routing_write(vm, table); + + /* Writing frees table, so we have to create another one. */ + table = kvm_gsi_routing_create(); + + /* Map vectors to gsis 1 to 1 */ + for (vector_and_gsi = 32; vector_and_gsi < NUM_INTERRUPTS; + ++vector_and_gsi) { + gsi_route_add(table, vector_and_gsi, + /*use_x2apic_format=*/true, + /*dest_id=*/vcpu->id, + /*vector=*/vector_and_gsi, + /*msi_redir_hint=*/false, + /*dest_mode=*/false, + /*trig_mode=*/false, + /*delivery_mode=*/0b000); + + efd = eventfd(0, EFD_NONBLOCK); + set_irqfd(vm, efd, vector_and_gsi, true); + + eventfds[vector_and_gsi] = efd; + } + + /* Configure KVM. Writing frees table. */ + kvm_gsi_routing_write(vm, table); + +} + +int main(int argc, char *argv[]) +{ + int eventfds[NUM_INTERRUPTS] = { 0 }; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + int vector, migration; + + TEST_REQUIRE(kvm_check_cap(KVM_CAP_SPLIT_IRQCHIP)); + + setbuf(stdout, NULL); + + vm = td_create(); + td_initialize(vm, VM_MEM_SRC_ANONYMOUS, 0); + + vcpu = td_vcpu_add(vm, 0, guest_code); + + for (vector = 0; vector < NUM_INTERRUPTS; ++vector) { + vm_install_exception_handler(vm, vector, + interrupt_handler_increment_count); + } + + setup_interrupt_count_per_vector(vm); + + td_finalize(vm); + + map_gsis_to_vectors(vm, vcpu, eventfds); + + tdx_run(vcpu); + handle_vcpu_exit(vcpu); + + for (migration = 0; migration < MIGRATION_LOOPS; ++migration) { + struct kvm_vcpu *next_vcpu; + struct kvm_vm *next_vm; + + next_vm = td_create(); + tdx_enable_capabilities(next_vm); + next_vcpu = vm_vcpu_recreate(next_vm, 0); + + /* Inject on source VM. */ + for (vector = 32; vector < NUM_INTERRUPTS; ++vector) + TEST_ASSERT_EQ(eventfd_write(eventfds[vector], 1), 0); + + map_gsis_to_vectors(next_vm, next_vcpu, eventfds); + + vcpu = next_vcpu; + + tdx_migrate_from(next_vm, vm); + kvm_vm_free(vm); + vm = next_vm; + + tdx_run(vcpu); + handle_vcpu_exit(vcpu); + + for (vector = 32; vector < NUM_INTERRUPTS; ++vector) + TEST_ASSERT_EQ((*interrupt_count_per_vector)[vector], + migration + 1); + } + + kvm_vm_free(vm); + for (vector = 32; vector < NUM_INTERRUPTS; ++vector) + close(eventfds[vector]); + return 0; +} diff --git a/tools/testing/selftests/kvm/x86/tdx_migrate_tests.c b/tools/testing/selftests/kvm/x86/tdx_migrate_tests.c index e15da2aa0437..498e42f37697 100644 --- a/tools/testing/selftests/kvm/x86/tdx_migrate_tests.c +++ b/tools/testing/selftests/kvm/x86/tdx_migrate_tests.c @@ -10,27 +10,6 @@ #define NR_MIGRATE_TEST_VMS 10 #define TDX_IOEXIT_TEST_PORT 0x50 -static int __tdx_migrate_from(int dst_fd, int src_fd) -{ - struct kvm_enable_cap cap = { - .cap = KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM, - .args = { src_fd } - }; - - return ioctl(dst_fd, KVM_ENABLE_CAP, &cap); -} - - -static void tdx_migrate_from(struct kvm_vm *dst_vm, struct kvm_vm *src_vm) -{ - int ret; - - vm_migrate_mem_regions(dst_vm, src_vm); - ret = __tdx_migrate_from(dst_vm->fd, src_vm->fd); - TEST_ASSERT(!ret, "Migration failed, ret: %d, errno: %d\n", ret, errno); - src_vm->enc_migrated = true; -} - void guest_code(void) { int ret;