* Re: [PATCH v2 16/21] x86/virt/seamldr: Handle TDX Module update failures
From: Xu Yilun @ 2026-01-15 6:24 UTC (permalink / raw)
To: Chao Gao
Cc: linux-coco, linux-kernel, x86, reinette.chatre, ira.weiny,
kai.huang, dan.j.williams, sagis, vannapurve, paulmck,
nik.borisov, Farrah Chen, Kirill A. Shutemov, Dave Hansen,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, H. Peter Anvin
In-Reply-To: <20251001025442.427697-17-chao.gao@intel.com>
On Tue, Sep 30, 2025 at 07:53:00PM -0700, Chao Gao wrote:
> Failures encountered after a successful module shutdown are unrecoverable,
> e.g., there is no way to restore the old TDX Module.
"e.g." is obscure. To me, the following sentence is explaining the
reason why the failure is not recoverable. Maybe "i.e." or "because"?
^ permalink raw reply
* Re: [PATCH v2 17/21] x86/virt/seamldr: Install a new TDX Module
From: Xu Yilun @ 2026-01-15 6:15 UTC (permalink / raw)
To: Chao Gao
Cc: linux-coco, linux-kernel, x86, reinette.chatre, ira.weiny,
kai.huang, dan.j.williams, sagis, vannapurve, paulmck,
nik.borisov, Farrah Chen, Kirill A. Shutemov, Dave Hansen,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, H. Peter Anvin
In-Reply-To: <20251001025442.427697-18-chao.gao@intel.com>
> static int do_seamldr_install_module(void *params)
> {
> + struct tdx_module_args args = { .rcx = __pa(params) };
Is it better we put the definition, or at least the value assignment in
case TDP_CPU_INSTALL? This pattern always appears here for a seamcall
wrapper but this function is far more complex than that.
And the .rcx = __pa(params) also confuse me a bit. Better we name it
e.g. seamldr_params which looks reasonable for seamcall arguments.
> enum tdp_state newstate, curstate = TDP_START;
> int cpu = smp_processor_id();
> bool primary;
> @@ -297,6 +302,10 @@ static int do_seamldr_install_module(void *params)
> if (primary)
> ret = tdx_module_shutdown();
> break;
> + case TDP_CPU_INSTALL:
> + scoped_guard(raw_spinlock, &seamldr_lock)
> + ret = seamldr_call(P_SEAMLDR_INSTALL, &args);
> + break;
> default:
> break;
> }
> --
> 2.47.3
>
^ permalink raw reply
* Re: [PATCH v2 16/21] x86/virt/seamldr: Handle TDX Module update failures
From: Xu Yilun @ 2026-01-15 5:37 UTC (permalink / raw)
To: Chao Gao
Cc: linux-coco, linux-kernel, x86, reinette.chatre, ira.weiny,
kai.huang, dan.j.williams, sagis, vannapurve, paulmck,
nik.borisov, Farrah Chen, Kirill A. Shutemov, Dave Hansen,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, H. Peter Anvin
In-Reply-To: <aQAwRrvYMcaMsu02@intel.com>
> static void print_update_failure_message(void)
> @@ -331,7 +323,7 @@ static int do_seamldr_install_module(void *params)
> touch_nmi_watchdog();
> rcu_momentary_eqs();
> }
> - } while (curstate != TDP_DONE);
> + } while (curstate != TDP_DONE && !atomic_read(&tdp_data.failed));
Ah, yes. That's idea of immediate error out I'm thinking of, your
implementation is better.
>
> return ret;
> }
>
^ permalink raw reply
* Re: [PATCH v2 04/21] x86/virt/tdx: Prepare to support P-SEAMLDR SEAMCALLs
From: Chao Gao @ 2026-01-15 5:37 UTC (permalink / raw)
To: Duan, Zhenzhong
Cc: linux-coco, linux-kernel, x86, reinette.chatre, ira.weiny,
kai.huang, dan.j.williams, yilun.xu, sagis, vannapurve, paulmck,
nik.borisov, Farrah Chen, Kirill A. Shutemov, Dave Hansen,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, H. Peter Anvin
In-Reply-To: <193df77d-4034-4ebc-8c39-9f01ec9936a6@intel.com>
>> diff --git a/arch/x86/virt/vmx/tdx/seamcall.h b/arch/x86/virt/vmx/tdx/seamcall.h
>> index 71b6ffddfa40..3f462e58d68e 100644
>> --- a/arch/x86/virt/vmx/tdx/seamcall.h
>> +++ b/arch/x86/virt/vmx/tdx/seamcall.h
>> @@ -14,6 +14,19 @@ u64 __seamcall_saved_ret(u64 fn, struct tdx_module_args *args);
>> typedef u64 (*sc_func_t)(u64 fn, struct tdx_module_args *args);
>> +static inline bool is_seamldr_call(u64 fn)
>> +{
>> + return fn & SEAMLDR_SEAMCALL_MASK;
>> +}
>> +
>> +static inline bool sc_need_retry(u64 fn, u64 error_code)
>> +{
>> + if (is_seamldr_call(fn))
>
>Do we need to have this check, can TDX module seamcall return
>
>value SEAMLDR_RND_NO_ENTROPY but for different reason?
Currently, The TDX module doesn't return SEAMLDR_RND_NO_ENTROPY for any reason,
and I think it probably won't in the future. But it isn't ruled out by the
spec. It's the same situation for P-SEAMLDR returning TDX_RND_NO_ENTROPY. So I
slightly prefer to keep this check.
^ permalink raw reply
* Re: [PATCH] KVM: TDX: Allow userspace to return errors to guest for MAPGPA
From: Sagi Shahar @ 2026-01-15 1:22 UTC (permalink / raw)
To: Sean Christopherson
Cc: Xiaoyao Li, Paolo Bonzini, Dave Hansen, Kiryl Shutsemau,
Rick Edgecombe, Thomas Gleixner, Borislav Petkov, H. Peter Anvin,
x86, kvm, linux-kernel, linux-coco, Vishal Annapurve,
Michael Roth
In-Reply-To: <aWgPFQOQRr3xcMjh@google.com>
On Wed, Jan 14, 2026 at 3:48 PM Sean Christopherson <seanjc@google.com> wrote:
>
> +Mike
>
> On Wed, Jan 14, 2026, Sean Christopherson wrote:
> > On Wed, Jan 14, 2026, Xiaoyao Li wrote:
> > > On 1/14/2026 8:30 AM, Sagi Shahar wrote:
> > > So it needs to be
> > >
> > > if (vcpu->run->hypercall.ret == -EBUSY)
> > > tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
> > > else
> > > tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
> >
> > No, because assuming everything except -EBUSY translates to
> > TDVMCALL_STATUS_INVALID_OPERAND paints KVM back into the same corner its already
> > in. What I care most about is eliminating KVM's assumption that a non-zero
> > hypercall.ret means TDVMCALL_STATUS_INVALID_OPERAND.
> >
> > For the new ABI, I see two options:
> >
> > 1. Translate -errno as done in this patch.
> > 2. Propagate hypercall.ret directly to the TDVMCALL return code, i.e. let
> > userspace set any return code it wants.
> >
> > #1 has the downside of needing KVM changes and new uAPI every time a new return
> > code is supported.
> >
> > #2 has the downside of preventing KVM from establishing its own ABI around the
> > return code, and making the return code vendor specific. E.g. if KVM ever wanted
> > to do something in response to -EBUSY beyond propagating the error to the guest,
> > then we can't reasonably do that with #2.
> >
> > Whatever we do, I want to change snp_complete_psc_msr() and snp_complete_one_psc()
> > in the same patch, so that whatever ABI we establish is common to TDX and SNP.
> >
> > See also https://lore.kernel.org/all/Zn8YM-s0TRUk-6T-@google.com.
>
> Aha! Finally. I *knew* we had discussed this more recently. The SNP series to
> add KVM_EXIT_SNP_REQ_CERTS uses a similar pattern. Note its intentional use of
> positive values, because that's what userspace sees in errno. This code should
> do the same. Oh, and we need to choose between EAGAIN and EBUSY...
>
> switch (READ_ONCE(vcpu->run->snp_req_certs.ret)) {
> case 0:
> return snp_handle_guest_req(svm, control->exit_info_1,
> control->exit_info_2);
> case ENOSPC:
> vcpu->arch.regs[VCPU_REGS_RBX] = vcpu->run->snp_req_certs.npages;
> return snp_req_certs_err(svm, SNP_GUEST_VMM_ERR_INVALID_LEN);
> case EAGAIN:
> return snp_req_certs_err(svm, SNP_GUEST_VMM_ERR_BUSY);
> case EIO:
> return snp_req_certs_err(svm, SNP_GUEST_VMM_ERR_GENERIC);
> default:
> break;
> }
>
I think EAGAIN makes more sense semantically in this case. So
something like this?
- tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
+ if (vcpu->run->hypercall.ret == EAGAIN)
+ tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
+ else if (vcpu->run->hypercall.ret == EINVAL)
+ tdvmcall_set_return_code(vcpu,
TDVMCALL_STATUS_INVALID_OPERAND);
+ else
+ return -EINVAL;
+
>
> https://lore.kernel.org/all/20260109231732.1160759-2-michael.roth@amd.com
^ permalink raw reply
* Re: [PATCH] KVM: TDX: Allow userspace to return errors to guest for MAPGPA
From: Sagi Shahar @ 2026-01-15 1:21 UTC (permalink / raw)
To: Sean Christopherson
Cc: Xiaoyao Li, Paolo Bonzini, Dave Hansen, Kiryl Shutsemau,
Rick Edgecombe, Thomas Gleixner, Borislav Petkov, H. Peter Anvin,
x86, kvm, linux-kernel, linux-coco, Vishal Annapurve
In-Reply-To: <aWe8zESCJ0ZeAOT3@google.com>
On Wed, Jan 14, 2026 at 9:57 AM Sean Christopherson <seanjc@google.com> wrote:
>
> On Wed, Jan 14, 2026, Xiaoyao Li wrote:
> > On 1/14/2026 8:30 AM, Sagi Shahar wrote:
> > > From: Vishal Annapurve <vannapurve@google.com>
> > >
> > > MAPGPA request from TDX VMs gets split into chunks by KVM using a loop
> > > of userspace exits until the complete range is handled.
> > >
> > > In some cases userspace VMM might decide to break the MAPGPA operation
> > > and continue it later. For example: in the case of intrahost migration
> > > userspace might decide to continue the MAPGPA operation after the
> > > migrration is completed
>
> migration
>
> > > Allow userspace to signal to TDX guests that the MAPGPA operation should
> > > be retried the next time the guest is scheduled.
>
> To Xiaoyao's point, changes like this either need new uAPI, or a detailed
> explanation in the changelog of why such uAPI isn't deemed necessary.
>
> > > Signed-off-by: Vishal Annapurve <vannapurve@google.com>
> > > Co-developed-by: Sagi Shahar <sagis@google.com>
> > > Signed-off-by: Sagi Shahar <sagis@google.com>
> > > ---
> > > arch/x86/kvm/vmx/tdx.c | 8 +++++++-
> > > 1 file changed, 7 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
> > > index 2d7a4d52ccfb..3244064b1a04 100644
> > > --- a/arch/x86/kvm/vmx/tdx.c
> > > +++ b/arch/x86/kvm/vmx/tdx.c
> > > @@ -1189,7 +1189,13 @@ static int tdx_complete_vmcall_map_gpa(struct kvm_vcpu *vcpu)
> > > struct vcpu_tdx *tdx = to_tdx(vcpu);
> > > if (vcpu->run->hypercall.ret) {
> > > - tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
> > > + if (vcpu->run->hypercall.ret == -EBUSY)
> > > + tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
> > > + else if (vcpu->run->hypercall.ret == -EINVAL)
> > > + tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
> > > + else
> > > + return -EINVAL;
> >
> > It's incorrect to return -EINVAL here.
>
> It's not incorrect, just potentially a breaking change.
>
> > The -EINVAL will eventually be
> > returned to userspace for the VCPU_RUN ioctl. It certainly breaks userspace.
>
> It _might_ break userspace. It certainly changes KVM's ABI, but if no userspace
> actually utilizes the existing ABI, then userspace hasn't been broken.
>
> And unless I'm missing something, QEMU _still_ doesn't set hypercall.ret. E.g.
> see this code in __tdx_map_gpa().
>
> /*
> * In principle this should have been -KVM_ENOSYS, but userspace (QEMU <=9.2)
> * assumed that vcpu->run->hypercall.ret is never changed by KVM and thus that
> * it was always zero on KVM_EXIT_HYPERCALL. Since KVM is now overwriting
> * vcpu->run->hypercall.ret, ensuring that it is zero to not break QEMU.
> */
> tdx->vcpu.run->hypercall.ret = 0;
>
> AFAICT, QEMU kills the VM if anything goes wrong.
>
> So while I initially had the exact same reaction of "this is a breaking change
> and needs to be opt-in", we might actually be able to get away with just making
> the change (assuming no other VMMs care, or are willing to change themselves).
Is there a better source of truth for whether QEMU uses hypercall.ret
or just point to this comment in the commit message.
>
> > So it needs to be
> >
> > if (vcpu->run->hypercall.ret == -EBUSY)
> > tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
> > else
> > tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
>
> No, because assuming everything except -EBUSY translates to
> TDVMCALL_STATUS_INVALID_OPERAND paints KVM back into the same corner its already
> in. What I care most about is eliminating KVM's assumption that a non-zero
> hypercall.ret means TDVMCALL_STATUS_INVALID_OPERAND.
>
> For the new ABI, I see two options:
>
> 1. Translate -errno as done in this patch.
> 2. Propagate hypercall.ret directly to the TDVMCALL return code, i.e. let
> userspace set any return code it wants.
>
> #1 has the downside of needing KVM changes and new uAPI every time a new return
> code is supported.
>
> #2 has the downside of preventing KVM from establishing its own ABI around the
> return code, and making the return code vendor specific. E.g. if KVM ever wanted
> to do something in response to -EBUSY beyond propagating the error to the guest,
> then we can't reasonably do that with #2.
>
> Whatever we do, I want to change snp_complete_psc_msr() and snp_complete_one_psc()
> in the same patch, so that whatever ABI we establish is common to TDX and SNP.
>
> See also https://lore.kernel.org/all/Zn8YM-s0TRUk-6T-@google.com.
>
> > But I'm not sure if such change breaks the userspace ABI that if needs to be
> > opted-in.
^ permalink raw reply
* Re: [PATCH v2 2/2] mm/memory_hotplug: Add support to unaccept memory after hot-remove
From: Pratik R. Sampat @ 2026-01-14 22:56 UTC (permalink / raw)
To: Kiryl Shutsemau
Cc: linux-mm, linux-coco, x86, linux-kernel, tglx, mingo, bp,
dave.hansen, ardb, akpm, david, osalvador, thomas.lendacky,
michael.roth
In-Reply-To: <aWdwIvzPDqY21cbS@thinkstation>
On 1/14/26 4:47 AM, Kiryl Shutsemau wrote:
> On Tue, Jan 13, 2026 at 12:22:33PM -0600, Pratik R. Sampat wrote:
>>
>>
>> On 1/13/26 11:53 AM, Kiryl Shutsemau wrote:
>>> On Tue, Jan 13, 2026 at 11:10:21AM -0600, Pratik R. Sampat wrote:
>>>>
>>>>
>>>> On 1/13/2026 4:28 AM, Kiryl Shutsemau wrote:
>>>>> On Mon, Jan 12, 2026 at 02:23:00PM -0600, Pratik R. Sampat wrote:
>>>>>> Transition memory to the shared state during a hot-remove operation so
>>>>>> that it can be re-used by the hypervisor. This also applies when memory
>>>>>> is intended to be hotplugged back in later, as those pages will need to
>>>>>> be re-accepted after crossing the trust boundary.
>>>>>
>>>>> Hm. What happens when we hot-remove memory that was there at the boot
>>>>> and there's bitmap space for it?
>>>>>
>>>>
>>>> While hotplug ranges gotten from SRAT don't seem to overlap with the
>>>> conventional ranges in the unaccepted table, EFI_MEMORY_HOT_PLUGGABLE
>>>> attribute could indicate boot time memory that could be hot-removed. I
>>>> could potentially unset the bitmap first, if the bit exists and then
>>>> unaccept.
>>>>
>>>> Similarly, I could also check if the bitmap is large enough to set the
>>>> bit before I call arch_accept_memory() (This may not really be needed
>>>> though).
>>>>
>>>>> Also, I'm not sure why it is needed. At least in TDX case, VMM can pull
>>>>> the memory from under guest at any time without a warning. Coverting
>>>>> memory to shared shouldn't make a difference as along as re-adding the
>>>>> same GPA range triggers accept.
>>>>>
>>>>
>>>> That makes sense. The only scenario where we could run into trouble on
>>>> SNP platforms is when we redo a qemu device_add after a device_del
>>>> without first removing the memory object entirely since same-state
>>>> transitions result in guest termination.
>>>>
>>>> This means we must always follow a device_del with an object_del on
>>>> removal. Otherwise, the onus would then be on the VMM to transition
>>>> the memory back to shared before re-adding it to the guest.
>>>
>>> This seems to be one-of-many possible ways of VMM to get guest terminated.
>>> DoS is not in something confidential computing aims to prevent.
>>>
>>>> However, if this flow is not a concern to begin with then I could
>>>> probably just drop this patch?
>>>
>>> Yes, please.
>>
>> Putting more thought into it, memory unacceptance on remove may be required
>> after all at least for SNP platforms.
>>
>> Consider a scenario:
>> * Guest accepts a GPA say G1, mapped to a host physical address H1.
>> * We attempt to hot-remove the memory. If the guest does not unaccept the memory
>> now then G1 to H1 mapping within the RMP will still exist.
>> * Then if the hypervisor later hot-adds the memory to G1, it will be now mapped
>> to H3 and this new mapping will be accepted.
>>
>> This will essentially mean that we have 2 RMP entries: One for H1 and another
>> for H3 mapped for G1 which are both validated / accepted which can then be
>> swapped at will and compromise integrity.
>
> I don't know much about SEV, but I assume RMP is similar to PAMT in TDX
> where TDX module maintains metadata for host physical memory.
>
> What side problems do you for guest here?
>
> I probably miss something, but it seems to be VMM problem, no? I mean if
> VMM doesn't update RMP on replacing one HPA to another for the GPA, it
> is bug in VMM housekeeping. Guest is not responsible for this.
>
Right, the problem is that we do not inherently trust the host to make change.
That is why in my understanding, the guest is responsible for validating and
rescinding those pages.
--Pratik
^ permalink raw reply
* Re: [PATCH v2 1/2] mm/memory_hotplug: Add support to accept memory during hot-add
From: Pratik R. Sampat @ 2026-01-14 22:56 UTC (permalink / raw)
To: David Hildenbrand (Red Hat), linux-mm, linux-coco, x86,
linux-kernel
Cc: tglx, mingo, bp, dave.hansen, kas, ardb, akpm, osalvador,
thomas.lendacky, michael.roth
In-Reply-To: <4f2c6106-9cc1-4196-8b20-83b297ae7195@kernel.org>
On 1/14/26 4:30 AM, David Hildenbrand (Red Hat) wrote:
> On 1/12/26 21:22, Pratik R. Sampat wrote:
>> Confidential computing guests require memory to be accepted before use.
>> The unaccepted memory bitmap maintained by firmware does not track
>> hotplugged memory ranges.
>>
>> Call arch_accept_memory() during the hot-add path to explicitly validate
>> and transition the newly added memory to a private state, making it
>> usable by the guest.
>>
>> Signed-off-by: Pratik R. Sampat <prsampat@amd.com>
>> ---
>> mm/memory_hotplug.c | 4 ++++
>> 1 file changed, 4 insertions(+)
>>
>> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
>> index a63ec679d861..8cfbf0541430 100644
>> --- a/mm/memory_hotplug.c
>> +++ b/mm/memory_hotplug.c
>> @@ -38,6 +38,7 @@
>> #include <linux/node.h>
>> #include <asm/tlbflush.h>
>> +#include <asm/unaccepted_memory.h>
>> #include "internal.h"
>> #include "shuffle.h"
>> @@ -1567,6 +1568,9 @@ int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
>> if (!strcmp(res->name, "System RAM"))
>> firmware_map_add_hotplug(start, start + size, "System RAM");
>> + if (IS_ENABLED(CONFIG_UNACCEPTED_MEMORY))
>> + arch_accept_memory(start, start + size);
>> +
>> /* device_online() will take the lock when calling online_pages() */
>> mem_hotplug_done();
>>
>
> As discussed, for things like virtio-mem or the HV-balloon this might be the wrong thing to do, but I don't expect these mechanisms to be used in CoCo environments just yet (and doing so would require enabling work for them).
>
Ack.
> So I'm fine with this for now.
>
Thanks!
^ permalink raw reply
* Re: [PATCH] KVM: TDX: Allow userspace to return errors to guest for MAPGPA
From: Sean Christopherson @ 2026-01-14 21:48 UTC (permalink / raw)
To: Xiaoyao Li
Cc: Sagi Shahar, Paolo Bonzini, Dave Hansen, Kiryl Shutsemau,
Rick Edgecombe, Thomas Gleixner, Borislav Petkov, H. Peter Anvin,
x86, kvm, linux-kernel, linux-coco, Vishal Annapurve,
Michael Roth
In-Reply-To: <aWe8zESCJ0ZeAOT3@google.com>
+Mike
On Wed, Jan 14, 2026, Sean Christopherson wrote:
> On Wed, Jan 14, 2026, Xiaoyao Li wrote:
> > On 1/14/2026 8:30 AM, Sagi Shahar wrote:
> > So it needs to be
> >
> > if (vcpu->run->hypercall.ret == -EBUSY)
> > tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
> > else
> > tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
>
> No, because assuming everything except -EBUSY translates to
> TDVMCALL_STATUS_INVALID_OPERAND paints KVM back into the same corner its already
> in. What I care most about is eliminating KVM's assumption that a non-zero
> hypercall.ret means TDVMCALL_STATUS_INVALID_OPERAND.
>
> For the new ABI, I see two options:
>
> 1. Translate -errno as done in this patch.
> 2. Propagate hypercall.ret directly to the TDVMCALL return code, i.e. let
> userspace set any return code it wants.
>
> #1 has the downside of needing KVM changes and new uAPI every time a new return
> code is supported.
>
> #2 has the downside of preventing KVM from establishing its own ABI around the
> return code, and making the return code vendor specific. E.g. if KVM ever wanted
> to do something in response to -EBUSY beyond propagating the error to the guest,
> then we can't reasonably do that with #2.
>
> Whatever we do, I want to change snp_complete_psc_msr() and snp_complete_one_psc()
> in the same patch, so that whatever ABI we establish is common to TDX and SNP.
>
> See also https://lore.kernel.org/all/Zn8YM-s0TRUk-6T-@google.com.
Aha! Finally. I *knew* we had discussed this more recently. The SNP series to
add KVM_EXIT_SNP_REQ_CERTS uses a similar pattern. Note its intentional use of
positive values, because that's what userspace sees in errno. This code should
do the same. Oh, and we need to choose between EAGAIN and EBUSY...
switch (READ_ONCE(vcpu->run->snp_req_certs.ret)) {
case 0:
return snp_handle_guest_req(svm, control->exit_info_1,
control->exit_info_2);
case ENOSPC:
vcpu->arch.regs[VCPU_REGS_RBX] = vcpu->run->snp_req_certs.npages;
return snp_req_certs_err(svm, SNP_GUEST_VMM_ERR_INVALID_LEN);
case EAGAIN:
return snp_req_certs_err(svm, SNP_GUEST_VMM_ERR_BUSY);
case EIO:
return snp_req_certs_err(svm, SNP_GUEST_VMM_ERR_GENERIC);
default:
break;
}
https://lore.kernel.org/all/20260109231732.1160759-2-michael.roth@amd.com
^ permalink raw reply
* Re: SVSM Development Call January 14, 2026
From: Stefano Garzarella @ 2026-01-14 17:23 UTC (permalink / raw)
To: coconut-svsm, linux-coco; +Cc: Jörg Rödel
In-Reply-To: <tlhepq4dp47rbq3vpzm4qtf65t62fqe26tk736sd3ulcbwcefy@btzt3fsapu5e>
On Tue, 13 Jan 2026 at 19:36, Jörg Rödel <joro@8bytes.org> wrote:
>
> Hi,
>
> Here is the call for agenda items for this weeks SVSM development call. Please
> send any agenda items you have in mind as a reply to this email or raise them
> in the meeting.
>
> Stefano will run the meeting tomorrow (Thanks again!) as I can not attend due
> to travel.
>
> We will use the LF Zoom instance. Details of the meeting can be found in our
> governance repository at:
>
> https://github.com/coconut-svsm/governance
>
> The link to the COCONUT-SVSM calendar is:
>
> https://zoom-lfx.platform.linuxfoundation.org/meetings/coconut-svsm?view=week
>
> The meeting will be recorded and the recording eventually published.
As I mentioned in the call, QEMU community is open to host some
COCONUT SVSM projects for GSoC 2026.
You can find more details at
https://lore.kernel.org/qemu-devel/CAJSP0QVXXX7GV5W4nj7kP35x_4gbF2nG1G1jdh9Q=XgSx=nX3A@mail.gmail.com/
Also, you can find proposals from the past years here:
https://wiki.qemu.org/Category:GSoC
Our goal is to come up with a couple of projects related to COCONUT
SVSM by next week, since the deadline to post them to QEMU is Jan 30.
If you have any idea in mind, please share with us :-)
Thanks,
Stefano
^ permalink raw reply
* Re: [PATCH 1/1] PCI/IDE: Fix reading a wrong reg for unused sel stream initialization
From: dan.j.williams @ 2026-01-14 16:15 UTC (permalink / raw)
To: Bjorn Helgaas, Li Ming
Cc: dan.j.williams, linux-pci, linux-coco, linux-kernel
In-Reply-To: <20260113191003.GA776139@bhelgaas>
Bjorn Helgaas wrote:
> On Sun, Jan 11, 2026 at 03:38:23PM +0800, Li Ming wrote:
> > During pci_ide_init(), it will write PCI_ID_RESERVED_STREAM_ID into all
> > unused selective IDE stream blocks. In a selective IDE stream block, IDE
> > stream ID field is in selective IDE stream control register instead of
> > selective IDE stream capability register.
> >
> > Fixes: 079115370d00 ("PCI/IDE: Initialize an ID for all IDE streams")
> > Signed-off-by: Li Ming <ming.li@zohomail.com>
>
> Acked-by: Bjorn Helgaas <bhelgaas@google.com>
>
> Dan, I assume you'll take this? It looks like you've merged
> everything to do with ide.c.
Yes, I have cleared some CXL backlog from over the holidays and will get
this queued.
^ permalink raw reply
* Re: [PATCH] KVM: TDX: Allow userspace to return errors to guest for MAPGPA
From: Sean Christopherson @ 2026-01-14 15:57 UTC (permalink / raw)
To: Xiaoyao Li
Cc: Sagi Shahar, Paolo Bonzini, Dave Hansen, Kiryl Shutsemau,
Rick Edgecombe, Thomas Gleixner, Borislav Petkov, H. Peter Anvin,
x86, kvm, linux-kernel, linux-coco, Vishal Annapurve
In-Reply-To: <43a0558a-4cca-4d9c-97dc-ffd085186fd9@intel.com>
On Wed, Jan 14, 2026, Xiaoyao Li wrote:
> On 1/14/2026 8:30 AM, Sagi Shahar wrote:
> > From: Vishal Annapurve <vannapurve@google.com>
> >
> > MAPGPA request from TDX VMs gets split into chunks by KVM using a loop
> > of userspace exits until the complete range is handled.
> >
> > In some cases userspace VMM might decide to break the MAPGPA operation
> > and continue it later. For example: in the case of intrahost migration
> > userspace might decide to continue the MAPGPA operation after the
> > migrration is completed
migration
> > Allow userspace to signal to TDX guests that the MAPGPA operation should
> > be retried the next time the guest is scheduled.
To Xiaoyao's point, changes like this either need new uAPI, or a detailed
explanation in the changelog of why such uAPI isn't deemed necessary.
> > Signed-off-by: Vishal Annapurve <vannapurve@google.com>
> > Co-developed-by: Sagi Shahar <sagis@google.com>
> > Signed-off-by: Sagi Shahar <sagis@google.com>
> > ---
> > arch/x86/kvm/vmx/tdx.c | 8 +++++++-
> > 1 file changed, 7 insertions(+), 1 deletion(-)
> >
> > diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
> > index 2d7a4d52ccfb..3244064b1a04 100644
> > --- a/arch/x86/kvm/vmx/tdx.c
> > +++ b/arch/x86/kvm/vmx/tdx.c
> > @@ -1189,7 +1189,13 @@ static int tdx_complete_vmcall_map_gpa(struct kvm_vcpu *vcpu)
> > struct vcpu_tdx *tdx = to_tdx(vcpu);
> > if (vcpu->run->hypercall.ret) {
> > - tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
> > + if (vcpu->run->hypercall.ret == -EBUSY)
> > + tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
> > + else if (vcpu->run->hypercall.ret == -EINVAL)
> > + tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
> > + else
> > + return -EINVAL;
>
> It's incorrect to return -EINVAL here.
It's not incorrect, just potentially a breaking change.
> The -EINVAL will eventually be
> returned to userspace for the VCPU_RUN ioctl. It certainly breaks userspace.
It _might_ break userspace. It certainly changes KVM's ABI, but if no userspace
actually utilizes the existing ABI, then userspace hasn't been broken.
And unless I'm missing something, QEMU _still_ doesn't set hypercall.ret. E.g.
see this code in __tdx_map_gpa().
/*
* In principle this should have been -KVM_ENOSYS, but userspace (QEMU <=9.2)
* assumed that vcpu->run->hypercall.ret is never changed by KVM and thus that
* it was always zero on KVM_EXIT_HYPERCALL. Since KVM is now overwriting
* vcpu->run->hypercall.ret, ensuring that it is zero to not break QEMU.
*/
tdx->vcpu.run->hypercall.ret = 0;
AFAICT, QEMU kills the VM if anything goes wrong.
So while I initially had the exact same reaction of "this is a breaking change
and needs to be opt-in", we might actually be able to get away with just making
the change (assuming no other VMMs care, or are willing to change themselves).
> So it needs to be
>
> if (vcpu->run->hypercall.ret == -EBUSY)
> tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
> else
> tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
No, because assuming everything except -EBUSY translates to
TDVMCALL_STATUS_INVALID_OPERAND paints KVM back into the same corner its already
in. What I care most about is eliminating KVM's assumption that a non-zero
hypercall.ret means TDVMCALL_STATUS_INVALID_OPERAND.
For the new ABI, I see two options:
1. Translate -errno as done in this patch.
2. Propagate hypercall.ret directly to the TDVMCALL return code, i.e. let
userspace set any return code it wants.
#1 has the downside of needing KVM changes and new uAPI every time a new return
code is supported.
#2 has the downside of preventing KVM from establishing its own ABI around the
return code, and making the return code vendor specific. E.g. if KVM ever wanted
to do something in response to -EBUSY beyond propagating the error to the guest,
then we can't reasonably do that with #2.
Whatever we do, I want to change snp_complete_psc_msr() and snp_complete_one_psc()
in the same patch, so that whatever ABI we establish is common to TDX and SNP.
See also https://lore.kernel.org/all/Zn8YM-s0TRUk-6T-@google.com.
> But I'm not sure if such change breaks the userspace ABI that if needs to be
> opted-in.
^ permalink raw reply
* [PATCH v2 1/1] PCI/IDE: Fix using wrong VF ID for RID range calculation
From: Li Ming @ 2026-01-14 11:14 UTC (permalink / raw)
To: helgaas, dan.j.williams; +Cc: linux-pci, linux-coco, linux-kernel, Li Ming
When allocate a new IDE stream for a PCI device in SR-IOV case, the RID
range of the new IDE stream should cover all VFs of the device. VF ID
range of a PCI device is [0, num_VFs - 1], so should use (num_VFs - 1)
as the last VF's ID.
Fixes: 1e4d2ff3ae45 ("PCI/IDE: Add IDE establishment helpers")
Signed-off-by: Li Ming <ming.li@zohomail.com>
---
v2:
* Make kernel-doc more detailed. (Yilun)
* Fix typos in commit log. (Bjorn)
---
drivers/pci/ide.c | 4 ++--
include/linux/pci-ide.h | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/ide.c b/drivers/pci/ide.c
index f0ef474e1a0d..799caa94ab94 100644
--- a/drivers/pci/ide.c
+++ b/drivers/pci/ide.c
@@ -283,8 +283,8 @@ struct pci_ide *pci_ide_stream_alloc(struct pci_dev *pdev)
/* for SR-IOV case, cover all VFs */
num_vf = pci_num_vf(pdev);
if (num_vf)
- rid_end = PCI_DEVID(pci_iov_virtfn_bus(pdev, num_vf),
- pci_iov_virtfn_devfn(pdev, num_vf));
+ rid_end = PCI_DEVID(pci_iov_virtfn_bus(pdev, num_vf - 1),
+ pci_iov_virtfn_devfn(pdev, num_vf - 1));
else
rid_end = pci_dev_id(pdev);
diff --git a/include/linux/pci-ide.h b/include/linux/pci-ide.h
index 37a1ad9501b0..381a1bf22a95 100644
--- a/include/linux/pci-ide.h
+++ b/include/linux/pci-ide.h
@@ -26,7 +26,7 @@ enum pci_ide_partner_select {
/**
* struct pci_ide_partner - Per port pair Selective IDE Stream settings
* @rid_start: Partner Port Requester ID range start
- * @rid_end: Partner Port Requester ID range end
+ * @rid_end: Partner Port Requester ID range end (inclusive)
* @stream_index: Selective IDE Stream Register Block selection
* @mem_assoc: PCI bus memory address association for targeting peer partner
* @pref_assoc: PCI bus prefetchable memory address association for
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v2 13/21] x86/virt/seamldr: Abort updates if errors occurred midway
From: Xu Yilun @ 2026-01-14 10:40 UTC (permalink / raw)
To: Chao Gao
Cc: linux-coco, linux-kernel, x86, reinette.chatre, ira.weiny,
kai.huang, dan.j.williams, sagis, vannapurve, paulmck,
nik.borisov, Farrah Chen, Kirill A. Shutemov, Dave Hansen,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, H. Peter Anvin
In-Reply-To: <20251001025442.427697-14-chao.gao@intel.com>
On Tue, Sep 30, 2025 at 07:52:57PM -0700, Chao Gao wrote:
> The TDX Module update process has multiple stages, each of which may
> encounter failures.
>
> The current state machine of updates proceeds to the next stage
> regardless of errors. But continuing updates when errors occur midway
> is pointless.
>
> Add support of transitioning directly to the final stage on errors,
> effectively aborting the update and skipping all remaining stages.
>
> Signed-off-by: Chao Gao <chao.gao@intel.com>
> Tested-by: Farrah Chen <farrah.chen@intel.com>
> ---
> arch/x86/virt/vmx/tdx/seamldr.c | 17 +++++++++++++++--
> 1 file changed, 15 insertions(+), 2 deletions(-)
>
> diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
> index b074630d42e3..fca558b90f72 100644
> --- a/arch/x86/virt/vmx/tdx/seamldr.c
> +++ b/arch/x86/virt/vmx/tdx/seamldr.c
> @@ -235,6 +235,7 @@ enum tdp_state {
> static struct {
> enum tdp_state state;
> atomic_t thread_ack;
> + atomic_t failed;
> } tdp_data;
>
> static void set_target_state(enum tdp_state state)
> @@ -249,8 +250,16 @@ static void set_target_state(enum tdp_state state)
> /* Last one to ack a state moves to the next state. */
> static void ack_state(void)
> {
> - if (atomic_dec_and_test(&tdp_data.thread_ack))
> - set_target_state(tdp_data.state + 1);
> + if (atomic_dec_and_test(&tdp_data.thread_ack)) {
> + /*
> + * If an error occurred, abort the update by skipping to
> + * the final state
> + */
> + if (atomic_read(&tdp_data.failed))
> + set_target_state(TDP_DONE);
Could we immediately move to TDP_DONE once the error happens, i.e. no
need to get all ack for current state?
> + else
> + set_target_state(tdp_data.state + 1);
> + }
> }
----8<-----
static void ack_state(void)
{
if (atomic_read(&tdp_data.failed)) {
set_target_state(TDP_DONE);
return;
}
if (atomic_dec_and_test(&tdp_data.thread_ack))
set_target_state(tdp_data.state + 1);
}
^ permalink raw reply
* Re: [PATCH v2 2/2] mm/memory_hotplug: Add support to unaccept memory after hot-remove
From: Kiryl Shutsemau @ 2026-01-14 10:47 UTC (permalink / raw)
To: Pratik R. Sampat
Cc: linux-mm, linux-coco, x86, linux-kernel, tglx, mingo, bp,
dave.hansen, ardb, akpm, david, osalvador, thomas.lendacky,
michael.roth
In-Reply-To: <7283516a-ee5b-4226-ba32-1d9325eb6748@amd.com>
On Tue, Jan 13, 2026 at 12:22:33PM -0600, Pratik R. Sampat wrote:
>
>
> On 1/13/26 11:53 AM, Kiryl Shutsemau wrote:
> > On Tue, Jan 13, 2026 at 11:10:21AM -0600, Pratik R. Sampat wrote:
> >>
> >>
> >> On 1/13/2026 4:28 AM, Kiryl Shutsemau wrote:
> >>> On Mon, Jan 12, 2026 at 02:23:00PM -0600, Pratik R. Sampat wrote:
> >>>> Transition memory to the shared state during a hot-remove operation so
> >>>> that it can be re-used by the hypervisor. This also applies when memory
> >>>> is intended to be hotplugged back in later, as those pages will need to
> >>>> be re-accepted after crossing the trust boundary.
> >>>
> >>> Hm. What happens when we hot-remove memory that was there at the boot
> >>> and there's bitmap space for it?
> >>>
> >>
> >> While hotplug ranges gotten from SRAT don't seem to overlap with the
> >> conventional ranges in the unaccepted table, EFI_MEMORY_HOT_PLUGGABLE
> >> attribute could indicate boot time memory that could be hot-removed. I
> >> could potentially unset the bitmap first, if the bit exists and then
> >> unaccept.
> >>
> >> Similarly, I could also check if the bitmap is large enough to set the
> >> bit before I call arch_accept_memory() (This may not really be needed
> >> though).
> >>
> >>> Also, I'm not sure why it is needed. At least in TDX case, VMM can pull
> >>> the memory from under guest at any time without a warning. Coverting
> >>> memory to shared shouldn't make a difference as along as re-adding the
> >>> same GPA range triggers accept.
> >>>
> >>
> >> That makes sense. The only scenario where we could run into trouble on
> >> SNP platforms is when we redo a qemu device_add after a device_del
> >> without first removing the memory object entirely since same-state
> >> transitions result in guest termination.
> >>
> >> This means we must always follow a device_del with an object_del on
> >> removal. Otherwise, the onus would then be on the VMM to transition
> >> the memory back to shared before re-adding it to the guest.
> >
> > This seems to be one-of-many possible ways of VMM to get guest terminated.
> > DoS is not in something confidential computing aims to prevent.
> >
> >> However, if this flow is not a concern to begin with then I could
> >> probably just drop this patch?
> >
> > Yes, please.
>
> Putting more thought into it, memory unacceptance on remove may be required
> after all at least for SNP platforms.
>
> Consider a scenario:
> * Guest accepts a GPA say G1, mapped to a host physical address H1.
> * We attempt to hot-remove the memory. If the guest does not unaccept the memory
> now then G1 to H1 mapping within the RMP will still exist.
> * Then if the hypervisor later hot-adds the memory to G1, it will be now mapped
> to H3 and this new mapping will be accepted.
>
> This will essentially mean that we have 2 RMP entries: One for H1 and another
> for H3 mapped for G1 which are both validated / accepted which can then be
> swapped at will and compromise integrity.
I don't know much about SEV, but I assume RMP is similar to PAMT in TDX
where TDX module maintains metadata for host physical memory.
What side problems do you for guest here?
I probably miss something, but it seems to be VMM problem, no? I mean if
VMM doesn't update RMP on replacing one HPA to another for the GPA, it
is bug in VMM housekeeping. Guest is not responsible for this.
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply
* Re: [PATCH v2 1/2] mm/memory_hotplug: Add support to accept memory during hot-add
From: David Hildenbrand (Red Hat) @ 2026-01-14 10:30 UTC (permalink / raw)
To: Pratik R. Sampat, linux-mm, linux-coco, x86, linux-kernel
Cc: tglx, mingo, bp, dave.hansen, kas, ardb, akpm, osalvador,
thomas.lendacky, michael.roth
In-Reply-To: <20260112202300.43546-2-prsampat@amd.com>
On 1/12/26 21:22, Pratik R. Sampat wrote:
> Confidential computing guests require memory to be accepted before use.
> The unaccepted memory bitmap maintained by firmware does not track
> hotplugged memory ranges.
>
> Call arch_accept_memory() during the hot-add path to explicitly validate
> and transition the newly added memory to a private state, making it
> usable by the guest.
>
> Signed-off-by: Pratik R. Sampat <prsampat@amd.com>
> ---
> mm/memory_hotplug.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> index a63ec679d861..8cfbf0541430 100644
> --- a/mm/memory_hotplug.c
> +++ b/mm/memory_hotplug.c
> @@ -38,6 +38,7 @@
> #include <linux/node.h>
>
> #include <asm/tlbflush.h>
> +#include <asm/unaccepted_memory.h>
>
> #include "internal.h"
> #include "shuffle.h"
> @@ -1567,6 +1568,9 @@ int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
> if (!strcmp(res->name, "System RAM"))
> firmware_map_add_hotplug(start, start + size, "System RAM");
>
> + if (IS_ENABLED(CONFIG_UNACCEPTED_MEMORY))
> + arch_accept_memory(start, start + size);
> +
> /* device_online() will take the lock when calling online_pages() */
> mem_hotplug_done();
>
As discussed, for things like virtio-mem or the HV-balloon this might be
the wrong thing to do, but I don't expect these mechanisms to be used in
CoCo environments just yet (and doing so would require enabling work for
them).
So I'm fine with this for now.
--
Cheers
David
^ permalink raw reply
* Re: [PATCH] dma-direct: swiotlb: Skip encryption toggles for swiotlb allocations
From: Aneesh Kumar K.V @ 2026-01-14 9:49 UTC (permalink / raw)
To: Robin Murphy, iommu, linux-kernel, linux-coco
Cc: Marek Szyprowski, steven.price, Suzuki K Poulose, Claire Chang
In-Reply-To: <yq5acy3eet1b.fsf@kernel.org>
Aneesh Kumar K.V <aneesh.kumar@kernel.org> writes:
> Robin Murphy <robin.murphy@arm.com> writes:
>
>> On 2026-01-09 2:51 am, Aneesh Kumar K.V wrote:
>>> Robin Murphy <robin.murphy@arm.com> writes:
>>>
>>>> On 2026-01-02 3:54 pm, Aneesh Kumar K.V (Arm) wrote:
>>>>> Swiotlb backing pages are already mapped decrypted via
>>>>> swiotlb_update_mem_attributes(), so dma-direct does not need to call
>>>>> set_memory_decrypted() during allocation or re-encrypt the memory on
>>>>> free.
>>>>>
>>>>> Handle swiotlb-backed buffers explicitly: obtain the DMA address and
>>>>> zero the linear mapping for lowmem pages, and bypass the decrypt/encrypt
>>>>> transitions when allocating/freeing from the swiotlb pool (detected via
>>>>> swiotlb_find_pool()).
>>>>
>>>> swiotlb_update_mem_attributes() only applies to the default SWIOTLB
>>>> buffer, while the dma_direct_alloc_swiotlb() path is only for private
>>>> restricted pools (because the whole point is that restricted DMA devices
>>>> cannot use the regular allocator/default pools). There is no redundancy
>>>> here AFAICS.
>>>>
>>>
>>> But rmem_swiotlb_device_init() is also marking the entire pool decrypted
>>>
>>> set_memory_decrypted((unsigned long)phys_to_virt(rmem->base),
>>> rmem->size >> PAGE_SHIFT);
>>
>> OK, so why doesn't the commit message mention that instead of saying
>> something which fails to justify the patch at all? ;)
>>
>> Furthermore, how much does this actually matter? The "real" restricted
>> DMA use-case is on systems where dma_set_decrypted() is a no-op anyway.
>> I know we used restricted DMA as a hack in the early days of CCA
>> prototyping, but is it intended to actually deploy that as a supported
>> and recommended mechanism now?
>>
>> Note also that the swiotlb_alloc path is essentially an emergency
>> fallback, which doesn't work for all situations anyway - any restricted
>> device that actually needs to make significant coherent allocations (or
>> rather, that firmware cannot assume won't want to do so) should really
>> have a proper coherent pool alongside its restricted one. The expected
>> use-case here is for something like a wifi driver that only needs to
>> allocate one or two small coherent buffers once at startup, then do
>> everything else with streaming DMA.
>>
>
>
> I was aiming to bring more consistency in how swiotlb buffers are
> handled, specifically by treating all swiotlb memory as decrypted
> buffers, which is also how the current code behaves.
>
> If we are concluding that restricted DMA is not used in conjunction with
> memory encryption, then we could, in fact, remove the
> set_memory_decrypted() call from rmem_swiotlb_device_init() and
> instead add failure conditions for force_dma_unencrypted(dev) in
> is_swiotlb_for_alloc(). However, it’s worth noting that the initial
> commit did take the memory encryption feature into account
> (0b84e4f8b793eb4045fd64f6f514165a7974cd16).
>
> Please let me know if you think this needs to be fixed.
>
Something like.
dma-direct: restricted-dma: Do not mark the restricted DMA pool unencrypted
As per commit f4111e39a52a ("swiotlb: Add restricted DMA alloc/free
support"), the restricted-dma-pool is used in conjunction with the
shared-dma-pool. Since allocations from the shared-dma-pool are not
marked unencrypted, skip marking the restricted-dma-pool as unencrypted
as well. We do not expect systems using the restricted-dma-pool to have
memory encryption or to run with confidential computing features enabled.
If a device requires unencrypted access (force_dma_unencrypted(dev)),
the dma-direct allocator will mark the restricted-dma-pool allocation as
unencrypted.
The only disadvantage is that, when running on a CC guest with a
different hypervisor page size, restricted-dma-pool allocation sizes
must now be aligned to the hypervisor page size. This alignment would
not be required if the entire pool were marked unencrypted. However, the
new code enables the use of the restricted-dma-pool for trusted devices.
Previously, because the entire pool was marked unencrypted, trusted
devices were unable to allocate from it.
There is still an open question regarding allocations from the
shared-dma-pool. Currently, they are not marked unencrypted.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
1 file changed, 2 deletions(-)
kernel/dma/swiotlb.c | 2 --
modified kernel/dma/swiotlb.c
@@ -1835,8 +1835,6 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
return -ENOMEM;
}
- set_memory_decrypted((unsigned long)phys_to_virt(rmem->base),
- rmem->size >> PAGE_SHIFT);
swiotlb_init_io_tlb_pool(pool, rmem->base, nslabs,
false, nareas);
mem->force_bounce = true;
^ permalink raw reply
* Re: [PATCH] KVM: TDX: Allow userspace to return errors to guest for MAPGPA
From: Binbin Wu @ 2026-01-14 9:32 UTC (permalink / raw)
To: Sagi Shahar
Cc: Xiaoyao Li, Sean Christopherson, Paolo Bonzini, Dave Hansen,
Kiryl Shutsemau, Rick Edgecombe, Thomas Gleixner, Borislav Petkov,
H. Peter Anvin, x86, kvm, linux-kernel, linux-coco,
Vishal Annapurve
In-Reply-To: <43a0558a-4cca-4d9c-97dc-ffd085186fd9@intel.com>
On 1/14/2026 10:59 AM, Xiaoyao Li wrote:
> On 1/14/2026 8:30 AM, Sagi Shahar wrote:
>> From: Vishal Annapurve <vannapurve@google.com>
>>
>> MAPGPA request from TDX VMs gets split into chunks by KVM using a loop
>> of userspace exits until the complete range is handled.
>>
>> In some cases userspace VMM might decide to break the MAPGPA operation
>> and continue it later. For example: in the case of intrahost migration
>> userspace might decide to continue the MAPGPA operation after the
>> migrration is completed.
>>
>> Allow userspace to signal to TDX guests that the MAPGPA operation should
>> be retried the next time the guest is scheduled.
How does the guest differentiate it from a retry due to pending events?
Will the guest retry immediately after returning back to the guest in this case?
>>
>> Signed-off-by: Vishal Annapurve <vannapurve@google.com>
>> Co-developed-by: Sagi Shahar <sagis@google.com>
>> Signed-off-by: Sagi Shahar <sagis@google.com>
>> ---
>> arch/x86/kvm/vmx/tdx.c | 8 +++++++-
>> 1 file changed, 7 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
>> index 2d7a4d52ccfb..3244064b1a04 100644
>> --- a/arch/x86/kvm/vmx/tdx.c
>> +++ b/arch/x86/kvm/vmx/tdx.c
>> @@ -1189,7 +1189,13 @@ static int tdx_complete_vmcall_map_gpa(struct kvm_vcpu *vcpu)
>> struct vcpu_tdx *tdx = to_tdx(vcpu);
>> if (vcpu->run->hypercall.ret) {
>> - tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
>> + if (vcpu->run->hypercall.ret == -EBUSY)
>> + tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
>> + else if (vcpu->run->hypercall.ret == -EINVAL)
>> + tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
>> + else
>> + return -EINVAL;
>
> It's incorrect to return -EINVAL here. The -EINVAL will eventually be returned to userspace for the VCPU_RUN ioctl. It certainly breaks userspace. So it needs to be
>
> if (vcpu->run->hypercall.ret == -EBUSY)
> tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
> else
> tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND);
>
> But I'm not sure if such change breaks the userspace ABI that if needs to be opted-in.
>
^ permalink raw reply
* Re: [PATCH v2 06/21] x86/virt/seamldr: Retrieve P-SEAMLDR information
From: Duan, Zhenzhong @ 2026-01-14 7:55 UTC (permalink / raw)
To: Chao Gao, linux-coco, linux-kernel, x86, reinette.chatre,
ira.weiny, kai.huang, dan.j.williams, yilun.xu, sagis, vannapurve,
paulmck, nik.borisov
Cc: Farrah Chen, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, Kirill A. Shutemov
In-Reply-To: <20251001025442.427697-7-chao.gao@intel.com>
On 10/1/2025 10:52 AM, Chao Gao wrote:
> P-SEAMLDR returns its information e.g., version and supported features, in
> response to the SEAMLDR.INFO SEAMCALL.
>
> This information is useful for userspace. For example, the admin can decide
> which TDX module versions are compatible with the P-SEAMLDR according to
> the P-SEAMLDR version.
>
> Add and export seamldr_get_info() which retrieves P-SEAMLDR information by
> invoking SEAMLDR.INFO SEAMCALL in preparation for exposing P-SEAMLDR
> version and other necessary information to userspace.
>
> Signed-off-by: Chao Gao <chao.gao@intel.com>
> Tested-by: Farrah Chen <farrah.chen@intel.com>
> ---
> arch/x86/include/asm/seamldr.h | 27 +++++++++++++++++++++++++++
> arch/x86/virt/vmx/tdx/seamldr.c | 17 ++++++++++++++++-
> 2 files changed, 43 insertions(+), 1 deletion(-)
> create mode 100644 arch/x86/include/asm/seamldr.h
>
> diff --git a/arch/x86/include/asm/seamldr.h b/arch/x86/include/asm/seamldr.h
> new file mode 100644
> index 000000000000..d1e9f6e16e8d
> --- /dev/null
> +++ b/arch/x86/include/asm/seamldr.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _ASM_X86_SEAMLDR_H
> +#define _ASM_X86_SEAMLDR_H
> +
> +#include <linux/types.h>
> +
> +struct seamldr_info {
> + u32 version;
> + u32 attributes;
> + u32 vendor_id;
> + u32 build_date;
> + u16 build_num;
> + u16 minor_version;
> + u16 major_version;
> + u16 update_version;
> + u8 reserved0[4];
> + u32 num_remaining_updates;
> + u8 reserved1[224];
> +} __packed;
> +
> +#ifdef CONFIG_INTEL_TDX_MODULE_UPDATE
> +const struct seamldr_info *seamldr_get_info(void);
> +#else
> +static inline const struct seamldr_info *seamldr_get_info(void) { return NULL; }
> +#endif
> +
> +#endif
> diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
> index b99d73f7bb08..08c2e3fe6071 100644
> --- a/arch/x86/virt/vmx/tdx/seamldr.c
> +++ b/arch/x86/virt/vmx/tdx/seamldr.c
> @@ -9,9 +9,16 @@
> #include <linux/irqflags.h>
> #include <linux/types.h>
>
> +#include <asm/seamldr.h>
> +
> #include "seamcall.h"
>
> -static __maybe_unused int seamldr_call(u64 fn, struct tdx_module_args *args)
> +/* P-SEAMLDR SEAMCALL leaf function */
> +#define P_SEAMLDR_INFO 0x8000000000000000
> +
> +static struct seamldr_info seamldr_info __aligned(256);
> +
> +static inline int seamldr_call(u64 fn, struct tdx_module_args *args)
> {
> unsigned long flags;
> u64 vmcs;
> @@ -54,3 +61,11 @@ static __maybe_unused int seamldr_call(u64 fn, struct tdx_module_args *args)
> WARN_ONCE(1, "Failed to save/restore the current VMCS");
> return -EIO;
> }
> +
> +const struct seamldr_info *seamldr_get_info(void)
> +{
> + struct tdx_module_args args = { .rcx = __pa(&seamldr_info) };
> +
> + return seamldr_call(P_SEAMLDR_INFO, &args) ? NULL : &seamldr_info;
> +}
> +EXPORT_SYMBOL_GPL_FOR_MODULES(seamldr_get_info, "tdx-host");
We can trigger seamldr call once and cache the result except seamldr
itself could be updated.
^ permalink raw reply
* Re: [PATCH v2 05/21] x86/virt/seamldr: Introduce a wrapper for P-SEAMLDR SEAMCALLs
From: Duan, Zhenzhong @ 2026-01-14 7:47 UTC (permalink / raw)
To: Chao Gao, linux-coco, linux-kernel, x86, reinette.chatre,
ira.weiny, kai.huang, dan.j.williams, yilun.xu, sagis, vannapurve,
paulmck, nik.borisov
Cc: Farrah Chen, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, Kirill A. Shutemov
In-Reply-To: <20251001025442.427697-6-chao.gao@intel.com>
On 10/1/2025 10:52 AM, Chao Gao wrote:
> Software needs to talk with P-SEAMLDR via P-SEAMLDR SEAMCALLs. So, add a
> wrapper for P-SEAMLDR SEAMCALLs.
>
> Save and restore the current VMCS using VMPTRST and VMPTRLD instructions
> to avoid breaking KVM. Doing so is because P-SEAMLDR SEAMCALLs would
> invalidate the current VMCS as documented in Intel® Trust Domain CPU
> Architectural Extensions (May 2021 edition) Chapter 2.3 [1]:
>
> SEAMRET from the P-SEAMLDR clears the current VMCS structure pointed
> to by the current-VMCS pointer. A VMM that invokes the P-SEAMLDR using
> SEAMCALL must reload the current-VMCS, if required, using the VMPTRLD
> instruction.
>
> Disable interrupts to prevent KVM code from interfering with P-SEAMLDR
> SEAMCALLs. For example, if a vCPU is scheduled before the current VMCS is
> restored, it may encounter an invalid current VMCS, causing its VMX
> instruction to fail. Additionally, if KVM sends IPIs to invalidate a
> current VMCS and the invalidation occurs right after the current VMCS is
> saved, that VMCS will be reloaded after P-SEAMLDR SEAMCALLs, leading to
> unexpected behavior.
>
> NMIs are not a problem, as the only scenario where instructions relying on
> the current-VMCS are used is during guest PMI handling in KVM. This occurs
> immediately after VM exits with IRQ and NMI disabled, ensuring no
> interference with P-SEAMLDR SEAMCALLs.
>
> Signed-off-by: Chao Gao <chao.gao@intel.com>
> Tested-by: Farrah Chen <farrah.chen@intel.com>
> Link: https://cdrdv2.intel.com/v1/dl/getContent/733582 # [1]
> ---
> v2:
> - don't create a new, inferior framework to save/restore VMCS
> - use human-friendly language, just "current VMCS" rather than
> SDM term "current-VMCS pointer"
> - don't mix guard() with goto
> ---
> arch/x86/Kconfig | 10 ++++++
> arch/x86/virt/vmx/tdx/Makefile | 1 +
> arch/x86/virt/vmx/tdx/seamldr.c | 56 +++++++++++++++++++++++++++++++++
> 3 files changed, 67 insertions(+)
> create mode 100644 arch/x86/virt/vmx/tdx/seamldr.c
>
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 58d890fe2100..6b47383d2958 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -1905,6 +1905,16 @@ config INTEL_TDX_HOST
>
> If unsure, say N.
>
> +config INTEL_TDX_MODULE_UPDATE
> + bool "Intel TDX module runtime update"
> + depends on TDX_HOST_SERVICES
> + help
> + This enables the kernel to support TDX module runtime update. This
> + allows the admin to update the TDX module to the same or any newer
> + version without the need to terminate running TDX guests.
> +
> + If unsure, say N.
> +
> config EFI
> bool "EFI runtime service support"
> depends on ACPI
> diff --git a/arch/x86/virt/vmx/tdx/Makefile b/arch/x86/virt/vmx/tdx/Makefile
> index 90da47eb85ee..26aea3531c36 100644
> --- a/arch/x86/virt/vmx/tdx/Makefile
> +++ b/arch/x86/virt/vmx/tdx/Makefile
> @@ -1,2 +1,3 @@
> # SPDX-License-Identifier: GPL-2.0-only
> obj-y += seamcall.o tdx.o
> +obj-$(CONFIG_INTEL_TDX_MODULE_UPDATE) += seamldr.o
Not clear if seamldr will support other features besides TDX module update,
if yes, maybe more general name CONFIG_INTEL_SEAMLDR?
> diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
> new file mode 100644
> index 000000000000..b99d73f7bb08
> --- /dev/null
> +++ b/arch/x86/virt/vmx/tdx/seamldr.c
> @@ -0,0 +1,56 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright(c) 2025 Intel Corporation.
> + *
> + * Intel TDX module runtime update
> + */
> +#define pr_fmt(fmt) "seamldr: " fmt
> +
> +#include <linux/irqflags.h>
> +#include <linux/types.h>
> +
> +#include "seamcall.h"
> +
> +static __maybe_unused int seamldr_call(u64 fn, struct tdx_module_args *args)
> +{
> + unsigned long flags;
> + u64 vmcs;
> + int ret;
> +
> + if (!is_seamldr_call(fn))
> + return -EINVAL;
> +
> + /*
> + * SEAMRET from P-SEAMLDR invalidates the current VMCS. Save/restore
> + * the VMCS across P-SEAMLDR SEAMCALLs to avoid clobbering KVM state.
> + * Disable interrupts as KVM is allowed to do VMREAD/VMWRITE in IRQ
> + * context (but not NMI context).
> + */
> + local_irq_save(flags);
> +
> + asm goto("1: vmptrst %0\n\t"
> + _ASM_EXTABLE(1b, %l[error])
> + : "=m" (vmcs) : : "cc" : error);
> +
> + ret = seamldr_prerr(fn, args);
> +
> + /*
> + * Restore the current VMCS pointer. VMPTSTR "returns" all ones if the
> + * current VMCS is invalid.
> + */
> + if (vmcs != -1ULL) {
> + asm goto("1: vmptrld %0\n\t"
> + "jna %l[error]\n\t"
> + _ASM_EXTABLE(1b, %l[error])
> + : : "m" (vmcs) : "cc" : error);
> + }
> +
> + local_irq_restore(flags);
> + return ret;
> +
> +error:
> + local_irq_restore(flags);
> +
> + WARN_ONCE(1, "Failed to save/restore the current VMCS");
> + return -EIO;
> +}
^ permalink raw reply
* Re: [PATCH v2 04/21] x86/virt/tdx: Prepare to support P-SEAMLDR SEAMCALLs
From: Duan, Zhenzhong @ 2026-01-14 7:33 UTC (permalink / raw)
To: Chao Gao, linux-coco, linux-kernel, x86, reinette.chatre,
ira.weiny, kai.huang, dan.j.williams, yilun.xu, sagis, vannapurve,
paulmck, nik.borisov
Cc: Farrah Chen, Kirill A. Shutemov, Dave Hansen, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, H. Peter Anvin
In-Reply-To: <20251001025442.427697-5-chao.gao@intel.com>
On 10/1/2025 10:52 AM, Chao Gao wrote:
> P-SEAMLDR is another component alongside the TDX module within the
> protected SEAM range. P-SEAMLDR can update the TDX module at runtime.
> Software can talk with P-SEAMLDR via SEAMCALLs with the bit 63 of RAX
> (leaf number) set to 1 (a.k.a P-SEAMLDR SEAMCALLs).
>
> P-SEAMLDR SEAMCALLs differ from SEAMCALLs of the TDX module in terms of
> error codes and the handling of the current VMCS.
>
> In preparation for adding support for P-SEAMLDR SEAMCALLs, do the two
> following changes to SEAMCALL low-level helpers:
>
> 1) Tweak sc_retry() to retry on "lack of entropy" errors reported by
> P-SEAMLDR because it uses a different error code.
>
> 2) Add seamldr_err() to log error messages on P-SEAMLDR SEAMCALL failures.
>
> Signed-off-by: Chao Gao <chao.gao@intel.com>
> Tested-by: Farrah Chen <farrah.chen@intel.com>
> ---
> Add seamldr_prerr() as a macro to be consistent with existing code. If
> maintainers would like to switch these to static inline functions then I
> would be happy to add a new patch to convert existing macros to static
> inline functions and build on that.
>
> v2:
> - use a macro rather than an inline function for seamldr_err() for
> consistency.
> ---
> arch/x86/include/asm/tdx.h | 5 +++++
> arch/x86/virt/vmx/tdx/seamcall.h | 29 ++++++++++++++++++++++++++++-
> 2 files changed, 33 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
> index e872a411a359..7ad026618a23 100644
> --- a/arch/x86/include/asm/tdx.h
> +++ b/arch/x86/include/asm/tdx.h
> @@ -32,6 +32,11 @@
> #define TDX_SUCCESS 0ULL
> #define TDX_RND_NO_ENTROPY 0x8000020300000000ULL
>
> +/* P-SEAMLDR SEAMCALL leaf function error codes */
> +#define SEAMLDR_RND_NO_ENTROPY 0x8000000000030001ULL
> +
> +#define SEAMLDR_SEAMCALL_MASK _BITUL(63)
> +
> #ifndef __ASSEMBLER__
>
> #include <uapi/asm/mce.h>
> diff --git a/arch/x86/virt/vmx/tdx/seamcall.h b/arch/x86/virt/vmx/tdx/seamcall.h
> index 71b6ffddfa40..3f462e58d68e 100644
> --- a/arch/x86/virt/vmx/tdx/seamcall.h
> +++ b/arch/x86/virt/vmx/tdx/seamcall.h
> @@ -14,6 +14,19 @@ u64 __seamcall_saved_ret(u64 fn, struct tdx_module_args *args);
>
> typedef u64 (*sc_func_t)(u64 fn, struct tdx_module_args *args);
>
> +static inline bool is_seamldr_call(u64 fn)
> +{
> + return fn & SEAMLDR_SEAMCALL_MASK;
> +}
> +
> +static inline bool sc_need_retry(u64 fn, u64 error_code)
> +{
> + if (is_seamldr_call(fn))
Do we need to have this check, can TDX module seamcall return
value SEAMLDR_RND_NO_ENTROPY but for different reason?
> + return error_code == SEAMLDR_RND_NO_ENTROPY;
> + else
> + return error_code == TDX_RND_NO_ENTROPY;
> +}
> +
> static __always_inline u64 sc_retry(sc_func_t func, u64 fn,
> struct tdx_module_args *args)
> {
> @@ -22,7 +35,7 @@ static __always_inline u64 sc_retry(sc_func_t func, u64 fn,
>
> do {
> ret = func(fn, args);
> - } while (ret == TDX_RND_NO_ENTROPY && --retry);
> + } while (sc_need_retry(fn, ret) && --retry);
>
> return ret;
> }
> @@ -48,6 +61,17 @@ static inline void seamcall_err_ret(u64 fn, u64 err,
> args->r9, args->r10, args->r11);
> }
>
> +static inline void seamldr_err(u64 fn, u64 err, struct tdx_module_args *args)
> +{
> + /*
> + * Get the actual leaf number. No need to print the bit used to
> + * differentiate between P-SEAMLDR and TDX module as the "P-SEAMLDR"
> + * string in the error message already provides that information.
> + */
> + fn &= ~SEAMLDR_SEAMCALL_MASK;
> + pr_err("P-SEAMLDR (%lld) failed: 0x%016llx\n", fn, err);
> +}
> +
> static __always_inline int sc_retry_prerr(sc_func_t func,
> sc_err_func_t err_func,
> u64 fn, struct tdx_module_args *args)
> @@ -76,4 +100,7 @@ static __always_inline int sc_retry_prerr(sc_func_t func,
> #define seamcall_prerr_ret(__fn, __args) \
> sc_retry_prerr(__seamcall_ret, seamcall_err_ret, (__fn), (__args))
>
> +#define seamldr_prerr(__fn, __args) \
> + sc_retry_prerr(__seamcall, seamldr_err, (__fn), (__args))
> +
> #endif
^ permalink raw reply
* Re: [PATCH v2 03/21] x86/virt/tdx: Move low level SEAMCALL helpers out of <asm/tdx.h>
From: Duan, Zhenzhong @ 2026-01-14 7:27 UTC (permalink / raw)
To: Chao Gao, linux-coco, linux-kernel, x86, reinette.chatre,
ira.weiny, kai.huang, dan.j.williams, yilun.xu, sagis, vannapurve,
paulmck, nik.borisov
Cc: Kirill A. Shutemov, Dave Hansen, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, H. Peter Anvin
In-Reply-To: <20251001025442.427697-4-chao.gao@intel.com>
On 10/1/2025 10:52 AM, Chao Gao wrote:
> From: Kai Huang <kai.huang@intel.com>
>
> TDX host core code implements three seamcall*() helpers to make SEAMCALL
> to the TDX module. Currently, they are implemented in <asm/tdx.h> and
> are exposed to other kernel code which includes <asm/tdx.h>.
>
> However, other than the TDX host core, seamcall*() are not expected to
> be used by other kernel code directly. For instance, for all SEAMCALLs
> that are used by KVM, the TDX host core exports a wrapper function for
> each of them.
>
> Move seamcall*() and related code out of <asm/tdx.h> and make them only
> visible to TDX host core.
>
> Since TDX host core tdx.c is already very heavy, don't put low level
> seamcall*() code there but to a new dedicated "seamcall.h". Also,
> currently tdx.c has seamcall_prerr*() helpers which additionally print
> error message when calling seamcall*() fails. Move them to "seamcall.h"
> as well. In such way all low level SEAMCALL helpers are in a dedicated
> place, which is much more readable.
>
> Signed-off-by: Kai Huang <kai.huang@intel.com>
> Signed-off-by: Chao Gao <chao.gao@intel.com>
Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
> ---
> v2:
> - new
> ---
> arch/x86/include/asm/tdx.h | 24 ----------
> arch/x86/virt/vmx/tdx/seamcall.h | 79 ++++++++++++++++++++++++++++++++
> arch/x86/virt/vmx/tdx/tdx.c | 46 +------------------
> 3 files changed, 80 insertions(+), 69 deletions(-)
> create mode 100644 arch/x86/virt/vmx/tdx/seamcall.h
>
> diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
> index cbea169b5fa0..e872a411a359 100644
> --- a/arch/x86/include/asm/tdx.h
> +++ b/arch/x86/include/asm/tdx.h
> @@ -97,31 +97,7 @@ static inline long tdx_kvm_hypercall(unsigned int nr, unsigned long p1,
> #endif /* CONFIG_INTEL_TDX_GUEST && CONFIG_KVM_GUEST */
>
> #ifdef CONFIG_INTEL_TDX_HOST
> -u64 __seamcall(u64 fn, struct tdx_module_args *args);
> -u64 __seamcall_ret(u64 fn, struct tdx_module_args *args);
> -u64 __seamcall_saved_ret(u64 fn, struct tdx_module_args *args);
> void tdx_init(void);
> -
> -#include <asm/archrandom.h>
> -
> -typedef u64 (*sc_func_t)(u64 fn, struct tdx_module_args *args);
> -
> -static __always_inline u64 sc_retry(sc_func_t func, u64 fn,
> - struct tdx_module_args *args)
> -{
> - int retry = RDRAND_RETRY_LOOPS;
> - u64 ret;
> -
> - do {
> - ret = func(fn, args);
> - } while (ret == TDX_RND_NO_ENTROPY && --retry);
> -
> - return ret;
> -}
> -
> -#define seamcall(_fn, _args) sc_retry(__seamcall, (_fn), (_args))
> -#define seamcall_ret(_fn, _args) sc_retry(__seamcall_ret, (_fn), (_args))
> -#define seamcall_saved_ret(_fn, _args) sc_retry(__seamcall_saved_ret, (_fn), (_args))
> int tdx_enable(void);
> const char *tdx_dump_mce_info(struct mce *m);
> const struct tdx_sys_info *tdx_get_sysinfo(void);
> diff --git a/arch/x86/virt/vmx/tdx/seamcall.h b/arch/x86/virt/vmx/tdx/seamcall.h
> new file mode 100644
> index 000000000000..71b6ffddfa40
> --- /dev/null
> +++ b/arch/x86/virt/vmx/tdx/seamcall.h
> @@ -0,0 +1,79 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (C) 2025 Intel Corporation */
> +#ifndef _X86_VIRT_SEAMCALL_H
> +#define _X86_VIRT_SEAMCALL_H
> +
> +#include <linux/printk.h>
> +#include <linux/types.h>
> +#include <asm/archrandom.h>
> +#include <asm/tdx.h>
> +
> +u64 __seamcall(u64 fn, struct tdx_module_args *args);
> +u64 __seamcall_ret(u64 fn, struct tdx_module_args *args);
> +u64 __seamcall_saved_ret(u64 fn, struct tdx_module_args *args);
> +
> +typedef u64 (*sc_func_t)(u64 fn, struct tdx_module_args *args);
> +
> +static __always_inline u64 sc_retry(sc_func_t func, u64 fn,
> + struct tdx_module_args *args)
> +{
> + int retry = RDRAND_RETRY_LOOPS;
> + u64 ret;
> +
> + do {
> + ret = func(fn, args);
> + } while (ret == TDX_RND_NO_ENTROPY && --retry);
> +
> + return ret;
> +}
> +
> +#define seamcall(_fn, _args) sc_retry(__seamcall, (_fn), (_args))
> +#define seamcall_ret(_fn, _args) sc_retry(__seamcall_ret, (_fn), (_args))
> +#define seamcall_saved_ret(_fn, _args) sc_retry(__seamcall_saved_ret, (_fn), (_args))
> +
> +typedef void (*sc_err_func_t)(u64 fn, u64 err, struct tdx_module_args *args);
> +
> +static inline void seamcall_err(u64 fn, u64 err, struct tdx_module_args *args)
> +{
> + pr_err("SEAMCALL (%llu) failed: %#016llx\n", fn, err);
> +}
> +
> +static inline void seamcall_err_ret(u64 fn, u64 err,
> + struct tdx_module_args *args)
> +{
> + seamcall_err(fn, err, args);
> + pr_err("RCX %#016llx RDX %#016llx R08 %#016llx\n",
> + args->rcx, args->rdx, args->r8);
> + pr_err("R09 %#016llx R10 %#016llx R11 %#016llx\n",
> + args->r9, args->r10, args->r11);
> +}
> +
> +static __always_inline int sc_retry_prerr(sc_func_t func,
> + sc_err_func_t err_func,
> + u64 fn, struct tdx_module_args *args)
> +{
> + u64 sret = sc_retry(func, fn, args);
> +
> + if (sret == TDX_SUCCESS)
> + return 0;
> +
> + if (sret == TDX_SEAMCALL_VMFAILINVALID)
> + return -ENODEV;
> +
> + if (sret == TDX_SEAMCALL_GP)
> + return -EOPNOTSUPP;
> +
> + if (sret == TDX_SEAMCALL_UD)
> + return -EACCES;
> +
> + err_func(fn, sret, args);
> + return -EIO;
> +}
> +
> +#define seamcall_prerr(__fn, __args) \
> + sc_retry_prerr(__seamcall, seamcall_err, (__fn), (__args))
> +
> +#define seamcall_prerr_ret(__fn, __args) \
> + sc_retry_prerr(__seamcall_ret, seamcall_err_ret, (__fn), (__args))
> +
> +#endif
> diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
> index f429a5fdced2..b367bb1d94ed 100644
> --- a/arch/x86/virt/vmx/tdx/tdx.c
> +++ b/arch/x86/virt/vmx/tdx/tdx.c
> @@ -38,6 +38,7 @@
> #include <asm/cpu_device_id.h>
> #include <asm/processor.h>
> #include <asm/mce.h>
> +#include "seamcall.h"
> #include "tdx.h"
>
> static u32 tdx_global_keyid __ro_after_init;
> @@ -58,51 +59,6 @@ static LIST_HEAD(tdx_memlist);
>
> static struct tdx_sys_info tdx_sysinfo;
>
> -typedef void (*sc_err_func_t)(u64 fn, u64 err, struct tdx_module_args *args);
> -
> -static inline void seamcall_err(u64 fn, u64 err, struct tdx_module_args *args)
> -{
> - pr_err("SEAMCALL (%llu) failed: %#016llx\n", fn, err);
> -}
> -
> -static inline void seamcall_err_ret(u64 fn, u64 err,
> - struct tdx_module_args *args)
> -{
> - seamcall_err(fn, err, args);
> - pr_err("RCX %#016llx RDX %#016llx R08 %#016llx\n",
> - args->rcx, args->rdx, args->r8);
> - pr_err("R09 %#016llx R10 %#016llx R11 %#016llx\n",
> - args->r9, args->r10, args->r11);
> -}
> -
> -static __always_inline int sc_retry_prerr(sc_func_t func,
> - sc_err_func_t err_func,
> - u64 fn, struct tdx_module_args *args)
> -{
> - u64 sret = sc_retry(func, fn, args);
> -
> - if (sret == TDX_SUCCESS)
> - return 0;
> -
> - if (sret == TDX_SEAMCALL_VMFAILINVALID)
> - return -ENODEV;
> -
> - if (sret == TDX_SEAMCALL_GP)
> - return -EOPNOTSUPP;
> -
> - if (sret == TDX_SEAMCALL_UD)
> - return -EACCES;
> -
> - err_func(fn, sret, args);
> - return -EIO;
> -}
> -
> -#define seamcall_prerr(__fn, __args) \
> - sc_retry_prerr(__seamcall, seamcall_err, (__fn), (__args))
> -
> -#define seamcall_prerr_ret(__fn, __args) \
> - sc_retry_prerr(__seamcall_ret, seamcall_err_ret, (__fn), (__args))
> -
> /*
> * Do the module global initialization once and return its result.
> * It can be done on any cpu. It's always called with interrupts
^ permalink raw reply
* Re: [PATCH v2 01/21] x86/virt/tdx: Print SEAMCALL leaf numbers in decimal
From: Duan, Zhenzhong @ 2026-01-14 7:13 UTC (permalink / raw)
To: Chao Gao, linux-coco, linux-kernel, x86, reinette.chatre,
ira.weiny, kai.huang, dan.j.williams, yilun.xu, sagis, vannapurve,
paulmck, nik.borisov
Cc: Kirill A. Shutemov, Farrah Chen, Kirill A. Shutemov, Dave Hansen,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, H. Peter Anvin
In-Reply-To: <20251001025442.427697-2-chao.gao@intel.com>
On 10/1/2025 10:52 AM, Chao Gao wrote:
> Both TDX spec and kernel defines SEAMCALL leaf numbers as decimal. Printing
> them in hex makes no sense. Correct it.
>
> Suggested-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
> Signed-off-by: Chao Gao <chao.gao@intel.com>
> Tested-by: Farrah Chen <farrah.chen@intel.com>
> Reviewed-by: Kai Huang <kai.huang@intel.com>
Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
> ---
> v2:
> - print leaf numbers with %llu
> ---
> arch/x86/virt/vmx/tdx/tdx.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
> index ada2fd4c2d54..e406edd28687 100644
> --- a/arch/x86/virt/vmx/tdx/tdx.c
> +++ b/arch/x86/virt/vmx/tdx/tdx.c
> @@ -62,7 +62,7 @@ typedef void (*sc_err_func_t)(u64 fn, u64 err, struct tdx_module_args *args);
>
> static inline void seamcall_err(u64 fn, u64 err, struct tdx_module_args *args)
> {
> - pr_err("SEAMCALL (0x%016llx) failed: 0x%016llx\n", fn, err);
> + pr_err("SEAMCALL (%llu) failed: 0x%016llx\n", fn, err);
> }
>
> static inline void seamcall_err_ret(u64 fn, u64 err,
^ permalink raw reply
* Re: [PATCH v2 11/21] x86/virt/seamldr: Allocate and populate a module update request
From: Xu Yilun @ 2026-01-14 6:45 UTC (permalink / raw)
To: Chao Gao
Cc: linux-coco, linux-kernel, x86, reinette.chatre, ira.weiny,
kai.huang, dan.j.williams, sagis, vannapurve, paulmck,
nik.borisov, Farrah Chen, Kirill A. Shutemov, Dave Hansen,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, H. Peter Anvin
In-Reply-To: <20251001025442.427697-12-chao.gao@intel.com>
> +/* Allocate and populate a seamldr_params */
> +static struct seamldr_params *alloc_seamldr_params(const void *module, int module_size,
> + const void *sig, int sig_size)
> +{
> + struct seamldr_params *params;
> + const u8 *ptr;
> + int i;
> +
> + BUILD_BUG_ON(sizeof(struct seamldr_params) != SZ_4K);
> + if (module_size > SEAMLDR_MAX_NR_MODULE_4KB_PAGES * SZ_4K)
> + return ERR_PTR(-EINVAL);
> +
> + if (!IS_ALIGNED(module_size, SZ_4K) || !IS_ALIGNED(sig_size, SZ_4K) ||
> + !IS_ALIGNED((unsigned long)module, SZ_4K) ||
> + !IS_ALIGNED((unsigned long)sig, SZ_4K))
> + return ERR_PTR(-EINVAL);
> +
> + /* seamldr_params accepts one 4KB-page for sigstruct */
> + if (sig_size != SZ_4K)
Why we check both IS_ALIGNED(sig_size, SZ_4K) and sig_size != SZ_4K, I
assume the former is redundant.
> + return ERR_PTR(-EINVAL);
> +
> + params = (struct seamldr_params *)get_zeroed_page(GFP_KERNEL);
> + if (!params)
> + return ERR_PTR(-ENOMEM);
> +
> + params->scenario = SEAMLDR_SCENARIO_UPDATE;
> + params->sigstruct_pa = (vmalloc_to_pfn(sig) << PAGE_SHIFT) +
This void * buffer comes from FW_UPLOAD callback, which is a kvmalloc
buffer, so we do vmalloc_to_pfn() here. But that knowledge resides in
FW_UPLOAD driver context, the kAPI entry, seamldr_install_module()
doesn't say that. So could we add the kernel-doc to specify this
"const u8 *data" at ...
... here
> int seamldr_install_module(const u8 *data, u32 size)
> {
> const struct seamldr_info *info = seamldr_get_info();
> @@ -82,6 +232,11 @@ int seamldr_install_module(const u8 *data, u32 size)
> if (!info->num_remaining_updates)
> return -ENOSPC;
>
> + struct seamldr_params *params __free(free_seamldr_params) =
> + init_seamldr_params(data, size);
> + if (IS_ERR(params))
> + return PTR_ERR(params);
> +
> guard(cpus_read_lock)();
> if (!cpumask_equal(cpu_online_mask, cpu_present_mask)) {
> pr_err("Cannot update TDX module if any CPU is offline\n");
> --
> 2.47.3
>
^ permalink raw reply
* Re: [PATCH v2 08/21] coco/tdx-host: Implement FW_UPLOAD sysfs ABI for TDX Module updates
From: Xu Yilun @ 2026-01-14 3:08 UTC (permalink / raw)
To: Chao Gao
Cc: linux-coco, linux-kernel, x86, reinette.chatre, ira.weiny,
kai.huang, dan.j.williams, sagis, vannapurve, paulmck,
nik.borisov, Farrah Chen, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H. Peter Anvin, Kirill A. Shutemov
In-Reply-To: <20251001025442.427697-9-chao.gao@intel.com>
On Tue, Sep 30, 2025 at 07:52:52PM -0700, Chao Gao wrote:
> The firmware upload framework provides a standard mechanism for firmware
> updates by allowing device drivers to expose sysfs interfaces for
> user-initiated updates.
>
> Register with this framework to expose sysfs interfaces for TDX Module
> updates and implement operations to process data blobs supplied by
> userspace.
>
> Note that:
> 1. P-SEAMLDR processes the entire update at once rather than
> chunk-by-chunk, so .write() is called only once per update; so the
> offset should be always 0.
> 2. TDX Module Updates complete synchronously within .write(), meaning
> .poll_complete() is only called after successful updates and therefore
> always returns success
>
> Why fw_upload instead of request_firmware()?
> ============================================
> The explicit file selection capabilities of fw_upload is preferred over
> the implicit file selection of request_firmware() for the following
> reasons:
>
> a. Intel distributes all versions of the TDX Module, allowing admins to
> load any version rather than always defaulting to the latest. This
> flexibility is necessary because future extensions may require reverting to
> a previous version to clear fatal errors.
>
> b. Some module version series are platform-specific. For example, the 1.5.x
> series is for certain platform generations, while the 2.0.x series is
> intended for others.
>
> c. The update policy for TDX Module updates is non-linear at times. The
> latest TDX Module may not be compatible. For example, TDX Module 1.5.x
> may be updated to 1.5.y but not to 1.5.y+1. This policy is documented
> separately in a file released along with each TDX Module release.
>
> So, the default policy of "request_firmware()" of "always load latest", is
> not suitable for TDX. Userspace needs to deploy a more sophisticated policy
> check (e.g., latest may not be compatible), and there is potential
> operator choice to consider.
>
> Just have userspace pick rather than add kernel mechanism to change the
> default policy of request_firmware().
>
> Signed-off-by: Chao Gao <chao.gao@intel.com>
> Tested-by: Farrah Chen <farrah.chen@intel.com>
> ---
> arch/x86/Kconfig | 2 +
> arch/x86/include/asm/seamldr.h | 2 +
> arch/x86/include/asm/tdx.h | 5 ++
> arch/x86/virt/vmx/tdx/seamldr.c | 7 ++
> drivers/virt/coco/tdx-host/tdx-host.c | 122 +++++++++++++++++++++++++-
> 5 files changed, 137 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 6b47383d2958..2bf4bb3dfe71 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -1908,6 +1908,8 @@ config INTEL_TDX_HOST
> config INTEL_TDX_MODULE_UPDATE
> bool "Intel TDX module runtime update"
> depends on TDX_HOST_SERVICES
> + select FW_LOADER
> + select FW_UPLOAD
> help
> This enables the kernel to support TDX module runtime update. This
> allows the admin to update the TDX module to the same or any newer
> diff --git a/arch/x86/include/asm/seamldr.h b/arch/x86/include/asm/seamldr.h
> index d1e9f6e16e8d..692bde5e9bb4 100644
> --- a/arch/x86/include/asm/seamldr.h
> +++ b/arch/x86/include/asm/seamldr.h
> @@ -20,8 +20,10 @@ struct seamldr_info {
>
> #ifdef CONFIG_INTEL_TDX_MODULE_UPDATE
> const struct seamldr_info *seamldr_get_info(void);
> +int seamldr_install_module(const u8 *data, u32 size);
> #else
> static inline const struct seamldr_info *seamldr_get_info(void) { return NULL; }
> +static inline int seamldr_install_module(const u8 *data, u32 size) { return -EOPNOTSUPP; }
> #endif
>
> #endif
> diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
> index 7ad026618a23..2422904079a3 100644
> --- a/arch/x86/include/asm/tdx.h
> +++ b/arch/x86/include/asm/tdx.h
> @@ -107,6 +107,11 @@ int tdx_enable(void);
> const char *tdx_dump_mce_info(struct mce *m);
> const struct tdx_sys_info *tdx_get_sysinfo(void);
>
> +static inline bool tdx_supports_runtime_update(const struct tdx_sys_info *sysinfo)
> +{
> + return false; /* To be enabled when kernel is ready */
> +}
> +
> int tdx_guest_keyid_alloc(void);
> u32 tdx_get_nr_guest_keyids(void);
> void tdx_guest_keyid_free(unsigned int keyid);
> diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
> index 08c2e3fe6071..69c059194c61 100644
> --- a/arch/x86/virt/vmx/tdx/seamldr.c
> +++ b/arch/x86/virt/vmx/tdx/seamldr.c
> @@ -69,3 +69,10 @@ const struct seamldr_info *seamldr_get_info(void)
> return seamldr_call(P_SEAMLDR_INFO, &args) ? NULL : &seamldr_info;
> }
> EXPORT_SYMBOL_GPL_FOR_MODULES(seamldr_get_info, "tdx-host");
> +
> +int seamldr_install_module(const u8 *data, u32 size)
> +{
> + /* TODO: Update TDX Module here */
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL_FOR_MODULES(seamldr_install_module, "tdx-host");
> diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c
> index 42570c5b221b..418e90797689 100644
> --- a/drivers/virt/coco/tdx-host/tdx-host.c
> +++ b/drivers/virt/coco/tdx-host/tdx-host.c
> @@ -10,6 +10,7 @@
> #include <linux/mod_devicetable.h>
> #include <linux/sysfs.h>
> #include <linux/device/faux.h>
> +#include <linux/firmware.h>
> #include <asm/cpu_device_id.h>
> #include <asm/seamldr.h>
> #include <asm/tdx.h>
> @@ -21,6 +22,13 @@ static const struct x86_cpu_id tdx_host_ids[] = {
> };
> MODULE_DEVICE_TABLE(x86cpu, tdx_host_ids);
>
> +struct tdx_fw_upload_status {
> + bool cancel_request;
> +};
> +
> +struct fw_upload *tdx_fwl;
> +static struct tdx_fw_upload_status tdx_fw_upload_status;
> +
> static struct faux_device *fdev;
Make the fdev declaration right before tdx_host_init(), try best to keep
the update stuff in one bluk.
[...]
> +static int seamldr_init(struct device *dev)
> +{
> + const struct seamldr_info *seamldr_info = seamldr_get_info();
> + const struct tdx_sys_info *tdx_sysinfo = tdx_get_sysinfo();
> + int ret;
> +
> + if (!tdx_sysinfo || !seamldr_info)
> + return -ENXIO;
> +
> + if (!tdx_supports_runtime_update(tdx_sysinfo)) {
> + pr_info("Current TDX Module cannot be updated. Consider BIOS updates\n");
> + return -EOPNOTSUPP;
I don't think we fail out the whole tdx-host here. We should skip the
optional feature if it is not supported to allow other features work.
E.g. the TDX Module version, the P-SEAMLOAD version, TDX Connect.
> + }
> +
> + if (!seamldr_info->num_remaining_updates) {
> + pr_info("P-SEAMLDR doesn't support TDX Module updates\n");
> + return -EOPNOTSUPP;
> + }
Ditto. And keeping num_remaining_updates sysfs node visible and returning 0
is valuable, it clearly tells why update is impossible and aligns with
the situation when the user keeps on updating and exhausts the available
updates.
> +
> + tdx_fwl = firmware_upload_register(THIS_MODULE, dev, "seamldr_upload",
> + &tdx_fw_ops, &tdx_fw_upload_status);
> + ret = PTR_ERR_OR_ZERO(tdx_fwl);
> + if (ret)
> + pr_err("failed to register module uploader %d\n", ret);
> +
> + return ret;
> +}
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox