From nobody Sun May 24 19:34:51 2026 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 709FE377019; Fri, 22 May 2026 17:26:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=193.142.43.55 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779470783; cv=none; b=fuwgHMNA22U0qC4uY72yS/4NR3uMV2WgP/FL+LMflPHKsdcNmmLg5khIi/ObKWGuT2+SLf1xnTfc9u/rUc0o5vDxeK/MXJ7Xg0i/4L2m/OQWcA6yssDkGKgoUH2BW9KLFQvUSYU42Uv3sGSQwe4aaBWEBT0w/3IKvj9bLYZf/h0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779470783; c=relaxed/simple; bh=NiDeZVXAWcZZK9soANVaoo5tcOvqGCbJX/wzWdnw3MU=; h=Date:From:To:Subject:Cc:In-Reply-To:References:MIME-Version: Message-ID:Content-Type; b=hQ//poEBCHhlnqC4wxXo0D4MSJk5lC8kDJlh7Et7kJNuO5Yz+Tqvclof5hBcYW4zK4X8ljM2RGi00m04WeCgEuJ7yKWFltw670J89U3TFMmmX2gVXohR/WBn2+aZ0icY6HChHgeR9CO0Ie/TbeHPJ7JKDwR8x/rNW8Xh5G1RNQE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de; spf=pass smtp.mailfrom=linutronix.de; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=dbkb7IAw; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=uJ4diuUA; arc=none smtp.client-ip=193.142.43.55 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linutronix.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="dbkb7IAw"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="uJ4diuUA" Date: Fri, 22 May 2026 17:26:18 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1779470780; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=KyOrVO/ctS9PTS8UOYihq8NiQPqbgIonOSjRPfPK8FM=; b=dbkb7IAwIIxXZhHCPcMdQx9Zd29ZgCJUQtEP2ZPr2tEqHN7cKljyk97ml812Ouc8B0PAE3 /0D2e3JC4vvgK6aL1b1Z0vkxOV5jNkaFkT31kMGg+zK3pbwWPtA3tPcusrpR5VwuadRiPa KxQfM8ddsR8mE27FCMidCiu87bcFFNOifpJlkLkBVGxZ616RRnrRvXJGpc3+YGik5anM2a 2pAr8HK///XXdHlClQwJahLVDL2FjcHzYSZBxMGZDGwNfmL3oSyb/mmCw+rNulO8LjtPiT edvt9sFR9TkAe+hbNCsTC4Qn7Pd2VaGYu3Gko1QHEeoxAjSSwwM3mPj7bG7bKg== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1779470780; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=KyOrVO/ctS9PTS8UOYihq8NiQPqbgIonOSjRPfPK8FM=; b=uJ4diuUAWgxKo6IsILUFfsFMiGoHJmpZExqdnxM1WZBUzvHQKBwMfHyPwMgJLDZ0cPQ0dX SblzppYJxb5KZiDQ== From: "tip-bot2 for Chao Gao" Sender: tip-bot2@linutronix.de Reply-to: linux-kernel@vger.kernel.org To: linux-tip-commits@vger.kernel.org Subject: [tip: x86/tdx] x86/virt/seamldr: Introduce skeleton for TDX module updates Cc: Chao Gao , Dave Hansen , Xu Yilun , Tony Lindgren , Kai Huang , "Kiryl Shutsemau (Meta)" , Rick Edgecombe , x86@kernel.org, linux-kernel@vger.kernel.org In-Reply-To: <20260520222902.A997E472@davehans-spike.ostc.intel.com> References: <20260520222902.A997E472@davehans-spike.ostc.intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <177947077858.711.8397162336249467639.tip-bot2@tip-bot2> Robot-ID: Robot-Unsubscribe: Contact to get blacklisted from these emails Precedence: bulk Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable The following commit has been merged into the x86/tdx branch of tip: Commit-ID: e16ce07a9053deb235e6d7a5702ba19ecbc27c67 Gitweb: https://git.kernel.org/tip/e16ce07a9053deb235e6d7a5702ba19ec= bc27c67 Author: Chao Gao AuthorDate: Wed, 20 May 2026 15:29:02 -07:00 Committer: Dave Hansen CommitterDate: Wed, 20 May 2026 15:37:10 -07:00 x86/virt/seamldr: Introduce skeleton for TDX module updates tl;dr: Use stop_machine() and a state machine based on the "MULTI_STOP" pattern to implement core TDX module update logic. Long version: TDX module updates require careful synchronization with other TDX operations. The requirements are (#1/#2 reflect current behavior that must be preserved): 1. SEAMCALLs need to be callable from both process and IRQ contexts. 2. SEAMCALLs need to be able to run concurrently across CPUs 3. During updates, only update-related SEAMCALLs are permitted; all other SEAMCALLs shouldn't be called. 4. During updates, all online CPUs must participate in the update work. No single lock primitive satisfies all requirements. For instance, rwlock_t handles #1/#2 but fails #4: CPUs spinning with IRQs disabled cannot be directed to perform update work. Use stop_machine() as it is the only well-understood mechanism that can meet all requirements. And TDX module updates consist of several steps (See Intel Trust Domain Extensions (Intel TDX) Module Base Architecture Specification, Chapter "TD-Preserving TDX module Update"). Ordering requirements between steps mandate lockstep synchronization across all CPUs. multi_cpu_stop() provides a good example of executing a multi-step task in lockstep across CPUs, but it does not synchronize the individual steps inside the callback itself. Implement a similar state machine as the skeleton for TDX module updates. Each state represents one step in the update flow, and the state advances only after all CPUs acknowledge completion of the current step. This acknowledgment mechanism provides the required lockstep execution. The update flow is intentionally simpler than multi_cpu_stop() in two ways: a) use a spinlock to protect the control data instead of atomic_t and explicit memory barriers. b) omit touch_nmi_watchdog() and rcu_momentary_eqs(), which exist there for debugging and are not strictly needed for this update flow Potential alternative to stop_machine() =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D An alternative approach is to lock all KVM entry points and kick all vCPUs. Here, KVM entry points refer to KVM VM/vCPU ioctl entry points, implemented in KVM common code (virt/kvm). Adding a locking mechanism there would affect all architectures KVM supports. And to lock only TDX vCPUs, new logic would be needed to identify TDX vCPUs, which the KVM common code currently lacks. This would add significant complexity and maintenance overhead to KVM for this TDX-specific use case, so don't take this approach. [ dhansen: normal changelog/style munging ] Signed-off-by: Chao Gao Signed-off-by: Dave Hansen Reviewed-by: Xu Yilun Reviewed-by: Tony Lindgren Reviewed-by: Kai Huang Reviewed-by: Kiryl Shutsemau (Meta) Reviewed-by: Rick Edgecombe Link: https://patch.msgid.link/20260520133909.409394-15-chao.gao@intel.com Link: https://patch.msgid.link/20260520222902.A997E472@davehans-spike.ostc.= intel.com --- arch/x86/virt/vmx/tdx/seamldr.c | 89 +++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamld= r.c index bcb1386..ec45b85 100644 --- a/arch/x86/virt/vmx/tdx/seamldr.c +++ b/arch/x86/virt/vmx/tdx/seamldr.c @@ -8,8 +8,10 @@ =20 #include #include +#include #include #include +#include =20 #include #include @@ -186,6 +188,85 @@ static int init_seamldr_params(struct seamldr_params *= params, return 0; } =20 +/* + * During a TDX module update, all CPUs start from MODULE_UPDATE_START and + * progress to MODULE_UPDATE_DONE. Each state is associated with certain + * work. For some states, just one CPU needs to perform the work, while + * other CPUs just wait during those states. + */ +enum module_update_state { + MODULE_UPDATE_START, + MODULE_UPDATE_DONE, +}; + +static struct update_ctrl { + enum module_update_state state; + int num_ack; + /* + * Protect update_ctrl. Raw spinlock as it will be acquired from + * interrupt-disabled contexts. + */ + raw_spinlock_t lock; +} update_ctrl; + +/* Called with ctrl->lock held or during initialization. */ +static void __set_target_state(struct update_ctrl *ctrl, + enum module_update_state newstate) +{ + /* Reset ack counter. */ + ctrl->num_ack =3D 0; + ctrl->state =3D newstate; +} + +/* Last one to ack a state moves to the next state. */ +static void ack_state(struct update_ctrl *ctrl) +{ + raw_spin_lock(&ctrl->lock); + + ctrl->num_ack++; + if (ctrl->num_ack =3D=3D num_online_cpus()) + __set_target_state(ctrl, ctrl->state + 1); + + raw_spin_unlock(&ctrl->lock); +} + +static void init_state(struct update_ctrl *ctrl) +{ + raw_spin_lock_init(&ctrl->lock); + __set_target_state(ctrl, MODULE_UPDATE_START + 1); +} + +/* + * See multi_cpu_stop() from where this multi-cpu state-machine was + * adopted. + */ +static int do_seamldr_install_module(void *seamldr_params) +{ + enum module_update_state curstate =3D MODULE_UPDATE_START; + enum module_update_state newstate; + int ret =3D 0; + + do { + newstate =3D READ_ONCE(update_ctrl.state); + + if (curstate =3D=3D newstate) { + cpu_relax(); + continue; + } + + curstate =3D newstate; + switch (curstate) { + /* TODO: add the update steps. */ + default: + break; + } + + ack_state(&update_ctrl); + } while (curstate !=3D MODULE_UPDATE_DONE); + + return ret; +} + /** * seamldr_install_module - Install a new TDX module. * @data: Pointer to the TDX module image. @@ -217,7 +298,13 @@ int seamldr_install_module(const u8 *data, u32 data_le= n) if (ret) goto out; =20 - /* TODO: Update TDX module here */ + /* Ensure a stable set of online CPUs for the update process. */ + cpus_read_lock(); + init_state(&update_ctrl); + ret =3D stop_machine_cpuslocked(do_seamldr_install_module, params, + cpu_online_mask); + cpus_read_unlock(); + out: kfree(params); return ret;